排程浇水历史记录

This commit is contained in:
fengjignqi
2026-06-01 15:54:06 +08:00
parent 694b74cda7
commit b7ab2e69a4
21 changed files with 953 additions and 34 deletions

View File

@@ -164,6 +164,10 @@
android:name="com.ckkj.water.device.AddDeviceActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize"/>
<activity
android:name="com.ckkj.water.device.AddDeviceActivity_copy"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize"/>
<activity
android:name="com.ckkj.water.device.SelectWifiActivity"
android:screenOrientation="portrait"

View File

@@ -20,6 +20,7 @@ import android.os.Bundle;
import android.os.Handler;
import android.provider.MediaStore;
import android.text.InputFilter;
import android.text.InputType;
import android.text.Spanned;
import android.text.TextUtils;
import android.util.Log;
@@ -100,6 +101,11 @@ public abstract class BaseActivity<T extends ViewBinding, P extends IPresenter>
public static final int REQUEST_EVENT_CODE_10001 = 10001;
public static final int REQUEST_EVENT_CODE_10002 = 10002;
public static final int REQUEST_EVENT_CODE_10003 = 10003;
public static final int INPUT_TYPE_NORMAL = 0; // 默认
public static final int INPUT_TYPE_NUMBER = 1; // 数字
public static final int INPUT_TYPE_LETTER = 2; // 英文字母
public static final int INPUT_TYPE_CHINESE = 3; // 中文
public static final int INPUT_TYPE_NUMBER_LETTER = 4;//数字+字母
public InputFilter mInputFilter;
private static final AtomicBoolean isDialogShown = new AtomicBoolean(false); // 使用 AtomicBoolean 保证线程安全
private ObjectAnimator rotateAnimator;
@@ -484,15 +490,29 @@ public abstract class BaseActivity<T extends ViewBinding, P extends IPresenter>
* @param confirmText 确定 文案
* @param click
*/
public void showEditDialog(String title, String hint,String content, boolean touchOutsideCancel, String cancelText, String confirmText,
public void showEditDialog(String title,
String hint,
String content,
boolean touchOutsideCancel,
String cancelText,
String confirmText,
int inputType,
final OnDialogClick click) {
View view = getLayoutInflater().inflate(R.layout.public_dialog_input_text, null);
EditText editText = view.findViewById(R.id.et_input);
// EditText自动获取焦点
editText.requestFocus();
//设置输入类型
setEditInputType(editText,inputType);
if(TextUtils.isEmpty(content)){
editText.setHint(hint);
}else{
editText.setText(content);
}
if (!TextUtils.isEmpty(content)) {
editText.setSelection(editText.getText().length());
}
setTextView(R.id.tv_title, title, view);
if (!TextUtils.isEmpty(confirmText) && !TextUtils.isEmpty(cancelText)) {
((TextView) view.findViewById(R.id.btnLeft)).setText(cancelText);
@@ -1128,4 +1148,72 @@ public abstract class BaseActivity<T extends ViewBinding, P extends IPresenter>
// 方法2通过Application检查如果有记录当前Activity
return MyApplication.getCurrentActivity() == this;
}
private void setEditInputType(EditText editText,int type){
switch (type){
case INPUT_TYPE_NUMBER:
//纯数字键盘
editText.setInputType(
InputType.TYPE_CLASS_NUMBER
);
break;
case INPUT_TYPE_LETTER:
//只能输入字母
editText.setFilters(
new InputFilter[]{
(source,start,end,dest,dstart,dend)->{
if(source.toString()
.matches("[a-zA-Z]+")){
return source;
}
return "";
}
});
break;
case INPUT_TYPE_CHINESE:
//只能输入中文
editText.setFilters(
new InputFilter[]{
(source,start,end,dest,dstart,dend)->{
if(source.toString()
.matches("[\\u4e00-\\u9fa5]+")){
return source;
}
return "";
}
});
break;
case INPUT_TYPE_NUMBER_LETTER:
//数字+字母
editText.setFilters(
new InputFilter[]{
(source,start,end,dest,dstart,dend)->{
if(source.toString()
.matches("[a-zA-Z0-9]+")){
return source;
}
return "";
}
});
break;
default:
editText.setInputType(
InputType.TYPE_CLASS_TEXT
);
break;
}
}
}

View File

@@ -0,0 +1,489 @@
package com.ckkj.water.device;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothManager;
import android.bluetooth.le.BluetoothLeScanner;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.graphics.drawable.AnimationDrawable;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import com.ckkj.water.ConstantUtil;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity;
import com.ckkj.water.databinding.AddDeviceLayoutBinding;
import com.ckkj.water.device.mvp.DeviceContract;
import com.ckkj.water.device.mvp.DeviceImpl;
import com.ckkj.water.device.mvp.DevicePresenter;
import com.ckkj.water.index.entity.WaterDeviceEntity;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.utils.manager.ActivityManager;
import com.ckkj.water.utils.manager.ThemeManager;
import com.ckkj.water.utils.show.ToastUtil;
import com.gyf.immersionbar.ImmersionBar;
import com.maning.mlkitscanner.scan.MNScanManager;
import com.maning.mlkitscanner.scan.callback.act.MNScanCallback;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import no.nordicsemi.android.support.v18.scanner.BluetoothLeScannerCompat;
import no.nordicsemi.android.support.v18.scanner.ScanCallback;
import no.nordicsemi.android.support.v18.scanner.ScanFilter;
import no.nordicsemi.android.support.v18.scanner.ScanResult;
import no.nordicsemi.android.support.v18.scanner.ScanSettings;
import pub.devrel.easypermissions.EasyPermissions;
/**
* 添加设备页面
*/
public class AddDeviceActivity_copy extends BaseActivity<AddDeviceLayoutBinding, DevicePresenter>
implements DeviceContract.DeviceView, EasyPermissions.PermissionCallbacks {
// 蓝牙相关
private BluetoothAdapter bluetoothAdapter;
private static final int REQUEST_ENABLE_BT = 1001;
private static final int REQUEST_BLUETOOTH_PERMISSION = 1002;
private List<BluetoothDevice> discoveredDevices = new ArrayList<>();
private AlertDialog deviceListDialog;
@Override
public DevicePresenter createPresenter() {
return new DevicePresenter(new DeviceImpl());
}
@Override
public void initData(Bundle savedInstanceState) {
super.initData(savedInstanceState);
ActivityManager.addActivity(this,getClass().getSimpleName() + "_" + ConstantUtil.ADD_DEVICE_FLAG);
ImmersionBar.with(this)
.statusBarColor(R.color.mainBackgroundColor) // 设置状态栏颜色
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext)) // 设置状态栏字体为黑色
.titleBar(viewBinding.rlTitle)
.init();
viewBinding.includedTitle.tvTitle.setText(R.string.add_device_title);
if(checkBluetoothPermissions()){
// 初始化蓝牙适配器
initBluetooth();
BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
ScanSettings settings = new ScanSettings.Builder()
.setLegacy(false)
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setReportDelay(5000)
.setUseHardwareBatchingIfSupported(true)
.build();
List<ScanFilter> filters = new ArrayList<>();
scanner.startScan(null, settings, new ScanCallback() {
@Override
public void onScanResult(int callbackType, @NonNull ScanResult result) {
super.onScanResult(callbackType, result);
}
@Override
public void onBatchScanResults(@NonNull List<ScanResult> results) {
super.onBatchScanResults(results);
if(results != null){
int a = 0;
}
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
}
});
}
// 启动帧动画
ImageView ivScanAnimation = viewBinding.ivScanAnimation;
if (ivScanAnimation.getDrawable() instanceof AnimationDrawable) {
AnimationDrawable animationDrawable = (AnimationDrawable) ivScanAnimation.getDrawable();
animationDrawable.start();
}
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
// 停止帧动画
ImageView ivScanAnimation = viewBinding.ivScanAnimation;
if (ivScanAnimation.getDrawable() instanceof AnimationDrawable) {
AnimationDrawable animationDrawable = (AnimationDrawable) ivScanAnimation.getDrawable();
if (animationDrawable.isRunning()) {
animationDrawable.stop();
viewBinding.ivScanAnimation.setVisibility(GONE);
viewBinding.imgDeviceItem.setVisibility(VISIBLE);
}
}
}
},3000);
initListener();
}
/**
* 初始化蓝牙适配器
*/
private void initBluetooth() {
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (bluetoothManager != null) {
bluetoothAdapter = bluetoothManager.getAdapter();
}
if (bluetoothAdapter == null) {
ToastUtil.showShort(mContext, "设备不支持蓝牙");
return;
}
// 检查蓝牙是否开启
if (!bluetoothAdapter.isEnabled()) {
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
}
}
/**
* 检查蓝牙权限
*/
private boolean checkBluetoothPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
String[] readPermission = new String[]{
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.ACCESS_FINE_LOCATION
};
if (!hasRecordPermission(readPermission)) {
// 没有权限只在ACTION_DOWN时去申请
getMorePermissionRequst(readPermission,"蓝牙扫描权限申请",
"智能浇水需要您的授权,获取蓝牙权限添加设备",REQUEST_EVENT_CODE_10003);
return false;
}
} else {
// Android 12 以下需要位置权限才能扫描蓝牙
if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{
Manifest.permission.ACCESS_FINE_LOCATION
}, REQUEST_BLUETOOTH_PERMISSION);
return false;
}
}
return true;
}
/**
* 开始扫描蓝牙设备
*/
private void startBleScan() {
if (bluetoothAdapter == null || !bluetoothAdapter.isEnabled()) {
ToastUtil.showShort(mContext, "请先开启蓝牙");
return;
}
if (!checkBluetoothPermissions()) {
return;
}
discoveredDevices.clear();
showLoadingDialog();
// 获取已配对设备
Set<BluetoothDevice> pairedDevices = bluetoothAdapter.getBondedDevices();
if (pairedDevices != null && !pairedDevices.isEmpty()) {
discoveredDevices.addAll(pairedDevices);
}
// 开始扫描
new Handler().postDelayed(() -> {
stopBleScan();
showDeviceListDialog();
}, 10000); // 10秒后自动停止扫描
}
/**
* 停止扫描
*/
private void stopBleScan() {
hideDialogLoading();
}
/**
* 显示设备列表对话框
*/
private void showDeviceListDialog() {
if (discoveredDevices.isEmpty()) {
ToastUtil.showShort(mContext, "未发现蓝牙设备");
return;
}
String[] deviceNames = new String[discoveredDevices.size()];
for (int i = 0; i < discoveredDevices.size(); i++) {
BluetoothDevice device = discoveredDevices.get(i);
String name = device.getName();
if (name == null || name.isEmpty()) {
name = "未知设备";
}
deviceNames[i] = name + "\n" + device.getAddress();
}
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("发现 " + discoveredDevices.size() + " 个设备");
builder.setItems(deviceNames, (dialog, which) -> {
BluetoothDevice selectedDevice = discoveredDevices.get(which);
connectToDevice(selectedDevice);
});
builder.setNegativeButton("取消", null);
deviceListDialog = builder.show();
}
/**
* 连接蓝牙设备
*/
private void connectToDevice(BluetoothDevice device) {
showLoadingDialog();
String deviceName = device.getName();
if (deviceName == null || deviceName.isEmpty()) {
deviceName = "未知设备";
}
ToastUtil.showShort(mContext, "正在连接: " + deviceName);
// TODO: 这里可以调用 DeviceBleManager 进行连接
// DeviceBleManager bleManager = new DeviceBleManager(this);
// bleManager.connect(device);
// 模拟连接成功
String finalDeviceName = deviceName;
new Handler().postDelayed(() -> {
hideDialogLoading();
ToastUtil.showShort(mContext, "连接成功: " + finalDeviceName);
// 获取设备信息
getDeviceInfo(device);
}, 2000);
}
/**
* 获取蓝牙设备信息
*/
private void getDeviceInfo(BluetoothDevice device) {
String deviceName = device.getName();
String deviceAddress = device.getAddress();
if (deviceName == null || deviceName.isEmpty()) {
deviceName = "未知设备";
}
ToastUtil.showShort(mContext, "设备名称: " + deviceName + "\n设备地址: " + deviceAddress);
// TODO: 将设备信息传递给后续业务逻辑
// 例如跳转到下一个页面或保存设备信息
}
private void initListener() {
setClick(viewBinding.includedTitle.imgBack);
setClick(viewBinding.tvScanAdd);
setClick(viewBinding.btnBleScan);
setClick(viewBinding.imgDeviceItem);
}
private void setClick(View view) {
view.setOnClickListener(this::onClick);
}
public void onClick(View v){
switch (v.getId()){
case R.id.img_back:
finish();
break;
case R.id.img_device_item:
Bundle bundle = new Bundle();
bundle.putInt(ConstantUtil.ACTIVITY_NAVIGATE_FLAG,100);
goTo(mContext, SelectWifiActivity.class,bundle);
break;
case R.id.tv_scan_add:
//检测有无相机权限
String readPermission = Manifest.permission.CAMERA;
if(!hasRecordPermission(readPermission)){
getPermissionRequst(readPermission,"权限申请",
"智能浇水需要您的授权访问相机,并使用扫一扫",REQUEST_EVENT_CODE_10001);
}else{
scanQrcode();
}
break;
case R.id.btn_ble_scan:
// 扫描蓝牙设备
startBleScan();
break;
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_ENABLE_BT) {
if (resultCode == RESULT_OK) {
ToastUtil.showShort(mContext, "蓝牙已开启");
} else {
ToastUtil.showShort(mContext, "请开启蓝牙以扫描设备");
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull @NotNull String[] permissions, @NonNull @NotNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
hidePermissionDialog();
if (requestCode == REQUEST_BLUETOOTH_PERMISSION) {
boolean allGranted = true;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allGranted = false;
break;
}
}
if (allGranted) {
// 初始化蓝牙适配器
initBluetooth();
startBleScan();
} else {
ToastUtil.showShort(mContext, "需要蓝牙权限才能扫描设备");
}
}
}
private void scanQrcode() {
//默认扫描
MNScanManager.startScan(mContext, new MNScanCallback() {
@Override
public void onActivityResult(int resultCode, Intent data) {
switch (resultCode) {
case MNScanManager.RESULT_SUCCESS:
ArrayList<String> results = data.getStringArrayListExtra(MNScanManager.INTENT_KEY_RESULT_SUCCESS);
if(results != null){
ToastUtil.showShort(mContext,results.get(0).toString());
}
break;
case MNScanManager.RESULT_FAIL:
String resultError = data.getStringExtra(MNScanManager.INTENT_KEY_RESULT_ERROR);
break;
case MNScanManager.RESULT_CANCLE:
ToastUtil.showShort(mContext,"取消扫码");
break;
}
}
});
}
@Override
protected void onPause() {
super.onPause();
// 停止帧动画
ImageView ivScanAnimation = viewBinding.ivScanAnimation;
if (ivScanAnimation.getDrawable() instanceof AnimationDrawable) {
AnimationDrawable animationDrawable = (AnimationDrawable) ivScanAnimation.getDrawable();
if (animationDrawable.isRunning()) {
animationDrawable.stop();
}
}
}
@Override
public int getLayoutResId(Bundle savedInstanceState) {
return 0;
}
@Override
public void showError(String message) {
}
@Override
public void logOut(BaseBean bean) {
}
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
hidePermissionDialog();
if (requestCode == REQUEST_EVENT_CODE_10001) {
scanQrcode();
}
}
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
}
@Override
public void addDeviceSuccess(BaseBean bean) {
}
@Override
public void getDeviceDetailInfo(WaterDeviceEntity bean) {
}
@Override
public void editDeviceInfoSuccess(BaseBean bean) {
}
@Override
public void deleteDeviceSuccess(BaseBean bean) {
}
@Override
public void turnOnOffDeviceSuccess(BaseBean bean) {
}
@Override
protected void onDestroy() {
super.onDestroy();
BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
scanner.stopScan(new ScanCallback() {
@Override
public void onScanResult(int callbackType, @NonNull ScanResult result) {
super.onScanResult(callbackType, result);
}
@Override
public void onBatchScanResults(@NonNull List<ScanResult> results) {
super.onBatchScanResults(results);
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
}
});
}
}

