Android蓝牙电子钟应用程序通过蓝牙设备发送消息给多功能数字电子钟,实现更新电子钟时间、设定电子钟监控时间、设定电子钟闹钟时间和调整电子钟时间误差等功能。
该应用程序的UI主要基于http://blog.csdn.net/kangweijian/article/details/43404801。本章主要讲解蓝牙设备的应用,包括蓝牙设备获取、启用、搜索、配对、连接、发送、接收等。
最终效果图:前四个效果图可通过左右滑动或者点击顶层四个控件切换得,点击连接按钮得到查找蓝牙设备的效果图
1、 主Activity(MainActivity.java)
a) 设定MainActivity的视图,该视图顶层4个Button,底层2个Button,中间一个fragment碎片,设定fragment碎片为ViewPager视图
b) BluetoothAdapter.getDefaultAdapter()获取本地蓝牙设备,如果本地蓝牙设备为空,则提示信息,并结束程序
c) .enable(),打开本地蓝牙适配器。
d) 连接按钮实例化,并设置点击监听器。该监听器函数中,如果.isEnabled()本地蓝牙未启用,则提示信息,本返回。如果BluetoothSocket为null,则使用startActivityForResult(..)跳转到蓝牙设备查找Activity,否则说明Socket已连接,此时则要断开Socket连接。
e) 退出按钮实例化,并设置点击监听器。一旦点击即退出程序。
f) onActivityResult(…)函数中,一旦DeviceListActivity返回结果为Activity.RESULT_OK,则由Intent.getExtras().getString()函数得到DeviceListActivity传递过来的远程蓝牙设备地址,由BluetoothDevice.getRemoteDevice(address)得到远程蓝牙设备,由BluetoothDevice.createRfcommSocketToServiceRecord(UUID.fromString(UUID))函数得到BluetoothSocket,最后由BluetoothSocket connect()函数连接本地蓝牙设备和远程蓝牙设备。
g) 最后记得在Activity生命周期的onDestroy()中,.close()关闭BluetoothSocket连接,.disable()关闭BluetoothAdapter蓝牙服务。已保证在程序退出后,释放底层设备句柄。
h) 补充说明:使用startActivityForResult(..)跳转Activity的作用是MainActivity需要在DeviceListActivity中执行任务,DeviceListActivity执行任务完毕后返回结果给MainActivity,MainActivity在onActivityResult(…)中得到所需的结果。onActivityResult(…)中有三个参数,第一个参数是请求代码requestCode,是由MainActivity发出,第二个参数是结束代码resultCode,是由DeviceListActivity返回,最后一个参数是Intent。故在onActivityResult(…)函数中,首先判断requestCode,即判断MainActivity请求哪个Activity执行任务,其次再判断resultCode,即判断那个Activity执行任务后返回了何种结果。
- package com.example.timerbluetooth2.Activity;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
- import java.util.UUID;
- import android.app.Activity;
- import android.bluetooth.BluetoothAdapter;
- import android.bluetooth.BluetoothDevice;
- import android.bluetooth.BluetoothSocket;
- import android.content.Intent;
- import android.os.Bundle;
- import android.os.Handler;
- import android.os.Message;
- import android.support.v4.app.FragmentActivity;
- import android.support.v4.app.FragmentManager;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.widget.Button;
- import android.widget.Toast;
- import com.example.timerbluetooth2.R;
- import com.example.timerbluetooth2.Fragment.ViewPagerFragment;
- public class MainActivity extends FragmentActivity {
- private final static int REQUEST_CONNECT_DEVICE = 1; //宏定义查询设备句柄
- private final static String MY_UUID = "00001101-0000-1000-8000-00805F9B34FB"; //SPP服务UUID号
- private BluetoothAdapter _bluetooth = BluetoothAdapter.getDefaultAdapter(); //获取本地蓝牙适配器,即蓝牙设备
- private InputStream is; //输入流,用来接收蓝牙数据
- private OutputStream os; //输出流,用来发送蓝牙数据
- BluetoothDevice _device = null; //蓝牙设备
- public BluetoothSocket _socket = null; //蓝牙通信socket
- private Button connectButton,quitButton;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onCreate(savedInstanceState);
- //设置MainActivity视图
- setContentView(R.layout.activity_main);
- //设置fragment碎片为ViewPager视图
- FragmentManager fm = getSupportFragmentManager();
- android.support.v4.app.Fragment fragment= fm.findFragmentById(R.id.fragmentContainer);
- if (fragment==null) {
- fragment = new ViewPagerFragment();
- fm.beginTransaction().add(R.id.fragmentContainer, fragment).commit();
- }
- //如果本地蓝牙设备为空,提示信息,结束程序
- if (_bluetooth == null){
- Toast.makeText(this, "无法打开手机蓝牙,请确认手机是否有蓝牙功能!", Toast.LENGTH_LONG).show();
- finish();
- return;
- }
- //设置设备可以被搜索
- new Thread(){
- public void run(){
- if(_bluetooth.isEnabled()==false){
- _bluetooth.enable();
- }
- }
- }.start();
- connectButton = (Button) findViewById(R.id.ConnectButton);
- quitButton = (Button) findViewById(R.id.QuitButton);
- connectButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View arg0) {
- // TODO Auto-generated method stub
- //如果蓝牙服务不可用则提示
- if(_bluetooth.isEnabled()==false){
- Toast.makeText(MainActivity.this, " 打开蓝牙中...", Toast.LENGTH_LONG).show();
- return;
- }
- //如未连接设备则打开DeviceListActivity进行设备搜索
- if(_socket==null){
- Intent serverIntent = new Intent(MainActivity.this, DeviceListActivity.class); //跳转程序设置
- startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE); //设置返回宏定义
- }
- else{
- //关闭连接socket
- try{
- is.close();
- _socket.close();
- _socket = null;
- connectButton.setText("连接");
- }catch(IOException e){}
- }
- }
- });
- quitButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View arg0) {
- // TODO Auto-generated method stub
- finish();
- }
- });
- }
- //接收活动结果,响应startActivityForResult()
- public void onActivityResult(int requestCode, int resultCode, Intent data) {
- switch(requestCode){
- // 响应返回结果//连接结果,由DeviceListActivity设置返回
- case REQUEST_CONNECT_DEVICE:
- //连接成功,由DeviceListActivity设置返回
- if (resultCode == Activity.RESULT_OK) {
- // MAC地址,由DeviceListActivity设置返回
- String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
- // 得到远程蓝牙设备句柄
- _device = _bluetooth.getRemoteDevice(address);
- // 用服务号得到socket
- try{
- _socket = _device.createRfcommSocketToServiceRecord(UUID.fromString(MY_UUID));
- }catch(IOException e){
- Toast.makeText(this, "连接失败!", Toast.LENGTH_SHORT).show();
- }
- //连接socket
- try{
- _socket.connect();
- Toast.makeText(this, "连接"+_device.getName()+"成功!", Toast.LENGTH_SHORT).show();
- connectButton.setText("断开");
- }catch(IOException e){
- try{
- Toast.makeText(this, "连接失败!", Toast.LENGTH_SHORT).show();
- _socket.close();
- _socket = null;
- }catch(IOException ee){
- Toast.makeText(this, "连接失败!", Toast.LENGTH_SHORT).show();
- }
- return;
- }
- // //打开接收线程
- // try{
- // is = _socket.getInputStream(); //得到蓝牙数据输入流
- // ReadThread.start();
- // }catch(IOException e){
- // Toast.makeText(this, "接收数据失败!", Toast.LENGTH_SHORT).show();
- // return;
- // }
- }
- break;
- default:break;
- }
- }
- //接收数据线程
- // Thread ReadThread=new Thread(){
- //
- // public void run(){
- //
- // try {
- //
- // while (!Thread.interrupted()) {
- //
- // // the number of bytes that can be read from the input stream without blocking.
- // // 返回输入流中可被读取的字节数
- // int count = is.available();
- // if (count <= 0) {
- // Thread.sleep(10);
- // continue;//等待有效数据读取
- // }
- //
- // // create buffer
- // byte[] buf = new byte[count];
- //
- // // read data into buffer
- // is.read(buf);
- //
- //
- //
- //
- // }
- // } catch (IOException e) {
- // // TODO Auto-generated catch block
- // e.printStackTrace();
- // } catch (NullPointerException e) {
- // // TODO Auto-generated catch block
- // e.printStackTrace();
- // } catch (Exception e) {
- // // TODO: handle exception
- // }
- // }
- // };
- //关闭程序掉用处理部分
- public void onDestroy(){
- super.onDestroy();
- //如果Socket不为空,则关闭连接Socket
- if(_socket!=null)
- try{
- _socket.close();
- }catch(IOException e){}
- //关闭蓝牙服务
- _bluetooth.disable();
- }
- }
2、 蓝牙设备查找Activity(DeviceListActivity.java)
a) 设定DeviceListActivity的视图,使用LinearLayout布局,从上到下,已配对设备的TextView和ListView、未配对设备的TextView和ListView,最后一个查找设备Button和一个取消Button。除了两个Button,其他的可见性都为隐藏。
b) requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS)设定Activity应用程序窗口显示特性为:不确定的进度
c) 设定startActivityForResult的默认返回值为Activity.RESULT_CANCELED
d) 扫描按钮实例化,并设置点击监听器。该监听器函数中,设置按钮可见性为隐藏,并开始服务和设备的查找。首先设置在Activity窗口的标题栏显示进度条的效果,设置标题栏的标题为”查找设备中”,以及设置未配对设备的可见性为可见。BluetoothAdapter.getDefaultAdapter()获取本地蓝牙设备,然后由.startDiscovery()函数开始对远程设备进行查找。
e) 取消按钮实例化,并设置点击监听器。一旦点击即退出程序。
f) 已配对设备适配器和已配对设备视图实例化,已配对视图添加已配对适配器,并设置点击监听器OnItemClickListener()。该监听器函数中,首先由BluetoothAdapter调用.cancelDiscovery()函数取消当前的设备发现查找进程。然后由OnItemClickListener()的第二个参数((TextView)View).getText().toString()得到远程蓝牙设备地址字符串,如果该地址等于NO_FOUND_DEVICE "没有找到新设备",则提示消息,并返回。如果不是,则取出该String.substring(s.length() - 17)字符串的最后17个字符,返回程序,设定startActivityForResult的默认返回值为Activity.RESULT_OK。
g) 未配对设备适配器和未配对设备视图同上。
h) 注册查找到设备action的广播接收器IntentFilter filter = newIntentFilter(BluetoothDevice.ACTION_FOUND); this.registerReceiver(mReceiver,filter);注册查找结束action的广播接收器filter = new IntentFilter(BluetoothAdapter. ACTION_DISCOVERY_FINISHED);this.registerReceiver(mReceiver, filter);
i) 广播接收器中,监听查找到设备和查找结束action。。。
j) 最后记得在Activity生命周期的onDestroy()中,BluetoothAdapter.cancelDiscovery()取消当前的设备发现查找进程。注销action接收器 this.unregisterReceiver(mReceiver);
- package com.example.timerbluetooth2.Activity;
- import com.example.timerbluetooth2.*;
- import android.app.Activity;
- import android.bluetooth.BluetoothAdapter;
- import android.bluetooth.BluetoothDevice;
- import android.content.BroadcastReceiver;
- import android.content.Context;
- import android.content.Intent;
- import android.content.IntentFilter;
- import android.os.Bundle;
- import android.util.Log;
- import android.view.View;
- import android.view.Window;
- import android.view.View.OnClickListener;
- import android.widget.AdapterView;
- import android.widget.ArrayAdapter;
- import android.widget.Button;
- import android.widget.ListView;
- import android.widget.TextView;
- import android.widget.AdapterView.OnItemClickListener;
- import android.widget.Toast;
- public class DeviceListActivity extends Activity {
- // 返回时数据标签
- public static String EXTRA_DEVICE_ADDRESS = "设备地址";
- public final static String NO_FOUND_DEVICE = "没有找到新设备";
- // 本地的蓝牙适配器设备,BluetoothAdapter类让用户能执行基本的蓝牙任务
- private BluetoothAdapter mBtAdapter;
- // 数组适配器,存入String集合,代表已配对和未配对的蓝牙设备的地址
- private ArrayAdapter<String> mPairedDevicesArrayAdapter;
- private ArrayAdapter<String> mNewDevicesArrayAdapter;
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- // Android应用程序的窗体显示,设置其特性为:不确定的进度
- requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- setContentView(R.layout.device_list);
- // 设定startActivityForResult的默认返回值为取消
- setResult(Activity.RESULT_CANCELED);
- // 扫描按钮实例化
- Button scanButton = (Button) findViewById(R.id.button_scan);
- // 设置扫描按钮点击监听器
- scanButton.setOnClickListener(new OnClickListener() {
- public void onClick(View v) {
- //开始服务和设备查找
- doDiscovery();
- //设置按钮可见性为隐藏
- v.setVisibility(View.GONE);
- }
- });
- // 取消按钮实例化
- Button cancelButton = (Button) findViewById(R.id.button_cancel);
- // 设置取消按钮点击监听器
- cancelButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View arg0) {
- // TODO Auto-generated method stub
- finish();
- }
- });
- // 已配对设备适配器实例化
- mPairedDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
- // 新的未配对过的设备适配器实例化
- mNewDevicesArrayAdapter = new ArrayAdapter<String>(this, R.layout.device_name);
- // 已配对视图实例化
- ListView pairedListView = (ListView) findViewById(R.id.paired_devices);
- // 已配对视图添加适配器
- pairedListView.setAdapter(mPairedDevicesArrayAdapter);
- // 已配对视图设置点击监听器
- pairedListView.setOnItemClickListener(mDeviceClickListener);
- // 新的未配对过的视图实例化
- ListView newDevicesListView = (ListView) findViewById(R.id.new_devices);
- // 新的未配对过的视图添加适配器
- newDevicesListView.setAdapter(mNewDevicesArrayAdapter);
- // 新的未配对过的视图设置点击监听器
- newDevicesListView.setOnItemClickListener(mDeviceClickListener);
- // 注册接收查找到设备action接收器
- IntentFilter filter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
- this.registerReceiver(mReceiver, filter);
- // 注册查找结束action接收器
- filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
- this.registerReceiver(mReceiver, filter);
- // 获取默认本地蓝牙适配器的操作权限
- mBtAdapter = BluetoothAdapter.getDefaultAdapter();
- }
- /***************************************************************************
- *
- * Activity销毁时
- *
- */
- @Override
- protected void onDestroy() {
- super.onDestroy();
- // 关闭服务查找
- if (mBtAdapter != null) {
- mBtAdapter.cancelDiscovery();
- }
- // 注销action接收器
- this.unregisterReceiver(mReceiver);
- }
- /****************************************************************************
- *
- * 开始服务和设备查找
- *
- */
- private void doDiscovery() {
- // 设置在Activity的窗口的标题栏显示进度条的效果
- setProgressBarIndeterminateVisibility(true);
- setTitle("查找设备中...");
- // 设定其它设备(未配对设备)列表的能见度为:可见的
- findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE);
- // 如果当前蓝牙适配器正处于设备发现查找进程中,则返回真
- if (mBtAdapter.isDiscovering()) {
- // 取消当前的设备发现查找进程
- mBtAdapter.cancelDiscovery();
- }
- // 开始对远程设备进行查找的进程
- mBtAdapter.startDiscovery();
- }
- /*****************************************************************************
- *
- * 视图点击选择设备连接函数
- *
- */
- private OnItemClickListener mDeviceClickListener = new OnItemClickListener() {
- public void onItemClick(AdapterView<?> av, View v, int arg2, long arg3) {
- // 取消当前的设备发现查找进程
- mBtAdapter.cancelDiscovery();
- // 得到mac地址
- String info = ((TextView) v).getText().toString();
- if (info.equals(NO_FOUND_DEVICE)) {
- Toast.makeText(DeviceListActivity.this, "没有找到蓝牙设备,无法连接", Toast.LENGTH_LONG).show();
- return ;
- }
- String address = info.substring(info.length() - 17);
- // 设置返回数据
- Intent intent = new Intent();
- intent.putExtra(EXTRA_DEVICE_ADDRESS, address);
- // 设置连接成功返回值
- setResult(Activity.RESULT_OK, intent);
- // 结束程序
- finish();
- }
- };
- // 广播接收器,监听查找到设备和搜索完成action
- private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- // 查找到设备action
- if (BluetoothDevice.ACTION_FOUND.equals(action)) {
- // 得到蓝牙设备
- BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
- // 如果是已配对的则略过,已得到显示,其余的在添加到列表中进行显示
- if (device.getBondState() != BluetoothDevice.BOND_BONDED) {
- mNewDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
- }else{ //添加到已配对设备列表
- mPairedDevicesArrayAdapter.add(device.getName() + "\n" + device.getAddress());
- }
- // 搜索完成action
- } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) {
- // 取消在Activity的窗口的标题栏显示进度条的效果
- setProgressBarIndeterminateVisibility(false);
- setTitle("选择要连接的设备");
- //如果没有找到新设备,则添加"没有找到新设备"到新设备适配器列表
- if (mNewDevicesArrayAdapter.getCount() == 0) {
- mNewDevicesArrayAdapter.add(NO_FOUND_DEVICE);
- }
- // if(mPairedDevicesArrayAdapter.getCount() > 0)
- // findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE);
- }
- }
- };
- }
3、 创建通信协议类(Communications_Protocol)
a) 定义各个功能键发送消息的起始位
- package com.example.timerbluetooth2.Other;
- public class Communications_Protocol {
- public final static byte Timer=(byte) 0xf0;
- public final static byte OpenClock=(byte) 0xf1;
- public final static byte CloseClock=(byte) 0xf2;
- public final static byte OpenMonitor=(byte) 0xf3;
- public final static byte CloseMonitor=(byte) 0xf4;
- public final static byte Adjustment=(byte) 0xf5;
- }
4、 创建ViewPager的Fragment类(ViewPagerFragment.java)
a) 详细叙述见http://blog.csdn.net/kangweijian/article/details/43404801
- package com.example.timerbluetooth2.Fragment;
- import android.os.Bundle;
- import android.support.v4.app.Fragment;
- import android.support.v4.app.FragmentManager;
- import android.support.v4.app.FragmentPagerAdapter;
- import android.support.v4.view.ViewPager;
- import android.support.v4.view.ViewPager.OnPageChangeListener;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.view.ViewGroup;
- import android.widget.Button;
- import com.example.timerbluetooth2.R;
- public class ViewPagerFragment extends Fragment {
- public Button []TextButton=new Button[4];
- private ViewPager mViewPager;
- @Override
- public void onCreate(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onCreate(savedInstanceState);
- }
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- //实例化TextButton
- TextButton[0] = (Button) getActivity().findViewById(R.id.TextButton1);
- TextButton[1] = (Button) getActivity().findViewById(R.id.TextButton2);
- TextButton[2] = (Button) getActivity().findViewById(R.id.TextButton3);
- TextButton[3] = (Button) getActivity().findViewById(R.id.TextButton4);
- //创建ViewPager实例
- mViewPager=new ViewPager(getActivity());
- //创建资源ID(res/values/ids.xml),并配置mViewPager
- mViewPager.setId(R.id.viewPager);
- //获取Activity的FragmentManager实例
- FragmentManager fm = getActivity().getSupportFragmentManager();
- //设置adapter为FagmentStatePagerAdapter的一个匿名实例
- //FragmentStatePagerAdapter负责管理与ViewPager的对话并协同工作。
- mViewPager.setAdapter(new FragmentPagerAdapter(fm) {
- //getCount()返回mViewPager包含的列表项数目
- @Override
- public int getCount() {
- // TODO Auto-generated method stub
- //return mCrimes.size();
- return 4;
- }
- //根据列表项ID,返回一个有效配置的Fragment
- @Override
- public Fragment getItem(int pos) {
- // TODO Auto-generated method stub
- switch (pos) {
- case 0:
- return TimerFragment.newInstance();
- case 1:
- return TimeMonitorFragment.newInstance();
- case 2:
- return ClockFragment.newInstance();
- case 3:
- return AdjustmentFragment.newInstance();
- default:
- return TimerFragment.newInstance();
- }
- }
- });
- //设置当前要显示的列表项
- mViewPager.setCurrentItem(0);
- LightUpSingleButton(0);
- //设置监听器,监听ViewPager当前显示页面的状态变化
- mViewPager.setOnPageChangeListener(new OnPageChangeListener() {
- //onPageSelected()方法告知我们当前哪个页面会被选中
- @Override
- public void onPageSelected(int pos) {
- // TODO Auto-generated method stub
- LightUpSingleButton(pos);
- }
- //onPageScrolled()方法告知我们页面将会滑向哪里
- @Override
- public void onPageScrolled(int arg0, float arg1, int arg2) {
- // TODO Auto-generated method stub
- }
- //onPageScrollStateChanged()方法告知我们当前页面所处的行为状态,如正在被用户滑动,页面滑动入位到完全静止以及页面切换完成后的闲置状态
- @Override
- public void onPageScrollStateChanged(int arg0) {
- // TODO Auto-generated method stub
- }
- });
- //设置ViewPager为activity的内容视图
- return mViewPager;
- }
- @Override
- public void onActivityCreated(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onActivityCreated(savedInstanceState);
- TextButton[0].setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View arg0) {
- // TODO Auto-generated method stub
- mViewPager.setCurrentItem(0);
- LightUpSingleButton(0);
- }
- });
- TextButton[1].setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View arg0) {
- // TODO Auto-generated method stub
- mViewPager.setCurrentItem(1);
- LightUpSingleButton(1);
- }
- });
- TextButton[2].setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View arg0) {
- // TODO Auto-generated method stub
- mViewPager.setCurrentItem(2);
- LightUpSingleButton(2);
- }
- });
- TextButton[3].setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View arg0) {
- // TODO Auto-generated method stub
- mViewPager.setCurrentItem(3);
- LightUpSingleButton(3);
- }
- });
- }
- public void LightUpSingleButton(int num){
- for (int i = 0; i < TextButton.length; i++) {
- if (num==i) {
- TextButton[i].setTextColor(0xFFDB7093);
- }else {
- TextButton[i].setTextColor(0xFFA9A9A9);
- }
- }
- }
- }
5、 创建第一个列表项的Fragment(TimerFragment.java)
a) TimePicker控件实例化,.setIs24HourView(true)设置是使用24小时制, .getCurrentHour();.getCurrentMinute();得到该控件当前所显示的小时和分钟。
b) 由getActivity()函数得到fragment所绑定的Activity,并强制转换成MainActivity,从而可得到MainActivity中的所有的public变量。我们由该方法获取所需的BluetoothSocket,由.getOutputStream()函数得到蓝牙的输出流,最后经.write(buf)函数发出对应的消息。
c) 时间更新按钮对应发送的数据的起始位是由通信协议类中双方约定好的数据,第一位和第二位是TimePicker控件的小时和分钟的BCD码,第三位本来应该是秒的BCD码,目前暂且设为0。最后一位结束位0xFF。
- package com.example.timerbluetooth2.Fragment;
- import java.io.IOException;
- import java.io.OutputStream;
- import com.example.timerbluetooth2.*;
- import com.example.timerbluetooth2.Activity.MainActivity;
- import com.example.timerbluetooth2.Other.Communications_Protocol;
- import android.bluetooth.BluetoothSocket;
- import android.os.Bundle;
- import android.support.v4.app.Fragment;
- import android.support.v4.app.FragmentManager;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.view.ViewGroup;
- import android.widget.Button;
- import android.widget.TimePicker;
- import android.widget.Toast;
- public class TimerFragment extends Fragment {
- private Button TimerUpdateButton;
- private TimePicker mTimerPicker;
- /*********************************
- *
- * 1、onCreate(Bundle)是公共方法,因此可以被托管fragment的任何activity调用
- * 2、类似activity,fragment同样具有保存及获取状态的bundle,可以根据需要覆盖Fragment.onSaveInstanceState(Bundle)
- * 3、onCreate(Bundle)中,没有生成fragment视图。
- */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onCreate(savedInstanceState);
- }
- /*************************************
- *
- * 通过该方法生成fragment视图的布局,然后将生成的View返回给托管activity。
- * LayoutInflater及ViewGroup是用来生成布局的必要参数。
- * Bundle包含了该方法的保存状态下重建视图所使用的数据。
- */
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup parent,
- Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- // 调用LayoutInflater.inflate(...)方法并传入布局的资源ID生成的。第二个参数是视图的父视图,通过父视图来正确配置组件。
- // 第三个参数告知布局生成器是否将生成的视图添加给父视图。这里我们传入了false,因为我们将通过activity代码的方式添加视图。
- View v = inflater.inflate(R.layout.fragment_timer, parent, false);
- //mTimerPicker实例化
- mTimerPicker = (TimePicker) v.findViewById(R.id.Timer_Picker);
- //mTimerPicker是否使用24小时制
- mTimerPicker.setIs24HourView(true);
- TimerUpdateButton = (Button) v.findViewById(R.id.Timer_UpdateButton);
- TimerUpdateButton.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View arg0) {
- // TODO Auto-generated method stub
- byte []buf =new byte[5];
- int h=mTimerPicker.getCurrentHour();
- int m=mTimerPicker.getCurrentMinute();
- buf[0] = Communications_Protocol.Timer;
- buf[1] = (byte)((h/10)*16+(h%10));
- buf[2] = (byte)((m/10)*16+(m%10));
- buf[3] = 0;
- buf[4] = (byte) 0xff;
- BluetoothSocket socket = ((MainActivity)getActivity())._socket;
- if (socket == null) {
- Toast.makeText(getActivity(), "请先连接蓝牙设备",Toast.LENGTH_SHORT).show();
- }else {
- try {
- OutputStream os=socket.getOutputStream();
- os.write(buf);
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- });
- return v;
- }
- /**************************************
- * 目的:附加argument bundle给Fragment,需要调用Fragment.setArguments(Bundle)方法。
- * 要求:该任务必须在fragment创建后,添加给activity前完成
- * 方法:使用newInstance()方法,完成Fragment实例及bundle对象的创建,然后将argument放入bundle中,最后附加给Fragment
- * 其他:托管activity需要fragment实例时,需要调用newInstance()方法,而非直接调用其构造方法。而且,为满足fragment创建argument的要求,activity可传入
- * 任何需要的参数给newInstance()方法。
- *
- * @param crimeID
- * @return
- */
- public static TimerFragment newInstance(){
- Bundle args = new Bundle();
- TimerFragment fragment=new TimerFragment();
- fragment.setArguments(args);
- return fragment;
- }
- }
6、 创建第二个列表项的Fragment(TimeMonitorFragment.java)
a) 与第一个列表项基本雷同,不再赘述
- package com.example.timerbluetooth2.Fragment;
- import java.io.IOException;
- import java.io.OutputStream;
- import com.example.timerbluetooth2.*;
- import com.example.timerbluetooth2.Activity.MainActivity;
- import com.example.timerbluetooth2.Other.Communications_Protocol;
- import android.bluetooth.BluetoothSocket;
- import android.os.Bundle;
- import android.support.v4.app.Fragment;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.view.ViewGroup;
- import android.widget.Button;
- import android.widget.CompoundButton;
- import android.widget.CompoundButton.OnCheckedChangeListener;
- import android.widget.Switch;
- import android.widget.TimePicker;
- import android.widget.Toast;
- public class TimeMonitorFragment extends Fragment {
- private TimePicker TimeMonitorPicker1,TimeMonitorPicker2;
- private Switch TimeMonitorSwitch;
- /*********************************
- *
- * 1、onCreate(Bundle)是公共方法,因此可以被托管fragment的任何activity调用
- * 2、类似activity,fragment同样具有保存及获取状态的bundle,可以根据需要覆盖Fragment.onSaveInstanceState(Bundle)
- * 3、onCreate(Bundle)中,没有生成fragment视图。
- */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onCreate(savedInstanceState);
- }
- /*************************************
- *
- * 通过该方法生成fragment视图的布局,然后将生成的View返回给托管activity。
- * LayoutInflater及ViewGroup是用来生成布局的必要参数。
- * Bundle包含了该方法的保存状态下重建视图所使用的数据。
- */
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup parent,
- Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- // 调用LayoutInflater.inflate(...)方法并传入布局的资源ID生成的。第二个参数是视图的父视图,通过父视图来正确配置组件。
- // 第三个参数告知布局生成器是否将生成的视图添加给父视图。这里我们传入了false,因为我们将通过activity代码的方式添加视图。
- View v = inflater.inflate(R.layout.fragment_time_monitor, parent, false);
- //TimeMonitorSwitch实例化
- TimeMonitorSwitch = (Switch) v.findViewById(R.id.TimeMonitor_Switch);
- //TimeMonitorPicker1实例化
- TimeMonitorPicker1 = (TimePicker) v.findViewById(R.id.TimeMonitor_Picker1);
- //TimeMonitorPicker1是否使用24小时制
- TimeMonitorPicker1.setIs24HourView(true);
- //TimeMonitorPicker2实例化
- TimeMonitorPicker2 = (TimePicker) v.findViewById(R.id.TimeMonitor_Picker2);
- //mTimerPicker是否使用24小时制
- TimeMonitorPicker2.setIs24HourView(true);
- TimeMonitorSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton arg0, boolean arg1) {
- // TODO Auto-generated method stub
- byte []buf =new byte[6];
- int h1=TimeMonitorPicker1.getCurrentHour();
- int m1=TimeMonitorPicker1.getCurrentMinute();
- int h2=TimeMonitorPicker2.getCurrentHour();
- int m2=TimeMonitorPicker2.getCurrentMinute();
- if (TimeMonitorSwitch.isChecked()) {
- buf[0] = Communications_Protocol.OpenMonitor;
- }else {
- buf[0] = Communications_Protocol.CloseMonitor;
- }
- buf[1] = (byte)((h1/10)*16+(h1%10));
- buf[2] = (byte)((m1/10)*16+(m1%10));
- buf[3] = (byte)((h2/10)*16+(h2%10));
- buf[4] = (byte)((m2/10)*16+(m2%10));
- buf[5] = (byte) 0xff;
- BluetoothSocket socket = ((MainActivity)getActivity())._socket;
- if (socket == null) {
- Toast.makeText(getActivity(), "请先连接蓝牙设备",Toast.LENGTH_SHORT).show();
- }else {
- try {
- OutputStream os=socket.getOutputStream();
- os.write(buf);
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- });
- return v;
- }
- /**************************************
- * 目的:附加argument bundle给Fragment,需要调用Fragment.setArguments(Bundle)方法。
- * 要求:该任务必须在fragment创建后,添加给activity前完成
- * 方法:使用newInstance()方法,完成Fragment实例及bundle对象的创建,然后将argument放入bundle中,最后附加给Fragment
- * 其他:托管activity需要fragment实例时,需要调用newInstance()方法,而非直接调用其构造方法。而且,为满足fragment创建argument的要求,activity可传入
- * 任何需要的参数给newInstance()方法。
- *
- * @param crimeID
- * @return
- */
- public static TimeMonitorFragment newInstance(){
- Bundle args = new Bundle();
- TimeMonitorFragment fragment=new TimeMonitorFragment();
- fragment.setArguments(args);
- return fragment;
- }
- }
7、 创建第三个列表项的Fragment(ClockFragment.java)
a) 与第一个列表项基本雷同,不再赘述
- package com.example.timerbluetooth2.Fragment;
- import java.io.IOException;
- import java.io.OutputStream;
- import com.example.timerbluetooth2.*;
- import com.example.timerbluetooth2.Activity.MainActivity;
- import com.example.timerbluetooth2.Other.Communications_Protocol;
- import android.bluetooth.BluetoothSocket;
- import android.os.Bundle;
- import android.support.v4.app.Fragment;
- import android.support.v4.app.FragmentManager;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.view.ViewGroup;
- import android.widget.Button;
- import android.widget.CheckBox;
- import android.widget.CompoundButton;
- import android.widget.CompoundButton.OnCheckedChangeListener;
- import android.widget.Switch;
- import android.widget.TimePicker;
- import android.widget.Toast;
- public class ClockFragment extends Fragment {
- private Button Clock_UpdateButton;
- private TimePicker mTimePicker;
- private Switch mSwitch;
- /*********************************
- *
- * 1、onCreate(Bundle)是公共方法,因此可以被托管fragment的任何activity调用
- * 2、类似activity,fragment同样具有保存及获取状态的bundle,可以根据需要覆盖Fragment.onSaveInstanceState(Bundle)
- * 3、onCreate(Bundle)中,没有生成fragment视图。
- */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onCreate(savedInstanceState);
- }
- /*************************************
- *
- * 通过该方法生成fragment视图的布局,然后将生成的View返回给托管activity。
- * LayoutInflater及ViewGroup是用来生成布局的必要参数。
- * Bundle包含了该方法的保存状态下重建视图所使用的数据。
- */
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup parent,
- Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- // 调用LayoutInflater.inflate(...)方法并传入布局的资源ID生成的。第二个参数是视图的父视图,通过父视图来正确配置组件。
- // 第三个参数告知布局生成器是否将生成的视图添加给父视图。这里我们传入了false,因为我们将通过activity代码的方式添加视图。
- View v = inflater.inflate(R.layout.fragment_clock, parent, false);
- //mSwitch实例化
- mSwitch = (Switch) v.findViewById(R.id.Clock_Switch);
- //mTimePicker实例化
- mTimePicker = (TimePicker) v.findViewById(R.id.Clock_Picker);
- //mTimePicker是否使用24小时制
- mTimePicker.setIs24HourView(true);
- mSwitch.setOnCheckedChangeListener(new OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton arg0, boolean arg1) {
- // TODO Auto-generated method stub
- byte []buf =new byte[4];
- int h=mTimePicker.getCurrentHour();
- int m=mTimePicker.getCurrentMinute();
- if (mSwitch.isChecked()) {
- buf[0] = Communications_Protocol.OpenClock;
- }else {
- buf[0] = Communications_Protocol.CloseClock;
- }
- buf[1] = (byte)((h/10)*16+(h%10));
- buf[2] = (byte)((m/10)*16+(m%10));
- buf[3] = (byte) 0xff;
- BluetoothSocket socket = ((MainActivity)getActivity())._socket;
- if (socket == null) {
- Toast.makeText(getActivity(), "请先连接蓝牙设备",Toast.LENGTH_SHORT).show();
- }else {
- try {
- OutputStream os=socket.getOutputStream();
- os.write(buf);
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- });
- return v;
- }
- /**************************************
- * 目的:附加argument bundle给Fragment,需要调用Fragment.setArguments(Bundle)方法。
- * 要求:该任务必须在fragment创建后,添加给activity前完成
- * 方法:使用newInstance()方法,完成Fragment实例及bundle对象的创建,然后将argument放入bundle中,最后附加给Fragment
- * 其他:托管activity需要fragment实例时,需要调用newInstance()方法,而非直接调用其构造方法。而且,为满足fragment创建argument的要求,activity可传入
- * 任何需要的参数给newInstance()方法。
- *
- * @param crimeID
- * @return
- */
- public static ClockFragment newInstance(){
- Bundle args = new Bundle();
- ClockFragment fragment=new ClockFragment();
- fragment.setArguments(args);
- return fragment;
- }
- }
8、 创建第四个列表项的Fragment(AdjustmentFragment.java)
a) 与第一个列表项基本雷同,不再赘述
- package com.example.timerbluetooth2.Fragment;
- import java.io.IOException;
- import java.io.OutputStream;
- import com.example.timerbluetooth2.*;
- import com.example.timerbluetooth2.Activity.MainActivity;
- import com.example.timerbluetooth2.Other.Communications_Protocol;
- import android.bluetooth.BluetoothSocket;
- import android.os.Bundle;
- import android.support.v4.app.Fragment;
- import android.support.v4.app.FragmentManager;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.view.ViewGroup;
- import android.webkit.WebView.FindListener;
- import android.widget.Button;
- import android.widget.ScrollView;
- import android.widget.TimePicker;
- import android.widget.Toast;
- public class AdjustmentFragment extends Fragment {
- private TimePicker mTimePicker;
- private Button Adjustment_Button;
- /*********************************
- *
- * 1、onCreate(Bundle)是公共方法,因此可以被托管fragment的任何activity调用
- * 2、类似activity,fragment同样具有保存及获取状态的bundle,可以根据需要覆盖Fragment.onSaveInstanceState(Bundle)
- * 3、onCreate(Bundle)中,没有生成fragment视图。
- */
- @Override
- public void onCreate(Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- super.onCreate(savedInstanceState);
- }
- /*************************************
- *
- * 通过该方法生成fragment视图的布局,然后将生成的View返回给托管activity。
- * LayoutInflater及ViewGroup是用来生成布局的必要参数。
- * Bundle包含了该方法的保存状态下重建视图所使用的数据。
- */
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup parent,
- Bundle savedInstanceState) {
- // TODO Auto-generated method stub
- // 调用LayoutInflater.inflate(...)方法并传入布局的资源ID生成的。第二个参数是视图的父视图,通过父视图来正确配置组件。
- // 第三个参数告知布局生成器是否将生成的视图添加给父视图。这里我们传入了false,因为我们将通过activity代码的方式添加视图。
- View v = inflater.inflate(R.layout.fragment_adjustment, parent, false);
- //mTimePicker实例化
- mTimePicker = (TimePicker) v.findViewById(R.id.Adjustment_Picker);
- //mTimePicker是否使用24小时制
- mTimePicker.setIs24HourView(true);
- //mTimePicker设置当前时间为0
- mTimePicker.setCurrentHour(0);
- mTimePicker.setCurrentMinute(0);
- Adjustment_Button = (Button) v.findViewById(R.id.Adjustment_UpdateButton);
- Adjustment_Button.setOnClickListener(new OnClickListener() {
- @Override
- public void onClick(View arg0) {
- // TODO Auto-generated method stub
- byte []buf =new byte[4];
- int h=mTimePicker.getCurrentHour();
- int m=mTimePicker.getCurrentMinute();
- buf[0] = Communications_Protocol.Adjustment;
- buf[1] = (byte)h;
- buf[2] = (byte)m;
- buf[3] = (byte) 0xff;
- BluetoothSocket socket = ((MainActivity)getActivity())._socket;
- if (socket == null) {
- Toast.makeText(getActivity(), "请先连接蓝牙设备",Toast.LENGTH_SHORT).show();
- }else {
- try {
- OutputStream os=socket.getOutputStream();
- os.write(buf);
- } catch (IOException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- });
- return v;
- }
- /**************************************
- * 目的:附加argument bundle给Fragment,需要调用Fragment.setArguments(Bundle)方法。
- * 要求:该任务必须在fragment创建后,添加给activity前完成
- * 方法:使用newInstance()方法,完成Fragment实例及bundle对象的创建,然后将argument放入bundle中,最后附加给Fragment
- * 其他:托管activity需要fragment实例时,需要调用newInstance()方法,而非直接调用其构造方法。而且,为满足fragment创建argument的要求,activity可传入
- * 任何需要的参数给newInstance()方法。
- *
- * @param crimeID
- * @return
- */
- public static AdjustmentFragment newInstance(){
- Bundle args = new Bundle();
- AdjustmentFragment fragment=new AdjustmentFragment();
- fragment.setArguments(args);
- return fragment;
- }
- }
9、 创建主框架的布局文件(activity_main.xml)
a) 包含顶上4个Button、底下2个Button以及中间的fragment
- <RelativeLayout 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: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=".MainActivity" >
- <LinearLayout
- 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">
- <Button
- android:id="@+id/TextButton1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:background="#00000000"
- android:textColor="#ffA9A9A9"
- android:text="时间" />
- <Button
- android:id="@+id/TextButton2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:background="#00000000"
- android:textColor="#ffA9A9A9"
- android:text="监控" />
- <Button
- android:id="@+id/TextButton3"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:background="#00000000"
- android:textColor="#ffA9A9A9"
- android:text="闹钟" />
- <Button
- android:id="@+id/TextButton4"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:background="#00000000"
- android:textColor="#ffA9A9A9"
- android:text="微调" />
- </LinearLayout>
- <FrameLayout
- android:id="@+id/fragmentContainer"
- android:layout_width="fill_parent"
- android:layout_height="match_parent"
- android:layout_weight="1"
- />
- <LinearLayout
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:orientation="horizontal">
- <Button
- android:id="@+id/ConnectButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="连接" />
- <Button
- android:id="@+id/QuitButton"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_weight="1"
- android:text="退出" />
- </LinearLayout>
- </LinearLayout>
- </RelativeLayout>
10、 创建第一个列表项的布局文件(fragment_timer.xml)
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <TimePicker
- android:id="@+id/Timer_Picker"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_weight="1" />
- <Button
- android:text="更新"
- android:id="@+id/Timer_UpdateButton"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"/>
- </LinearLayout>
11、 创建第二个列表项的布局文件(fragment_time_monitor.xml)
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <LinearLayout
- android:orientation="vertical"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_weight="1"
- >
- <TimePicker
- android:id="@+id/TimeMonitor_Picker1"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_weight="1" />
- <TimePicker
- android:id="@+id/TimeMonitor_Picker2"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_weight="1" />
- </LinearLayout>
- <Switch
- android:id="@+id/TimeMonitor_Switch"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center" >
- </Switch>
- </LinearLayout>
12、 创建第三个列表项的布局文件(fragment_clock.xml)
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <TimePicker
- android:id="@+id/Clock_Picker"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_weight="1" />
- <Switch
- android:id="@+id/Clock_Switch"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center_horizontal" >
- </Switch>
- </LinearLayout>
13、 创建第四个列表项的布局文件(fragment_adjustment.xml)
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:orientation="vertical"
- android:layout_width="fill_parent"
- android:layout_height="fill_parent"
- >
- <TimePicker
- android:id="@+id/Adjustment_Picker"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- android:layout_weight="1" />
- <Button
- android:text="更新"
- android:id="@+id/Adjustment_UpdateButton"
- android:layout_width="match_parent"
- android:layout_height="wrap_content" />
- </LinearLayout>
14、 AndroidManifest.xml添加权限和activity清单
a) 将DeviceListActivity的主题改为对话框主题
- <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
- <uses-permission android:name="android.permission.BLUETOOTH" />
- <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS" />
- <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
- <activity android:name="com.example.timerbluetooth2.Activity.DeviceListActivity"
- android:label="选取连接设备"
- android:theme="@android:style/Theme.Dialog"
- android:configChanges="orientation|keyboardHidden" />