diff --git a/appmain/utils/device/ble/BLERawUtils.java b/appmain/utils/device/ble/BLERawUtils.java index d642b9d..8d2b891 100644 --- a/appmain/utils/device/ble/BLERawUtils.java +++ b/appmain/utils/device/ble/BLERawUtils.java @@ -1,116 +1,116 @@ -package com.proxgrind.chameleon.utils.device.ble; - -import androidx.annotation.NonNull; - -import com.proxgrind.chameleon.utils.system.LogUtils; -import com.proxgrind.chameleon.utils.tools.ArrayUtils; -import com.proxgrind.chameleon.utils.tools.HexUtil; - -import java.util.ArrayList; - -public class BLERawUtils { - // 解析! - public static Details[] parse(byte[] raw) { - ArrayList
details = new ArrayList<>(3); - // 02 01 02 - // 02 0A 04 - // 03 02 F0 FF - // 0C FF 5A 69 63 6F 78 DC 0D 30 02 74 CC - // 06 09 58 54 34 32 33 - // 0000000000000000000000000000000000000000000000000000000000000000 - for (int i = 0; i < raw.length; ) { - // 分三段取长度字节! - int len = raw[i]; - // 必定超过一个字节! - if (len > 1) { - // 足够一帧数据! - if (len < raw.length + i) { - // 建立存储对象! - Details tmp = new Details(); - tmp.setLength(len); - tmp.setType(raw[i + 1]); - // 建立一个存放值字节的数组,长度减去类型可以得到有效的值长度! - byte[] bytes = new byte[len - 1]; - // 进行值拷贝! - try { - System.arraycopy(raw, i + 2, bytes, 0, bytes.length); - } catch (Exception e) { - e.printStackTrace(); - } - // 存放进对象中! - tmp.setValue(bytes); - // 添加到结果集! - details.add(tmp); - i += (len + 1); - } else { - break; - } - } else { - break; - } - } - return ArrayUtils.list2Arr(details); - } - - // 按照某个类型搜索值 - public static Details find(int typeByte, Details[] details) { - if (details != null) { - for (Details tmp : details) { - /*LogUtils.d("len: " - + HexUtil.toHexString(tmp.getLength()) + ", " - + "type: " + HexUtil.toHexString(tmp.getType()) + ", " - + "value: " + HexUtil.toHexString(tmp.getValue()) + ", " - + "str: " + new String(tmp.getValue()) - );*/ - if (tmp.getType() == typeByte) { - return tmp; - } - } - } - return null; - } - - // 直接获得某个类型的值! - public static Details find(int typeByte, byte[] raw) { - // 进行raw分包查询! - BLERawUtils.Details[] details = BLERawUtils.parse(raw); - return BLERawUtils.find(typeByte, details); - } - - // 存放详细信息的类! - public static class Details { - public int getLength() { - return length; - } - - private void setLength(int length) { - this.length = length; - } - - public int getType() { - return type; - } - - private void setType(int type) { - this.type = type; - } - - public byte[] getValue() { - return value; - } - - private void setValue(byte[] value) { - this.value = value; - } - - int length; - int type; - byte[] value; - - @NonNull - @Override - public String toString() { - return "[Type=" + type + "]" + "[Value=]" + HexUtil.toHexString(value) + "]" + "[Length=" + length + "]"; - } - } -} +package com.proxgrind.chameleon.utils.device.ble; + +import androidx.annotation.NonNull; + +import com.proxgrind.chameleon.utils.system.LogUtils; +import com.proxgrind.chameleon.utils.tools.ArrayUtils; +import com.proxgrind.chameleon.utils.tools.HexUtil; + +import java.util.ArrayList; + +public class BLERawUtils { + // 解析! + public static Details[] parse(byte[] raw) { + ArrayList
details = new ArrayList<>(3); + // 02 01 02 + // 02 0A 04 + // 03 02 F0 FF + // 0C FF 5A 69 63 6F 78 DC 0D 30 02 74 CC + // 06 09 58 54 34 32 33 + // 0000000000000000000000000000000000000000000000000000000000000000 + for (int i = 0; i < raw.length; ) { + // 分三段取长度字节! + int len = raw[i]; + // 必定超过一个字节! + if (len > 1) { + // 足够一帧数据! + if (len < raw.length + i) { + // 建立存储对象! + Details tmp = new Details(); + tmp.setLength(len); + tmp.setType(raw[i + 1]); + // 建立一个存放值字节的数组,长度减去类型可以得到有效的值长度! + byte[] bytes = new byte[len - 1]; + // 进行值拷贝! + try { + System.arraycopy(raw, i + 2, bytes, 0, bytes.length); + } catch (Exception e) { + e.printStackTrace(); + } + // 存放进对象中! + tmp.setValue(bytes); + // 添加到结果集! + details.add(tmp); + i += (len + 1); + } else { + break; + } + } else { + break; + } + } + return ArrayUtils.list2Arr(details); + } + + // 按照某个类型搜索值 + public static Details find(int typeByte, Details[] details) { + if (details != null) { + for (Details tmp : details) { + /*LogUtils.d("len: " + + HexUtil.toHexString(tmp.getLength()) + ", " + + "type: " + HexUtil.toHexString(tmp.getType()) + ", " + + "value: " + HexUtil.toHexString(tmp.getValue()) + ", " + + "str: " + new String(tmp.getValue()) + );*/ + if (tmp.getType() == typeByte) { + return tmp; + } + } + } + return null; + } + + // 直接获得某个类型的值! + public static Details find(int typeByte, byte[] raw) { + // 进行raw分包查询! + BLERawUtils.Details[] details = BLERawUtils.parse(raw); + return BLERawUtils.find(typeByte, details); + } + + // 存放详细信息的类! + public static class Details { + public int getLength() { + return length; + } + + private void setLength(int length) { + this.length = length; + } + + public int getType() { + return type; + } + + private void setType(int type) { + this.type = type; + } + + public byte[] getValue() { + return value; + } + + private void setValue(byte[] value) { + this.value = value; + } + + int length; + int type; + byte[] value; + + @NonNull + @Override + public String toString() { + return "[Type=" + type + "]" + "[Value=]" + HexUtil.toHexString(value) + "]" + "[Length=" + length + "]"; + } + } +} diff --git a/appmain/utils/device/ble/BluetoothUtils.java b/appmain/utils/device/ble/BluetoothUtils.java new file mode 100644 index 0000000..5f63a45 --- /dev/null +++ b/appmain/utils/device/ble/BluetoothUtils.java @@ -0,0 +1,157 @@ +package com.proxgrind.chameleon.utils.device.ble; + +import android.annotation.SuppressLint; +import android.bluetooth.BluetoothAdapter; +import android.bluetooth.BluetoothManager; +import android.content.BroadcastReceiver; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; + +import com.proxgrind.devices.BleSerialControl; + +import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import static android.bluetooth.BluetoothDevice.ACTION_BOND_STATE_CHANGED; + +/** + * 蓝牙状态监听! + */ +public class BluetoothUtils { + public static BroadcastReceiver startStatusReceicer(Context context, OnStatusChange callback) { + if (callback != null) { + if (!check(context, callback)) return null; + // 创建广播! + BroadcastReceiver blueToothValueReceiver = new BroadcastReceiver() { + public int DEFAULT_VALUE_BULUETOOTH = 1000; + + @Override + public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (BluetoothAdapter.ACTION_STATE_CHANGED.equals(action)) { + int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, DEFAULT_VALUE_BULUETOOTH); + switch (state) { + case BluetoothAdapter.STATE_OFF: + callback.onStateOff(context); + break; + case BluetoothAdapter.STATE_ON: + callback.onStateOn(context); + break; + case BluetoothAdapter.STATE_TURNING_ON: + callback.onStateTurningOn(context); + break; + case BluetoothAdapter.STATE_TURNING_OFF: + callback.onStateTurningOff(context); + break; + case BluetoothAdapter.STATE_CONNECTING: + callback.onConnecting(context); + break; + case BluetoothAdapter.STATE_CONNECTED: + callback.onConnected(context); + break; + case BluetoothAdapter.STATE_DISCONNECTING: + callback.onDisconnecting(context); + break; + case BluetoothAdapter.STATE_DISCONNECTED: + callback.onDisconnected(context); + break; + default: + break; + } + } + } + }; + //注册广播,蓝牙状态监听 + IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED); + context.registerReceiver(blueToothValueReceiver, filter); + return blueToothValueReceiver; + } + return null; + } + + public static BroadcastReceiver startActionReceiver(Context context, OnActionChange callback) { + if (callback != null) { + if (!check(context, callback)) return null; + // 创建广播! + BroadcastReceiver blueToothValueReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + if (BluetoothAdapter.ACTION_DISCOVERY_STARTED.equals(intent.getAction())) { + callback.onScanStarted(context); + } + if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(intent.getAction())) { + callback.onScanFinished(context); + } + } + }; + //注册广播,蓝牙状态监听 + IntentFilter filter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_STARTED); + filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_STARTED); + filter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); + context.registerReceiver(blueToothValueReceiver, filter); + return blueToothValueReceiver; + } + return null; + } + + private static boolean check(Context context, OnBlueNoSupport callback) { + BluetoothManager manager = (BluetoothManager) context.getSystemService(Context.BLUETOOTH_SERVICE); + if (manager == null || manager.getAdapter() == null) { + callback.onBlueNoSupported(context); + return false; + } + return true; + } + + public static void stop(BroadcastReceiver receiver, Context context) { + try { + context.unregisterReceiver(receiver); + } catch (Exception ignored) { + } + } + + private static class OnBlueNoSupport { + public void onBlueNoSupported(Context context) { + } + } + + public static class OnStatusChange extends OnBlueNoSupport { + public void onStateOff(Context context) { + } + + public void onStateOn(Context context) { + } + + public void onStateTurningOn(Context context) { + } + + public void onStateTurningOff(Context context) { + } + + public void onConnecting(Context context) { + } + + public void onConnected(Context context) { + } + + public void onDisconnecting(Context context) { + } + + public void onDisconnected(Context context) { + } + } + + public static class OnActionChange extends OnBlueNoSupport { + public void onScanStarted(Context context) { + } + + public void onScanFinished(Context context) { + } + } + + public static boolean isBlueOpen(Context context) { + return BleSerialControl.get().getAdapter().isEnabled(); + } +} diff --git a/appmain/utils/device/ble/ClsUtils.java b/appmain/utils/device/ble/ClsUtils.java new file mode 100644 index 0000000..19c5c98 --- /dev/null +++ b/appmain/utils/device/ble/ClsUtils.java @@ -0,0 +1,144 @@ +package com.proxgrind.chameleon.utils.device.ble; + + +import android.annotation.SuppressLint; +import android.bluetooth.BluetoothDevice; +import android.bluetooth.BluetoothGatt; +import android.os.Build; +import android.util.Log; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class ClsUtils { + /** + * 与设备配对 参考源码:platform/packages/apps/Settings.git + * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java + */ + @SuppressLint("ObsoleteSdkInt") + static public boolean createBond(Class btClass, BluetoothDevice btDevice) { + if (btDevice.getBondState() == BluetoothDevice.BOND_BONDED) return false; + try { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + Method createBondMethod = btClass.getMethod("createBond"); + Object obj = createBondMethod.invoke(btDevice); + return obj instanceof Boolean ? (Boolean) obj : false; + } else { + return btDevice.createBond(); + } + } catch (Exception e) { + return false; + } + } + + /** + * 与设备解除配对 参考源码:platform/packages/apps/Settings.git + * /Settings/src/com/android/settings/bluetooth/CachedBluetoothDevice.java + */ + static public boolean removeBond(Class btClass, BluetoothDevice btDevice) { + try { + Method removeBondMethod = btClass.getMethod("removeBond"); + Object obj = removeBondMethod.invoke(btDevice); + return obj instanceof Boolean ? (Boolean) obj : false; + } catch (Exception e) { + return false; + } + } + + static public boolean setPin(Class btClass, BluetoothDevice btDevice, String str) throws Exception { + byte[] key = str != null ? str.getBytes() : null; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT) { + try { + Method removeBondMethod = btClass.getDeclaredMethod("setPin", byte[].class); + Boolean returnValue = (Boolean) removeBondMethod.invoke(btDevice, new Object[]{key}); + Log.d("returnValue", "" + returnValue); + } 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; + } else { + return btDevice.setPin(key); + } + } + + // 取消用户输入 + static public boolean cancelPairingUserInput(Class btClass, BluetoothDevice device) throws Exception { + Method createBondMethod = btClass.getMethod("cancelPairingUserInput"); +// cancelBondProcess(btClass, device); + Boolean returnValue = (Boolean) createBondMethod.invoke(device); + return returnValue.booleanValue(); + } + + // 取消配对 + static public boolean cancelBondProcess(Class btClass, BluetoothDevice device) throws Exception { + Method createBondMethod = btClass.getMethod("cancelBondProcess"); + Boolean returnValue = (Boolean) createBondMethod.invoke(device); + return returnValue.booleanValue(); + } + + //确认配对 + static public void setPairingConfirmation(Class btClass, BluetoothDevice device, boolean isConfirm) throws Exception { + Method setPairingConfirmation = btClass.getDeclaredMethod("setPairingConfirmation", boolean.class); + setPairingConfirmation.invoke(device, isConfirm); + } + + /** + * @param clsShow + */ + static public void printAllInform(Class clsShow) { + try { + // 取得所有方法 + Method[] hideMethod = clsShow.getMethods(); + int i = 0; + for (; i < hideMethod.length; i++) { + Log.e("method name", hideMethod[i].getName() + ";and the i is:" + + i); + } + // 取得所有常量 + Field[] allFields = clsShow.getFields(); + for (i = 0; i < allFields.length; i++) { + Log.e("Field name", allFields[i].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(); + } + } + + /** + * 刷新设备的缓存! + */ + static public void refreshDeviceCache(BluetoothGatt gatt) { + if (gatt == null) return; + /* + * If the device is bonded this is up to the Service Changed characteristic to notify Android that the services has changed. + * There is no need for this trick in that case. + * If not bonded, the Android should not keep the services cached when the Service Changed characteristic is present in the target device database. + * However, due to the Android bug (still exists in Android 5.0.1), it is keeping them anyway and the only way to clear services is by using this hidden refresh method. + */ + if (gatt.getDevice().getBondState() == BluetoothDevice.BOND_NONE) { + /* + * There is a refresh() method in BluetoothGatt class but for now it's hidden. We will call it using reflections. + */ + try { + //noinspection JavaReflectionMemberAccess + final Method refresh = gatt.getClass().getMethod("refresh"); + refresh.invoke(gatt); + } catch (Exception ignored) { + } + } + } +} \ No newline at end of file