View File

@@ -2,6 +2,7 @@ package com.ckkj.water.device;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import androidx.annotation.Nullable;
@@ -10,6 +11,7 @@ import com.chad.library.adapter.base.BaseQuickAdapter;
import com.ckkj.water.EventMessage;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity;
import com.ckkj.water.base.BaseFragment;
import com.ckkj.water.custom.GridSpacingItemDecoration;
import com.ckkj.water.custom.view.CustomBubbleAttachPopup;
import com.ckkj.water.databinding.DeviceControlLayoutBinding;
@@ -19,6 +21,7 @@ import com.ckkj.water.device.mvp.DeviceImpl;
import com.ckkj.water.device.mvp.DevicePresenter;
import com.ckkj.water.index.entity.WaterDeviceEntity;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.plan.entity.WaterPlanWeekEntity;
import com.ckkj.water.utils.show.ToastUtil;
import com.gyf.immersionbar.ImmersionBar;
import com.lxj.xpopup.XPopup;
@@ -38,6 +41,8 @@ public class DeviceControlActivity extends BaseActivity<DeviceControlLayoutBindi
private List<String> mTimeList = new ArrayList<>();
private WaterDeviceEntity mDeviceEntity;
private DeviceTimeSelectAdapter mAdapter;
//用户自定义的浇水时长
private int mCustomDuration = 0;
@Override
public DevicePresenter createPresenter() {
return new DevicePresenter(new DeviceImpl());
@@ -81,8 +86,15 @@ public class DeviceControlActivity extends BaseActivity<DeviceControlLayoutBindi
@Override
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
mAdapter.setOnSelPosition(position);
mCustomDuration = 0;
viewBinding.tvWorkDuartion.setHint(getString(R.string.device_custom_work_time));
viewBinding.tvWorkDuartion.setText("");
}
});
//用户自定义时间
if(mCustomDuration == 0){
viewBinding.tvWorkDuartion.setHint(getString(R.string.device_custom_work_time));
}
initListener();
}
@@ -94,6 +106,7 @@ public class DeviceControlActivity extends BaseActivity<DeviceControlLayoutBindi
setClick(viewBinding.linearWifi);
setClick(viewBinding.linearBat);
setClick(viewBinding.tvConfirm);
setClick(viewBinding.tvWorkDuartion);
}
private void setClick(View view) {
@@ -130,12 +143,105 @@ public class DeviceControlActivity extends BaseActivity<DeviceControlLayoutBindi
.show();
break;
case R.id.tv_work_duartion:
inputDuration(mCustomDuration);
break;
case R.id.tv_confirm:
showCenterTipDialog(R.mipmap.icon_dialog_success,"开始浇水");
if(mCustomDuration == 0 && mAdapter.getmSelPostion() == -1){
ToastUtil.showShort(mContext,"请选择浇水时长");
return;
}
showCenterTipDialog(R.mipmap.icon_dialog_success,"开始浇水");//
String status = mDeviceEntity.getStatus();
if(status.equals("1")){
status = "0";
}else{
status = "1";
}
mPresenter.switchDeviceWork(mDeviceEntity.getId(),status,
mCustomDuration == 0 ? convertToMinutes(mTimeList.get(mAdapter.getmSelPostion()))
: mCustomDuration);
break;
}
}
/**
* 输入浇水时间
* @param currentDuration
*/
private void inputDuration(int currentDuration) {
showEditDialog(getString(R.string.device_work_times_title),
getString(R.string.input_water_plan_time),
currentDuration == 0 ? "" : currentDuration + "",
true,
getString(R.string.cancel_text),
getString(R.string.confirm_text),
INPUT_TYPE_NUMBER,
new BaseActivity.OnDialogClick() {
@Override
public void onCancelClick() {}
@Override
public void onConfirmClisk() {}
@Override
public void onConfirmClisk(String content) {
if (Integer.parseInt(content) <= 0) {
ToastUtil.showShort(mContext, getString(R.string.input_water_plan_time_error));
return;
}
viewBinding.tvWorkDuartion.setText(content);
mCustomDuration = Integer.parseInt(content);
if(mAdapter != null){
mAdapter.setOnSelPosition(-1);
}
}
@Override
public void onView1Click() {
}
});
}
/**
* 将时间字符串转换为分钟数
*
* 1min -> 1
* 10min -> 10
* 1h -> 60
* 2h -> 120
*
* @param timeValue 时间字符串
* @return 分钟数
*/
public static int convertToMinutes(String timeValue) {
if (TextUtils.isEmpty(timeValue)) {
return 0;
}
try {
if (timeValue.endsWith("min")) {
return Integer.parseInt(
timeValue.substring(0, timeValue.length() - 3)
);
}
if (timeValue.endsWith("h")) {
return Integer.parseInt(
timeValue.substring(0, timeValue.length() - 1)
) * 60;
}
} catch (Exception e) {
e.printStackTrace();
}
return 0;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);

