引入蓝牙依赖

This commit is contained in:
fengjignqi
2026-06-03 16:25:13 +08:00
parent 5089e760e5
commit 07edcb4664
8 changed files with 190 additions and 23 deletions

View File

@@ -226,6 +226,7 @@ dependencies {
implementation "androidx.camera:camera-camera2:1.0.2" implementation "androidx.camera:camera-camera2:1.0.2"
implementation "androidx.camera:camera-lifecycle:1.0.2" implementation "androidx.camera:camera-lifecycle:1.0.2"
implementation "androidx.camera:camera-view:1.0.0-alpha25" implementation "androidx.camera:camera-view:1.0.0-alpha25"
//蓝牙扫描
implementation 'no.nordicsemi.android.support.v18:scanner:1.6.0' implementation 'no.nordicsemi.android.support.v18:scanner:1.6.0'
//蓝牙通讯 //蓝牙通讯
implementation 'no.nordicsemi.android:ble:2.11.0' implementation 'no.nordicsemi.android:ble:2.11.0'

View File

@@ -35,6 +35,14 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" /> <uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<!-- 蓝牙权限 -->
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<!-- 支持ble的设备 -->
<uses-feature
android:name="android.hardware.bluetooth_le"
android:required="true" />
<!--后台获取位置信息,若需后台定位则必选--> <!--后台获取位置信息,若需后台定位则必选-->
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<!--用于申请调用A-GPS模块,卫星定位加速--> <!--用于申请调用A-GPS模块,卫星定位加速-->

View File

@@ -120,4 +120,7 @@ public class AppConfig {
public static String APP_CLIENT_ID = "428a8310cd442757ae699df5d894f051"; public static String APP_CLIENT_ID = "428a8310cd442757ae699df5d894f051";
public static String APP_USER_TYPE = "app_user"; public static String APP_USER_TYPE = "app_user";
//蓝牙设备的uuid
public static String BLUETOOTH_DEVICE_UUID = "1775244d-6b43-439b-877c-060f2d9bed07";
} }

View File

