diff --git a/gradle.properties b/gradle.properties index 1ad6d6642..334992c02 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,5 +26,5 @@ android.enableJetifier=true android.useAndroidX=true android.useDeprecatedNdk=true -versionCode=43 -versionName=4.11.3 \ No newline at end of file +versionCode=45 +versionName=4.11.5 \ No newline at end of file diff --git a/iFish7/src/main/AndroidManifest.xml b/iFish7/src/main/AndroidManifest.xml index aa1aa03d5..2435574dd 100644 --- a/iFish7/src/main/AndroidManifest.xml +++ b/iFish7/src/main/AndroidManifest.xml @@ -194,6 +194,12 @@ android:exported="false" android:launchMode="singleTop" android:theme="@style/Transparent" /> + + adapterView, View view, int i, long l) { + BluetoothDevice bluetoothDevice = (BluetoothDevice) lvDevicesAdapter.getItem(i); + tvName.setText(bluetoothDevice.getName()); + tvAddress.setText(bluetoothDevice.getAddress()); + curBluetoothDevice = bluetoothDevice; + } + }); + } + + @Override + public void onClick(View view) { + switch (view.getId()) { + case R.id.bt_search: //搜索蓝牙 + Toast.makeText(BlueTooth2Activity.this,"",Toast.LENGTH_SHORT).show(); + llDataSendReceive.setVisibility(View.GONE); + llDeviceList.setVisibility(View.VISIBLE); + searchBtDevice(); + break; + + case R.id.bt_connect: //连接蓝牙 + if(!curConnState) { + startConnectDevice(curBluetoothDevice, MY_BLUETOOTH_UUID, 10000); + }else{ + Toast.makeText(this, "当前设备已连接", Toast.LENGTH_SHORT).show(); + } + break; + + case R.id.bt_disconnect: //断开连接 + if(curConnState) { + clearConnectedThread(); + }else{ + Toast.makeText(this, "当前设备未连接", Toast.LENGTH_SHORT).show(); + } + break; + + case R.id.bt_bound: //配对设备 + if(!curBondState) { + boundDevice(curBluetoothDevice); + }else{ + Toast.makeText(this, "当前设备已经与系统蓝牙建立配对", Toast.LENGTH_SHORT).show(); + } + break; + + case R.id.bt_disBound: //解除配对 + if(curBondState) { + disBoundDevice(curBluetoothDevice); + }else{ + Toast.makeText(this, "当前设备尚未与系统蓝牙建立配对", Toast.LENGTH_SHORT).show(); + } + break; + + case R.id.bt_to_send: //发送数据 + if(curConnState){ + String sendMsg = etSendMsg.getText().toString(); + if(sendMsg.isEmpty()){ + Toast.makeText(this, "发送数据为空!", Toast.LENGTH_SHORT).show(); + return; + } + sendData(sendMsg,true); //以16进制字符串形式发送数据 + }else{ + Toast.makeText(this, "请先连接当前设备", Toast.LENGTH_SHORT).show(); + } + break; + } + } + + + ////////////////////////////////// 搜索设备 ///////////////////////////////////////////////// + @SuppressLint("MissingPermission") + private void searchBtDevice() { + if (bluetoothAdapter.isDiscovering()) { //当前正在搜索设备... + return; + } + //开始搜索 + bluetoothAdapter.startDiscovery(); + } + + ////////////////////////////////// 配对/接触配对设备 //////////////////////////////////////////// + /** + * 执行绑定 反射 + * @param bluetoothDevice 蓝牙设备 + * @return true 执行绑定 false 未执行绑定 + */ + public boolean boundDevice(BluetoothDevice bluetoothDevice){ + if(bluetoothDevice == null){ + Log.e(TAG,"boundDevice-->bluetoothDevice == null"); + return false; + } + + try { + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT && false) { + L.i(bluetoothDevice.getName()+"jjia--------1--------type="+bluetoothDevice.getType()); + return bluetoothDevice.createBond(); + }else { + L.i(bluetoothDevice.getName()+"jjia--------2--------type="+bluetoothDevice.getType()); +// return ClsUtils.createBond(BluetoothDevice.class,bluetoothDevice); + return ClsUtils.connn(bluetoothDevice,MY_BLUETOOTH_UUID); + } + } catch (Exception e) { + e.printStackTrace(); + } + + return true; + } + + /** + * 执行解绑 反射 + * @param bluetoothDevice 蓝牙设备 + * @return true 执行解绑 false未执行解绑 + */ + public boolean disBoundDevice(BluetoothDevice bluetoothDevice){ + if(bluetoothDevice == null){ + Log.e(TAG,"disBoundDevice-->bluetoothDevice == null"); + return false; + } + + try { + return ClsUtils.removeBond(BluetoothDevice.class,bluetoothDevice); + } catch (Exception e) { + e.printStackTrace(); + } + + return true; + } + + ////////////////////////////////// 连接设备 /////////////////////////////////////////////// + /** + * 开始连接设备 + * @param bluetoothDevice 蓝牙设备 + * @param uuid 发起连接的UUID + * @param conOutTime 连接超时时间 + */ + public void startConnectDevice(final BluetoothDevice bluetoothDevice, String uuid, long conOutTime){ + if(bluetoothDevice == null){ + Log.e(TAG,"startConnectDevice-->bluetoothDevice == null"); + return; + } + if(bluetoothAdapter == null){ + Log.e(TAG,"startConnectDevice-->bluetooth3Adapter == null"); + return; + } + //发起连接 + connectThread = new ConnectThread(bluetoothAdapter,curBluetoothDevice,uuid); + connectThread.setOnBluetoothConnectListener(new ConnectThread.OnBluetoothConnectListener() { + @SuppressLint("MissingPermission") + @Override + public void onStartConn() { + Log.d(TAG,"startConnectDevice-->开始连接..." + bluetoothDevice.getName() + "-->" + bluetoothDevice.getAddress()); + } + + + @Override + public void onConnSuccess(BluetoothSocket bluetoothSocket) { + //移除连接超时 + mHandler.removeCallbacks(connectOuttimeRunnable); + Log.d(TAG,"startConnectDevice-->移除连接超时"); + Log.w(TAG,"startConnectDevice-->连接成功"); + + Message message = new Message(); + message.what = CONNECT_SUCCESS; + mHandler.sendMessage(message); + + //标记当前连接状态为true + curConnState = true; + //管理连接,收发数据 + managerConnectSendReceiveData(bluetoothSocket); + } + + @Override + public void onConnFailure(String errorMsg) { + Log.e(TAG,"startConnectDevice-->" + errorMsg); + + Message message = new Message(); + message.what = CONNECT_FAILURE; + mHandler.sendMessage(message); + + //标记当前连接状态为false + curConnState = false; + + //断开管理连接 + clearConnectedThread(); + } + }); + + connectThread.start(); + //设置连接超时时间 + mHandler.postDelayed(connectOuttimeRunnable,conOutTime); + + } + + //连接超时 + private Runnable connectOuttimeRunnable = new Runnable() { + @Override + public void run() { + Log.e(TAG,"startConnectDevice-->连接超时" ); + + Message message = new Message(); + message.what = CONNECT_FAILURE; + mHandler.sendMessage(message); + + //标记当前连接状态为false + curConnState = false; + //断开管理连接 + clearConnectedThread(); + } + }; + + ////////////////////////////////////// 断开连接 ////////////////////////////////////////////// + /** + * 断开已有的连接 + */ + public void clearConnectedThread(){ + Log.d(TAG,"clearConnectedThread-->即将断开"); + + //connectedThread断开已有连接 + if(connectedThread == null){ + Log.e(TAG,"clearConnectedThread-->connectedThread == null"); + return; + } + connectedThread.terminalClose(connectThread); + + //等待线程运行完后再断开 + mHandler.postDelayed(new Runnable() { + @Override + public void run() { + connectedThread.cancel(); //释放连接 + + connectedThread = null; + } + },10); + + Log.w(TAG,"clearConnectedThread-->成功断开连接"); + Message message = new Message(); + message.what = DISCONNECT_SUCCESS; + mHandler.sendMessage(message); + + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + /////////////////////////////////// 管理已有连接、收发数据 ////////////////////////////////// + + /** + * 管理已建立的连接,收发数据 + * @param bluetoothSocket 已建立的连接 + */ + public void managerConnectSendReceiveData(BluetoothSocket bluetoothSocket){ + //管理已有连接 + connectedThread = new ConnectedThread(bluetoothSocket); + connectedThread.start(); + connectedThread.setOnSendReceiveDataListener(new ConnectedThread.OnSendReceiveDataListener() { + @Override + public void onSendDataSuccess(byte[] data) { + Log.w(TAG,"发送数据成功,长度" + data.length + "->" + bytes2HexString(data,data.length)); + Message message = new Message(); + message.what = SEND_SUCCESS; + message.obj = "发送数据成功,长度" + data.length + "->" + bytes2HexString(data,data.length); + mHandler.sendMessage(message); + } + + @Override + public void onSendDataError(byte[] data,String errorMsg) { + Log.e(TAG,"发送数据出错,长度" + data.length + "->" + bytes2HexString(data,data.length)); + Message message = new Message(); + message.what = SEND_FAILURE; + message.obj = "发送数据出错,长度" + data.length + "->" + bytes2HexString(data,data.length); + mHandler.sendMessage(message); + } + + @Override + public void onReceiveDataSuccess(byte[] buffer) { + Log.w(TAG,"成功接收数据,长度" + buffer.length + "->" + bytes2HexString(buffer,buffer.length)); + Message message = new Message(); + message.what = RECEIVE_SUCCESS; + message.obj = "成功接收数据,长度" + buffer.length + "->" + bytes2HexString(buffer,buffer.length); + mHandler.sendMessage(message); + } + + @Override + public void onReceiveDataError(String errorMsg) { + Log.e(TAG,"接收数据出错:" + errorMsg); + Message message = new Message(); + message.what = RECEIVE_FAILURE; + message.obj = "接收数据出错:" + errorMsg; + mHandler.sendMessage(message); + } + }); + } + + ///////////////////////////////// 发送数据 ///////////////////////////////////////////////// + + /** + * 发送数据 + * @param data 要发送的数据 字符串 + * @param isHex 是否是16进制字符串 + * @return true 发送成功 false 发送失败 + */ + public boolean sendData(String data,boolean isHex){ + if(connectedThread == null){ + Log.e(TAG,"sendData:string -->connectedThread == null"); + return false; + } + if(data == null || data.length() == 0){ + Log.e(TAG,"sendData:string-->要发送的数据为空"); + return false; + } + + if(isHex){ //是16进制字符串 + data.replace(" ",""); //取消空格 + //检查16进制数据是否合法 + if(data.length() % 2 != 0){ + //不合法,最后一位自动填充0 + String lasts = "0" + data.charAt(data.length() - 1); + data = data.substring(0,data.length() - 2) + lasts; + } + Log.d(TAG,"sendData:string -->准备写入:" + data); //加空格显示 + return connectedThread.write(hexString2Bytes(data)); + } + + //普通字符串 + Log.d(TAG,"sendData:string -->准备写入:" + data); + return connectedThread.write(data.getBytes()); + } + + ////////////////////////////////////////////////////////////////////////////////////////////// + + /////////////////////////////// 数据类型转换 ////////////////////////////////////////////// + /** + * 字节数组-->16进制字符串 + * @param b 字节数组 + * @param length 字节数组长度 + * @return 16进制字符串 有空格类似“0A D5 CD 8F BD E5 F8” + */ + public static String bytes2HexString(byte[] b, int length) { + StringBuffer result = new StringBuffer(); + String hex; + for (int i = 0; i < length; i++) { + hex = Integer.toHexString(b[i] & 0xFF); + if (hex.length() == 1) { + hex = '0' + hex; + } + result.append(hex.toUpperCase()).append(" "); + } + return result.toString(); + } + + /** + * hexString2Bytes + * 16进制字符串-->字节数组 + * @param src 16进制字符串 + * @return 字节数组 + */ + public static byte[] hexString2Bytes(String src) { + int l = src.length() / 2; + byte[] ret = new byte[l]; + for (int i = 0; i < l; i++) { + ret[i] = (byte) Integer + .valueOf(src.substring(i * 2, i * 2 + 2), 16).byteValue(); + } + return ret; + } + + /////////////////////////////////////////////////////////////////////////////////////////////// + + /** + * 初始化蓝牙 + */ + @SuppressLint("MissingPermission") + private void initBluetooth() { + bluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); + if (bluetoothAdapter == null) { + Toast.makeText(this, "当前手机设备不支持蓝牙", Toast.LENGTH_SHORT).show(); + } else { + //手机设备支持蓝牙,判断蓝牙是否已开启 + if (bluetoothAdapter.isEnabled()) { + Toast.makeText(this, "手机蓝牙已开启", Toast.LENGTH_SHORT).show(); + } else { + //蓝牙没有打开,去打开蓝牙。推荐使用第二种打开蓝牙方式 + //第一种方式:直接打开手机蓝牙,没有任何提示 +// bluetoothAdapter.enable(); //BLUETOOTH_ADMIN权限 + //第二种方式:友好提示用户打开蓝牙 + Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); + startActivity(enableBtIntent); + } + } + } + + /** + * 初始化蓝牙广播 + */ + private void initBtBroadcast() { + //注册广播接收 + btBroadcastReceiver = new BtBroadcastReceiver(); + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); //开始扫描 + intentFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);//扫描结束 + intentFilter.addAction(BluetoothDevice.ACTION_FOUND);//搜索到设备 + intentFilter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); //配对状态监听 + registerReceiver(btBroadcastReceiver,intentFilter); + + } + + @Override + protected void onDestroy() { + super.onDestroy(); + + //注销广播接收 + unregisterReceiver(btBroadcastReceiver); + } + + /** + * 蓝牙广播接收器 + */ + private class BtBroadcastReceiver extends BroadcastReceiver { + + @SuppressLint("MissingPermission") + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (TextUtils.equals(action, BluetoothAdapter.ACTION_DISCOVERY_STARTED)) { //开启搜索 + Log.d(TAG,"开启搜索..."); + Message message = new Message(); + message.what = START_DISCOVERY; + mHandler.sendMessage(message); + + } else if (TextUtils.equals(action, BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {//完成搜素 + Log.d(TAG,"停止搜索..."); + Message message = new Message(); + message.what = STOP_DISCOVERY; + mHandler.sendMessage(message); + + } else if (TextUtils.equals(action, BluetoothDevice.ACTION_FOUND)) { //3.0搜索到设备 + //蓝牙设备 + BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + //信号强度 + int rssi = intent.getShortExtra(BluetoothDevice.EXTRA_RSSI, Short.MIN_VALUE); + + Log.w(TAG, "扫描到设备:" + bluetoothDevice.getName() + "-->" + bluetoothDevice.getAddress()); + Message message = new Message(); + message.what = DISCOVERY_DEVICE; + message.obj = bluetoothDevice; + mHandler.sendMessage(message); + + }else if(TextUtils.equals(action,BluetoothDevice.ACTION_BOND_STATE_CHANGED)){ + BluetoothDevice bluetoothDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); + int bondSate = bluetoothDevice.getBondState(); + L.i(bluetoothDevice.getName()+"jjia----------------bondSate="+bondSate); + switch(bondSate) { + case BluetoothDevice.BOND_NONE: + Log.d(TAG, "已解除配对"); + Message message1 = new Message(); + message1.what = DEVICE_BOND_NONE; + mHandler.sendMessage(message1); + break; + + case BluetoothDevice.BOND_BONDING: + Log.d(TAG, "正在配对..."); + Message message2 = new Message(); + message2.what = DEVICE_BONDING; + mHandler.sendMessage(message2); + break; + + case BluetoothDevice.BOND_BONDED: + Log.d(TAG, "已配对"); + Message message3 = new Message(); + message3.what = DEVICE_BONDED; + mHandler.sendMessage(message3); + break; + } + } + } + } +} \ No newline at end of file diff --git a/iFish7/src/main/java/com/ifish/activity/BluetoothActivity.java b/iFish7/src/main/java/com/ifish/activity/BluetoothActivity.java index c6095d426..2678bf4bc 100644 --- a/iFish7/src/main/java/com/ifish/activity/BluetoothActivity.java +++ b/iFish7/src/main/java/com/ifish/activity/BluetoothActivity.java @@ -18,6 +18,7 @@ import android.widget.TextView; import com.ifish.adapter.BlueToothController; import com.ifish.baseclass.BaseActivity; +import com.ifish.bluetooth.ClsUtils; import com.ifish.utils.BlueToothUtil; import com.ifish.utils.L; import com.ifish.utils.ToastUtil; @@ -26,7 +27,7 @@ import com.ifish.utils.ToastUtil; public class BluetoothActivity extends BaseActivity { BlueToothController blueToothController = new BlueToothController(); - + public static final String MY_BLUETOOTH_UUID = "00001101-0000-1000-8000-00805F9B34FB"; //蓝牙通讯 IntentFilter foundFilter = null; TextView name ; Button tv_pair; @@ -50,7 +51,14 @@ public class BluetoothActivity extends BaseActivity { @Override public void onClick(View view) { if (bluetoothDevice!=null){ - BlueToothUtil.Companion.boundDevice(bluetoothDevice); + try { + + L.i("jjia---------getBondState----"+bluetoothDevice.getBondState()); + ClsUtils.createBond1( bluetoothDevice); + } catch (Exception e) { + throw new RuntimeException(e); + } +// BlueToothUtil.Companion.boundDevice(bluetoothDevice,MY_BLUETOOTH_UUID); } } }); @@ -75,7 +83,7 @@ public class BluetoothActivity extends BaseActivity { if (!TextUtils.isEmpty(deviceName) && ( // deviceName.contains("ifishly") || - deviceName.equals("ifishly-fee0") + deviceName.startsWith("ifishly") // deviceName.contains("LE-Bose") ) diff --git a/iFish7/src/main/java/com/ifish/activity/FeedFishSettingActivity.java b/iFish7/src/main/java/com/ifish/activity/FeedFishSettingActivity.java index b5655369b..5644a6ce6 100644 --- a/iFish7/src/main/java/com/ifish/activity/FeedFishSettingActivity.java +++ b/iFish7/src/main/java/com/ifish/activity/FeedFishSettingActivity.java @@ -416,22 +416,22 @@ public class FeedFishSettingActivity extends BaseActivity { }else if(v == add_rel_setting){ //添加关联设备 showTimeSelected(); }else if(v == rl_xunhuan_bang){ //循环泵 - showOpenOrCloseDialog(4,R.drawable.xunhuanbang,"循环泵",event.isLight_status1()); + showOpenOrCloseDialog(4,R.drawable.xunhuanbang,"循环泵",event.isLight_status4()); }else if(v == rl_zengyang_bang){ //增氧泵 String type = Commons.DEVICE.get(Commons.DevicePosition).type; if(Device.TYPE_5F.equals(type)){ //海水 - showOpenOrCloseDialog(1,R.drawable.addobang,"蛋分器",event.isLight_status2()); + showOpenOrCloseDialog(1,R.drawable.addobang,"蛋分器",event.isLight_status1()); }else{ //淡水 - showOpenOrCloseDialog(1,R.drawable.addobang,"增氧泵",event.isLight_status2()); + showOpenOrCloseDialog(1,R.drawable.addobang,"增氧泵",event.isLight_status1()); } }else if(v == rl_light1){ //灯光1 - showOpenOrCloseDialog(2,R.drawable.linght1,"灯光1",event.isLight_status3()); + showOpenOrCloseDialog(2,R.drawable.linght1,"灯光1",event.isLight_status2()); }else if(v == rl_light2){ //灯光2 - showOpenOrCloseDialog(3,R.drawable.linght2,"灯光2",event.isLight_status4()); + showOpenOrCloseDialog(3,R.drawable.linght2,"灯光2",event.isLight_status3()); }else if(v == rl_zaolangbang){ //造浪泵 - showOpenOrCloseDialog(6,R.drawable.zaolangbang,"造浪泵",event.isLight_status5()); + showOpenOrCloseDialog(6,R.drawable.zaolangbang,"造浪泵",event.isLight_status6()); }else if(v == rl_shajun_light){ //杀菌灯 - showOpenOrCloseDialog(5,R.drawable.shajundeng,"杀菌灯",event.isLight_status6()); + showOpenOrCloseDialog(5,R.drawable.shajundeng,"杀菌灯",event.isLight_status5()); } } diff --git a/iFish7/src/main/java/com/ifish/adapter/BlueToothController.java b/iFish7/src/main/java/com/ifish/adapter/BlueToothController.java index fd0230816..8a75141fa 100644 --- a/iFish7/src/main/java/com/ifish/adapter/BlueToothController.java +++ b/iFish7/src/main/java/com/ifish/adapter/BlueToothController.java @@ -28,13 +28,10 @@ public class BlueToothController { } + @SuppressLint("MissingPermission") public void open(Activity activity) { Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); - if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.BLUETOOTH_CONNECT) != PackageManager.PERMISSION_GRANTED) { - - ToastUtil.show(activity,"请"); - return; - } +// Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE); activity.startActivityForResult(intent, 11); } diff --git a/iFish7/src/main/java/com/ifish/bluetooth/ClsUtils.java b/iFish7/src/main/java/com/ifish/bluetooth/ClsUtils.java new file mode 100644 index 000000000..c1136e45e --- /dev/null +++ b/iFish7/src/main/java/com/ifish/bluetooth/ClsUtils.java @@ -0,0 +1,197 @@ +package com.ifish.bluetooth; + +import android.annotation.SuppressLint; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.bluetooth.BluetoothSocket; +import android.util.Log; + +import com.ifish.utils.L; + +import java.io.IOException; +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.UUID; + +public class ClsUtils { + + public static BluetoothDevice remoteDevice = null; + + /** + * 与设备配对 参考源码:platform/packages/apps/Settings.git + * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java + */ + @SuppressLint("MissingPermission") + static public boolean connn(BluetoothDevice btDevice,String uuid){ + try { + BluetoothSocket socket = btDevice.createInsecureRfcommSocketToServiceRecord(UUID.fromString(uuid)); + socket.connect(); + + return true; + + } catch (IOException e) { + throw new RuntimeException(e); + } + + + } + + static public boolean createBond1(BluetoothDevice btDevice) + throws Exception { + Method createBondMethod = btDevice.getClass().getMethod("createBond"); + Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice); + return returnValue.booleanValue(); + } + + + @SuppressWarnings("unchecked") + static public boolean createBond(@SuppressWarnings("rawtypes") Class btClass, BluetoothDevice btDevice) + throws Exception { + Method createBondMethod = btClass.getMethod("createBond"); + Boolean returnValue = (Boolean) createBondMethod.invoke(btDevice); + return returnValue.booleanValue(); + } + + //自动配对设置Pin值 + static public boolean autoBond(Class btClass,BluetoothDevice device,String strPin) throws Exception { + Method autoBondMethod = btClass.getMethod("setPin",new Class[]{byte[].class}); + Boolean result = (Boolean)autoBondMethod.invoke(device,new Object[]{strPin.getBytes()}); + return result; + } + + static public void setPairingConfirmation(BluetoothDevice device){ + try { + Field field = device.getClass().getDeclaredField("sService"); + field.setAccessible(true); + + Object service = field.get(device); + Method method = service.getClass().getDeclaredMethod("setPairingConfirmation", + BluetoothDevice.class, boolean.class); + method.setAccessible(true); + method.invoke(service, device, true); + + } catch (NoSuchFieldException e) { + e.printStackTrace(); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + } catch (InvocationTargetException e) { + e.printStackTrace(); + } catch (IllegalAccessException e) { + e.printStackTrace(); + } + } + + + /** + * 与设备解除配对 参考源码:platform/packages/apps/Settings.git + * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java + */ + @SuppressWarnings("unchecked") + static public boolean removeBond(Class btClass, BluetoothDevice btDevice) + throws Exception { + Method removeBondMethod = btClass.getMethod("removeBond"); + Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice); + return returnValue.booleanValue(); + } + + @SuppressWarnings("unchecked") + static public boolean setPin(Class btClass, BluetoothDevice btDevice, + String str) throws Exception { + try { + Method removeBondMethod = btClass.getDeclaredMethod("setPin", + new Class[] + {byte[].class}); + Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice, + new Object[] + {str.getBytes()}); + } catch (SecurityException e) { + // throw new RuntimeException(e.getMessage()); + e.printStackTrace(); + } catch (IllegalArgumentException e) { + // throw new RuntimeException(e.getMessage()); + e.printStackTrace(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return true; + + } + + // 取消用户输入 + //cancelPairingUserInput()取消用户输入密钥框, + // 个人觉得一般情况下不要和 + // setPin(setPasskey、setPairingConfirmation、 setRemoteOutOfBandData)一起用, + // 这几个方法都会remove掉map里面的key:value(也就是互斥的 ) + @SuppressWarnings("unchecked") + static public boolean cancelPairingUserInput(Class btClass, + BluetoothDevice device) + + throws Exception { + Method createBondMethod = btClass.getMethod("cancelPairingUserInput"); + // cancelBondProcess() + Boolean returnValue = (Boolean) createBondMethod.invoke(device); + + return returnValue.booleanValue(); + } + + // 取消配对 + @SuppressWarnings("unchecked") + static public boolean cancelBondProcess(Class btClass, + BluetoothDevice device) + throws Exception { + Method createBondMethod = btClass.getMethod("cancelBondProcess"); + Boolean returnValue = (Boolean) createBondMethod.invoke(device); + return returnValue.booleanValue(); + } + + /** + * @param clsShow + */ + @SuppressWarnings("unchecked") + static public void printAllInform(Class clsShow) { + try { + // 取得所有方法 + Method[] hideMethod = clsShow.getMethods(); + int i = 0; + for (; i < hideMethod.length; i++) { + //Log.e("method name", hideMethod.getName() + ";and the i is:" + // + i); + } + // 取得所有常量 + Field[] allFields = clsShow.getFields(); + for (i = 0; i < allFields.length; i++) { + //Log.e("Field name", allFields.getName()); + } + } catch (SecurityException e) { + // throw new RuntimeException(e.getMessage()); + e.printStackTrace(); + } catch (IllegalArgumentException e) { + // throw new RuntimeException(e.getMessage()); + e.printStackTrace(); + } catch (Exception e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + } + + /** + * Clears the internal cache and forces a refresh of the services from the * remote device. + */ + public static boolean refreshDeviceCache(BluetoothGatt mBluetoothGatt) { + if (mBluetoothGatt != null) { + try { + BluetoothGatt localBluetoothGatt = mBluetoothGatt; + Method localMethod = localBluetoothGatt.getClass().getMethod("refresh", new Class[0]); + if (localMethod != null) { + boolean bool = ((Boolean) localMethod.invoke(localBluetoothGatt, new Object[0])).booleanValue(); + return bool; + } + } catch (Exception localException) { + Log.i("Config", "An exception occured while refreshing device"); + } + } + return false; + } +} diff --git a/iFish7/src/main/java/com/ifish/bluetooth/ConnectThread.java b/iFish7/src/main/java/com/ifish/bluetooth/ConnectThread.java new file mode 100644 index 000000000..62eba2b5a --- /dev/null +++ b/iFish7/src/main/java/com/ifish/bluetooth/ConnectThread.java @@ -0,0 +1,136 @@ +package com.ifish.bluetooth; + +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothSocket; +import android.util.Log; + +import java.io.IOException; +import java.util.UUID; + + +/** + * 发起蓝牙连接 + */ +public class ConnectThread extends Thread { + private static final String TAG = "ConnectThread"; + private final BluetoothAdapter mBluetoothAdapter; + private BluetoothSocket mmSocket; + private final BluetoothDevice mmDevice; + + public ConnectThread(BluetoothAdapter bluetoothAdapter,BluetoothDevice bluetoothDevice,String uuid) { + this.mBluetoothAdapter = bluetoothAdapter; + this.mmDevice = bluetoothDevice; + + //使用一个临时变量,等会赋值给mmSocket + //因为mmSocket是静态的 + BluetoothSocket tmp = null ; + + if(mmSocket != null){ + Log.e(TAG,"ConnectThread-->mmSocket != null先去释放"); + try { + mmSocket.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + Log.d(TAG,"ConnectThread-->mmSocket != null已释放"); + + //1、获取BluetoothSocket + try { + //建立安全的蓝牙连接,会弹出配对框 + tmp = mmDevice.createRfcommSocketToServiceRecord(UUID.fromString(uuid)); + + } catch (IOException e) { + Log.e(TAG,"ConnectThread-->获取BluetoothSocket异常!" + e.getMessage()); + } + + mmSocket = tmp; + if(mmSocket != null){ + Log.w(TAG,"ConnectThread-->已获取BluetoothSocket"); + } + + } + + @Override + public void run(){ + + //连接之前先取消发现设备,否则会大幅降低连接尝试的速度,并增加连接失败的可能性 + if(mBluetoothAdapter == null){ + Log.e(TAG,"ConnectThread:run-->mBluetoothAdapter == null"); + return; + } + //取消发现设备 + if(mBluetoothAdapter.isDiscovering()){ + mBluetoothAdapter.cancelDiscovery(); + } + + if(mmSocket == null){ + Log.e(TAG,"ConnectThread:run-->mmSocket == null"); + return; + } + + //2、通过socket去连接设备 + try { + Log.d(TAG,"ConnectThread:run-->去连接..."); + if(onBluetoothConnectListener != null){ + onBluetoothConnectListener.onStartConn(); //开始去连接回调 + } + mmSocket.connect(); //connect()为阻塞调用,连接失败或 connect() 方法超时(大约 12 秒之后),它将会引发异常 + + if(onBluetoothConnectListener != null){ + onBluetoothConnectListener.onConnSuccess(mmSocket); //连接成功回调 + Log.w(TAG,"ConnectThread:run-->连接成功"); + } + + } catch (IOException e) { + Log.e(TAG,"ConnectThread:run-->连接异常!" + e.getMessage()); + + if(onBluetoothConnectListener != null){ + onBluetoothConnectListener.onConnFailure("连接异常:" + e.getMessage()); + } + + //释放 + cancel(); + } + + } + + /** + * 释放 + */ + public void cancel() { + try { + if (mmSocket != null && mmSocket.isConnected()) { + Log.d(TAG,"ConnectThread:cancel-->mmSocket.isConnected() = " + mmSocket.isConnected()); + mmSocket.close(); + mmSocket = null; + return; + } + + if (mmSocket != null) { + mmSocket.close(); + mmSocket = null; + } + + Log.d(TAG,"ConnectThread:cancel-->关闭已连接的套接字释放资源"); + + } catch (IOException e) { + Log.e(TAG,"ConnectThread:cancel-->关闭已连接的套接字释放资源异常!" + e.getMessage()); + } + } + + private OnBluetoothConnectListener onBluetoothConnectListener; + + public void setOnBluetoothConnectListener(OnBluetoothConnectListener onBluetoothConnectListener) { + this.onBluetoothConnectListener = onBluetoothConnectListener; + } + + //连接状态监听者 + public interface OnBluetoothConnectListener{ + void onStartConn(); //开始连接 + void onConnSuccess(BluetoothSocket bluetoothSocket); //连接成功 + void onConnFailure(String errorMsg); //连接失败 + } + +} diff --git a/iFish7/src/main/java/com/ifish/bluetooth/ConnectedThread.java b/iFish7/src/main/java/com/ifish/bluetooth/ConnectedThread.java new file mode 100644 index 000000000..61d288900 --- /dev/null +++ b/iFish7/src/main/java/com/ifish/bluetooth/ConnectedThread.java @@ -0,0 +1,206 @@ +package com.ifish.bluetooth; + +import android.bluetooth.BluetoothSocket; +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Arrays; + + +/** + * 管理连接 + * 1、发送数据 + * 2、接收数据 + */ +public class ConnectedThread extends Thread{ + private static final String TAG = "ConnectedThread"; + private BluetoothSocket mmSocket; + private InputStream mmInStream; + private OutputStream mmOutStream; + //是否是主动断开 + private boolean isStop = false; + //发起蓝牙连接的线程 + private ConnectThread connectThread; + + public void terminalClose(ConnectThread connectThread){ + isStop = true; + this.connectThread = connectThread; + } + + public ConnectedThread(BluetoothSocket socket){ + mmSocket = socket; + + InputStream tmpIn = null; + OutputStream tmpOut = null; + + //使用临时对象获取输入和输出流,因为成员流是静态类型 + + //1、获取 InputStream 和 OutputStream + try { + tmpIn = socket.getInputStream(); + tmpOut = socket.getOutputStream(); + + } catch (IOException e) { + Log.e(TAG,"ConnectedThread-->获取InputStream 和 OutputStream异常!"); + } + + mmInStream = tmpIn; + mmOutStream = tmpOut; + + if(mmInStream != null){ + Log.d(TAG,"ConnectedThread-->已获取InputStream"); + } + + if(mmOutStream != null){ + Log.d(TAG,"ConnectedThread-->已获取OutputStream"); + } + + } + + public void run(){ + //最大缓存区 存放流 + byte[] buffer = new byte[1024 * 2]; //buffer store for the stream + //从流的read()方法中读取的字节数 + int bytes = 0; //bytes returned from read() + + //持续监听输入流直到发生异常 + while(!isStop){ + try { + + if(mmInStream == null){ + Log.e(TAG,"ConnectedThread:run-->输入流mmInStream == null"); + break; + } + //先判断是否有数据,有数据再读取 + if(mmInStream.available() != 0){ + //2、接收数据 + bytes = mmInStream.read(buffer); //从(mmInStream)输入流中(读取内容)读取的一定数量字节数,并将它们存储到缓冲区buffer数组中,bytes为实际读取的字节数 + byte[] b = Arrays.copyOf(buffer,bytes); //存放实际读取的数据内容 + Log.w(TAG,"ConnectedThread:run-->收到消息,长度" + b.length + "->" + bytes2HexString(b, b.length)); //有空格的16进制字符串 + if(onSendReceiveDataListener != null){ + onSendReceiveDataListener.onReceiveDataSuccess(b); //成功收到消息 + } + } + + } catch (IOException e) { + Log.e(TAG,"ConnectedThread:run-->接收消息异常!" + e.getMessage()); + if(onSendReceiveDataListener != null){ + onSendReceiveDataListener.onReceiveDataError("接收消息异常:" + e.getMessage()); //接收消息异常 + } + //关闭流和socket + boolean isClose = cancel(); + if(isClose){ + Log.e(TAG,"ConnectedThread:run-->接收消息异常,成功断开连接!"); + } + break; + } + } + //关闭流和socket + boolean isClose = cancel(); + if(isClose){ + Log.d(TAG,"ConnectedThread:run-->接收消息结束,断开连接!"); + } + } + + //发送数据 + public boolean write(byte[] bytes){ + try { + + if(mmOutStream == null){ + Log.e(TAG, "mmOutStream == null"); + return false; + } + + //发送数据 + mmOutStream.write(bytes); + Log.d(TAG, "写入成功:"+ bytes2HexString(bytes, bytes.length)); + if(onSendReceiveDataListener != null){ + onSendReceiveDataListener.onSendDataSuccess(bytes); //发送数据成功回调 + } + return true; + + } catch (IOException e) { + Log.e(TAG, "写入失败:"+ bytes2HexString(bytes, bytes.length)); + if(onSendReceiveDataListener != null){ + onSendReceiveDataListener.onSendDataError(bytes,"写入失败"); //发送数据失败回调 + } + return false; + } + } + + /** + * 释放 + * @return true 断开成功 false 断开失败 + */ + public boolean cancel(){ + try { + if(mmInStream != null){ + mmInStream.close(); //关闭输入流 + } + if(mmOutStream != null){ + mmOutStream.close(); //关闭输出流 + } + if(mmSocket != null){ + mmSocket.close(); //关闭socket + } + if(connectThread != null){ + connectThread.cancel(); + } + + connectThread = null; + mmInStream = null; + mmOutStream = null; + mmSocket = null; + + Log.w(TAG,"ConnectedThread:cancel-->成功断开连接"); + return true; + + } catch (IOException e) { + // 任何一部分报错,都将强制关闭socket连接 + mmInStream = null; + mmOutStream = null; + mmSocket = null; + + Log.e(TAG, "ConnectedThread:cancel-->断开连接异常!" + e.getMessage()); + return false; + } + } + + /** + * 字节数组-->16进制字符串 + * @param b 字节数组 + * @param length 字节数组长度 + * @return 16进制字符串 有空格类似“0A D5 CD 8F BD E5 F8” + */ + public static String bytes2HexString(byte[] b, int length) { + StringBuffer result = new StringBuffer(); + String hex; + for (int i = 0; i < length; i++) { + hex = Integer.toHexString(b[i] & 0xFF); + if (hex.length() == 1) { + hex = '0' + hex; + } + result.append(hex.toUpperCase()).append(" "); + } + return result.toString(); + } + + private OnSendReceiveDataListener onSendReceiveDataListener; + + public void setOnSendReceiveDataListener(OnSendReceiveDataListener onSendReceiveDataListener) { + this.onSendReceiveDataListener = onSendReceiveDataListener; + } + + //收发数据监听者 + public interface OnSendReceiveDataListener{ + void onSendDataSuccess(byte[] data); //发送数据结束 + void onSendDataError(byte[] data, String errorMsg); //发送数据出错 + void onReceiveDataSuccess(byte[] buffer); //接收到数据 + void onReceiveDataError(String errorMsg); //接收数据出错 + } + + + +} diff --git a/iFish7/src/main/java/com/ifish/bluetooth/LVDevicesAdapter.java b/iFish7/src/main/java/com/ifish/bluetooth/LVDevicesAdapter.java new file mode 100644 index 000000000..d5e55712b --- /dev/null +++ b/iFish7/src/main/java/com/ifish/bluetooth/LVDevicesAdapter.java @@ -0,0 +1,115 @@ +package com.ifish.bluetooth; + +import android.bluetooth.BluetoothDevice; +import android.content.Context; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.TextView; + +import com.ifish.activity.R; + +import java.util.ArrayList; +import java.util.List; + +/** + * 搜索到的设备列表适配器 + */ +public class LVDevicesAdapter extends BaseAdapter { + + private Context context; + private List list; + + public LVDevicesAdapter(Context context) { + this.context = context; + list = new ArrayList<>(); + } + + @Override + public int getCount() { + return list == null ? 0 : list.size(); + } + + @Override + public Object getItem(int i) { + if(list == null){ + return null; + } + return list.get(i); + } + + @Override + public long getItemId(int i) { + return i; + } + + @Override + public View getView(int i, View view, ViewGroup viewGroup) { + DeviceViewHolder viewHolder; + if(view == null){ + view = LayoutInflater.from(context).inflate(R.layout.layout_lv_devices_item,null); + viewHolder = new DeviceViewHolder(); + viewHolder.tvDeviceName = view.findViewById(R.id.tv_device_name); + viewHolder.tvDeviceAddress = view.findViewById(R.id.tv_device_address); + view.setTag(viewHolder); + }else{ + viewHolder = (DeviceViewHolder) view.getTag(); + } + + if(list.get(i).getName() == null){ + viewHolder.tvDeviceName.setText("NULL"); + }else{ + viewHolder.tvDeviceName.setText(list.get(i).getName()); + } + + viewHolder.tvDeviceAddress.setText(list.get(i).getAddress()); + + return view; + } + + /** + * 初始化所有设备列表 + * @param bluetoothDevices + */ + public void addAllDevice(List bluetoothDevices){ + if(list != null){ + list.clear(); + } + for (BluetoothDevice bluetoothDevice : bluetoothDevices) { + list.add(bluetoothDevice); + } + notifyDataSetChanged(); + } + + /** + * 添加列表子项 + * @param bluetoothDevice + */ + public void addDevice(BluetoothDevice bluetoothDevice){ + if(list == null){ + return; + } + if(!list.contains(bluetoothDevice)){ + list.add(bluetoothDevice); + } + notifyDataSetChanged(); //刷新 + } + + /** + * 清空列表 + */ + public void clear(){ + if(list != null){ + list.clear(); + } + notifyDataSetChanged(); //刷新 + } + + class DeviceViewHolder { + + TextView tvDeviceName; + TextView tvDeviceAddress; + } + +} diff --git a/iFish7/src/main/java/com/ifish/utils/BlueToothUtil.kt b/iFish7/src/main/java/com/ifish/utils/BlueToothUtil.kt index 9dfb01b0c..86503c099 100644 --- a/iFish7/src/main/java/com/ifish/utils/BlueToothUtil.kt +++ b/iFish7/src/main/java/com/ifish/utils/BlueToothUtil.kt @@ -8,10 +8,10 @@ class BlueToothUtil { companion object{ @SuppressLint("MissingPermission") - fun boundDevice(devicex: BluetoothDevice?){ + fun boundDevice(devicex: BluetoothDevice?,ii:String){ var method = BluetoothDevice::class.java.getMethod("createBond") method.invoke(devicex) - var clientSocket = devicex!!.createRfcommSocketToServiceRecord(UUID.fromString(devicex.address)) + var clientSocket = devicex!!.createRfcommSocketToServiceRecord(UUID.fromString(ii)) clientSocket.connect() } diff --git a/iFish7/src/main/res/layout/bindtwodevice_activity.xml b/iFish7/src/main/res/layout/bindtwodevice_activity.xml index a73df12aa..f48393a63 100644 --- a/iFish7/src/main/res/layout/bindtwodevice_activity.xml +++ b/iFish7/src/main/res/layout/bindtwodevice_activity.xml @@ -57,6 +57,16 @@ android:layout_height="wrap_content" android:adjustViewBounds="true" android:src="@drawable/device_petdevice" + android:visibility="gone" + android:scaleType="fitXY" /> + + + + + + + + +