View File

@@ -81,7 +81,9 @@ public class DeviceSettingsActivity extends BaseActivity<DeviceSettingsLayoutBin
mDeviceEntity.getDeviceName(),
true,
getString(R.string.cancel_text),
getString(R.string.confirm_text), new OnDialogClick() {
getString(R.string.confirm_text),
BaseActivity.INPUT_TYPE_NORMAL,
new OnDialogClick() {
@Override
public void onCancelClick() {
}

View File

@@ -17,7 +17,16 @@ public class DeviceTimeSelectAdapter extends BaseQuickAdapter<String, BaseViewHo
private List<String> mDatas = new ArrayList<>();
private int mSelPostion = -1;
public DeviceTimeSelectAdapter(Context context,int layoutResId, @Nullable List<String> data) {
public int getmSelPostion() {
return mSelPostion;
}
public void setmSelPostion(int mSelPostion) {
this.mSelPostion = mSelPostion;
}
public DeviceTimeSelectAdapter(Context context, int layoutResId, @Nullable List<String> data) {
super(layoutResId, data);
this.mContext = context;
this.mDatas = data;

View File

@@ -166,8 +166,43 @@ public class DeviceImpl implements DeviceContract.DataModel{
}
}
/**
* 手动开启关闭设备
* @param json
* @param callBack
*/
@Override
public void turnOnOffDevice(JSONObject json, IndexContract.CallBack callBack) {
try {
NetWorkManager.getRequest().switchDevice(NetWorkManager.getToken(), NetWorkManager.toRequestBody(json))
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean bean) {
if (bean.getCode() == 200) {
callBack.success(bean,0);
} else {
callBack.fail(bean.getMessage());
}
}
@Override
public void onError(@NonNull Throwable e) {
callBack.fail(e.getMessage());
}
@Override
public void onComplete() {
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@@ -129,4 +129,37 @@ public class DevicePresenter extends BasePresenter<DeviceContract.DeviceView> {
}
});
}
/**
* 开启/关闭浇水
* @param id
* @param status
* @param duration
*/
public void switchDeviceWork(String id, String status, int duration) {
try {
JSONObject json = new JSONObject();
json.put("id",id);
json.put("workStatus",status);
// json.put("startTime","09:50");
json.put("durationMin",duration + "");
impl.turnOnOffDevice(json, new IndexContract.CallBack<BaseBean>() {
@Override
public void success(BaseBean bean,int total) {
if(DevicePresenter.this.getView() != null){
DevicePresenter.this.getView().turnOnOffDeviceSuccess(bean);
}
}
@Override
public void fail(String msg) {
if(DevicePresenter.this.getView() != null){
DevicePresenter.this.getView().showError( msg);
}
}
});
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -10,6 +10,7 @@ import androidx.fragment.app.Fragment;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseFragment;
import com.ckkj.water.databinding.HistoryFragmentLayoutBinding;
import com.ckkj.water.history.entity.WateringRecord;
import com.ckkj.water.history.mvp.HistoryContract;
import com.ckkj.water.history.mvp.HistoryPresenter;
import com.ckkj.water.network.BaseBean;
@@ -97,4 +98,9 @@ public class HistoryFragment extends BaseFragment<HistoryFragmentLayoutBinding,
public void logOut(BaseBean bean) {
}
@Override
public void getHistoryListSuccess(List<WateringRecord> msg, int total) {
}
}

View File

@@ -8,6 +8,7 @@ import com.ckkj.water.R;
import com.ckkj.water.base.BaseFragment;
import com.ckkj.water.custom.CustomTimeFilterPopupView;
import com.ckkj.water.custom.CustomTypeFilterPopupView;
import com.ckkj.water.custom.view.CustomRefreshHeader;
import com.ckkj.water.databinding.TabHistoryFragmentLayoutBinding;
import com.ckkj.water.history.adapter.WateringHistoryAdapter;
import com.ckkj.water.history.entity.WateringRecord;
@@ -22,6 +23,8 @@ import com.google.gson.reflect.TypeToken;
import com.lxj.xpopup.XPopup;
import com.lxj.xpopup.core.BasePopupView;
import com.lxj.xpopup.interfaces.SimpleCallback;
import com.scwang.smart.refresh.footer.ClassicsFooter;
import com.scwang.smart.refresh.layout.api.RefreshHeader;
import java.lang.reflect.Type;
import java.util.ArrayList;
@@ -35,17 +38,37 @@ import java.util.List;
public class TabHistoryFragment extends BaseFragment<TabHistoryFragmentLayoutBinding, HistoryPresenter>
implements HistoryContract.HistoryView {
List<WateringRecord> recordList = new ArrayList<>();
private List<WateringRecord> recordList = new ArrayList<>();
private WateringHistoryAdapter mAdapter;
private int mCurrentPage = 1;
@Override
protected void onResumeInitData() {
super.onResumeInitData();
showLoadingDialog();
mPresenter.getWaterHistoryList(mCurrentPage);
}
@Override
public void initData(Bundle state) {
String jsonContent = getString(R.string.watering_device_list_json);
Type type = new TypeToken<List<WateringRecord>>() {}.getType();
recordList = new Gson().fromJson(jsonContent, type);
mAdapter = new WateringHistoryAdapter(mContext,R.layout.tab_history_item_layout,recordList);
viewBinding.rvHisttory.setAdapter(mAdapter);
initListener();
mPresenter.getWaterHistoryList();
RefreshHeader header = new CustomRefreshHeader(mContext);
viewBinding.refresh.setRefreshHeader(header);
viewBinding.refresh.setRefreshFooter(new ClassicsFooter(mContext));
viewBinding.refresh.setOnRefreshListener(layout -> {
mCurrentPage = 1;
viewBinding.refresh.setNoMoreData(false);
mPresenter.getWaterHistoryList(mCurrentPage);
});
viewBinding.refresh.setOnLoadMoreListener(layout -> {
mCurrentPage++;
mPresenter.getWaterHistoryList(mCurrentPage);
});
}
private void initListener() {
@@ -139,6 +162,46 @@ public class TabHistoryFragment extends BaseFragment<TabHistoryFragmentLayoutBin
}
}
/**
* 获取浇水记录成功
* @param list
* @param total
*/
@Override
public void getHistoryListSuccess(List<WateringRecord> list, int total) {
hideDialogLoading();
List<WateringRecord> pageList = list == null ? new ArrayList<>() : list;
if (mCurrentPage == 1) {
viewBinding.refresh.finishRefresh(300);
recordList.clear(); // 只在刷新时清除
recordList = pageList;
} else {
viewBinding.refresh.finishLoadMore(300);
recordList.addAll(pageList);
}
mAdapter.setNewData(recordList);
// 4. 判断是否还有更多数据
if (mAdapter.getItemCount() >= total || pageList.size() == 0) {
// 使用 post 延迟调用,避免内部状态冲突导致的循环调用
viewBinding.refresh.postDelayed(() -> viewBinding.refresh.setNoMoreData(true), 300);
viewBinding.relNoMore.setVisibility(View.VISIBLE); // 自定义底部提示"暂无更多数据"
} else {
viewBinding.refresh.setNoMoreData(false);
viewBinding.relNoMore.setVisibility(View.GONE);
}
// 5. 控制空布局和内容显示
if (mAdapter.getItemCount() != 0) {
viewBinding.refresh.setVisibility(View.VISIBLE);
viewBinding.relNoresult.setVisibility(View.GONE);
} else {
viewBinding.refresh.setVisibility(View.GONE);
viewBinding.relNoresult.setVisibility(View.VISIBLE);
}
}
@Override
public HistoryPresenter createPresenter() {
return new HistoryPresenter(new HistoryImpl());
@@ -151,11 +214,14 @@ public class TabHistoryFragment extends BaseFragment<TabHistoryFragmentLayoutBin
@Override
public void showError(String message) {
hideDialogLoading();
ToastUtil.showShort(mContext,message);
}
@Override
public void logOut(BaseBean bean) {
}
}

View File

@@ -26,11 +26,11 @@ public class WateringHistoryAdapter extends BaseQuickAdapter<WateringRecord, Bas
@Override
protected void convert(BaseViewHolder helper, WateringRecord item) {
helper.setText(R.id.tv_device_name,item.getDeviceName());
helper.setText(R.id.tv_device_serial,item.getDeviceCode());
helper.setText(R.id.tv_device_serial,item.getDeviceNo());
helper.setText(R.id.tv_plan_time,mContext.getString(R.string.history_watering_adapter_time)
+ ":" + item.getStartTime() + "-" + item.getEndTime());
helper.setText(R.id.tv_plan_duration,mContext.getString(R.string.device_work_times_title)
+ ":" + item.getDuration());
+ ":" + item.getDurationMin() + "min");
helper.setText(R.id.tv_plan_type,mContext.getString(R.string.tab_filter_type)
+ ":" + item.getTriggerType());
helper.addOnClickListener(R.id.img_delete);

View File

@@ -8,10 +8,10 @@ import lombok.NoArgsConstructor;
/**
* @author fengxiaoyang
* @date 2026/5/15
* @description TODO: 描述该类的作用
* @description 浇水历史记录
*/
@NoArgsConstructor
@Data
@NoArgsConstructor
public class WateringRecord implements Serializable {
/**
@@ -29,4 +29,25 @@ public class WateringRecord implements Serializable {
private String endTime;
private String duration;
private String triggerType;
/**
* id : 2060156839441596420
* userId : 2056543507514097665
* deviceId : 2056641699765178369
* scheduleId : 1
* durationMin : 2
* zones : null
* createTime : 2026-06-09 09:30:41
*/
private String id;
private String userId;
private String deviceNo;
private String deviceId;
private int scheduleId;
private String durationMin;
private Object zones;
private String createTime;
}

View File

@@ -1,12 +1,14 @@
package com.ckkj.water.history.mvp;
import com.ckkj.water.base.IView;
import com.ckkj.water.history.entity.WateringRecord;
import java.util.List;
import java.util.Map;
public class HistoryContract {
public interface CallBack<T> {
void success(T msg);
void success(T msg, int total);
void fail(String msg);
}
public interface DataModel{
@@ -16,5 +18,7 @@ public class HistoryContract {
}
public interface HistoryView extends IView {
//历史记录成功
void getHistoryListSuccess(List<WateringRecord> msg, int total);
}
}

View File

@@ -1,9 +1,11 @@
package com.ckkj.water.history.mvp;
import com.ckkj.water.base.entity.MineUserInfoBean;
import com.ckkj.water.history.entity.WateringRecord;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.network.NetWorkManager;
import java.util.List;
import java.util.Map;
import io.reactivex.Observer;
@@ -25,16 +27,16 @@ public class HistoryImpl implements HistoryContract.DataModel{
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean>() {
.subscribe(new Observer<BaseBean<List<WateringRecord>>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean bean) {
public void onNext(@NonNull BaseBean<List<WateringRecord>> bean) {
if (bean.getCode() == 200) {
callBack.success(bean.getData());
callBack.success(bean.getRows(),bean.getTotal());
}
}

View File

@@ -1,9 +1,10 @@
package com.ckkj.water.history.mvp;
import com.ckkj.water.base.BasePresenter;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.history.entity.WateringRecord;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HistoryPresenter extends BasePresenter<HistoryContract.HistoryView> {
@@ -16,14 +17,14 @@ public class HistoryPresenter extends BasePresenter<HistoryContract.HistoryView>
/**
* 获取浇水历史
*/
public void getWaterHistoryList() {
public void getWaterHistoryList(int mCurrentPage) {
Map<String,String> map = new HashMap<>();
impl.getHistoryList(map, new HistoryContract.CallBack<BaseBean>() {
impl.getHistoryList(map, new HistoryContract.CallBack<List<WateringRecord>>() {
@Override
public void success(BaseBean bean) {
public void success(List<WateringRecord> list,int total) {
if(HistoryPresenter.this.getView() != null){
HistoryPresenter.this.getView().getHistoryListSuccess(list,total);
}
}

View File

@@ -119,7 +119,9 @@ public class MineInfomationActivity extends BaseActivity<MineInformationLayoutBi
mUserBean.getUser().getNickName(),
true,
"取消",
"确定", new OnDialogClick() {
"确定",
INPUT_TYPE_NORMAL,
new OnDialogClick() {
@Override
public void onCancelClick() {
}

View File

@@ -3,6 +3,7 @@ package com.ckkj.water.network;
import com.ckkj.water.base.entity.MineUserInfoBean;
import com.ckkj.water.history.entity.WateringRecord;
import com.ckkj.water.index.entity.WaterDeviceEntity;
import com.ckkj.water.login.entity.LoginEntity;
import com.ckkj.water.main.entity.CheckVersionEntity;
@@ -56,6 +57,11 @@ public interface Request {
//获取设备详细信息
@GET("app/v1/device/{id}")
Observable<BaseBean<WaterDeviceEntity>> getDeviceDetailInfo(@HeaderMap Map<String, String> headMap, @Path("id") String id);
//开启/关闭设备
@PUT("app/v1/switchDevice")
Observable<BaseBean<WaterPlanWeekEntity>> switchDevice(@HeaderMap Map<String, String> headMap, @Body RequestBody requestBody);
//修改更新设备信息
@PUT("app/v1/updateDeviceInfo")
Observable<BaseBean> updateDeviceInfo(@HeaderMap Map<String, String> headMap, @Body RequestBody requestBody);
@@ -113,7 +119,7 @@ public interface Request {
//查询排程列表
@GET("app/v1/waterLogList")
Observable<BaseBean> waterLogList(@HeaderMap Map<String, String> headMap,@QueryMap Map<String, String> map);
Observable<BaseBean<List<WateringRecord>>> waterLogList(@HeaderMap Map<String, String> headMap, @QueryMap Map<String, String> map);

View File

@@ -76,4 +76,17 @@
android:layout_marginHorizontal="@dimen/dp_20"
android:layout_marginTop="@dimen/dp_40"
android:gravity="center"/>
<TextView
android:id="@+id/btn_ble_scan"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="14sp"
android:textColor="@color/white"
android:text="蓝牙扫描"
android:background="@drawable/shape_green_button_bg"
android:paddingVertical="@dimen/dp_10"
android:layout_marginHorizontal="@dimen/dp_20"
android:layout_marginTop="@dimen/dp_40"
android:gravity="center"/>
</LinearLayout>

View File

@@ -187,8 +187,8 @@
android:orientation="horizontal"
android:background="@drawable/shape_f9f9f9_corner_5"
android:layout_marginHorizontal="@dimen/dp_20">
<EditText
android:id="@+id/et_wort_time"
<TextView
android:id="@+id/tv_work_duartion"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="@dimen/dp_5"

View File

@@ -59,11 +59,42 @@
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_histtory"
<com.scwang.smart.refresh.layout.SmartRefreshLayout
android:id="@+id/refresh"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="@layout/tab_history_item_layout"
tools:itemCount="3"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- 设备列表区域 -->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_histtory"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:listitem="@layout/tab_history_item_layout"
tools:itemCount="3"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
<RelativeLayout
android:id="@+id/rel_no_more"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone" >
<include
android:id="@+id/lay_no_more"
layout="@layout/layout_no_more"/>
</RelativeLayout>
</LinearLayout>
</com.scwang.smart.refresh.layout.SmartRefreshLayout>
<RelativeLayout
android:id="@+id/rel_noresult"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone">
<include
android:id="@+id/included_noresult"
layout="@layout/include_no_result_layout"/>
</RelativeLayout>
</LinearLayout>