@@ -29,7 +29,9 @@ import com.ckkj.water.BuildConfig;
import com.ckkj.water.ConstantUtil; import com.ckkj.water.ConstantUtil;
import com.ckkj.water.R; import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity; import com.ckkj.water.base.BaseActivity;
import com.ckkj.water.custom.GridSpacingItemDecoration;
import com.ckkj.water.databinding.AddDeviceLayoutBinding; import com.ckkj.water.databinding.AddDeviceLayoutBinding;
import com.ckkj.water.device.adapter.BleScanDeviceAdapter;
import com.ckkj.water.device.mvp.DeviceContract; import com.ckkj.water.device.mvp.DeviceContract;
import com.ckkj.water.device.mvp.DeviceImpl; import com.ckkj.water.device.mvp.DeviceImpl;
import com.ckkj.water.device.mvp.DevicePresenter; import com.ckkj.water.device.mvp.DevicePresenter;
@@ -72,6 +74,8 @@ public class AddDeviceActivity_copy extends BaseActivity<AddDeviceLayoutBinding,
private String mUuid = "1775244d-6b43-439b-877c-060f2d9bed07"; private String mUuid = "1775244d-6b43-439b-877c-060f2d9bed07";
private ScanCallback mScanCallback; private ScanCallback mScanCallback;
private boolean bluetoothReady = false; private boolean bluetoothReady = false;
private BleScanDeviceAdapter bleAdapter;
private List<WaterDeviceEntity> mDeviceList = new ArrayList<>();
@Override @Override
public DevicePresenter createPresenter() { public DevicePresenter createPresenter() {
@@ -90,6 +94,10 @@ public class AddDeviceActivity_copy extends BaseActivity<AddDeviceLayoutBinding,
.titleBar(viewBinding.rlTitle) .titleBar(viewBinding.rlTitle)
.init(); .init();
viewBinding.includedTitle.tvTitle.setText(R.string.add_device_title); viewBinding.includedTitle.tvTitle.setText(R.string.add_device_title);
viewBinding.rvDevices.addItemDecoration(new GridSpacingItemDecoration(4, 10, true));
bleAdapter = new BleScanDeviceAdapter(mContext,R.layout.scan_ble_device_item,mDeviceList);
viewBinding.rvDevices.setAdapter(bleAdapter);
if(checkBluetoothPermissions()){ if(checkBluetoothPermissions()){
// 初始化蓝牙适配器 // 初始化蓝牙适配器
initBluetooth(); initBluetooth();
@@ -115,7 +123,6 @@ public class AddDeviceActivity_copy extends BaseActivity<AddDeviceLayoutBinding,
if (animationDrawable.isRunning()) { if (animationDrawable.isRunning()) {
animationDrawable.stop(); animationDrawable.stop();
viewBinding.ivScanAnimation.setVisibility(GONE); viewBinding.ivScanAnimation.setVisibility(GONE);
viewBinding.imgDeviceItem.setVisibility(VISIBLE);
} }
} }
} }
@@ -141,21 +148,18 @@ public class AddDeviceActivity_copy extends BaseActivity<AddDeviceLayoutBinding,
@Override @Override
public void onScanResult(int callbackType, @NonNull ScanResult result) { public void onScanResult(int callbackType, @NonNull ScanResult result) {
super.onScanResult(callbackType, result); super.onScanResult(callbackType, result);
WaterDeviceEntity bean = new WaterDeviceEntity();
//设备名字
String deviceName = result.getScanRecord() != null ? String deviceName = result.getScanRecord() != null ?
result.getScanRecord().getDeviceName() : "无名称"; result.getScanRecord().getDeviceName() : "无名称";
//Mac地址
String deviceAddress = result.getDevice().getAddress(); String deviceAddress = result.getDevice().getAddress();
Log.e("TEST", "扫描到: " + deviceName + " (" + deviceAddress + ")");
ScanRecord record = result.getScanRecord(); bean.setDeviceName(deviceName);
bean.setMacAddress(deviceAddress);
mDeviceList.add(bean);
bleAdapter.notifyItemInserted(mDeviceList.size() - 1);
if (record != null) {
List<ParcelUuid> uuids =
record.getServiceUuids();
Log.e("TEST", "uuids=" + uuids);
}
ToastUtil.showShort(mContext, "扫描到: " + deviceName);
} }
@Override @Override
@@ -238,7 +242,6 @@ public class AddDeviceActivity_copy extends BaseActivity<AddDeviceLayoutBinding,
setClick(viewBinding.includedTitle.imgBack); setClick(viewBinding.includedTitle.imgBack);
setClick(viewBinding.tvScanAdd); setClick(viewBinding.tvScanAdd);
setClick(viewBinding.btnBleScan); setClick(viewBinding.btnBleScan);
setClick(viewBinding.imgDeviceItem);
} }
private void setClick(View view) { private void setClick(View view) {
@@ -251,11 +254,11 @@ public class AddDeviceActivity_copy extends BaseActivity<AddDeviceLayoutBinding,
finish(); finish();
break; break;
case R.id.img_device_item: // case R.id.img_device_item:
Bundle bundle = new Bundle(); // Bundle bundle = new Bundle();
bundle.putInt(ConstantUtil.ACTIVITY_NAVIGATE_FLAG,100); // bundle.putInt(ConstantUtil.ACTIVITY_NAVIGATE_FLAG,100);
goTo(mContext, SelectWifiActivity.class,bundle); // goTo(mContext, SelectWifiActivity.class,bundle);
break; // break;
case R.id.tv_scan_add: case R.id.tv_scan_add:
//检测有无相机权限 //检测有无相机权限

View File

@@ -0,0 +1,30 @@
package com.ckkj.water.device.adapter;
import android.content.Context;
import androidx.annotation.Nullable;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.ckkj.water.R;
import com.ckkj.water.index.entity.WaterDeviceEntity;
import java.util.List;
/**
* @author fengxiaoyang
* @date 2026/6/3
* @description 扫描到的设备适配器
*/
public class BleScanDeviceAdapter extends BaseQuickAdapter<WaterDeviceEntity, BaseViewHolder> {
private Context mContext;
public BleScanDeviceAdapter(Context context,int layoutResId, @Nullable List<WaterDeviceEntity> data) {
super(layoutResId, data);
mContext = context;
}
@Override
protected void convert(BaseViewHolder helper, WaterDeviceEntity item) {
helper.setText(R.id.tv_device_name,item.getDeviceName());
}
}

View File

@@ -0,0 +1,92 @@
package com.ckkj.water.utils.manager;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattService;
import android.content.Context;
import android.util.Log;
import androidx.annotation.NonNull;
import java.util.Arrays;
import java.util.UUID;
import no.nordicsemi.android.ble.BleManager;
/**
* @author fengxiaoyang
* @date 2026/6/3
* @description 蓝牙设备管理
*/
public class WaterDeviceManager extends BleManager {
private BluetoothGattCharacteristic writeCharacteristic;
private BluetoothGattCharacteristic notifyCharacteristic;
public WaterDeviceManager(@NonNull Context context) {
super(context);
}
@NonNull
@Override
protected BleManagerGattCallback getGattCallback() {
return new DeviceGattCallback();
}
private class DeviceGattCallback extends BleManagerGattCallback {
@Override
protected void initialize() {
setNotificationCallback(notifyCharacteristic)
.with((device, data) -> {
byte[] bytes = data.getValue();
Log.e("TEST", "收到数据: "
+ Arrays.toString(bytes));
});
enableNotifications(notifyCharacteristic)
.enqueue();
}
@Override
public boolean isRequiredServiceSupported(@NonNull BluetoothGatt gatt) {
BluetoothGattService service =
gatt.getService(UUID.fromString(
"0000ffff-0000-1000-8000-00805f9b34fb"));
if (service == null) {
return false;
}
writeCharacteristic =
service.getCharacteristic(UUID.fromString(
"0000ff01-0000-1000-8000-00805f9b34fb"));
notifyCharacteristic =
service.getCharacteristic(UUID.fromString(
"0000ff02-0000-1000-8000-00805f9b34fb"));
return writeCharacteristic != null
&& notifyCharacteristic != null;
}
@Override
protected void onServicesInvalidated() {
writeCharacteristic = null;
notifyCharacteristic = null;
}
}
public void send(byte[] data) {
if (writeCharacteristic == null) {
return;
}
writeCharacteristic(writeCharacteristic, data)
.enqueue();
}
}

View File

@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical" android:orientation="vertical"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
@@ -43,14 +45,19 @@
android:layout_width="@dimen/dp_200" android:layout_width="@dimen/dp_200"
android:layout_height="@dimen/dp_200" android:layout_height="@dimen/dp_200"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:src="@drawable/anim_add_device_scan"/> android:src="@drawable/anim_add_device_scan"
android:visibility="visible"/>
<ImageView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/img_device_item" android:id="@+id/rv_devices"
android:layout_width="@dimen/dp_150" android:layout_width="match_parent"
android:layout_height="@dimen/dp_150" android:layout_height="wrap_content"
android:layout_gravity="center_horizontal" android:layout_gravity="center_horizontal"
android:src="@mipmap/ic_launcher" android:paddingHorizontal="@dimen/dp_5"
android:layout_marginTop="@dimen/dp_5"
tools:listitem="@layout/scan_ble_device_item"
app:spanCount="4"
app:layoutManager="androidx.recyclerview.widget.GridLayoutManager"
android:visibility="gone"/> android:visibility="gone"/>
<TextView <TextView

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_5"
android:gravity="center_horizontal">
<com.makeramen.roundedimageview.RoundedImageView
android:id="@+id/img_device"
android:layout_width="@dimen/dp_60"
android:layout_height="@dimen/dp_60"
android:src="@mipmap/app_logo"
app:riv_corner_radius="@dimen/dp_10"/>
<TextView
android:id="@+id/tv_device_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_5"
android:text="设备名称"
android:textColor="@color/textPrimaryColor"
android:textSize="13sp"/>
</LinearLayout>