Compare commits

..

101 Commits

Author SHA1 Message Date
fengjignqi
0c692c77bd 优化 2026-08-19 16:07:09 +08:00
fengjignqi
22f197a929 优化 2026-07-30 14:03:11 +08:00
fengjignqi
b25e3540fb 优化 2026-07-27 14:06:12 +08:00
fengjignqi
ec59249b3b 优化 2026-07-20 15:47:08 +08:00
fengjignqi
192ca7d3a4 优化 2026-07-17 15:34:23 +08:00
fengjignqi
69830dfc69 优化密码相关,隐藏/显示 2026-07-15 11:35:32 +08:00
fengjignqi
a9ed6f9452 优化 2026-07-15 10:02:14 +08:00
fengjignqi
5dae94788c 优化 2026-07-14 18:00:38 +08:00
fengjignqi
a31443fa4c 优化 2026-07-13 14:54:21 +08:00
fengjignqi
3f0260a37a 优化 2026-07-10 15:22:01 +08:00
fengjignqi
ed9c825bc7 优化 2026-07-09 11:30:50 +08:00
fengjignqi
79e51ad044 注销账号 2026-07-08 14:15:21 +08:00
fengjignqi
1cfc6f9d73 排程进度 2026-07-06 16:48:39 +08:00
fengjignqi
fa44e0edbd 优化 2026-07-06 10:35:56 +08:00
fengjignqi
d8d546c90a 优化 2026-07-04 10:47:05 +08:00
fengjignqi
01e4f0aefa 优化配网进度实时更新 2026-07-03 15:48:47 +08:00
fengjignqi
5a72fd98af 更新版本 2026-07-03 11:42:04 +08:00
fengjignqi
21d834d805 更新版本 2026-07-03 11:08:07 +08:00
fengjignqi
d2610d5b61 检测版本更新App 2026-07-02 08:45:59 +08:00
fengjignqi
b5e9f0de3b 图片上传 2026-06-29 17:59:39 +08:00
fengjignqi
f53408654b 图片上传 2026-06-29 16:57:43 +08:00
fengjignqi
fbef699b50 优化浇水流程,以及中英文遗漏点 2026-06-29 14:13:31 +08:00
fengjignqi
73d5003655 优化浇水流程,以及中英文遗漏点 2026-06-26 10:12:45 +08:00
fengjignqi
5e920037f5 去除部分无用类 2026-06-24 10:34:28 +08:00
fengjignqi
a4f9d630df 去除部分无用类 2026-06-24 10:05:39 +08:00
fengjignqi
133045fadd 优化401,以及部分中英文显示调整 2026-06-22 18:00:08 +08:00
fengjignqi
5842837a9e 修复401报错 2026-06-22 14:40:54 +08:00
fengjignqi
bc3091ac02 优化开启浇水 2026-06-17 11:34:23 +08:00
fengjignqi
d4cc897e26 优化开启浇水 2026-06-17 10:01:33 +08:00
fengjignqi
8b3c6d1761 修改蓝牙连接、配网时动画设置。 2026-06-17 08:42:54 +08:00
fengjignqi
92ca8067d2 调整App内获取授权的位置 2026-06-16 14:49:32 +08:00
fengjignqi
45dfc2d793 更换wifi扫描模式,从设备获取回传Wifi列表。 2026-06-14 15:23:33 +08:00
fengjignqi
dab61da6c5 扫描二维码,连接配网1 2026-06-14 09:06:26 +08:00
fengjignqi
4d4df82c6e 扫描二维码,连接配网1 2026-06-12 17:30:12 +08:00
fengjignqi
56af585524 扫描蓝牙二维码,准备配网 2026-06-12 08:37:00 +08:00
fengjignqi
02acc5403d 蓝牙连接不稳定修复 2026-06-11 09:23:33 +08:00
fengjignqi
2a933d09a4 蓝牙连接不稳定修复 2026-06-10 11:34:47 +08:00
fengjignqi
121c69fdfc 修改有关于设备相关的接口,入参参数更改 2026-06-10 08:20:26 +08:00
fengjignqi
97ee780561 语言中英文完善 2026-06-09 11:24:15 +08:00
fengjignqi
77f6001a31 语言中英文完善 2026-06-09 10:50:23 +08:00
fengjignqi
e040be11c3 语言中英文完善 2026-06-06 18:01:18 +08:00
fengjignqi
bafd193a96 语言选择 2026-06-06 16:16:52 +08:00
fengjignqi
b621a77e8d 语言选择 2026-06-06 15:25:52 +08:00
fengjignqi
e9c17d355a 适配黑色主题,调整细节14:26 2026-06-06 14:26:38 +08:00
fengjignqi
e0249fa0fa 适配黑色主题,调整细节 2026-06-06 10:47:02 +08:00
fengjignqi
bc270205d0 权限调整 2026-06-05 09:17:02 +08:00
fengjignqi
e40cf952c0 连接蓝牙,配网 2026-06-04 17:48:36 +08:00
fengjignqi
edfa6331f5 连接蓝牙,配网 2026-06-04 16:49:14 +08:00
fengjignqi
7e63101fc9 连接蓝牙,配网 2026-06-04 14:30:24 +08:00
fengjignqi
0f9c8c7329 显示附近蓝牙设备 2026-06-04 08:49:28 +08:00
fengjignqi
20a08e9b61 显示附近蓝牙设备 2026-06-03 16:47:42 +08:00
fengjignqi
07edcb4664 引入蓝牙依赖 2026-06-03 16:25:13 +08:00
fengjignqi
5089e760e5 历史记录筛选 2026-06-03 15:43:43 +08:00
fengjignqi
2507657285 历史记录筛选 2026-06-03 15:14:49 +08:00
fengjignqi
bd75b8baeb 历史记录筛选 2026-06-02 18:00:45 +08:00
fengjignqi
03e67932b1 电池电量icon优化 2026-06-02 13:58:54 +08:00
fengjignqi
b7ab2e69a4 排程浇水历史记录 2026-06-01 15:54:06 +08:00
fengjignqi
694b74cda7 排程部分功能优化,
解绑,删除,绑定
2026-05-30 16:32:17 +08:00
fengjignqi
7aed064ddb 排程部分功能优化,
解绑,删除,绑定
2026-05-30 15:53:15 +08:00
fengjignqi
89df3119f8 排程部分功能优化,
解绑,删除,绑定
2026-05-30 09:01:24 +08:00
fengjignqi
ed9cdaf126 排程解绑设备,添加已关联设备tab 2026-05-29 18:02:04 +08:00
fengjignqi
436b65c5e6 更新后新增排程提交 2026-05-29 16:28:55 +08:00
fengjignqi
5a42515f77 更新后新增排程提交 2026-05-29 15:10:40 +08:00
fengjignqi
a8b9aabf20 更新后新增排程提交 2026-05-29 10:47:02 +08:00
fengjignqi
e1d13665ab 更新后新增排程提交 2026-05-29 10:24:43 +08:00
fengjignqi
1000c43321 更新后新增排程提交 2026-05-29 10:18:41 +08:00
fengjignqi
9318646aa4 time配置校验时间冲突 2026-05-29 08:17:06 +08:00
fengjignqi
644a68d865 time列表设置时间 2026-05-28 17:58:32 +08:00
fengjignqi
693acda192 time列表设置时间 2026-05-28 17:11:04 +08:00
fengjignqi
967ddca167 侧滑删除 2026-05-28 16:54:28 +08:00
fengjignqi
9d41f8c1eb 侧滑删除 2026-05-28 15:45:42 +08:00
fengjignqi
21f70b68eb 排程重做 2026-05-28 15:18:57 +08:00
fengjignqi
79e66a20ef 排程重做 2026-05-28 14:56:39 +08:00
fengjignqi
401d1892bb 排程重做 2026-05-28 12:00:18 +08:00
fengjignqi
47769d1003 编辑排程状态 2026-05-27 17:08:57 +08:00
fengjignqi
f7bee61a56 编辑排程状态 2026-05-27 15:47:23 +08:00
fengjignqi
20aaf98235 排程分页2 2026-05-27 10:01:09 +08:00
fengjignqi
30e88c8067 排程分页 2026-05-27 09:41:27 +08:00
fengjignqi
0ca0a395cd 排程编辑更新 2026-05-27 08:52:18 +08:00
fengjignqi
f2b8062398 排程编辑更新 2026-05-26 17:41:27 +08:00
fengjignqi
9ab6d134ab 排程编辑更新 2026-05-26 17:35:08 +08:00
fengjignqi
65dfc1b5f1 排程编辑更新 2026-05-26 15:31:26 +08:00
fengjignqi
5ca81f565a 排程详情 2026-05-26 14:25:41 +08:00
fengjignqi
1ed3a68a5e 手机号、邮箱 + 验证码修改密码 2026-05-26 10:47:46 +08:00
fengjignqi
e14bdaf046 新增排程接口+排程更新 2026-05-26 09:06:11 +08:00
fengjignqi
6c6d211701 新增排程接口 2026-05-25 15:43:34 +08:00
fengjignqi
e6e3f755d0 新增排程接口 2026-05-25 10:16:33 +08:00
fengjignqi
819965faec 新增排程接口 2026-05-25 09:52:26 +08:00
fengjignqi
f86fedd6f1 新增排程页面重做 2026-05-22 15:05:27 +08:00
fengjignqi
1b7c8f0c2d 修改密码 2026-05-21 18:00:42 +08:00
fengjignqi
c90d26afc1 修改设备信息/解绑设备 2026-05-20 16:54:41 +08:00
fengjignqi
dcce26edf2 修改设备信息 2026-05-20 14:50:35 +08:00
fengjignqi
50912b10c9 首页设备列表,上拉加载,下拉刷新 2026-05-19 18:03:35 +08:00
fengjignqi
99e0ee56a5 首页设备列表,添加设备 2026-05-19 16:40:34 +08:00
fengjignqi
182289f82d 首页设备列表 2026-05-19 15:02:09 +08:00
fengjignqi
4ef664125e 注册/登录接口联调
个人信息接口联调
2026-05-19 14:05:22 +08:00
fengjignqi
695ace9209 注册/登录接口联调
个人信息接口联调
2026-05-19 11:43:49 +08:00
fengjignqi
c485b1e059 注册/登录接口联调 2026-05-19 08:15:24 +08:00
fengjignqi
cfa606f65e 语言选择调整 2026-05-18 08:48:31 +08:00
fengjignqi
c58ed6dc65 语言选择调整 2026-05-18 08:17:44 +08:00
fengjignqi
8311026790 夜间模式UI调整 2026-05-17 15:09:25 +08:00
310 changed files with 13835 additions and 12448 deletions

View File

@@ -4,10 +4,10 @@
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2026-05-15T02:49:19.581976200Z">
<DropdownSelection timestamp="2026-07-14T09:31:19.203364400Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\Admin\.android\avd\Pixel_7a_2.avd" />
<DeviceId pluginId="Default" identifier="serial=emulator-5554;connection=53435af5" />
</handle>
</Target>
</DropdownSelection>

View File

@@ -3,6 +3,10 @@
<component name="DeviceTable">
<option name="columnSorters">
<list>
<ColumnSorterState>
<option name="column" value="Name" />
<option name="order" value="ASCENDING" />
</ColumnSorterState>
<ColumnSorterState>
<option name="column" value="API" />
<option name="order" value="DESCENDING" />

10
.idea/planningMode.xml generated Normal file
View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="PlanningModeManager">
<option name="approvalStates">
<map>
<entry key="20260517-090322-0c9b00e4-f7c2-49da-a8ea-f2aa02b94996" value="true" />
</map>
</option>
</component>
</project>

View File

@@ -4,7 +4,7 @@ apply plugin: 'com.hihonor.mcs.asplugin'
apply plugin: 'io.freefair.lombok' // ✅ 加上这个
android {
compileSdkVersion 33
compileSdkVersion 34
lintOptions {
checkReleaseBuilds false // 关闭 release 打包时 lint 检查
abortOnError false // 即使有错误也不终止编译
@@ -39,7 +39,7 @@ android {
minSdkVersion 26
targetSdkVersion 30
versionCode 1
versionName "1.0.1"//version 1.0.0-appstore version 1.0.0-xh
versionName "Version 1.0.1"//version 1.0.0-appstore version 1.0.0-xh
// 添加自定义字段
buildConfigField "String", "APP_VERSION_NAME", "\"${versionName}\""
buildConfigField "int", "APP_VERSION_CODE", "${versionCode}"
@@ -121,6 +121,7 @@ dependencies {
implementation project(path: ':imagepickerl')
//消息传递
implementation 'de.greenrobot:eventbus:3.0.0-beta1'
implementation 'org.greenrobot:eventbus:3.3.1'
implementation 'com.trello.rxlifecycle2:rxlifecycle:2.2.1'
implementation 'com.trello.rxlifecycle2:rxlifecycle-android:2.2.1'
implementation 'com.trello.rxlifecycle2:rxlifecycle-components:2.2.1'
@@ -159,8 +160,8 @@ dependencies {
//鲁班图片压缩
implementation 'top.zibin:Luban:1.1.8'
//带有侧滑功能的recyclerview
// implementation 'com.yanzhenjie.recyclerview:x:1.3.2'
implementation 'com.github.luqiming666:SwipeRecyclerView:1.4.8'
implementation 'com.yanzhenjie.recyclerview:x:1.3.2'
// implementation 'com.github.luqiming666:SwipeRecyclerView:1.4.8'
//Activity与Fragmen实现沉浸式状态栏
implementation 'com.github.tangguna:immersion:1.0.1'
//viewpager2
@@ -173,7 +174,7 @@ dependencies {
implementation 'com.github.ns3chz:Picker:1.0.2'
implementation 'com.github.zzy0516alex:FileSelectorRelease:v6.2'
implementation 'com.github.chrisbanes:PhotoView:2.3.0'
implementation 'androidx.appcompat:appcompat:1.3.1'//(最低版本)
implementation 'androidx.appcompat:appcompat:1.6.1'//(最低版本)
// implementation 'com.umeng.umsdk:share-wx:7.1.5' //微信完整版
// implementation 'com.tencent.mm.opensdk:wechat-sdk-android-without-mta:6.7.0'//微信官方依赖库,必选
//商品评价星星
@@ -226,6 +227,13 @@ dependencies {
implementation "androidx.camera:camera-camera2:1.0.2"
implementation "androidx.camera:camera-lifecycle:1.0.2"
implementation "androidx.camera:camera-view:1.0.0-alpha25"
//蓝牙扫描
implementation 'no.nordicsemi.android.support.v18:scanner:1.6.0'
//蓝牙通讯
implementation 'no.nordicsemi.android:ble:2.11.0'
//espressif官方sdk用于esp固件的扫描配网等。
implementation 'com.github.espressif:esp-idf-provisioning-android:lib-2.2.3'
//自定义progressBar
implementation 'com.timqi.sectorprogressview:library:2.0.1'
}

Binary file not shown.

View File

@@ -34,6 +34,16 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<!-- 蓝牙权限 -->
<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" />
<!--用于申请调用A-GPS模块,卫星定位加速-->
@@ -55,14 +65,16 @@
<application
android:name="com.ckkj.water.MyApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:icon="@mipmap/app_logo"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/app_logo"
android:requestLegacyExternalStorage="true"
android:theme="@style/custom_app_theme"
android:supportsRtl="true"
android:hardwareAccelerated="true"
android:networkSecurityConfig="@xml/network_security_config">
android:networkSecurityConfig="@xml/network_security_config"
android:localeConfig="@xml/locale_config"
tools:replace="android:localeConfig">
<!-- 高德地图 -->
<meta-data android:name="com.amap.api.v2.apikey" android:value="a91364c6bbc2f5c773a949926fae7fb6">
</meta-data>
@@ -110,15 +122,11 @@
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize"/>
<activity
android:name="com.ckkj.water.chat.ChatHistoryActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize"/>
<activity
android:name="com.ckkj.water.login.LoginActivity"
android:theme="@style/AppTheme.FullScreen"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize"/>
android:windowSoftInputMode="adjustPan"/>
<activity
android:name="com.ckkj.water.mine.WebviewUserAgreement"
android:screenOrientation="portrait"
@@ -222,6 +230,11 @@
android:name="com.ckkj.water.mine.ResetPsdActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize"/>
<activity
android:name="com.ckkj.water.mine.AppThemActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize"/>
</application>
</manifest>
</manifest>

View File

@@ -1,7 +1,10 @@
package com.ckkj.water;
public class AppConfig {
//用户当前保存的语言状态,用于接口请求头传参
//zh_CN 中文
//en_US 英文
public static String LOCAL_LANUAGE = "zh_CN";
/**************************生产*******************************/
@@ -12,13 +15,14 @@ public class AppConfig {
/**************************测试*******************************/
// public static String BASE_URL = "http://47.106.83.229:8081/";//测试http://47.106.83.166:8001/
// public static String BASE_URL = "http://47.106.83.229:8081/";//测试http://47.106.83.166:8001/ .
// public static String BASE_URL_TWO = BASE_URL;//测试
// public static String BASE_URL_THREE = BASE_URL;//测试
// public static boolean IS_TEST_REQUEST = true;
/**************************本地*******************************/
public static String BASE_URL = "http://192.168.1.67:8081/";//测试
public static String BASE_URL = "http://192.168.1.217:8082/";//后端本地测试
// public static String BASE_URL = "http://47.97.217.123:8081/";//服务器测试
public static String BASE_URL_TWO = BASE_URL;//测试
public static String BASE_URL_THREE = BASE_URL;//测试
public static boolean IS_TEST_REQUEST = true;
@@ -110,10 +114,17 @@ public class AppConfig {
//测试使用 token
public static String TEST_USER_ID = "1897194667309666304";
//用户协议
public static String USER_AGREEMENT = "https://sta.mingdengbazi.site/yhxy.html";
public static String USER_AGREEMENT = "http://47.97.217.123/user-agreement";
//隐私政策
public static String USER_PRIVACY = "https://sta.mingdengbazi.site/ysxy.html";
public static String USER_PRIVACY = "http://47.97.217.123/privacy";
//用户分享链接
public static String USER_SHARE_URL = "https://sta.mingdengbazi.site/on/index.html?code=";
//clientID 固定 token
public static String APP_CLIENT_ID = "428a8310cd442757ae699df5d894f051";
public static String APP_USER_TYPE = "app_user";
//蓝牙设备的uuid
public static String BLUETOOTH_DEVICE_UUID = "1775244d-6b43-439b-877c-060f2d9bed07";
}

View File

@@ -7,9 +7,9 @@ package com.ckkj.water;
*/
public class ConstantUtil {
public static final String ADD_DEVICE_FLAG = "add_device_flag";
//页面跳转来源
public static final String ACTIVITY_NAVIGATE_FLAG = "activity_navigate_flag";
public static final String SHARE_PRE_NAME = "mingdengbazi_pre";
public static final String FIRST_ENTER_APP_AGREEMENT_TIPS = "first_enter_app_agreement_tips";
public static final String FIRST_ENTER_APP_FLAG = "first_enter_app_flag";
@@ -20,47 +20,12 @@ public class ConstantUtil {
public static final String USER_ID = "user_id";
public static final String USER_RONGCLOUD_ID = "user_rongcloud_id";
public static final String USER_INFO_JSON_CONTENT = "user_info_json_content";
public static final String SEARCH_HISTORY = "search_history";
public static final String USER_CONFIRM_UPLOAD_INFO = "user_confirm_upload_info";
public static final String GROUP_LAST_TIME = "group_last_time";
//用户是否同意隐私政策
public static final String PRIVACY_POLICY_AGREED = "privacy_policy_agreed";
//App访问验证code
public static final String APP_ENTER_CODE_VALUE = "app_enter_code_value";
//用户是否允许定位授权
public static final String USER_AUTH_LOCATION = "user_auth_location";
//用户是否请求过定位权限
public static final String USER_REQUEST_LOCATION_PERMISSION = "user_request_location_permission";
//用户是否请求过文件访问权限
public static final String USER_REQUEST_READ_WRITE_PERMISSION = "user_request_read_write_permission";
//用户是否请求过麦克风权限
public static final String USER_REQUEST_RECORD_AUDIO_PERMISSION = "user_request_record_audio_permission";
//用户是否请求过相机权限
public static final String USER_REQUEST_CAMERA_PERMISSION = "user_request_camera_permission";
//用户当前定位省份
public static final String USER_LOCATION_PROVICE_NAME = "user_location_provice_name";
//用户当前定位城市
public static final String USER_LOCATION_CITY_NAME = "user_location_city_name";
//用户当前定位县区
public static final String USER_LOCATION_COUNTY_NAME = "user_location_county_name";
public static final String USER_SELECT_PROVICE_NAME = "user_select_provice_name";
//用户当前定位城市
public static final String USER_SELECT_CITY_NAME = "user_select_city_name";
//用户当前定位县区
public static final String USER_SELECT_COUNTY_NAME = "user_select_county_name";
//首页用户定位code
public static final String INDEX_LOCATION_PROVINCE_CODE = "index_location_province_code";
//用户当前定位县区
public static final String INDEX_LOCATION_CITY_CODE = "index_location_city_code";
//首页用户定位code
public static final String INDEX_SELECT_PROVINCE_CODE = "index_select_province_code";
//用户当前定位县区
public static final String INDEX_SELECT_CITY_CODE = "index_select_city_code";
//用户提交的企业会员认证结果
public static final String USER_REVIEW_STATUS_CODE = "user_review_status_code";
//用户是否为企业会员
public static final String USER_IS_VIP_FLAG = "user_is_vip_flag";
//用户资料中的个人手机号码
public static final String USER_INFO_PHONE_NUMBER = "user_info_phone_number";
//用户资料中头像
@@ -69,36 +34,6 @@ public class ConstantUtil {
public static final String USER_NICKNAME = "user_nickname";
//企业名称
public static final String USER_UPLOAD_COMPANY_NAME = "user_upload_company_name";
//单位名称
public static final String GOODS_UNIT_JSON = "goods_unit_json";
//运费json
public static final String TRANS_COST_JSON = "trans_cost_json";
//品牌名称json
public static final String GOODS_BRAND_JSON = "goods_brand_json";
//所有字典
public static final String MRO_BRAND_DICT_JSON = "mro_dict_json";
/********************大宗相关*********************/
public static final String DICT_DATAS_JSON = "dict_datas_json";
//生成订单来自何处 100商品详情 200购物车
public static final int CREAT_ORDER_FROM_PRODUCT_CODE = 100;
public static final int CREAT_ORDER_FROM_SHOPCAR_CODE = 200;
/********************订单相关*********************/
public static final int ORDER_ALLCODE = -1;//全部
public static final int ORDER_DAIZHIFU = 0;//待支付
public static final int ORDER_WEIFAHUO = 1;//未发货
public static final int ORDER_DAISHOUHUO = 2;//待收货
public static final int ORDER_YIWANCHENG = 3;//已完成
public static final int ORDER_CANCELED = 4;
public static final int ORDER_SHOUHOU = 5;//待收货
public static final int ORDER_BUFENFAHUO = 7;//待收货
public static final int ORDER_DAIQUEREN = 8;//待确认
public static final int ORDER_JIESUANZHONG = 9;//结算中
public static final int ORDER_CHULIZHONG = 0;//处理中
/*******询价单********/
public static final int XJ_BAOJIAZHONG_CODE = 0;//报价中
public static final int XJ_YIBAOJIA_CODE = 1;//已报价
public static final int XJ_YICHEXIAO_CODE = 2;//已撤销
//权限申请请求
public static final int PERMISSION_QUEST = 500;
/***************权限相关*******************/
@@ -106,13 +41,17 @@ public class ConstantUtil {
public static final int ACCESS_FINE_LOCATION = 1;
//授权读取文件
public static final int WRITE_FILE_PERMISSON_CPDE = 1001;
public static final int POST_NOTIFICATIONS_PERMISSION_CODE = 1002;
//读取文件
public static final int READ_EXTERNAL_STORAGE = 2;
//相机权限
public static final int CAMERA_AUTH_CODE = 1003;
//快速下单,下载模板到手机
public static final int DOWN_TEMPLATE_CODE = 1004;
/***************设备相关*******************/
//设备解绑成功广播
public static final String DEVICE_UNBIND_SUCCESS = "device_unbind_success";
//应用语言设置
public static final String KEY_APP_LANGUAGE = "key_app_language";
}

View File

@@ -10,6 +10,7 @@ public class EventMessage {
public static final String DATA_REFRESH_EVENT = "data_refresh_event";
public static final String USER_LOGIN_LOST = "user_login_lost";
public static final String CODE_LOGIN_LOST = "code_login_lost";
public static final String ACTIVITY_FINISH = "activity_finish";
public static final String WX_PAY_SUCCESS = "wx_pay_success";
public static final String USER_LOGIN_SUCCESS = "user_login_success";
public static final String HISTORY_SESSIONID_DELETE_SUCCESS = "history_sessionid_delete_success";

View File

@@ -11,12 +11,13 @@ import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.ckkj.water.mine.entity.MineUserInfoBean;
import com.google.gson.Gson;
import com.ckkj.water.fate.util.LunarDateLoader;
import com.ckkj.water.login.entity.LoginEntity;
import com.ckkj.water.network.NetWorkManager;
import com.ckkj.water.utils.dataUtil.SharePreUtil;
import com.ckkj.water.utils.manager.MingDengUserInfoManager;
import com.ckkj.water.utils.manager.LanguageManager;
import com.ckkj.water.utils.manager.ThemeManager;
import com.ckkj.water.utils.manager.WaterUserInfoManager;
import java.util.List;
@@ -66,8 +67,12 @@ public class MyApplication extends Application {
super.onCreate();
instance = this;
context = getApplicationContext();
// 异步预加载农历数据缓存
LunarDateLoader.preloadLunarJson(this);
// 恢复应用语言设置
applyStoredLocale();
// 检查并设置默认主题色(如果之前没有设置过,默认设为白色主题)
applyDefaultTheme();
boolean isUserAgreed = SharePreUtil.getBoolean(ConstantUtil.SHARE_PRE_NAME,context,ConstantUtil.PRIVACY_POLICY_AGREED);
if(isUserAgreed){
@@ -76,16 +81,11 @@ public class MyApplication extends Application {
NetWorkManager.getInstance().init(this);
AppConfig.USER_LOGIN = SharePreUtil.getBoolean(ConstantUtil.SHARE_PRE_NAME,context,ConstantUtil.USER_LOGIN);
AppConfig.USER_TOKEN = SharePreUtil.getString(ConstantUtil.SHARE_PRE_NAME,context,ConstantUtil.USER_TOKEN);
AppConfig.USER_SET_BIRTH_INFO = SharePreUtil.getBoolean(ConstantUtil.SHARE_PRE_NAME,context,ConstantUtil.USER_HAS_SET_BIRTH);
//单位JSON
AppConfig.PRODUCT_UNIT_JSON = SharePreUtil.getString(ConstantUtil.SHARE_PRE_NAME,context,ConstantUtil.GOODS_UNIT_JSON);
AppConfig.PRODUCT_BRAND_JSON = SharePreUtil.getString(ConstantUtil.SHARE_PRE_NAME,context,ConstantUtil.GOODS_BRAND_JSON);
AppConfig.USER_ID = SharePreUtil.getString(ConstantUtil.SHARE_PRE_NAME,context, ConstantUtil.USER_ID);
String userInfoJson = SharePreUtil.getString(ConstantUtil.SHARE_PRE_NAME,context, ConstantUtil.USER_INFO_JSON_CONTENT);
if(!TextUtils.isEmpty(userInfoJson)){
LoginEntity entity = new Gson().fromJson(userInfoJson, LoginEntity.class);
MingDengUserInfoManager.getInstance().setEntity(entity);
MingDengUserInfoManager.getInstance().setUserInfo(entity.getUser_info());
MineUserInfoBean entity = new Gson().fromJson(userInfoJson, MineUserInfoBean.class);
WaterUserInfoManager.getInstance().setUserInfo(entity);
}
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
@@ -156,11 +156,12 @@ public class MyApplication extends Application {
public void onActivityDestroyed(Activity activity) {}
});
AutoSizeConfig.getInstance()
// 是否使用设备真实尺寸
.setUseDeviceSize(true)
// 是否支持 fragment
.setCustomFragment(true);
// AutoSizeConfig.getInstance()
// // 是否使用设备真实尺寸
// .setUseDeviceSize(true)
// // 是否支持 fragment
// .setCustomFragment(true);
android.util.Log.e("TEST", "hello");
}
private boolean shouldInit() {
@@ -184,4 +185,26 @@ public class MyApplication extends Application {
public static Context getContext() {
return context;
}
/**
* 应用启动时恢复保存的语言设置
* 使用 AppCompatDelegate.setApplicationLocales() API
*/
private void applyStoredLocale() {
LanguageManager.applyStoredLocale();
}
/**
* 检查并设置默认主题色
* 如果之前没有设置过主题(默认为 MODE_FOLLOW_SYSTEM则设置为白色主题MODE_LIGHT
*/
private void applyDefaultTheme() {
int currentTheme = ThemeManager.getCurrentTheme(context);
if (currentTheme == ThemeManager.MODE_FOLLOW_SYSTEM) {
ThemeManager.setCurrentTheme(context, ThemeManager.MODE_LIGHT);
ThemeManager.applyTheme(ThemeManager.MODE_LIGHT);
} else {
ThemeManager.applyTheme(currentTheme);
}
}
}

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;
@@ -38,9 +39,12 @@ import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.core.app.ActivityCompat;
import androidx.viewbinding.ViewBinding;
import com.ckkj.water.dialog.LoadingProgressDialog;
import com.ckkj.water.utils.manager.ThemeManager;
import com.esafirm.imagepicker.features.ImagePicker;
import com.hjq.permissions.OnPermissionCallback;
import com.hjq.permissions.XXPermissions;
@@ -98,12 +102,18 @@ 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;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
applyTheme();
super.onCreate(savedInstanceState);
try {
EventBus.getDefault().register(this);
@@ -134,6 +144,13 @@ public abstract class BaseActivity<T extends ViewBinding, P extends IPresenter>
initData(savedInstanceState);
}
/**
* 所有Activity主题一致
*/
protected void applyTheme() {
int nightMode = ThemeManager.getCurrentTheme(this);
AppCompatDelegate.setDefaultNightMode(nightMode);
}
/**
* 初始化窗口
@@ -177,6 +194,7 @@ public abstract class BaseActivity<T extends ViewBinding, P extends IPresenter>
if (message.msg.equals(EventMessage.USER_LOGIN_LOST)) {
Log.d("DINGDING","Activity接收一次");
Log.d("DINGDING", "触发堆栈:", new Throwable("401触发堆栈追踪"));
Log.d("DINGDING", "当前Activity状态: isFinishing=" + isFinishing() + ", isDestroyed=" + isDestroyed());
// 检查Activity状态如果正在销毁则不处理
@@ -259,6 +277,16 @@ public abstract class BaseActivity<T extends ViewBinding, P extends IPresenter>
} catch (Exception e) {
e.printStackTrace();
}
LoadingProgressDialog progressDialog = mProgressLoadingDialog;
try {
if (progressDialog != null && mContext != null) {
mProgressLoadingDialog = null;
progressDialog.dismiss();
}
} catch (Exception e) {
e.printStackTrace();
}
}
//每个activity都需要创建Presenter
@@ -301,6 +329,7 @@ public abstract class BaseActivity<T extends ViewBinding, P extends IPresenter>
@Override
public void hideLoading() {
loadingDialog.dismiss();
}
@Override
@@ -343,6 +372,9 @@ public abstract class BaseActivity<T extends ViewBinding, P extends IPresenter>
protected LoadingDialog mLoadingDialog;
/**
* 普通加载loading
*/
public void showLoadingDialog() {
LoadingDialog dialog = mLoadingDialog;
if (dialog == null && mContext != null) {
@@ -365,6 +397,33 @@ public abstract class BaseActivity<T extends ViewBinding, P extends IPresenter>
}
}
/**
* 进度条加载loading
*/
protected LoadingProgressDialog mProgressLoadingDialog;
public void showProgressLoadingDialog(int progress) {
LoadingProgressDialog dialog = mProgressLoadingDialog;
if (dialog == null && mContext != null) {
dialog = new LoadingProgressDialog(mContext);
// 不可触摸取消
dialog.setCanceledOnTouchOutside(false);
// 强制取消关闭界面
dialog.setCancelable(true);
dialog.setOnCancelListener(dialog1 -> finish());
dialog.setProgress( progress);
mProgressLoadingDialog = dialog;
} else {
dialog.dismiss();
}
if (dialog != null && mContext != null) {
try {
dialog.show();
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void onNetChange(int netModel) {
@@ -468,16 +527,34 @@ public abstract class BaseActivity<T extends ViewBinding, P extends IPresenter>
*
* @param title 标题
* @param hint 提示内容2
* @param content 内容
* @param touchOutsideCancel 弹窗类型
* @param cancelText 取消 文案
* @param confirmText 确定 文案
* @param click
*/
public void showEditDialog(String title, String hint, 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);
editText.setHint(hint);
if(!TextUtils.isEmpty(content)){
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);
@@ -1025,7 +1102,8 @@ public abstract class BaseActivity<T extends ViewBinding, P extends IPresenter>
false, false, true,
title,
content,
"不允许", "允许",
getString(R.string.button_deny),
getString(R.string.button_allow),
new BaseFragment.OnDialogClick() {
@Override
public void onCancelClick() {
@@ -1113,4 +1191,77 @@ 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
);
editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(4)});
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;
case INPUT_TYPE_NORMAL:
editText.setFilters(new InputFilter[]{new InputFilter.LengthFilter(20)});
break;
default:
editText.setInputType(
InputType.TYPE_CLASS_TEXT
);
break;
}
}
}

View File

@@ -11,6 +11,8 @@ import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
import android.text.InputFilter;
import android.text.InputType;
import android.text.TextUtils;
import android.util.Log;
import android.view.Gravity;
@@ -20,6 +22,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -30,6 +34,7 @@ import androidx.core.app.ActivityCompat;
import androidx.fragment.app.Fragment;
import androidx.viewbinding.ViewBinding;
import com.ckkj.water.utils.show.ToastUtil;
import com.esafirm.imagepicker.features.ImagePicker;
import com.ckkj.water.AppConfig;
import com.ckkj.water.ConstantUtil;
@@ -70,6 +75,12 @@ public abstract class BaseFragment<T extends ViewBinding,P extends IPresenter> e
protected static final int REQUEST_EVENT_CODE_10002 = 10002;
protected 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;//数字+字母
private static final AtomicBoolean isDialogShown = new AtomicBoolean(false); // 使用 AtomicBoolean 保证线程安全
@@ -90,6 +101,10 @@ public abstract class BaseFragment<T extends ViewBinding,P extends IPresenter> e
return EasyPermissions.hasPermissions(mContext, permission);
}
public boolean hasRecordPermission(String[] permission) {
return EasyPermissions.hasPermissions(mContext, permission);
}
/**
* 获取权限
*
@@ -108,7 +123,8 @@ public abstract class BaseFragment<T extends ViewBinding,P extends IPresenter> e
false, false,
title,
content,
"不允许", "允许",
getString(R.string.button_deny),
getString(R.string.button_allow),
new BaseFragment.OnDialogClick() {
@Override
public void onCancelClick() {
@@ -134,6 +150,50 @@ public abstract class BaseFragment<T extends ViewBinding,P extends IPresenter> e
});
}
/**
* 一次获取多个权限
*
* @param permis
* @param title
* @param content
* @param requestCode String... permis
*/
public void getMorePermissionRequst(String[] permis, String title, String content, int requestCode) {
if (EasyPermissions.hasPermissions(mContext, permis)) {
// 已有权限,不需要处理
return;
}
// 还没权限,弹自定义确认弹窗
showDialogtextInterBackKey(
null,
false,
false,
title,
content,
getString(R.string.button_deny),
getString(R.string.button_allow),
new BaseFragment.OnDialogClick() {
@Override
public void onCancelClick() {
}
@Override
public void onConfirmClick() {
showPermissionDialog(mContext.getResources().getDrawable(R.mipmap.img_permission_file),
true, title, content);
boolean shouldShowRationale = false;
for (String p : permis) {
if (ActivityCompat.shouldShowRequestPermissionRationale(mContext, p)) {
shouldShowRationale = true;
break;
}
}
// 使用 EasyPermissions 请求权限(系统决定是否弹系统框)
EasyPermissions.requestPermissions(mContext, content, requestCode, permis);
}
},false);
}
/**
* 判断Fragment是否真正对用户可见
*/
@@ -860,6 +920,202 @@ public abstract class BaseFragment<T extends ViewBinding,P extends IPresenter> e
});
}
/**
* +
*
* @param title 标题
* @param hint 提示内容2
* @param content 内容
* @param touchOutsideCancel 弹窗类型
* @param cancelText 取消 文案
* @param confirmText 确定 文案
* @param click
*/
public void showEditDialog(String title,
String hint,
String content,
boolean touchOutsideCancel,
String cancelText,
String confirmText,
int inputType,
final BaseActivity.OnDialogClick click) {
View view = getLayoutInflater().inflate(
R.layout.public_dialog_input_text,
null
);
EditText editText = view.findViewById(R.id.et_input);
//设置输入类型
setEditInputType(editText,inputType);
editText.setHint(hint);
if(!TextUtils.isEmpty(content)){
editText.setText(content);
editText.setSelection(content.length());
}
setTextView(R.id.tv_title,title,view);
if (!TextUtils.isEmpty(confirmText)
&& !TextUtils.isEmpty(cancelText)) {
((TextView)view.findViewById(R.id.btnLeft))
.setText(cancelText);
((TextView)view.findViewById(R.id.btnRight))
.setText(confirmText);
}
view.findViewById(R.id.btnLeft)
.setOnClickListener(v -> {
if(dialog!=null&&dialog.isShowing())
dialog.dismiss();
if(click!=null)
click.onCancelClick();
});
view.findViewById(R.id.btnRight)
.setOnClickListener(v -> {
String value=editText.getText()
.toString()
.trim();
if(TextUtils.isEmpty(value)){
ToastUtil.showShort(mContext,hint);
return;
}
if(dialog!=null&&dialog.isShowing())
dialog.dismiss();
if(click!=null)
click.onConfirmClisk(value);
});
AlertDialog.Builder builder;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
builder = new AlertDialog.Builder(
mContext,
AlertDialog.THEME_HOLO_LIGHT
);
} else {
builder = new AlertDialog.Builder(mContext);
}
builder.setInverseBackgroundForced(true);
builder.setView(view);
dialog = builder.show();
editText.requestFocus();
editText.postDelayed(() -> {
if (editText != null) {
InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT);
}
}
}, 200);
dialog.setCanceledOnTouchOutside(
touchOutsideCancel
);
try {
((View)dialog.getWindow()
.getDecorView()
.findViewById(R.id.contentDialog)
.getParent()
.getParent())
.setBackgroundColor(
getResources().getColor(
R.color.transparent
)
);
} catch (Exception e) {
e.printStackTrace();
}
}
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

@@ -1,51 +0,0 @@
package com.ckkj.water.base.entity;
import java.io.Serializable;
/**
* @author fengxiaoyang
* @date 2025/7/28
* @description TODO: 描述该类的作用
*/
public class MineUserInfoBean implements Serializable {
private UserInfoEntity userInfo;
private boolean hasEightCharacters;
private String constellation;
private String birthTime;
public String getConstellation() {
return constellation;
}
public void setConstellation(String constellation) {
this.constellation = constellation;
}
public String getBirthTime() {
return birthTime;
}
public void setBirthTime(String birthTime) {
this.birthTime = birthTime;
}
public UserInfoEntity getUserInfo() {
return userInfo;
}
public void setUserInfo(UserInfoEntity userInfo) {
this.userInfo = userInfo;
}
public boolean isHasEightCharacters() {
return hasEightCharacters;
}
public void setHasEightCharacters(boolean hasEightCharacters) {
this.hasEightCharacters = hasEightCharacters;
}
}

View File

@@ -1,900 +0,0 @@
package com.ckkj.water.chat;
import static android.app.Activity.RESULT_OK;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.MotionEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.core.widget.NestedScrollView;
import androidx.recyclerview.widget.GridLayoutManager;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.ckkj.water.AppConfig;
import com.ckkj.water.EventMessage;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseFragment;
import com.ckkj.water.chat.adapter.ChatQuestionDetailAdapter;
import com.ckkj.water.chat.adapter.ChatZixunAdapter;
import com.ckkj.water.chat.entity.ChatQuestionEntity;
import com.ckkj.water.custom.GridSpacingItemDecoration;
import com.ckkj.water.databinding.IndexLayoutBinding;
import com.ckkj.water.chat.adapter.IndexChatAdapter;
import com.ckkj.water.chat.adapter.IndexOptionAdapter;
import com.ckkj.water.chat.adapter.IndexEnterHintAdapter;
import com.ckkj.water.chat.adapter.IndexQuickAskAdapter;
import com.ckkj.water.chat.entity.CharHistoryEntity;
import com.ckkj.water.chat.entity.ChatAiMsgEntity;
import com.ckkj.water.chat.entity.ChatMultiItem;
import com.ckkj.water.chat.entity.IndexTemplateEntity;
import com.ckkj.water.chat.mvp.IndexContract;
import com.ckkj.water.chat.mvp.IndexImpl;
import com.ckkj.water.chat.mvp.IndexPresenter;
import com.ckkj.water.dialog.ContentReportDialog;
import com.ckkj.water.login.LoginActivity;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.plate.entity.BaziResultBean;
import com.ckkj.water.plate.entity.PaipanCache;
import com.ckkj.water.utils.click.ClickUtil;
import com.ckkj.water.utils.manager.ColorManage;
import com.ckkj.water.utils.show.ToastUtil;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import de.greenrobot.event.Subscribe;
import de.greenrobot.event.ThreadMode;
import android.content.ClipData;
import android.content.ClipboardManager;
/**
* @author fengxiaoyang
* @date 2025/7/7
* @description TODO: 描述该类的作用
*/
public class ChatFragment extends BaseFragment<IndexLayoutBinding, IndexPresenter> implements IndexContract.IndexView {
//顶部 adapter
private IndexOptionAdapter mOptionAdapter;
private ChatZixunAdapter mZixunQuestAdapter;
private ChatQuestionDetailAdapter mQuesDetailAdapter;
//快速提问 adapter
private IndexQuickAskAdapter mQuickAskAdapter;
private List<IndexTemplateEntity> mQuickAskList = new ArrayList<>();
private List<ChatMultiItem> mChatMsgList = new ArrayList<>();
//聊天 adapter
private IndexChatAdapter mChatAdapter;
//欢迎语 adapter
private IndexEnterHintAdapter mHintAdapter;
//输入的内容
private String mCurrentInputContent = "";
//当前options 的 pos
private int mCurrentStreamPos = 0;
//模板数据 list
private List<IndexTemplateEntity> mIndexTemList = new ArrayList<>();
//输出是否完成
private boolean mIsStreamStop = true;
//当前选中 option 的 typeID
private String mSelectTypeID = "1";
//会话 ID
private String mSessionId = "";
// 添加流式数据累积变量
private StringBuilder mStreamContentBuilder = new StringBuilder();
private ChatAiMsgEntity mCurrentStreamEntity = null;
// 智能滚动管理
private boolean mIsUserScrolling = false;
// 优化防抖机制
private Handler mUpdateHandler = new Handler();
private Runnable mUpdateRunnable;
private List<ChatQuestionEntity> mQuesTypeList = new ArrayList<>();
private boolean mIsShowQuestions = true;
@Override
public void initData(Bundle state) {
mOptionAdapter = new IndexOptionAdapter(mContext, R.layout.index_device_list_item, mIndexTemList);
viewBinding.rvOptions.setAdapter(mOptionAdapter);
mQuickAskAdapter = new IndexQuickAskAdapter(mContext, R.layout.index_home_quick_ask_item, mQuickAskList);
viewBinding.rvQuickAsk.setAdapter(mQuickAskAdapter);
mChatAdapter = new IndexChatAdapter(mContext, mChatMsgList, viewBinding.rvChat);
viewBinding.rvChat.setAdapter(mChatAdapter);
//提问类型
GridLayoutManager manager = new GridLayoutManager(mContext, 2);
viewBinding.rvChatQuick.setLayoutManager(manager);
List<ChatQuestionEntity.SubheadingsDTO> subList = new ArrayList<>();
mQuesDetailAdapter = new ChatQuestionDetailAdapter(R.layout.chat_quest_detail_item, subList);
viewBinding.rvChatQuick.setAdapter(mQuesDetailAdapter);
viewBinding.rvChatQuick.addItemDecoration(new GridSpacingItemDecoration(2, 10, true));
mZixunQuestAdapter = new ChatZixunAdapter(mContext, R.layout.chat_zixun_type_item, mQuesTypeList);
viewBinding.rvChatType.setAdapter(mZixunQuestAdapter);
// 设置聊天RecyclerView的LayoutManager禁用滚动
androidx.recyclerview.widget.LinearLayoutManager chatLayoutManager =
new androidx.recyclerview.widget.LinearLayoutManager(mContext);
chatLayoutManager.setStackFromEnd(false);
viewBinding.rvChat.setLayoutManager(chatLayoutManager);
// 禁用RecyclerView的滚动让NestedScrollView处理滚动
viewBinding.rvChat.setNestedScrollingEnabled(false);
mHintAdapter = new IndexEnterHintAdapter(mContext, R.layout.index_enter_hint_item, new ArrayList<>());
viewBinding.rvEnterHint.setAdapter(mHintAdapter);
setAdapterClick();
// 添加EditText的焦点监听器
viewBinding.editInput.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
// EditText获取焦点时延迟滚动到底部确保键盘弹出后再滚动
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
scrollNestedScrollViewToBottom();
}
}, 300); // 延迟300ms等待键盘弹出
}
}
});
viewBinding.editInput.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (s != null) {
mCurrentInputContent = s.toString().trim();
if (TextUtils.isEmpty(s.toString().trim())) {
viewBinding.imgSend.setImageDrawable(ContextCompat.getDrawable(mContext, R.mipmap.icon_index_send));
viewBinding.editInput.setHint("请输入您的问题");
} else {
viewBinding.imgSend.setImageDrawable(ContextCompat.getDrawable(mContext, R.mipmap.icon_index_send_b));
}
}
}
});
//请求页面模板数据
mPresenter.getIndexTemplateDatas();
mPresenter.getChatTemplateDatas();
//组件增加点击区域
ClickUtil.addClickPart(viewBinding.imgCloseQuick, 30);
ClickUtil.addClickPart(viewBinding.imgRefresh, 30);
ClickUtil.addClickPart(viewBinding.imgHistory, 30);
// 设置智能滚动监听
setupSmartScrollListener();
// 设置页面点击监听,用于隐藏键盘
setupPageClickListener();
// 检查是否有传递过来的参数
checkAndHandleBundle();
}
/**
* 检查并处理传递过来的Bundle参数
*/
private void checkAndHandleBundle() {
Bundle args = getArguments();
if (args != null) {
handleBirthInfoBundle(args);
}
}
/**
* 处理出生信息Bundle参数
*
* @param bundle 包含出生信息的Bundle
*/
public void handleBirthInfoBundle(Bundle bundle) {
if (bundle != null) {
BaziResultBean baseEntity = PaipanCache.get();
// 获取出生信息
BaziResultBean.BaseInfoDTO birthInfo = baseEntity.getBaseInfo();
BaziResultBean.DetailInfoDTO.SizhuInfoDTO sizhuInfoDTO = baseEntity.getDetailInfo().getSizhuInfo();
BaziResultBean.DetailInfoDTO.DayunInfoDTO defaulLiunian = (BaziResultBean.DetailInfoDTO.DayunInfoDTO) bundle.getSerializable("defaul_liunian");
if (birthInfo != null) {
viewBinding.editInput.setText("我叫" + birthInfo.getName() + "," +
((birthInfo.getSex().equals("乾造") ? "" : "") + "," +
"生于公历" + birthInfo.getGongli() + "," + "当前大运为" + defaulLiunian.getDayunStartYear() + "--" +
defaulLiunian.getDayunEndYear() + "" + defaulLiunian.getDayunStartGanzhi() + "大运,四柱为"
+ sizhuInfoDTO.getYear().getTg() + sizhuInfoDTO.getYear().getDz() + "" +
sizhuInfoDTO.getMonth().getTg() + sizhuInfoDTO.getMonth().getDz() + "" +
sizhuInfoDTO.getDay().getTg() + sizhuInfoDTO.getDay().getDz() + "" +
sizhuInfoDTO.getHour().getTg() + sizhuInfoDTO.getHour().getDz() + "," +
"现在需要你根据我的八字,给我具体分析一下我的事业、感情、健康、财富、格局、喜用神"));
}
}
}
/**
* 当Fragment参数更新时调用用于处理onNewIntent的情况
*/
@Override
public void onResume() {
super.onResume();
// 检查是否有新的参数需要处理
Bundle args = getArguments();
if (args != null && args.getBoolean("goto_index_fragment", false)) {
// 处理参数
handleBirthInfoBundle(args);
// 清除标记,避免重复处理
args.remove("goto_index_fragment");
}
}
@Subscribe(threadMode = ThreadMode.MainThread)
public void onGetMessage(EventMessage message) {
//用户添加地址成功
if (message.getEventStrKey().equals(EventMessage.HISTORY_SESSIONID_DELETE_SUCCESS)) {
if (message.getMsg().equals(mSessionId)) {
// viewBinding.linearOptionHint.setVisibility(View.VISIBLE);
mChatMsgList.clear();
mChatAdapter.setNewData(mChatMsgList);
mSessionId = "";
}
}
}
/**
* 设置页面点击监听器
*/
private void setupPageClickListener() {
viewBinding.rvChat.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// 当用户触摸NestedScrollView时隐藏键盘并清除焦点
if (event.getAction() == MotionEvent.ACTION_DOWN) {
hideKeyboardAndClearFocus();
}
return false;
}
});
}
/**
* 隐藏键盘并清除EditText焦点
*/
private void hideKeyboardAndClearFocus() {
// 清除EditText焦点
viewBinding.editInput.clearFocus();
// 隐藏键盘
InputMethodManager imm = (InputMethodManager) mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(viewBinding.editInput.getWindowToken(), 0);
}
}
/**
* 滚动NestedScrollView到底部
*/
private void scrollNestedScrollViewToBottom() {
if (viewBinding.nestedScrollChat != null) {
// 获取NestedScrollView的子视图RecyclerView
View childView = viewBinding.nestedScrollChat.getChildAt(0);
if (childView != null) {
// 滚动到底部
viewBinding.nestedScrollChat.smoothScrollTo(0, childView.getHeight());
}
}
}
/**
* 设置智能滚动监听器修改为监听NestedScrollView
*/
private void setupSmartScrollListener() {
// 监听NestedScrollView的滚动
viewBinding.nestedScrollChat.setOnScrollChangeListener(new NestedScrollView.OnScrollChangeListener() {
@Override
public void onScrollChange(NestedScrollView v, int scrollX, int scrollY, int oldScrollX, int oldScrollY) {
// 检测用户是否向上滑动
if (scrollY < oldScrollY) {
// 用户向上滑动
mIsUserScrolling = true;
} else if (scrollY > oldScrollY) {
// 用户向下滑动
mIsUserScrolling = true;
}
// 检查是否滚动到底部
if (scrollY >= (v.getChildAt(0).getHeight() - v.getHeight())) {
// 滚动到底部,恢复自动滚动
mIsUserScrolling = false;
}
}
});
}
private void setAdapterClick() {
//顶部的选项 adapter 点击事件
mOptionAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
@Override
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
if (mCurrentStreamPos != position) {
if (mIsStreamStop) {
viewBinding.rlQuickAsk.setVisibility(View.GONE);
mOptionAdapter.setSelectPos(position);
setOptionSelStyle(position);
mCurrentStreamPos = position;
mSessionId = "";
mSelectTypeID = mIndexTemList.get(position).getId();
} else {
ToastUtil.showShort(mContext, "Ai解答中请稍后操作");
}
}
}
});
//咨询类型 adapter
mZixunQuestAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
@Override
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
if (mIsStreamStop) {
mZixunQuestAdapter.setSelectPos(position);
mQuesDetailAdapter.setNewData(mQuesTypeList.get(position).getSubheadings());
} else {
ToastUtil.showShort(mContext, "Ai解答中请稍后操作");
}
}
});
//快速提问
mQuesDetailAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
@Override
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
if (!AppConfig.USER_LOGIN) {
goTo(mContext, LoginActivity.class, null);
return;
}
if (!mIsStreamStop) {
ToastUtil.showShort(mContext, "Ai解答中请稍后操作");
return;
}
String quickAskContent = mQuesTypeList.get(mZixunQuestAdapter.getmSelPos()).
getSubheadings().get(position).getContent();
sendMsgToAi(quickAskContent);
}
});
//流式输出,是否完成
mChatAdapter.setOnStreamCompleteListener(new IndexChatAdapter.OnStreamCompleteListener() {
@Override
//此时已经流式输出完成
public void onStreamComplete(int position) {
mCurrentStreamPos = position;
viewBinding.imgSend.setVisibility(View.VISIBLE);
viewBinding.imgStop.setVisibility(View.GONE);
mIsStreamStop = true;
}
@Override
public void onStreamStop() {
viewBinding.imgSend.setVisibility(View.VISIBLE);
viewBinding.imgStop.setVisibility(View.GONE);
mIsStreamStop = true;
}
});
mChatAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
@Override
public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
switch (view.getId()) {
case R.id.img_jb:
ContentReportDialog cancelDialog = new ContentReportDialog(mContext, R.layout.dialog_report_reason, "");
cancelDialog.show();
cancelDialog.setOnDialogListener(new ContentReportDialog.DialogListener() {
@Override
public void callBackDatas(String reason) {
showLoadingDialog();
//举报
mPresenter.commitReport(reason);
}
});
break;
}
}
});
// 设置长按菜单监听器
mChatAdapter.setOnItemLongClickMenuListener(new IndexChatAdapter.OnItemLongClickMenuListener() {
@Override
public void onCopyClick(ChatMultiItem item, int position) {
// 处理复制操作
String content = item.getMessage().getContent();
if (!TextUtils.isEmpty(content)) {
ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
ClipData clip = ClipData.newPlainText("chat_content", content);
clipboard.setPrimaryClip(clip);
ToastUtil.showShort(mContext, "内容已复制");
}
}
@Override
public void onReportClick(ChatMultiItem item, int position) {
// 处理举报操作
ContentReportDialog cancelDialog = new ContentReportDialog(mContext, R.layout.dialog_report_reason, "");
cancelDialog.show();
cancelDialog.setOnDialogListener(new ContentReportDialog.DialogListener() {
@Override
public void callBackDatas(String reason) {
showLoadingDialog();
//举报
mPresenter.commitReport(reason);
}
});
}
});
// 添加长按监听器
// mChatAdapter.setOnItemChildLongClickListener(new BaseQuickAdapter.OnItemChildLongClickListener() {
// @Override
// public boolean onItemChildLongClick(BaseQuickAdapter adapter, View view, int position) {
// switch (view.getId()) {
// case R.id.tv_text: // 文本内容的view id
// XPopup.fixLongClick(view); // 能保证弹窗弹出后下层的View无法滑动
//
// // 获取当前消息内容
// ChatMultiItem item = mChatMsgList.get(position);
// String content = item.getMessage().getContent();
//
// // 使用链式调用确保atView在asAttachList之前
// new XPopup.Builder(getContext())
// .hasShadowBg(false)
// .atView(view) // 使用atView而不是watchView
// .asAttachList(new String[]{"复制", "举报"},
// null,
// new OnSelectListener() {
// @Override
// public void onSelect(int selectPosition, String text) {
// switch (selectPosition) {
// case 0: // 复制
// if (!TextUtils.isEmpty(content)) {
// ClipboardManager clipboard = (ClipboardManager) mContext.getSystemService(Context.CLIPBOARD_SERVICE);
// ClipData clip = ClipData.newPlainText("chat_content", content);
// clipboard.setPrimaryClip(clip);
// ToastUtil.showShort(mContext, "内容已复制");
// }
// break;
// case 1: // 举报
// ContentReportDialog cancelDialog = new ContentReportDialog(mContext, R.layout.dialog_report_reason, "");
// cancelDialog.show();
// cancelDialog.setOnDialogListener(new ContentReportDialog.DialogListener() {
// @Override
// public void callBackDatas(String reason) {
// showLoadingDialog();
// //举报
// mPresenter.commitReport(reason);
// }
// });
// break;
// }
// }
// })
// .show();
// return true;
// }
// return false;
// }
// });
}
/**
* 停止输出
*/
private void stopSteamOutput() {
// 停止当前网络请求
mPresenter.stopCurrentRequest();
// 获取当前正在流式输出的位置
int currentPosition = mChatMsgList.size() - 1;
// 标记当前消息为被停止状态
mChatAdapter.markAsStopped(currentPosition);
// 停止适配器中的流式输出
mChatAdapter.setStreamerStop(currentPosition);
// 清除当前流式输出位置
mChatAdapter.setCurrentStreamingPosition(-1);
// 强制更新当前item以显示停止标识
mChatAdapter.notifyItemChanged(currentPosition);
// 恢复UI状态
viewBinding.imgSend.setVisibility(View.VISIBLE);
viewBinding.imgStop.setVisibility(View.GONE);
mIsStreamStop = true;
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (data != null) {
if (resultCode == RESULT_OK) {
switch (requestCode) {
case REQUEST_EVENT_CODE_10001:
String sessionId = data.getStringExtra("session_id");
String numerologyType = data.getStringExtra("numerology_type");
int showSelPos = -1;
for (int i = 0; i < mIndexTemList.size(); i++) {
if (numerologyType.equals(mIndexTemList.get(i).getId())) {
showSelPos = i;
}
}
if (mCurrentStreamPos != showSelPos) {
}
if (mIsStreamStop) {
viewBinding.rlQuickAsk.setVisibility(View.GONE);
mOptionAdapter.setSelectPos(showSelPos);
setOptionSelStyle(showSelPos);
mCurrentStreamPos = showSelPos;
mSelectTypeID = mIndexTemList.get(showSelPos).getId();
if (!TextUtils.isEmpty(sessionId)) {
mPresenter.getChatDetailByID(sessionId);
}
} else {
ToastUtil.showShort(mContext, "Ai解答中请稍后操作");
}
break;
}
}
}
}
/**
* 发送消息给 AI
*/
private void sendMsgToAi(String senMsg) {
if (!AppConfig.USER_LOGIN) {
goTo(mContext, LoginActivity.class, null);
return;
}
viewBinding.cardQuestion.setVisibility(View.GONE);
viewBinding.imgClose.setImageDrawable(ContextCompat.getDrawable(mContext, R.mipmap.icon_chat_down));
mIsShowQuestions = false;
viewBinding.linearOptionHint.setVisibility(View.GONE);
// 重置滚动状态
mIsUserScrolling = false;
// 清理适配器缓存
mChatAdapter.clearCache();
// 创建用户消息
ChatAiMsgEntity chatMsgEntity = new ChatAiMsgEntity();
chatMsgEntity.setSessionId(mSessionId);
chatMsgEntity.setType(mSelectTypeID);
chatMsgEntity.setContent(senMsg);
ChatMultiItem userEntity = new ChatMultiItem(ChatMultiItem.TYPE_TEXT_RIGHT, chatMsgEntity);
mChatMsgList.add(userEntity);
mChatAdapter.notifyItemInserted(mChatMsgList.size() - 1);
// 创建AI回复消息初始为空
mCurrentStreamEntity = new ChatAiMsgEntity();
mCurrentStreamEntity.setSessionId(mSessionId);
mCurrentStreamEntity.setType(mSelectTypeID);
mCurrentStreamEntity.setContent(""); // 初始为空
ChatMultiItem aiEntity = new ChatMultiItem(ChatMultiItem.TYPE_TEXT_LEFT, mCurrentStreamEntity);
mChatMsgList.add(aiEntity);
// 设置当前流式输出位置
int aiMessagePosition = mChatMsgList.size() - 1;
mChatAdapter.setCurrentStreamingPosition(aiMessagePosition);
mChatAdapter.notifyItemInserted(aiMessagePosition);
// 重置流式数据累积器
mStreamContentBuilder.setLength(0);
// 清空输入框
viewBinding.editInput.setText("");
// 设置UI状态
viewBinding.imgSend.setVisibility(View.GONE);
viewBinding.imgStop.setVisibility(View.VISIBLE);
// 发送消息后自动滚动到底部
scrollToBottomAfterSend();
// 发起流式请求
mPresenter.talkToAi(senMsg, mSelectTypeID, mSessionId);
}
@Override
public IndexPresenter createPresenter() {
return new IndexPresenter(new IndexImpl());
}
@Override
public void getTalkResultSuccess(ChatAiMsgEntity bean) {
hideDialogLoading();
}
/**
* 首页数据获取成功
*
* @param list
*/
@Override
public void getIndexTemplateSuccess(List<IndexTemplateEntity> list) {
if (list != null && list.size() != 0) {
mIndexTemList = list;
mOptionAdapter.setNewData(mIndexTemList);
setAdapterClick();
setOptionSelStyle(0);
mSelectTypeID = mIndexTemList.get(0).getId();
}
}
/**
* 切换设置显示内容
*
* @param pos
*/
private void setOptionSelStyle(int pos) {
try {
mChatMsgList.clear();
mChatAdapter.setNewData(mChatMsgList);
// 清理缓存
mChatAdapter.clearCache();
// viewBinding.linearOptionHint.setVisibility(View.VISIBLE);
IndexTemplateEntity defaulEntity = mIndexTemList.get(pos);
viewBinding.tvTitleExpert.setText(defaulEntity.getTitle() + "专家");
viewBinding.tvSelectOptionLable.setText(defaulEntity.getTitle() + "专家");
viewBinding.tvSelectOptionSlogan.setText(defaulEntity.getSubheading());
if (ColorManage.getInstance().isParsableColor(defaulEntity.getColor())) {
viewBinding.tvSelectOptionSlogan.setTextColor(Color.parseColor(defaulEntity.getColor()));
} else {
viewBinding.tvSelectOptionSlogan.setTextColor(ContextCompat.getColor(mContext, R.color.color_zhu));
}
//提示语
List<String> hintMsgList = new ArrayList<>();
hintMsgList = Arrays.asList(defaulEntity.getIntermediateContent().split("#"));
mHintAdapter.setNewData(hintMsgList);
//快速提问
mQuickAskList.clear();
List<String> quickAskList = new ArrayList<>();
quickAskList = Arrays.asList(defaulEntity.getQuickQuestions().split("#"));
for (String content : quickAskList) {
IndexTemplateEntity entity = new IndexTemplateEntity();
entity.setColor(defaulEntity.getColor());
entity.setBlackcolor(defaulEntity.getBlackcolor());
entity.setSpliteItemContent(content);
mQuickAskList.add(entity);
}
mQuickAskAdapter.setNewData(mQuickAskList);
} catch (Exception e) {
}
}
@Override
public void onNetChange(int netModel) {
}
@Override
public void showError(String message) {
hideDialogLoading();
ToastUtil.showShort(mContext, message);
// 流式传输完成恢复UI状态
viewBinding.imgSend.setVisibility(View.VISIBLE);
viewBinding.imgStop.setVisibility(View.GONE);
mIsStreamStop = true;
// 可以在这里处理会话ID等
if (mCurrentStreamEntity != null && !TextUtils.isEmpty(mCurrentStreamEntity.getSessionId())) {
mSessionId = mCurrentStreamEntity.getSessionId();
}
// 清理当前流式实体
mCurrentStreamEntity = null;
mStreamContentBuilder.setLength(0);
}
@Override
public void logOut(BaseBean bean) {
}
// 实现流式消息接收回调
@Override
public void onStreamMessageReceived(String messageChunk) {
mIsStreamStop = false;
if (mCurrentStreamEntity != null) {
// 更新当前AI消息的内容
mCurrentStreamEntity.setContent(messageChunk);
// 取消之前的更新任务
if (mUpdateRunnable != null) {
mUpdateHandler.removeCallbacks(mUpdateRunnable);
}
mUpdateRunnable = () -> {
// 只更新最后一个item
int lastPosition = mChatMsgList.size() - 1;
if (lastPosition >= 0) {
// 使用更轻量的更新方式,避免重新布局
mChatAdapter.notifyItemChanged(lastPosition, "content_only");
// 流式输出时如果用户没有手动滑动则自动滚动NestedScrollView
if (!mIsUserScrolling) {
smoothScrollNestedScrollView();
}
}
};
// 增加延迟时间,减少更新频率
mUpdateHandler.postDelayed(mUpdateRunnable, 100);
}
}
// 实现流式完成回调
@Override
public void onStreamComplete(String complateMsg) {
// 流式传输完成恢复UI状态
viewBinding.imgSend.setVisibility(View.VISIBLE);
viewBinding.imgStop.setVisibility(View.GONE);
mIsStreamStop = true;
// 清除当前流式输出位置
mChatAdapter.setCurrentStreamingPosition(-1);
// 注意:这里不标记为停止状态,因为这是正常完成
// 可以在这里处理会话ID等
if (mCurrentStreamEntity != null && !TextUtils.isEmpty(mCurrentStreamEntity.getSessionId())) {
mSessionId = mCurrentStreamEntity.getSessionId();
}
// 清理当前流式实体
mCurrentStreamEntity = null;
mStreamContentBuilder.setLength(0);
// 假设你最后一个消息是流式的 placeholder现在替换它
ChatMultiItem lastMessage = mChatMsgList.get(mChatMsgList.size() - 1);
lastMessage.getMessage().setContent(complateMsg); // 你可以解析 Markdown 变 PlainText或保留 Markdown 渲染
// mChatAdapter.notifyItemChanged(mChatMsgList.size() - 1); // 更新 UI
int lastPos = mChatAdapter.getItemCount() - 1;
mChatAdapter.updateTipsVisibility(lastPos);
}
/**
* 根据会话 id 查询对话详情
*
* @param list
*/
@Override
public void getChatDetailSuccess(List<ChatAiMsgEntity> list) {
mChatMsgList.clear();
if (list != null && list.size() != 0) {
mSessionId = list.get(0).getSessionId();
}
for (ChatAiMsgEntity entity : list) {
ChatMultiItem item;
if (entity.getRole().equals("user")) {
item = new ChatMultiItem(ChatMultiItem.TYPE_TEXT_RIGHT, entity);
} else {
item = new ChatMultiItem(ChatMultiItem.TYPE_TEXT_LEFT, entity);
}
mChatMsgList.add(item);
}
mChatAdapter.setNewData(mChatMsgList);
viewBinding.linearOptionHint.setVisibility(View.GONE);
}
@Override
public void loginLost() {
if (isFragmentVisible()) {
handleLoginLost();
}
stopSteamOutput();
if (mChatAdapter != null) {
mChatAdapter.remove(mChatMsgList.size() - 1);
}
}
/**
* 获取咨询模板成功
*
* @param list
*/
@Override
public void getChatZixunSuccess(List<ChatQuestionEntity> list) {
this.mQuesTypeList = list;
if (list != null) {
mZixunQuestAdapter.setNewData(list);
mQuesDetailAdapter.setNewData(list.get(0).getSubheadings());
setAdapterClick();
}
}
/**
* 举报成功
* @param bean
*/
@Override
public void commitReportSuccess(BaseBean bean) {
ToastUtil.showShort(mContext, "星语明灯感谢您的反馈");
hideDialogLoading();
}
// 实现sessionId接收回调
@Override
public void onSessionReceived(String sessionId) {
// 保存sessionId
mSessionId = sessionId;
}
@Override
public void getHistoryDataSuccess(CharHistoryEntity entity) {
}
@Override
public void deleteHistorySuccess(BaseBean bean, int delPos) {
}
/**
* 平滑滚动NestedScrollView到底部
*/
private void smoothScrollNestedScrollView() {
if (mChatAdapter != null && mChatAdapter.getItemCount() > 0) {
// 直接滚动NestedScrollView到底部
viewBinding.nestedScrollChat.post(() -> {
viewBinding.nestedScrollChat.fullScroll(View.FOCUS_DOWN);
});
}
}
/**
* 发送消息后滚动到底部
*/
private void scrollToBottomAfterSend() {
new Handler().postDelayed(() -> {
if (isFragmentVisible()) {
viewBinding.nestedScrollChat.fullScroll(View.FOCUS_DOWN);
}
}, 100);
}
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
}
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
}
}

View File

@@ -1,225 +0,0 @@
package com.ckkj.water.chat;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.gyf.immersionbar.ImmersionBar;
import com.ckkj.water.chat.entity.ChatQuestionEntity;
import com.scwang.smart.refresh.footer.ClassicsFooter;
import com.scwang.smart.refresh.layout.api.RefreshHeader;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity;
import com.ckkj.water.custom.view.CustomRefreshHeader;
import com.ckkj.water.databinding.IndexHistoryLayoutBinding;
import com.ckkj.water.chat.adapter.ChatHistoryAdapter;
import com.ckkj.water.chat.entity.CharHistoryEntity;
import com.ckkj.water.chat.entity.ChatAiMsgEntity;
import com.ckkj.water.chat.entity.IndexTemplateEntity;
import com.ckkj.water.chat.mvp.IndexContract;
import com.ckkj.water.chat.mvp.IndexImpl;
import com.ckkj.water.chat.mvp.IndexPresenter;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.utils.show.ToastUtil;
import java.util.ArrayList;
import java.util.List;
/**
* @author fengxiaoyang
* @date 2025/7/11
* @description 历史记录
*/
public class ChatHistoryActivity extends BaseActivity<IndexHistoryLayoutBinding, IndexPresenter>
implements IndexContract.IndexView {
private int mCurrentPage = 1;
private ChatHistoryAdapter mAdapter;
//历史记录
private List<CharHistoryEntity.Sessions> mChatList = new ArrayList<>();
@Override
public IndexPresenter createPresenter() {
return new IndexPresenter(new IndexImpl());
}
@Override
public int getLayoutResId(Bundle savedInstanceState) {
return 0;
}
@Override
public void initData(Bundle savedInstanceState) {
super.initData(savedInstanceState);
ImmersionBar.with(this)
.statusBarColor(R.color.white)
.statusBarDarkFont(true)
.titleBar(viewBinding.rlTitle) // 替换为你的标题栏View
.keyboardEnable(true)
.navigationBarDarkIcon(true)
.init();
viewBinding.includedTitle.tvTitle.setText("历史记录");
viewBinding.includedTitle.imgBack.setOnClickListener(c ->finish());
mAdapter = new ChatHistoryAdapter(mContext, R.layout.history_item_layout, mChatList);
viewBinding.rvData.setAdapter(mAdapter);
setAdapterClick();
// 设置自定义刷新头
RefreshHeader header = new CustomRefreshHeader(mContext);
viewBinding.refresh.setRefreshHeader(header);
viewBinding.refresh.setRefreshFooter(new ClassicsFooter(mContext));
viewBinding.refresh.setOnRefreshListener(layout -> {
mCurrentPage = 1;
getHistoryDatas();
});
viewBinding.refresh.setOnLoadMoreListener(layout -> {
mCurrentPage++;
getHistoryDatas();
});
showLoadingDialog();
getHistoryDatas();
}
private void setAdapterClick() {
mAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
@Override
public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
switch (view.getId()){
case R.id.img_delete:
showLoadingDialog();
mPresenter.deleteHistoryBySessionID(mChatList.get(position).getSessionId(),position);
break;
}
}
});
mAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
@Override
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
Intent intent = new Intent();
intent.putExtra("session_id",mChatList.get(position).getSessionId());
intent.putExtra("numerology_type",mChatList.get(position).getNumerologyType());
setResult(RESULT_OK,intent);
finish();
}
});
}
private void getHistoryDatas() {
mPresenter.getHistoryDatas(mCurrentPage);
}
/**
* 查询历史数据成功 g
*
* @param entity
*/
@Override
public void getHistoryDataSuccess(CharHistoryEntity entity) {
hideDialogLoading();
int totalPage = entity.getPages();
viewBinding.refresh.finishRefresh(300);
viewBinding.refresh.finishLoadMore(300);
List<CharHistoryEntity.Sessions> list = entity.getSessions();
if (mCurrentPage == 1) {
mChatList.clear(); // 只在刷新时清除
mChatList = list;
} else {
mChatList.addAll(list);
}
mAdapter.setNewData(mChatList);
// 4. 判断是否还有更多数据
if (totalPage == mCurrentPage || list == null || list.size() == 0) {
// ✅ 告诉 SmartRefreshLayout 没有更多数据了
viewBinding.refresh.setNoMoreData(true);
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);
}
}
/**
* 删除历史会话记录成功
* @param bean
*/
@Override
public void deleteHistorySuccess(BaseBean bean, int delPos) {
hideDialogLoading();
mAdapter.remove(delPos);
if(mAdapter.getItemCount() == 0){
viewBinding.refresh.setVisibility(View.GONE);
viewBinding.relNoresult.setVisibility(View.VISIBLE);
viewBinding.relNoMore.setVisibility(View.GONE);
}
}
@Override
public void getChatDetailSuccess(List<ChatAiMsgEntity> list) {
}
@Override
public void loginLost() {
}
@Override
public void getChatZixunSuccess(List<ChatQuestionEntity> list) {
}
@Override
public void commitReportSuccess(BaseBean bean) {
}
@Override
public void getTalkResultSuccess(ChatAiMsgEntity bean) {
}
@Override
public void getIndexTemplateSuccess(List<IndexTemplateEntity> list) {
}
@Override
public void onStreamMessageReceived(String messageChunk) {
}
@Override
public void onStreamComplete(String complateMsg) {
}
@Override
public void onSessionReceived(String sessionId) {
}
@Override
public void showError(String message) {
viewBinding.refresh.finishRefresh(500);
viewBinding.refresh.finishLoadMore(200);
ToastUtil.showShort(mContext, message);
hideDialogLoading();
}
@Override
public void logOut(BaseBean bean) {
}
}

View File

@@ -1,75 +0,0 @@
package com.ckkj.water.chat.adapter;
import android.content.Context;
import android.widget.ImageView;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.ckkj.water.R;
import com.ckkj.water.chat.entity.CharHistoryEntity;
import com.ckkj.water.utils.time.ZYTimeUtils;
import java.util.List;
/**
* @author fengxiaoyang
* @date 2025/7/11
* @description TODO: 描述该类的作用
*/
public class ChatHistoryAdapter extends BaseQuickAdapter<CharHistoryEntity.Sessions, BaseViewHolder> {
private Context mContext;
public ChatHistoryAdapter(Context context,int layoutResId, @Nullable List<CharHistoryEntity.Sessions> data) {
super(layoutResId, data);
this.mContext = context;
}
@Override
protected void convert(BaseViewHolder helper, CharHistoryEntity.Sessions item) {
ImageView imgType = helper.getView(R.id.img_logo);
switch (item.getNumerologyType()){
case "1"://八字命理
imgType.setImageDrawable(ContextCompat.getDrawable(mContext,R.mipmap.icon_index_bzml));
helper.setBackgroundRes(R.id.tv_type_name,R.drawable.shape_history_type_bzml_bg);
helper.setTextColor(R.id.tv_type_name,ContextCompat.getColor(mContext,R.color.color_319EF4));
break;
case "2"://紫微斗数
imgType.setImageDrawable(ContextCompat.getDrawable(mContext,R.mipmap. icon_index_zwds));
helper.setBackgroundRes(R.id.tv_type_name,R.drawable.shape_history_type_zwds_bg);
helper.setTextColor(R.id.tv_type_name,ContextCompat.getColor(mContext,R.color.color_B85CE6));
break;
case "3"://风水堪舆
imgType.setImageDrawable(ContextCompat.getDrawable(mContext,R.mipmap.icon_index_fsky));
helper.setBackgroundRes(R.id.tv_type_name,R.drawable.shape_history_type_fsky_bg);
helper.setTextColor(R.id.tv_type_name,ContextCompat.getColor(mContext,R.color.color_24B326));
break;
case "6"://奇门遁甲
imgType.setImageDrawable(ContextCompat.getDrawable(mContext,R.mipmap.icon_index_qmdj));
helper.setBackgroundRes(R.id.tv_type_name,R.drawable.shape_history_type_qmdj_bg);
helper.setTextColor(R.id.tv_type_name,ContextCompat.getColor(mContext,R.color.color_2A5CAA));
break;
case "10"://五行起名
imgType.setImageDrawable(ContextCompat.getDrawable(mContext,R.mipmap. icon_index_wxqm));
helper.setBackgroundRes(R.id.tv_type_name,R.drawable.shape_history_type_wxqm_bg);
helper.setTextColor(R.id.tv_type_name,ContextCompat.getColor(mContext,R.color.color_E69900));
break;
default:
imgType.setImageDrawable(ContextCompat.getDrawable(mContext,R.mipmap.icon_index_bzml));
helper.setBackgroundRes(R.id.tv_type_name,R.drawable.shape_history_type_bzml_bg);
helper.setTextColor(R.id.tv_type_name,ContextCompat.getColor(mContext,R.color.color_319EF4));
break;
}
helper.setText(R.id.tv_type_content,item.getFirstUserMessage());
helper.setText(R.id.tv_type_name,item.getSessionName());
String chatTime = ZYTimeUtils.formatDateOrOneYearAgo(item.getCreateTime());
helper.setText(R.id.tv_time,chatTime);
helper.addOnClickListener(R.id.img_delete);
// helper.addOnClickListener(R.id.rl_title);
// helper.addOnClickListener(R.id.tv_type_content);
// helper.addOnClickListener(R.id.img_logo);
}
}

View File

@@ -1,29 +0,0 @@
package com.ckkj.water.chat.adapter;
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.chat.entity.ChatQuestionEntity;
import java.util.List;
/**
* @author fengxiaoyang
* @date 2025/8/19
* @description TODO: 描述该类的作用
*/
public class ChatQuestionDetailAdapter extends BaseQuickAdapter<ChatQuestionEntity.SubheadingsDTO, BaseViewHolder> {
public ChatQuestionDetailAdapter(int layoutResId, @Nullable List<ChatQuestionEntity.SubheadingsDTO> data) {
super(layoutResId, data);
}
@Override
protected void convert(BaseViewHolder helper, ChatQuestionEntity.SubheadingsDTO item) {
helper.setText(R.id.tv_title,item.getTitle());
helper.setText(R.id.tv_content,item.getContent());
}
}

View File

@@ -1,49 +0,0 @@
package com.ckkj.water.chat.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.chat.entity.ChatQuestionEntity;
import java.util.List;
/**
* @author fengxiaoyang
* @date 2025/7/7
* @description TODO: 描述该类的作用
*/
public class ChatZixunAdapter extends BaseQuickAdapter<ChatQuestionEntity, BaseViewHolder> {
private Context mContext;
private int mSelPos = 0;
public ChatZixunAdapter(Context context, int layoutResId, @Nullable List<ChatQuestionEntity> data) {
super(layoutResId, data);
this.mContext = context;
}
@Override
protected void convert(BaseViewHolder helper, ChatQuestionEntity item) {
helper.setText(R.id.tv_content,item.getTeb());
if(helper.getLayoutPosition() == mSelPos){
helper.setTextColor(R.id.tv_content,mContext.getColor(R.color.color_zhu));
helper.setBackgroundRes(R.id.tv_content, R.drawable.shape_chat_zixun_type);
}else{
helper.setTextColor(R.id.tv_content,mContext.getColor(R.color.color_7A8699));
helper.setBackgroundRes(R.id.tv_content, R.drawable.shape_trans_touming);
}
}
public void setSelectPos(int position) {
this.mSelPos = position;
notifyDataSetChanged();
}
public int getmSelPos() {
return mSelPos;
}
}

View File

@@ -1,244 +0,0 @@
package com.ckkj.water.chat.adapter;
import android.content.Context;
import android.text.Spanned;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.chad.library.adapter.base.BaseMultiItemQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.ckkj.water.R;
import com.ckkj.water.chat.entity.ChatAiMsgEntity;
import com.ckkj.water.chat.entity.ChatMultiItem;
import com.ckkj.water.utils.show.ToastUtil;
import com.ckkj.water.utils.stream.MarkdownStreamer;
import com.lxj.xpopup.XPopup;
import com.lxj.xpopup.interfaces.OnSelectListener;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.noties.markwon.Markwon;
/**
* @author fengxiaoyang
* @date 2025/7/7
* @description TODO: 描述该类的作用
*/
public class IndexChatAdapter extends BaseMultiItemQuickAdapter<ChatMultiItem, BaseViewHolder> {
private Context mContext;
private RecyclerView recyclerView;
private OnStreamCompleteListener streamCompleteListener;
//convert() 中每次绑定 item 时缓存 MarkdownStreamer 实例
private final Map<Integer, MarkdownStreamer> streamerMap = new HashMap<>();
// 新增Markdown 解析器
private Markwon markwon;
// 新增:当前正在流式输出的位置
private int mCurrentStreamingPosition = -1;
// 用于标记哪个 position 需要显示 llTips
// 在 IndexChatAdapter 类中新增字段
private Integer pendingTipsCheckPos = null;
// 新增:记录被停止的位置
private final Map<Integer, Boolean> stoppedPositions = new HashMap<>();
public IndexChatAdapter(Context context, List<ChatMultiItem> data, RecyclerView recyclerView) {
super(data);
this.mContext = context;
this.recyclerView = recyclerView;
this.markwon = Markwon.create(context);
addItemType(ChatMultiItem.TYPE_TEXT_LEFT, R.layout.item_text_left);
addItemType(ChatMultiItem.TYPE_TEXT_RIGHT, R.layout.item_text_right);
}
public void setStreamerStop(int position) {
MarkdownStreamer streamer = streamerMap.get(position);
if (streamer != null) {
streamer.stopStreaming();
streamerMap.remove(position);
}
if (mCurrentStreamingPosition == position) {
mCurrentStreamingPosition = -1;
}
}
/**
* 标记某个位置为被停止的状态
*/
public void markAsStopped(int position) {
stoppedPositions.put(position, true);
// 通知该位置更新显示
notifyItemChanged(position);
}
public interface OnStreamCompleteListener {
void onStreamComplete(int position);
void onStreamStop();
}
public void setOnStreamCompleteListener(OnStreamCompleteListener listener) {
this.streamCompleteListener = listener;
}
// 新增:长按菜单项点击回调接口
public interface OnItemLongClickMenuListener {
void onCopyClick(ChatMultiItem item, int position);
void onReportClick(ChatMultiItem item, int position);
}
private OnItemLongClickMenuListener mLongClickMenuListener;
public void setOnItemLongClickMenuListener(OnItemLongClickMenuListener listener) {
this.mLongClickMenuListener = listener;
}
/**
* 设置当前正在流式输出的位置
*/
public void setCurrentStreamingPosition(int position) {
mCurrentStreamingPosition = position;
}
/**
* 对外暴露的方法:检查某个 position 是否为最后一条,并据此显示/隐藏 llTips。
* 仅记录“待检查的位置”并触发该 position 的刷新;实际显示逻辑在 convert() 末尾统一处理。
*/
public void updateTipsVisibility(int position) {
if (position < 0 || position >= getItemCount()) return;
pendingTipsCheckPos = position;
// notifyItemChanged(position);
notifyDataSetChanged();
}
public void setPendingTipsCheckPos(Integer pendingTipsCheckPos) {
this.pendingTipsCheckPos = pendingTipsCheckPos;
}
@Override
protected void convert(BaseViewHolder helper, ChatMultiItem item) {
ChatAiMsgEntity msgEntity = item.getMessage();
TextView tvContent = helper.getView(R.id.tv_text);
LinearLayout llThinking = helper.getView(R.id.linear_thinking);
LinearLayout llStop = helper.getView(R.id.linear_stop);
LinearLayout llTips = helper.getView(R.id.linear_tips);
int position = helper.getBindingAdapterPosition();
helper.addOnClickListener(R.id.img_jb);
// helper.addOnLongClickListener(R.id.tv_text);
if (item.getItemType() == ChatMultiItem.TYPE_TEXT_LEFT) {
String content = msgEntity.getContent();
if(TextUtils.isEmpty(content)){
llThinking.setVisibility(View.VISIBLE);
tvContent.setText("");
llStop.setVisibility(View.GONE);
} else {
llThinking.setVisibility(View.GONE);
// 优化流式输出更新逻辑
if (position == mCurrentStreamingPosition) {
// 当前正在流式输出的位置
try {
Spanned spanned = markwon.toMarkdown(content);
tvContent.setText(spanned);
} catch (Exception e) {
tvContent.setText(content);
}
// 显示停止标识(如果被停止)
if (stoppedPositions.containsKey(position) && stoppedPositions.get(position)) {
llStop.setVisibility(View.VISIBLE);
llTips.setVisibility(View.VISIBLE);
} else {
llStop.setVisibility(View.GONE);
}
} else {
// 历史消息,直接显示完整内容
try {
Spanned spanned = markwon.toMarkdown(content);
tvContent.setText(spanned);
} catch (Exception e) {
tvContent.setText(content);
}
// 历史消息也检查是否被停止
if (stoppedPositions.containsKey(position) && stoppedPositions.get(position)) {
llStop.setVisibility(View.VISIBLE);
llTips.setVisibility(View.VISIBLE);
} else {
llStop.setVisibility(View.GONE);
}
}
}
Log.i("mingdenglog","XXXXX---position======" + pendingTipsCheckPos);
if (pendingTipsCheckPos != null && position == pendingTipsCheckPos) {
Log.i("mingdenglog","position======" + position);
Log.i("mingdenglog","getItemCount() - 1 ------" + (getItemCount() - 1) );
// 仅当是最后一条才显示 llTips否则隐藏
llTips.setVisibility(position == getItemCount() - 1 ? View.VISIBLE : View.GONE);
// 用一次即清
// pendingTipsCheckPos = null;
}else{
llTips.setVisibility(View.GONE);
}
}
switch (item.getItemType()) {
case ChatMultiItem.TYPE_TEXT_RIGHT:
tvContent.setText(msgEntity.getContent());
break;
}
// //必须要在事件发生之前就watch
final XPopup.Builder builder = new XPopup.Builder(mContext)
.hasShadowBg(false).watchView(tvContent);
tvContent.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
// 如果正在流式输出,则拦截长按事件,不执行任何操作
if (mCurrentStreamingPosition != -1) {
// 可以选择性地提示用户
ToastUtil.showShort(mContext, "Ai解答中请稍后操作");
return true; // 拦截事件
}
builder.asAttachList(new String[]{"复制", "举报"}, null, new OnSelectListener() {
@Override
public void onSelect(int menuPosition, String text) {
// 通过接口回调处理菜单项点击事件
if (mLongClickMenuListener != null) {
switch (menuPosition){
case 0: // 复制
mLongClickMenuListener.onCopyClick(item, position);
break;
case 1: // 举报
mLongClickMenuListener.onReportClick(item, position);
break;
}
}
}
}).show();
return false;
}
});
}
/**
* 清理缓存
*/
public void clearCache() {
streamerMap.clear();
stoppedPositions.clear();
mCurrentStreamingPosition = -1;
}
}

View File

@@ -1,38 +0,0 @@
package com.ckkj.water.chat.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 java.util.List;
/**
* @author fengxiaoyang
* @date 2025/7/7
* @description TODO: 描述该类的作用
*/
public class IndexEnterHintAdapter extends BaseQuickAdapter<String, BaseViewHolder> {
private Context mContext;
private int mSelPos = 0;
private String textColor;
private String bgColor;
public void setColor(String textColor,String bgColor){
this.textColor = textColor;
this.bgColor = bgColor;
}
public IndexEnterHintAdapter(Context context, int layoutResId, @Nullable List<String> data) {
super(layoutResId, data);
this.mContext = context;
}
@Override
protected void convert(BaseViewHolder helper, String item) {
helper.setText(R.id.tv_quick_ask_name,item);
}
}

View File

@@ -1,45 +0,0 @@
package com.ckkj.water.chat.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.chat.entity.IndexTemplateEntity;
import java.util.List;
/**
* @author fengxiaoyang
* @date 2025/7/7
* @description TODO: 描述该类的作用
*/
public class IndexOptionAdapter extends BaseQuickAdapter<IndexTemplateEntity, BaseViewHolder> {
private Context mContext;
private int mSelPos = 0;
public IndexOptionAdapter(Context context,int layoutResId, @Nullable List<IndexTemplateEntity> data) {
super(layoutResId, data);
this.mContext = context;
}
@Override
protected void convert(BaseViewHolder helper, IndexTemplateEntity item) {
// helper.setText(R.id.tv_option_name,item.getTitle());
// if(helper.getLayoutPosition() == mSelPos){
// helper.setTextColor(R.id.tv_option_name,mContext.getColor(R.color.white));
// helper.setBackgroundRes(R.id.tv_option_name, R.drawable.shape_index_option_selected);
// }else{
// helper.setTextColor(R.id.tv_option_name,mContext.getColor(R.color.color_zhu));
// helper.setBackgroundRes(R.id.tv_option_name, R.drawable.shape_index_option_un_selected);
// }
}
public void setSelectPos(int position) {
this.mSelPos = position;
notifyDataSetChanged();
}
}

View File

@@ -1,58 +0,0 @@
package com.ckkj.water.chat.adapter;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.util.TypedValue;
import android.view.View;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.ckkj.water.R;
import com.ckkj.water.chat.entity.IndexTemplateEntity;
import com.ckkj.water.utils.manager.ColorManage;
import java.util.List;
/**
* @author fengxiaoyang
* @date 2025/7/8
* @description TODO: 描述该类的作用
*/
public class IndexQuickAskAdapter extends BaseQuickAdapter<IndexTemplateEntity, BaseViewHolder> {
private Context context;
public IndexQuickAskAdapter(Context context,int layoutResId, @Nullable List<IndexTemplateEntity> data) {
super(layoutResId, data);
this.context = context;
}
@Override
protected void convert(BaseViewHolder helper, IndexTemplateEntity item) {
helper.setText(R.id.tv_quick_ask_name,item.getSpliteItemContent());
if(ColorManage.getInstance().isParsableColor(item.getColor())){
helper.setTextColor(R.id.tv_quick_ask_name, Color.parseColor(item.getColor()));
}else{
helper.setTextColor(R.id.tv_quick_ask_name, ContextCompat.getColor(mContext,R.color.color_zhu));
}
if(ColorManage.getInstance().isParsableColor(item.getBlackcolor())){
setRoundedBackground(helper.getView(R.id.tv_quick_ask_name),Color.parseColor(item.getBlackcolor()),30);
}else{
setRoundedBackground(helper.getView(R.id.tv_quick_ask_name),ContextCompat.getColor(mContext,R.color.color_E5F2FB),30);
}
}
private void setRoundedBackground(View view, int fillColor, float cornerRadiusDp) {
GradientDrawable drawable = new GradientDrawable();
drawable.setShape(GradientDrawable.RECTANGLE);
drawable.setColor(fillColor); // 设置填充色
float radiusPx = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP, cornerRadiusDp, view.getResources().getDisplayMetrics()
);
drawable.setCornerRadius(radiusPx); // 设置圆角半径单位px
view.setBackground(drawable); // 应用背景
}
}

View File

@@ -1,122 +0,0 @@
package com.ckkj.water.chat.entity;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* @author fengxiaoyang
* @date 2025/7/11
* @description TODO: 描述该类的作用
*/
public class CharHistoryEntity implements Serializable {
private int total;
private List<Sessions> sessions;
private int pages;
private int pageSize;
private long userId;
private int pageNum;
public void setTotal(int total) {
this.total = total;
}
public int getTotal() {
return total;
}
public void setSessions(List<Sessions> sessions) {
this.sessions = sessions;
}
public List<Sessions> getSessions() {
return sessions;
}
public void setPages(int pages) {
this.pages = pages;
}
public int getPages() {
return pages;
}
public void setPageSize(int pageSize) {
this.pageSize = pageSize;
}
public int getPageSize() {
return pageSize;
}
public void setUserId(long userId) {
this.userId = userId;
}
public long getUserId() {
return userId;
}
public void setPageNum(int pageNum) {
this.pageNum = pageNum;
}
public int getPageNum() {
return pageNum;
}
public static class Sessions implements Serializable{
private String sessionId;
private long userId;
private String sessionName;
private String numerologyType;
private String firstUserMessage;
private Date createTime;
private Date updateTime;
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
public String getSessionId() {
return sessionId;
}
public void setUserId(long userId) {
this.userId = userId;
}
public long getUserId() {
return userId;
}
public void setSessionName(String sessionName) {
this.sessionName = sessionName;
}
public String getSessionName() {
return sessionName;
}
public void setNumerologyType(String numerologyType) {
this.numerologyType = numerologyType;
}
public String getNumerologyType() {
return numerologyType;
}
public void setFirstUserMessage(String firstUserMessage) {
this.firstUserMessage = firstUserMessage;
}
public String getFirstUserMessage() {
return firstUserMessage;
}
public void setCreateTime(Date createTime) {
this.createTime = createTime;
}
public Date getCreateTime() {
return createTime;
}
public void setUpdateTime(Date updateTime) {
this.updateTime = updateTime;
}
public Date getUpdateTime() {
return updateTime;
}
}
}

View File

@@ -1,75 +0,0 @@
package com.ckkj.water.chat.entity;
import java.io.Serializable;
/**
* @author fengxiaoyang
* @date 2025/7/7
* @description TODO: 描述该类的作用
*/
public class ChatAiMsgEntity implements Serializable {
private int id;
private String sessionId;
private String type;
private String content;
private String role;
private String messageOrder;
private String createTime;
public String getSessionId() {
return sessionId;
}
public void setSessionId(String sessionId) {
this.sessionId = sessionId;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getContent() {
return content;
}
public void setContent(String content) {
this.content = content;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getRole() {
return role;
}
public void setRole(String role) {
this.role = role;
}
public String getMessageOrder() {
return messageOrder;
}
public void setMessageOrder(String messageOrder) {
this.messageOrder = messageOrder;
}
public String getCreateTime() {
return createTime;
}
public void setCreateTime(String createTime) {
this.createTime = createTime;
}
}

View File

@@ -1,44 +0,0 @@
package com.ckkj.water.chat.entity;
import com.chad.library.adapter.base.entity.MultiItemEntity;
/**
* @author fengxiaoyang
* @date 2025/7/7
* @description TODO: 描述该类的作用
*/
public class ChatMultiItem implements MultiItemEntity {
public static final int TYPE_TEXT_LEFT = 1;
public static final int TYPE_TEXT_RIGHT = 2;
public static final int TYPE_IMAGE_LEFT = 3;
public static final int TYPE_IMAGE_RIGHT = 4;
public static final int TYPE_VOICE_LEFT = 5;
public static final int TYPE_VOICE_RIGHT = 6;
public static final int TYPE_TIME = 7; // 新增时间类型
private int itemType;
private ChatAiMsgEntity message;
public ChatMultiItem(int itemType, ChatAiMsgEntity message) {
this.itemType = itemType;
this.message = message;
}
@Override
public int getItemType() {
return itemType;
}
public void setItemType(int itemType) {
this.itemType = itemType;
}
public ChatAiMsgEntity getMessage() {
return message;
}
public void setMessage(ChatAiMsgEntity message) {
this.message = message;
}
}

View File

@@ -1,36 +0,0 @@
package com.ckkj.water.chat.entity;
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import java.util.List;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author fengxiaoyang
* @date 2025/8/19
* @description TODO: 描述该类的作用
*/
@NoArgsConstructor
@Data
public class ChatQuestionEntity implements Serializable {
@SerializedName("teb")
private String teb;
@SerializedName("subheadings")
private List<SubheadingsDTO> subheadings;
@SerializedName("id")
private String id;
@NoArgsConstructor
@Data
public static class SubheadingsDTO {
@SerializedName("title")
private String title;
@SerializedName("content")
private String content;
}
}

View File

@@ -1,111 +0,0 @@
package com.ckkj.water.chat.entity;
import java.io.Serializable;
/**
* @author fengxiaoyang
* @date 2025/7/8
* @description TODO: 描述该类的作用
*/
public class IndexTemplateEntity implements Serializable {
private String id;
private String title;
private String titletp;
private String color;
private String blackcolor;
private String subheading;
private String intermediateContent;
private String quickQuestions;
private String hide;
private String type;
private String spliteItemContent;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public String getTitletp() {
return titletp;
}
public void setTitletp(String titletp) {
this.titletp = titletp;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public String getBlackcolor() {
return blackcolor;
}
public void setBlackcolor(String blackcolor) {
this.blackcolor = blackcolor;
}
public String getSubheading() {
return subheading;
}
public void setSubheading(String subheading) {
this.subheading = subheading;
}
public String getIntermediateContent() {
return intermediateContent;
}
public void setIntermediateContent(String intermediateContent) {
this.intermediateContent = intermediateContent;
}
public String getQuickQuestions() {
return quickQuestions;
}
public void setQuickQuestions(String quickQuestions) {
this.quickQuestions = quickQuestions;
}
public String getHide() {
return hide;
}
public void setHide(String hide) {
this.hide = hide;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getSpliteItemContent() {
return spliteItemContent;
}
public void setSpliteItemContent(String spliteItemContent) {
this.spliteItemContent = spliteItemContent;
}
}

View File

@@ -1,78 +0,0 @@
package com.ckkj.water.chat.mvp;
import com.ckkj.water.base.IView;
import com.ckkj.water.chat.entity.CharHistoryEntity;
import com.ckkj.water.chat.entity.ChatAiMsgEntity;
import com.ckkj.water.chat.entity.ChatQuestionEntity;
import com.ckkj.water.chat.entity.IndexTemplateEntity;
import com.ckkj.water.network.BaseBean;
import java.util.List;
import java.util.Map;
/**
* @author fengxiaoyang
* @date 2025/7/7
* @description TODO: 描述该类的作用
*/
public class IndexContract {
public interface DataModel{
//发送消息给 AI
void sendMsgToAi(Map<String,Object> map, CallBack callBack);
//发送消息给 AI
void sendMsgToAi(Map<String,Object> map, AiStreamCallback callBack);
//停止当前请求
void stopCurrentRequest();
//获取首页数据
void getIndexTemplateDatas(CallBack callBack);
//获取会话历史
void getHistoryChatDatas(Map<String,Object> map, CallBack callBack);
//删除会话
void deleteHistoryBySessionID(Map<String,Object> map, CallBack callBack);
//根据会话 id查询记录详情
void getChatDetailById(Map<String,Object> map, CallBack callBack);
//首页咨询模板
void getChatemplateDatas(CallBack callBack);
//举报好友
void commitReport(Map<String,Object> map,CallBack callBack);
}
public interface CallBack<T> {
void success(T msg);
void fail(String msg);
}
public interface AiStreamCallback {
void onMessage(String messageChunk); // 每次返回一段内容
void onError(String error);
void onComplete(String complateContent); // 结束
void onSessionReceived(String sessionId); // 新增接收sessionId
void loginLost();
}
public interface IndexView extends IView {
//发送消息给 AI获取结果
void getTalkResultSuccess(ChatAiMsgEntity bean);
//获取首页数据成功
void getIndexTemplateSuccess(List<IndexTemplateEntity> list);
// 新增流式回调方法
void onStreamMessageReceived(String messageChunk);
//完成
void onStreamComplete(String complateMsg);
//接收sessionId
void onSessionReceived(String sessionId);
//查询历史记录成功 g
void getHistoryDataSuccess(CharHistoryEntity entity);
//删除会话成功
void deleteHistorySuccess(BaseBean bean,int delPos);
//根据 id 查询会话详情
void getChatDetailSuccess(List<ChatAiMsgEntity> list);
//登录失效
void loginLost();
//获取首页聊天模板
void getChatZixunSuccess(List<ChatQuestionEntity> list);
//提交举报反馈成功
void commitReportSuccess(BaseBean bean);
}
}

View File

@@ -1,575 +0,0 @@
package com.ckkj.water.chat.mvp;
import static android.content.ContentValues.TAG;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.ckkj.water.AppConfig;
import com.ckkj.water.chat.entity.CharHistoryEntity;
import com.ckkj.water.chat.entity.ChatAiMsgEntity;
import com.ckkj.water.chat.entity.ChatQuestionEntity;
import com.ckkj.water.chat.entity.IndexTemplateEntity;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.network.NetWorkManager;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.net.SocketTimeoutException;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
/**
* @author fengxiaoyang
* @date 2025/7/7
* @description TODO: 描述该类的作用
*/
public class IndexImpl implements IndexContract.DataModel{
@Override
public void sendMsgToAi(Map<String, Object> map, IndexContract.CallBack callBack) {
try {
NetWorkManager.getRequest().indexTalkToAi(NetWorkManager.getToken(),map)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean<ChatAiMsgEntity>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean<ChatAiMsgEntity> bean) {
if(bean.getCode() == 200){
callBack.success(bean.getData());
}else{
callBack.fail(bean.getMessage());
}
}
@Override
public void onError(@NonNull Throwable e) {
callBack.fail(e.getMessage());
}
@Override
public void onComplete() {
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 发送消息给 AI - 流式版本
* @param map
* @param callBack
*/
private final OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(60, TimeUnit.SECONDS) // 连接超时60秒
.readTimeout(60, TimeUnit.SECONDS) // 读取超时60秒
.writeTimeout(60, TimeUnit.SECONDS) // 写入超时60秒
.callTimeout(60, TimeUnit.SECONDS) // 整个调用超时60秒
.build();
private Call currentCall = null; // 新增保存当前请求的Call对象
private volatile boolean isRequestStopped = false; // 新增:标记是否已停止请求
@Override
public void sendMsgToAi(Map<String, Object> map, IndexContract.AiStreamCallback callBack) {
try {
String finalUrl = AppConfig.BASE_URL + "mobile/context?" + buildQueryParams(map);
Log.d(TAG, "Request URL: " + finalUrl);
Request request = new Request.Builder()
.addHeader("Authorization", "Bearer " + AppConfig.USER_TOKEN)
.url(finalUrl)
.get()
.build();
currentCall = client.newCall(request);
isRequestStopped = false;
currentCall.enqueue(new Callback() {
@Override
public void onFailure(Call call, IOException e) {
if (isRequestStopped) return; // 如果已停止,不处理失败回调
// 检查是否是超时异常
String errorMessage = e.getMessage();
if (e instanceof SocketTimeoutException ||
(errorMessage != null && errorMessage.contains("timeout"))) {
errorMessage = "请求超时,请稍后重试";
}
String finalErrorMessage = errorMessage;
new Handler(Looper.getMainLooper()).post(() -> {
if (callBack != null) callBack.onError(finalErrorMessage);
});
}
@Override
public void onResponse(Call call, Response response) {
if (isRequestStopped) return;
if (!response.isSuccessful()) {
new Handler(Looper.getMainLooper()).post(() -> {
if (callBack != null) callBack.onError("请求失败: " + response.code());
});
return;
}
InputStream inputStream = response.body().byteStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
try {
reader.mark(1024); // 标记流的当前位置
String peekLine = reader.readLine();
reader.reset(); // 回到标记位置
// 判断是否是标准 JSON非流式错误返回
if (peekLine != null && peekLine.trim().startsWith("{")) {
StringBuilder jsonBuilder = new StringBuilder();
String line;
while ((line = reader.readLine()) != null) {
jsonBuilder.append(line);
}
String responseJson = jsonBuilder.toString();
JSONObject json = new JSONObject(responseJson);
int code = json.optInt("code", 0);
String msg = json.optString("msg", "");
if (code == 401) {
new Handler(Looper.getMainLooper()).post(() -> {
if (callBack != null) callBack.loginLost();
});
} else {
new Handler(Looper.getMainLooper()).post(() -> {
if (callBack != null) callBack.onError("未知错误: " + msg);
});
}
return;
}
// 否则处理为 SSE 流式数据
handleSSEStream(reader, callBack);
} catch (Exception e) {
new Handler(Looper.getMainLooper()).post(() -> {
// if (callBack != null) callBack.onError("响应异常: " + e.getMessage());
});
} finally {
try {
reader.close();
response.close();
} catch (IOException ignored) {}
}
}
});
} catch (Exception e) {
new Handler(Looper.getMainLooper()).post(() -> {
if (callBack != null) callBack.onError("请求异常: " + e.getMessage());
});
}
}
private void handleSSEStream(BufferedReader reader, IndexContract.AiStreamCallback callBack) throws IOException {
String line;
StringBuilder accumulatedContent = new StringBuilder();
StringBuilder lastLineBuffer = new StringBuilder();
String currentEvent = null;
String currentData = null;
String sessionId = "";
while ((line = reader.readLine()) != null) {
if (isRequestStopped) break;
if (line.startsWith("event:")) {
currentEvent = line.substring(6).trim();
} else if (line.startsWith("data:")) {
currentData = line.substring(5).trim();
if ("session".equals(currentEvent)) {
try {
JsonObject sessionData = JsonParser.parseString(currentData).getAsJsonObject();
sessionId = sessionData.get("sessionId").getAsString();
if (callBack != null && !isRequestStopped) {
callBack.onSessionReceived(sessionId);
}
} catch (Exception e) {
Log.e(TAG, "解析session数据失败", e);
}
} else if ("chunk".equals(currentEvent)) {
try {
JsonObject chunkData = JsonParser.parseString(currentData).getAsJsonObject();
String content = chunkData.get("content").getAsString();
boolean hasNewline = chunkData.get("hasNewline").getAsBoolean();
if (isTableLineFragment(lastLineBuffer.toString(), content)) {
lastLineBuffer.append(content);
if (hasNewline) {
accumulatedContent.append(lastLineBuffer).append("\n");
lastLineBuffer.setLength(0);
}
} else {
if (lastLineBuffer.length() > 0) {
accumulatedContent.append(lastLineBuffer);
lastLineBuffer.setLength(0);
}
lastLineBuffer.append(content);
if (hasNewline) {
accumulatedContent.append(lastLineBuffer).append("\n");
lastLineBuffer.setLength(0);
}
}
final String currentContent = accumulatedContent.toString();
new Handler(Looper.getMainLooper()).post(() -> {
if (callBack != null && !isRequestStopped) {
callBack.onMessage(currentContent);
}
});
} catch (Exception e) {
accumulatedContent.append(currentData);
final String currentContent = accumulatedContent.toString();
new Handler(Looper.getMainLooper()).post(() -> {
if (callBack != null && !isRequestStopped) {
callBack.onMessage(currentContent);
}
});
}
} else if ("complete".equals(currentEvent)) {
// 先处理剩余的 lastLineBuffer 内容,但不通过 onMessage 回调
if (lastLineBuffer.length() > 0) {
accumulatedContent.append(lastLineBuffer);
// 注意:这里不调用 callBack.onMessage(),避免触发 onStreamMessageReceived
}
String complateContent = "";
try {
JsonObject completeData = JsonParser.parseString(currentData).getAsJsonObject();
complateContent = completeData.get("fullResponse").getAsString();
} catch (Exception e) {
Log.e(TAG, "解析complete数据失败", e);
}
final String finalComplateContent = complateContent;
new Handler(Looper.getMainLooper()).post(() -> {
if (callBack != null && !isRequestStopped) {
callBack.onComplete(finalComplateContent); // 只调用 onComplete
}
});
}
}
}
// 处理结束前残留内容
// if (lastLineBuffer.length() > 0 && !isRequestStopped) {
// accumulatedContent.append(lastLineBuffer);
// final String finalContent = accumulatedContent.toString();
// new Handler(Looper.getMainLooper()).post(() -> {
// if (callBack != null && !isRequestStopped) {
// callBack.onMessage(finalContent);
// }
// });
// }
}
/**
* 停止当前请求
*/
public void stopCurrentRequest() {
isRequestStopped = true;
if (currentCall != null && !currentCall.isCanceled()) {
currentCall.cancel();
currentCall = null;
}
}
/**
* 判断是否是表格分隔线片段
*/
private boolean isTableLineFragment(String last, String curr) {
return (last.contains("|") && last.contains("-")) || (curr.contains("|") && curr.contains("-"));
}
// 构造 GET 参数
private String buildQueryParams(Map<String, Object> map) {
StringBuilder sb = new StringBuilder();
for (Map.Entry<String, Object> entry : map.entrySet()) {
try {
if (sb.length() > 0) sb.append("&");
sb.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
sb.append("=");
sb.append(URLEncoder.encode(String.valueOf(entry.getValue()), "UTF-8"));
} catch (Exception e) {
Log.e(TAG, "Encoding error", e);
}
}
return sb.toString();
}
/**
* 获取首页数据
*/
@Override
public void getIndexTemplateDatas(IndexContract.CallBack callBack) {
try {
NetWorkManager.getRequest().getIndexTemplateDatas()
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean<List<IndexTemplateEntity>>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean<List<IndexTemplateEntity>> bean) {
if(bean.getCode() == 200){
callBack.success(bean.getData());
}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();
}
}
/**
* 获取会话历史记录
* @param map
* @param callBack
*/
@Override
public void getHistoryChatDatas(Map<String, Object> map, IndexContract.CallBack callBack) {
try {
NetWorkManager.getRequest().getHistoryList(NetWorkManager.getToken(),map)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean<CharHistoryEntity>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean<CharHistoryEntity> bean) {
if(bean.getCode() == 200){
callBack.success(bean.getData());
}else{
callBack.fail(bean.getMessage());
}
}
@Override
public void onError(@NonNull Throwable e) {
callBack.fail(e.getMessage());
}
@Override
public void onComplete() {
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 删除历史会话
* @param map
* @param callBack
*/
@Override
public void deleteHistoryBySessionID(Map<String, Object> map, IndexContract.CallBack callBack) {
try {
NetWorkManager.getRequest().deleteHistoryBySessionID(NetWorkManager.getToken(),map)
.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);
}else{
callBack.fail(bean.getMessage());
}
}
@Override
public void onError(@NonNull Throwable e) {
callBack.fail(e.getMessage());
}
@Override
public void onComplete() {
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 根据会话 id 查询详情
* @param map
* @param callBack
*/
@Override
public void getChatDetailById(Map<String, Object> map, IndexContract.CallBack callBack) {
try {
NetWorkManager.getRequest().getChatDetailById(NetWorkManager.getToken(),map)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean<List<ChatAiMsgEntity>>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean<List<ChatAiMsgEntity>> bean) {
if(bean.getCode() == 200){
callBack.success(bean.getData());
}else{
callBack.fail(bean.getMessage());
}
}
@Override
public void onError(@NonNull Throwable e) {
callBack.fail(e.getMessage());
}
@Override
public void onComplete() {
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 对话页,咨询模板获取
* @param callBack
*/
@Override
public void getChatemplateDatas(IndexContract.CallBack callBack) {
try {
NetWorkManager.getRequest().aitebcontent()
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean<List<ChatQuestionEntity>>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean<List<ChatQuestionEntity>> bean) {
if(bean.getCode() == 200){
callBack.success(bean.getData());
}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();
}
}
/**
* 举报反馈
* @param map
* @param callBack
*/
@Override
public void commitReport(Map<String, Object> map, IndexContract.CallBack callBack) {
try {
NetWorkManager.getRequest().commitReporting(NetWorkManager.getToken(),map)
.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.getData());
}else{
callBack.fail(bean.getMessage());
}
}
@Override
public void onError(@NonNull Throwable e) {
callBack.fail(e.getMessage());
}
@Override
public void onComplete() {
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -1,249 +0,0 @@
package com.ckkj.water.chat.mvp;
import com.ckkj.water.EventMessage;
import com.ckkj.water.base.BasePresenter;
import com.ckkj.water.chat.entity.CharHistoryEntity;
import com.ckkj.water.chat.entity.ChatAiMsgEntity;
import com.ckkj.water.chat.entity.ChatQuestionEntity;
import com.ckkj.water.chat.entity.IndexTemplateEntity;
import com.ckkj.water.network.BaseBean;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import de.greenrobot.event.EventBus;
/**
* @author fengxiaoyang
* @date 2025/7/7
* @description TODO: 描述该类的作用
*/
public class IndexPresenter extends BasePresenter<IndexContract.IndexView> {
private IndexImpl impl;
public IndexPresenter(IndexImpl impl) {
this.impl = impl;
}
/**
* 发送消息给 AI
*
* @param content
*/
// public void talkToAi(String content,String type) {
// Map<String,Object> map = new HashMap<>();
// map.put("content",content);
// map.put("type",type);
// map.put("sessionId","");
// impl.sendMsgToAi(map, new IndexContract.CallBack<ChatAiMsgEntity>() {
// @Override
// public void success(ChatAiMsgEntity bean) {
// if( IndexPresenter.this.getView() != null){
// IndexPresenter.this.getView().getTalkResultSuccess(bean);
// }
// }
//
// @Override
// public void fail(String msg) {
// if( IndexPresenter.this.getView() != null){
// IndexPresenter.this.getView().showError(msg);
// }
// }
// });
// }
public void talkToAi(String content, String type, String sessionID) {
Map<String, Object> map = new HashMap<>();
map.put("content", content);
map.put("type", type);
map.put("sessionId", sessionID);
impl.sendMsgToAi(map, new IndexContract.AiStreamCallback() {
@Override
public void onMessage(String messageChunk) {
// 流式数据到达时通知View更新UI
if (IndexPresenter.this.getView() != null) {
IndexPresenter.this.getView().onStreamMessageReceived(messageChunk);
}
}
@Override
public void onError(String msg) {
if (IndexPresenter.this.getView() != null) {
IndexPresenter.this.getView().showError(msg);
}
}
@Override
public void onComplete(String complateMsg) {
// 流式传输完成
if (IndexPresenter.this.getView() != null) {
IndexPresenter.this.getView().onStreamComplete(complateMsg);
}
}
@Override
public void onSessionReceived(String sessionId) {
// 接收到sessionId时通知View
if (IndexPresenter.this.getView() != null) {
IndexPresenter.this.getView().onSessionReceived(sessionId);
}
}
@Override
public void loginLost() {
if (IndexPresenter.this.getView() != null) {
IndexPresenter.this.getView().loginLost();
}
}
});
}
/**
* 停止当前流式请求
*/
public void stopCurrentRequest() {
impl.stopCurrentRequest();
}
/**
* 首页模板消息数据获取
*/
public void getIndexTemplateDatas() {
impl.getIndexTemplateDatas(new IndexContract.CallBack<List<IndexTemplateEntity>>() {
@Override
public void success(List<IndexTemplateEntity> list) {
if (IndexPresenter.this.getView() != null) {
IndexPresenter.this.getView().getIndexTemplateSuccess(list);
}
}
@Override
public void fail(String msg) {
if (IndexPresenter.this.getView() != null) {
IndexPresenter.this.getView().showError(msg);
}
}
});
}
/**
* 获取历史记录
* @param mCurrentPage
*/
public void getHistoryDatas(int mCurrentPage) {
Map<String, Object> map = new HashMap<>();
map.put("pageNum", mCurrentPage);
map.put("pageSize", 10);
impl.getHistoryChatDatas(map, new IndexContract.CallBack<CharHistoryEntity>() {
@Override
public void success(CharHistoryEntity entity) {
if (IndexPresenter.this.getView() != null) {
IndexPresenter.this.getView().getHistoryDataSuccess(entity);
}
}
@Override
public void fail(String msg) {
if (IndexPresenter.this.getView() != null) {
IndexPresenter.this.getView().showError(msg);
}
}
});
}
/**
* 删除历史记录
* @param sessionID
*/
public void deleteHistoryBySessionID(String sessionID,int delPos) {
Map<String, Object> map = new HashMap<>();
map.put("sessionId", sessionID);
impl.deleteHistoryBySessionID(map, new IndexContract.CallBack<BaseBean>() {
@Override
public void success(BaseBean entity) {
EventBus.getDefault().post(new EventMessage(EventMessage.HISTORY_SESSIONID_DELETE_SUCCESS,sessionID));
if (IndexPresenter.this.getView() != null) {
IndexPresenter.this.getView().deleteHistorySuccess(entity,delPos);
}
}
@Override
public void fail(String msg) {
if (IndexPresenter.this.getView() != null) {
IndexPresenter.this.getView().showError(msg);
}
}
});
}
/**
* 根据sessionID 查询消息记录
* @param sessionId
*/
public void getChatDetailByID(String sessionId) {
Map<String, Object> map = new HashMap<>();
map.put("sessionId", sessionId);
impl.getChatDetailById(map, new IndexContract.CallBack<List<ChatAiMsgEntity>>() {
@Override
public void success(List<ChatAiMsgEntity> list) {
if (IndexPresenter.this.getView() != null) {
IndexPresenter.this.getView().getChatDetailSuccess(list);
}
}
@Override
public void fail(String msg) {
if (IndexPresenter.this.getView() != null) {
IndexPresenter.this.getView().showError(msg);
}
}
});
}
/**
* 获取首页咨询会话模板
*/
public void getChatTemplateDatas() {
impl.getChatemplateDatas(new IndexContract.CallBack<List<ChatQuestionEntity>>() {
@Override
public void success(List<ChatQuestionEntity> list) {
if (IndexPresenter.this.getView() != null) {
IndexPresenter.this.getView().getChatZixunSuccess(list);
}
}
@Override
public void fail(String msg) {
if (IndexPresenter.this.getView() != null) {
IndexPresenter.this.getView().showError(msg);
}
}
});
}
/**
* 举报
* @param reason
*/
public void commitReport(String reason) {
Map<String, Object> map = new HashMap<>();
map.put("content", reason);
impl.commitReport(map, new IndexContract.CallBack<BaseBean>() {
@Override
public void success(BaseBean bean) {
if (IndexPresenter.this.getView() != null) {
IndexPresenter.this.getView().commitReportSuccess(bean);
}
}
@Override
public void fail(String msg) {
if (IndexPresenter.this.getView() != null) {
IndexPresenter.this.getView().showError(msg);
}
}
});
}
}

View File

@@ -30,12 +30,21 @@ public class CustomTypeFilterPopupView extends PartShadowPopupView implements Vi
private boolean mSelManual = false;
//是否选择了自动排程
private boolean mSelAuto = false;
private int mFilterType = -1;
public CustomTypeFilterPopupView(@NonNull Context context, OnClickListener listener) {
super(context);
this.mContext = context;
this.listener = listener;
}
public CustomTypeFilterPopupView(@NonNull Context context, OnClickListener listener, int filterType) {
super(context);
this.mContext = context;
this.listener = listener;
this.mFilterType = filterType;
}
public interface OnClickListener{
void callBack(boolean mSelManual,boolean mSelAuto);
}
@@ -59,7 +68,18 @@ public class CustomTypeFilterPopupView extends PartShadowPopupView implements Vi
tvAuto.setOnClickListener(this);
tvReset.setOnClickListener(this);
tvFinish.setOnClickListener(this);
tvManual.setSelected(false);
// 根据filterType设置初始选中状态
if (mFilterType == 0) {
// 排程
mSelAuto = true;
tvAuto.setTextColor(mContext.getColor(R.color.white));
tvAuto.setSelected(true);
} else if (mFilterType == 1) {
// 手动
mSelManual = true;
tvManual.setTextColor(mContext.getColor(R.color.white));
tvManual.setSelected(true);
}
}
@@ -68,25 +88,35 @@ public class CustomTypeFilterPopupView extends PartShadowPopupView implements Vi
switch (v.getId()){
case R.id.tv_manual:
if(mSelManual){
// 已选中,取消选中
mSelManual = false;
tvManual.setTextColor(mContext.getColor(R.color.color_zhu));
tvManual.setSelected(mSelManual);
tvManual.setSelected(false);
}else{
// 选中手动,同时取消自动
mSelManual = true;
mSelAuto = false;
tvManual.setTextColor(mContext.getColor(R.color.white));
tvManual.setSelected(mSelManual);
tvManual.setSelected(true);
tvAuto.setTextColor(mContext.getColor(R.color.color_zhu));
tvAuto.setSelected(false);
}
break;
case R.id.tv_auto:
if(mSelAuto){
// 已选中,取消选中
mSelAuto = false;
tvAuto.setTextColor(mContext.getColor(R.color.color_zhu));
tvAuto.setSelected(mSelAuto);
tvAuto.setSelected(false);
}else{
// 选中自动,同时取消手动
mSelAuto = true;
mSelManual = false;
tvAuto.setTextColor(mContext.getColor(R.color.white));
tvAuto.setSelected(mSelAuto);
tvAuto.setSelected(true);
tvManual.setTextColor(mContext.getColor(R.color.color_zhu));
tvManual.setSelected(false);
}
break;
@@ -95,15 +125,11 @@ public class CustomTypeFilterPopupView extends PartShadowPopupView implements Vi
mSelAuto = false;
tvManual.setTextColor(mContext.getColor(R.color.color_zhu));
tvAuto.setTextColor(mContext.getColor(R.color.color_zhu));
tvManual.setSelected(mSelManual);
tvAuto.setSelected(mSelAuto);
tvManual.setSelected(false);
tvAuto.setSelected(false);
break;
case R.id.tv_finish:
if(!mSelManual && !mSelAuto){
ToastUtil.showShort(mContext,mContext.getString(R.string.history_filter_type_toast));
return;
}
if(listener != null){
listener.callBack(mSelManual,mSelAuto);
this.dismiss();

View File

@@ -32,6 +32,10 @@ public class CustomRefreshHeader extends RelativeLayout implements RefreshHeader
private ImageView ivRefresh;
private ObjectAnimator rotateAnimator;
// 用于 onMoving 中的动态效果
private float mLastPercent = 0f;
// 记录当前缩放值,避免重复设置
private float mCurrentScale = 1f;
public CustomRefreshHeader(Context context) {
super(context);
@@ -47,11 +51,14 @@ public class CustomRefreshHeader extends RelativeLayout implements RefreshHeader
LayoutInflater.from(context).inflate(R.layout.view_refresh_header, this);
ivRefresh = findViewById(R.id.ivRefresh);
// 开启硬件加速,提升动画性能
setLayerType(LAYER_TYPE_HARDWARE, null);
// 初始化 ObjectAnimator
rotateAnimator = ObjectAnimator.ofFloat(ivRefresh, "rotation", 0f, 360f);
rotateAnimator.setDuration(1500);
rotateAnimator.setRepeatCount(ValueAnimator.INFINITE);
rotateAnimator.setInterpolator(new LinearInterpolator()); // 匀速旋转
rotateAnimator.setInterpolator(new LinearInterpolator());
}
@NonNull
@@ -72,16 +79,36 @@ public class CustomRefreshHeader extends RelativeLayout implements RefreshHeader
@Override
public int onFinish(@NonNull RefreshLayout layout, boolean success) {
if (rotateAnimator != null && rotateAnimator.isRunning()) {
// 为了平滑停止,先设置减速插值器,再 cancel 动画
rotateAnimator.setInterpolator(new DecelerateInterpolator());
postDelayed(() -> {
if (rotateAnimator != null) {
rotateAnimator.cancel();
rotateAnimator.setInterpolator(new LinearInterpolator()); // 恢复为匀速
rotateAnimator.setInterpolator(new LinearInterpolator());
}
}, 300); // 给一点缓冲时间
}, 300);
}
return 500; // 动画停止后延迟 500ms 收起 Header
// 动画过渡回正常大小
animateToNormalScale();
return 500;
}
/**
* 平滑动画过渡回正常大小
*/
private void animateToNormalScale() {
if (ivRefresh == null) return;
float startScale = mCurrentScale;
ValueAnimator scaleAnimator = ValueAnimator.ofFloat(startScale, 1f);
scaleAnimator.setDuration(300);
scaleAnimator.setInterpolator(new DecelerateInterpolator());
scaleAnimator.addUpdateListener(animation -> {
float value = (float) animation.getAnimatedValue();
ivRefresh.setScaleX(value);
ivRefresh.setScaleY(value);
mCurrentScale = value;
});
scaleAnimator.start();
}
// --- 必须实现的方法 ---
@@ -107,7 +134,24 @@ public class CustomRefreshHeader extends RelativeLayout implements RefreshHeader
@SuppressLint("RestrictedApi")
@Override
public void onMoving(boolean isDragging, float percent, int offset, int height, int maxDragHeight) {}
public void onMoving(boolean isDragging, float percent, int offset, int height, int maxDragHeight) {
// 只在拖动过程中处理,避免过度计算
if (percent != mLastPercent) {
mLastPercent = percent;
// 1. 动态缩放效果:从 0.5 放大到 1.2
// 使用 clamp 限制范围percent 0~1 -> scale 0.5~1.2
float targetScale = 0.5f + (0.7f * Math.min(percent, 1.0f));
if (Math.abs(targetScale - mCurrentScale) > 0.01f) {
mCurrentScale = targetScale;
ivRefresh.setScaleX(targetScale);
ivRefresh.setScaleY(targetScale);
}
// 2. 透明度渐变效果(可选)
// ivRefresh.setAlpha(Math.min(percent * 1.5f, 1.0f));
}
}
@SuppressLint("RestrictedApi")
@Override
@@ -125,6 +169,10 @@ public class CustomRefreshHeader extends RelativeLayout implements RefreshHeader
@SuppressLint("RestrictedApi")
@Override
public void onStateChanged(@NonNull RefreshLayout refreshLayout, @NonNull RefreshState oldState, @NonNull RefreshState newState) {
// 状态切换时平滑恢复
if (newState == RefreshState.PullDownCanceled) {
// 用户取消下拉,恢复到初始状态
animateToNormalScale();
}
}
}

View File

@@ -4,24 +4,41 @@ 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.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.os.ParcelUuid;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.View;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AlertDialog;
import androidx.core.content.ContextCompat;
import com.chad.library.adapter.base.BaseQuickAdapter;
import com.ckkj.water.ConstantUtil;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity;
import com.ckkj.water.chat.mvp.IndexPresenter;
import com.ckkj.water.custom.GridSpacingItemDecoration;
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.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.manager.WaterDeviceManager;
import com.ckkj.water.utils.show.ToastUtil;
import com.gyf.immersionbar.ImmersionBar;
import com.maning.mlkitscanner.scan.MNScanManager;
@@ -30,8 +47,17 @@ import com.maning.mlkitscanner.scan.callback.act.MNScanCallback;
import org.jetbrains.annotations.NotNull;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.UUID;
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.ScanRecord;
import no.nordicsemi.android.support.v18.scanner.ScanResult;
import no.nordicsemi.android.support.v18.scanner.ScanSettings;
import pub.devrel.easypermissions.EasyPermissions;
/**
@@ -39,6 +65,21 @@ import pub.devrel.easypermissions.EasyPermissions;
*/
public class AddDeviceActivity 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;
private String mUuid = "1775244d-6b43-439b-877c-060f2d9bed07";
private ScanCallback mScanCallback;
private boolean bluetoothReady = false;
private BleScanDeviceAdapter bleAdapter;
private List<WaterDeviceEntity> mDeviceList = new ArrayList<>();
private Set<String> discoveredMacSet = new HashSet<>();
private WaterDeviceManager waterDeviceManager;
@Override
public DevicePresenter createPresenter() {
return new DevicePresenter(new DeviceImpl());
@@ -48,43 +89,207 @@ public class AddDeviceActivity extends BaseActivity<AddDeviceLayoutBinding, Devi
@Override
public void initData(Bundle savedInstanceState) {
super.initData(savedInstanceState);
ActivityManager.addActivity(this,getClass().getSimpleName() + "_" + ConstantUtil.ADD_DEVICE_FLAG);
ImmersionBar.with(this)
.statusBarColor(R.color.color_F9F9F9) // 设置状态栏颜色
.statusBarDarkFont(true) // 设置状态栏字体为黑色
.statusBarColor(R.color.mainBackgroundColor) // 设置状态栏颜色
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext)) // 设置状态栏字体为黑色
.titleBar(viewBinding.rlTitle)
.init();
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()){
// 初始化蓝牙适配器
initBluetooth();
if (bluetoothReady) {
startBleScan();
}
}
// 启动帧动画
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();
}
/**
* 初始化监听g
*/
private void initListener() {
setClick(viewBinding.includedTitle.imgBack);
setClick(viewBinding.tvScanAdd);
setClick(viewBinding.imgDeviceItem);
setClick(viewBinding.btnBleScan);
if(bleAdapter != null){
bleAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
@Override
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
WaterDeviceEntity device = mDeviceList.get(position);
String deviceName = TextUtils.isEmpty(device.getDeviceName()) ?
"未知设备" : device.getDeviceName();
// 停止扫描
stopBleScan();
Bundle bundle = new Bundle();
bundle.putInt(ConstantUtil.ACTIVITY_NAVIGATE_FLAG,100);
bundle.putSerializable("ble_device_entity",mDeviceList.get(position));
goTo(mContext, SelectWifiActivity.class,bundle);
}
});
}
}
/**
* 开始 BLE 扫描
*/
private void startBleScan() {
BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
UUID SERVICE_UUID = UUID.fromString(mUuid);//1775244d-6b43-439b-877c-060f2d9bed07
ParcelUuid parcelUuid = new ParcelUuid(SERVICE_UUID);
ScanSettings settings = new ScanSettings.Builder()
.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY)
.setReportDelay(0)
.build();
List<ScanFilter> filters = new ArrayList<>();
filters.add(new ScanFilter.Builder().setServiceUuid(parcelUuid).build());
// filters.add(new ScanFilter.Builder().setDeviceName("PROV_Water_01").build());
Log.e("TEST", "开始扫描");
mScanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, @NonNull ScanResult result) {
super.onScanResult(callbackType, result);
//Mac地址
String deviceAddress = result.getDevice().getAddress();
// 去重:已存在的设备不再添加
if (discoveredMacSet.contains(deviceAddress)) {//dc:da:0c:fa:2b:3a
return;
}
discoveredMacSet.add(deviceAddress);
WaterDeviceEntity bean = new WaterDeviceEntity();
ScanRecord record = result.getScanRecord();
SparseArray<byte[]> manufacturerData = record.getManufacturerSpecificData();
//设备名字
String deviceName = result.getScanRecord() != null ?
result.getScanRecord().getDeviceName() : "无名称";
bean.setDeviceName(deviceName);
bean.setMacAddress(deviceAddress);
mDeviceList.add(bean);
bleAdapter.notifyItemInserted(mDeviceList.size() - 1);
if(bleAdapter.getItemCount() != 0){
viewBinding.rvDevices.setVisibility(VISIBLE);
// 停止帧动画
ImageView ivScanAnimation = viewBinding.ivScanAnimation;
if (ivScanAnimation.getDrawable() instanceof AnimationDrawable) {
AnimationDrawable animationDrawable = (AnimationDrawable) ivScanAnimation.getDrawable();
if (animationDrawable.isRunning()) {
animationDrawable.stop();
}
viewBinding.ivScanAnimation.setVisibility(GONE);
}
}
}
@Override
public void onBatchScanResults(@NonNull List<ScanResult> results) {
super.onBatchScanResults(results);
Log.e("TEST", "批量结果: " + results.size() + " 个设备");
}
@Override
public void onScanFailed(int errorCode) {
super.onScanFailed(errorCode);
Log.e("TEST", "扫描失败: " + errorCode);
ToastUtil.showShort(mContext, "扫描失败,错误码: " + errorCode);
}
};
scanner.startScan(filters, settings, mScanCallback);
}
/**
* 停止 BLE 扫描
*/
private void stopBleScan() {
if (mScanCallback != null) {
BluetoothLeScannerCompat scanner = BluetoothLeScannerCompat.getScanner();
scanner.stopScan(mScanCallback);
Log.e("TEST", "停止扫描");
}
}
/**
* 初始化蓝牙适配器
*/
private void initBluetooth() {
BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
if (bluetoothManager != null) {
bluetoothAdapter = bluetoothManager.getAdapter();
}
if (bluetoothAdapter == null) {
ToastUtil.showShort(mContext, "设备不支持蓝牙");
return;
}
try {
// 检查蓝牙是否开启
if (!bluetoothAdapter.isEnabled()) {
Log.e("TEST", "initBluetooth: 蓝牙未开启,请求开启");
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
} else {
bluetoothReady = true;
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 检查蓝牙权限
*/
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 setClick(View view) {
view.setOnClickListener(this::onClick);
}
@@ -95,12 +300,6 @@ public class AddDeviceActivity extends BaseActivity<AddDeviceLayoutBinding, Devi
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;
@@ -111,6 +310,48 @@ public class AddDeviceActivity extends BaseActivity<AddDeviceLayoutBinding, Devi
scanQrcode();
}
break;
case R.id.btn_ble_scan:
// 扫描蓝牙设备
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, "蓝牙已开启");
Log.e("BluetoothScan", "onActivityResult: 蓝牙已开启,开始扫描");
bluetoothReady = true;
startBleScan();
} else {
ToastUtil.showShort(mContext, "请开启蓝牙以扫描设备");
Log.e("BluetoothScan", "onActivityResult: 用户拒绝开启蓝牙");
}
}
}
@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();
} else {
ToastUtil.showShort(mContext, "需要蓝牙权限才能扫描设备");
}
}
}
@@ -167,17 +408,13 @@ public class AddDeviceActivity extends BaseActivity<AddDeviceLayoutBinding, Devi
}
@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);
}
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
hidePermissionDialog();
if (requestCode == REQUEST_EVENT_CODE_10001) {
scanQrcode();
}else if(requestCode == REQUEST_EVENT_CODE_10002){
}
}
@@ -185,4 +422,52 @@ public class AddDeviceActivity extends BaseActivity<AddDeviceLayoutBinding, Devi
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
}
@Override
public void addDeviceSuccess(WaterDeviceEntity 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, String status) {
}
@Override
public void queryDeviceBindStatusSuccess(WaterDeviceEntity bean) {
}
@Override
public void queryDeviceWorkTimeSuccess(WaterDeviceEntity bean) {
}
@Override
protected void onDestroy() {
super.onDestroy();
// 停止扫描
stopBleScan();
// 断开蓝牙连接
if (waterDeviceManager != null) {
waterDeviceManager.disconnect().enqueue();
waterDeviceManager.close();
waterDeviceManager = null;
}
}
}

View File

@@ -3,18 +3,25 @@ package com.ckkj.water.device;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import android.content.Intent;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import com.ckkj.water.ConstantUtil;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity;
import com.ckkj.water.databinding.DeviceConnectLayoutBinding;
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.espBle.EspProvisionManager;
import com.ckkj.water.espBle.ProvisionCallback;
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.show.ToastUtil;
import com.gyf.immersionbar.ImmersionBar;
@@ -28,6 +35,14 @@ public class DeviceConnectActivity extends BaseActivity<DeviceConnectLayoutBindi
private Handler handler = new Handler(Looper.getMainLooper());
private static final long ANIMATION_DURATION = 5000L; // 5秒
private String mWifiName;
private String mWifiPassword;
private WaterDeviceEntity mBleDeviceEntity;
private EspProvisionManager espProvisionManager;
private boolean isBleConnecting = false;
private boolean isBleConnected = false;
private boolean provisioningStarted = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -36,34 +51,198 @@ public class DeviceConnectActivity extends BaseActivity<DeviceConnectLayoutBindi
@Override
public void initData(Bundle savedInstanceState) {
super.initData(savedInstanceState);
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
mWifiName = bundle.getString("wifi_name", "");
mWifiPassword = bundle.getString("wifi_password", "");
mBleDeviceEntity = (WaterDeviceEntity) bundle.getSerializable("ble_device_entity");
}
ActivityManager.addActivity(this,getClass().getSimpleName() + "_" + ConstantUtil.ADD_DEVICE_FLAG);
ImmersionBar.with(this)
.statusBarColor(R.color.color_F9F9F9)
.statusBarDarkFont(true)
.titleBar(viewBinding.rlTitle)
.init();
viewBinding.includedTitle.tvTitle.setText(R.string.select_wifi_tip);
initEspProvision();
startConnectAnimation();
startProvisionWhenReady();
}
private void startConnectAnimation() {
viewBinding.tvConnectProgress.setText("正在配置网络");
if (animationDrawable != null && animationDrawable.isRunning()) {
animationDrawable.stop();
}
viewBinding.ivConnectAnimation.setImageResource(R.drawable.anim_connect_device);
animationDrawable = (AnimationDrawable) viewBinding.ivConnectAnimation.getDrawable();
if (animationDrawable != null) {
animationDrawable.start();
handler.postDelayed(() -> {
if (animationDrawable != null && animationDrawable.isRunning()) {
animationDrawable.stop();
}
viewBinding.linearConnectIng.setVisibility(VISIBLE);
viewBinding.linearSuccess.setVisibility(GONE);
}
private void stopConnectAnimation() {
handler.removeCallbacksAndMessages(null);
if (animationDrawable != null) {
animationDrawable.stop();
animationDrawable = null;
}
viewBinding.linearConnectIng.setVisibility(GONE);
viewBinding.linearSuccess.setVisibility(VISIBLE);
}
private void initEspProvision() {
espProvisionManager = EspProvisionManager.init(mContext).setCallback(new ProvisionCallback() {
@Override
public void onConnecting() {
isBleConnecting = true;
isBleConnected = false;
Log.i("TEST", "DeviceConnectActivity===== onConnecting");
}
@Override
public void onConnected() {
isBleConnecting = false;
isBleConnected = true;
startProvisionWhenReady();
Log.i("TEST", "DeviceConnectActivity===== onConnected设备连接成功");
}
@Override
public void onProvisioning(int progress) {
Log.i("TEST", "DeviceConnectActivity===== WIFI连接进度 == " + progress);
viewBinding.tvConnectProgress.setText("正在配置网络" + progress + "%");
if(progress == 100){
stopConnectAnimation();
}
viewBinding.linearConnectIng.setVisibility(GONE);
viewBinding.linearSuccess.setVisibility(VISIBLE);
new Handler().postDelayed(new Runnable() {
}
@Override
public void onSuccess(String deviceName, String deviceType) {
Log.i("TEST", "DeviceConnectActivity===== deviceName" + deviceName + "配网成功");
ToastUtil.showShort(mContext, deviceName + "配网成功");
handler.postDelayed(new Runnable() {
@Override
public void run() {
goTo(mContext, DeviceReNameActivity.class,null);
Bundle bundle = new Bundle();
mBleDeviceEntity.setWifiName(mWifiName);
mBleDeviceEntity.setWifiPassword(mWifiPassword);
bundle.putSerializable("ble_device_entity", mBleDeviceEntity);
goTo(mContext, DeviceReNameActivity.class, bundle);
finish();
}
},1000);
}, ANIMATION_DURATION);
}, 800);
}
@Override
public void onFailed(int errorCode, String message) {
isBleConnecting = false;
if (errorCode != 0) {
isBleConnected = espProvisionManager != null && espProvisionManager.isConnected();
}
Log.i("TEST", "DeviceConnectActivity===== errorCode == " + errorCode + " message == " + message);
ToastUtil.showShort(mContext, message);
//配网失败,返回到设备添加页面
returnToAddDeviceAfterFailed();
}
@Override
public void onDisconnected() {
isBleConnecting = false;
isBleConnected = false;
hideDialogLoading();
Log.i("TEST", "DeviceConnectActivity===== onDisconnected");
}
@Override
public void onWifiListReceived(String[] wifiList) {
Log.i("TEST", "DeviceConnectActivity===== onWifiListReceived" + wifiList.toString());
}
@Override
public void onWifiListFailed(String message) {
Log.i("TEST", "DeviceConnectActivity===== onWifiListFailed-Msg==" + message);
}
@Override
public void onProvisioningRejected() {
Log.i("TEST", "DeviceConnectActivity===== onProvisioningRejected");
}
});
}
private void returnToAddDeviceAfterFailed() {
hideDialogLoading();
handler.removeCallbacksAndMessages(null);
if (animationDrawable != null && animationDrawable.isRunning()) {
animationDrawable.stop();
animationDrawable = null;
}
provisioningStarted = false;
isBleConnecting = false;
isBleConnected = false;
if (espProvisionManager != null) {
espProvisionManager.setCallback(null);
espProvisionManager.disconnect();
}
Intent intent = new Intent(this, AddDeviceActivity.class);
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
startActivity(intent);
finish();
}
private void connectBleDevice() {
if (mBleDeviceEntity == null || espProvisionManager == null) {
ToastUtil.showShort(mContext, "\u8bbe\u5907\u8fde\u63a5\u672a\u521d\u59cb\u5316");
return;
}
if (isBleConnected || isBleConnecting) {
return;
}
espProvisionManager.connect(mBleDeviceEntity.getMacAddress(), mBleDeviceEntity.getDeviceName());
}
private void startProvisionWhenReady() {
if (provisioningStarted || espProvisionManager == null) {
return;
}
isBleConnected = espProvisionManager.isConnected();
if (isBleConnected) {
provisionWifi();
return;
}
if (!isBleConnecting) {
connectBleDevice();
}
}
private void provisionWifi() {
if (provisioningStarted) {
return;
}
if (espProvisionManager == null) {
ToastUtil.showShort(mContext, "设备连接未初始化");
return;
}
if (!espProvisionManager.isConnected()) {
startProvisionWhenReady();
return;
}
provisioningStarted = true;
espProvisionManager.provisionWifi(mWifiName, mWifiPassword);
}
@Override
@@ -73,6 +252,10 @@ public class DeviceConnectActivity extends BaseActivity<DeviceConnectLayoutBindi
if (animationDrawable != null && animationDrawable.isRunning()) {
animationDrawable.stop();
}
if (espProvisionManager != null) {
espProvisionManager.disconnect();
espProvisionManager.setCallback(null);
}
}
@Override
@@ -94,4 +277,39 @@ public class DeviceConnectActivity extends BaseActivity<DeviceConnectLayoutBindi
public void logOut(BaseBean bean) {
}
@Override
public void addDeviceSuccess(WaterDeviceEntity 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, String status) {
}
@Override
public void queryDeviceBindStatusSuccess(WaterDeviceEntity bean) {
}
@Override
public void queryDeviceWorkTimeSuccess(WaterDeviceEntity bean) {
}
}

View File

@@ -1,26 +1,44 @@
package com.ckkj.water.device;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import android.view.View;
import androidx.annotation.Nullable;
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.CustomAttachPopup;
import com.ckkj.water.custom.view.CustomBubbleAttachPopup;
import com.ckkj.water.databinding.DeviceControlLayoutBinding;
import com.ckkj.water.device.adapter.DeviceTimeSelectAdapter;
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.FastClickUtils;
import com.ckkj.water.utils.manager.PowerIconManage;
import com.ckkj.water.utils.manager.ThemeManager;
import com.ckkj.water.utils.show.ToastUtil;
import com.gyf.immersionbar.ImmersionBar;
import com.lxj.xpopup.XPopup;
import com.lxj.xpopup.core.BasePopupView;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import de.greenrobot.event.Subscribe;
import de.greenrobot.event.ThreadMode;
/**
* 设备控制页面
@@ -28,8 +46,37 @@ import java.util.List;
public class DeviceControlActivity extends BaseActivity<DeviceControlLayoutBinding, DevicePresenter>
implements DeviceContract.DeviceView {
private static final long DEVICE_DETAIL_POLL_INTERVAL_MS = 10_000L;
private List<String> mTimeList = new ArrayList<>();
private WaterDeviceEntity mDeviceEntity;
private WaterDeviceEntity mRemoteDeviceEntity;
private DeviceTimeSelectAdapter mAdapter;
//用户自定义的浇水时长
private int mCustomDuration = 0;
private final Handler mProgressHandler = new Handler(Looper.getMainLooper());
private final Handler mDeviceDetailPollHandler = new Handler(Looper.getMainLooper());
private boolean mIsDeviceDetailPolling;
private long mWorkStartTime = 0L;
private long mWorkEndTime = 0L;
private boolean mHasRefreshAfterWorkFinished = false;
private final Runnable mWorkProgressRunnable = new Runnable() {
@Override
public void run() {
updateWorkProgress();
}
};
private final Runnable mDeviceDetailPollRunnable = new Runnable() {
@Override
public void run() {
if (!mIsDeviceDetailPolling) {
return;
}
if (mDeviceEntity != null && !TextUtils.isEmpty(mDeviceEntity.getDeviceNo())) {
mPresenter.getDeviceDetailInfo(mDeviceEntity.getDeviceNo());
}
mDeviceDetailPollHandler.postDelayed(this, DEVICE_DETAIL_POLL_INTERVAL_MS);
}
};
@Override
public DevicePresenter createPresenter() {
return new DevicePresenter(new DeviceImpl());
@@ -38,6 +85,22 @@ public class DeviceControlActivity extends BaseActivity<DeviceControlLayoutBindi
@Override
public void initData(Bundle savedInstanceState) {
super.initData(savedInstanceState);
Bundle bundle = getIntent().getExtras();
if(bundle != null){
mDeviceEntity = (WaterDeviceEntity) bundle.getSerializable("device_info");
if(mDeviceEntity != null){
showLoadingDialog();
//查询设备详情
mPresenter.getDeviceDetailInfo(mDeviceEntity.getDeviceNo());
//查询设备当前工作进度
if(mDeviceEntity.getWorkStatus().equals("1")){
mPresenter.getDeviceWorkProgress(mDeviceEntity.getDeviceNo());
}
viewBinding.includedTitle.tvTitle.setText(mDeviceEntity.getDeviceName());
updateDeviceStatus(mDeviceEntity);
}
}
mTimeList.add("1min");
mTimeList.add("2min");
mTimeList.add("3min");
@@ -51,25 +114,66 @@ public class DeviceControlActivity extends BaseActivity<DeviceControlLayoutBindi
mTimeList.add("3h");
mTimeList.add("4h");
ImmersionBar.with(this)
.statusBarColor(R.color.color_F9F9F9) // 设置状态栏颜色
.statusBarDarkFont(true) // 设置状态栏字体为黑色
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext)) // 白色状态栏图标
.navigationBarColor(R.color.mainBackgroundColor)
.titleBar(viewBinding.rlTitle)
.init();
viewBinding.includedTitle.tvTitle.setText("设备名称");
viewBinding.includedTitle.imgSetting.setVisibility(View.VISIBLE);
// viewBinding.includedTitle.imgSetting.setImageDrawable(getDrawable(R.mipmap.device_ctrl_setting));
mAdapter = new DeviceTimeSelectAdapter(mContext,R.layout.device_work_time_item,mTimeList);
viewBinding.rvTimeList.setAdapter(mAdapter);
viewBinding.rvTimeList.addItemDecoration(new GridSpacingItemDecoration(4, 10, true));
mAdapter.setOnItemClickListener(new BaseQuickAdapter.OnItemClickListener() {
@Override
public void onItemClick(BaseQuickAdapter adapter, View view, int position) {
//此时处于工作中,不可点击
if(mDeviceEntity.getWorkStatus().equals("1")){
ToastUtil.showShort(mContext,getString(R.string.tips_device_working_stop_task));
return;
}
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();
}
private void updateDeviceStatus(WaterDeviceEntity entity) {
if(entity.getStatus().equals("1") || entity.getStatus().equals("2")){
if(entity.getWorkStatus().equals("1")){
viewBinding.tvDeviceStatus.setText(getString(R.string.device_status_working));
viewBinding.tvDeviceStatus.setTextColor(mContext.getResources().getColor(R.color.color_zhu));
viewBinding.tvConfirm.setText(getString(R.string.device_control_stop));
}else{
viewBinding.tvDeviceStatus.setText(getString(R.string.device_status_not_working));
viewBinding.tvDeviceStatus.setTextColor(mContext.getResources().getColor(R.color.color_999999));
viewBinding.tvConfirm.setText(getString(R.string.device_control_start));
stopWorkProgressUpdate();
updateProgressView(0);
}
viewBinding.linearWifi.setBackground(mContext.getResources().getDrawable(R.drawable.shape_device_ctrl_green_cor12));
viewBinding.imgWifiLogo.setImageResource(R.mipmap.icon_ctrl_wifi_white);
}else{
viewBinding.tvDeviceStatus.setText(getString(R.string.device_status_offline));
viewBinding.tvDeviceStatus.setTextColor(mContext.getResources().getColor(R.color.color_E60000));
viewBinding.tvConfirm.setEnabled(false);
viewBinding.tvConfirm.setTextColor(mContext.getResources().getColor(R.color.color_999999));
viewBinding.tvConfirm.setBackgroundResource((R.drawable.shape_gray_button_bg_enable));
viewBinding.linearWifi.setBackground(mContext.getResources().getDrawable(R.drawable.shape_device_ctrl_white_cor12));
viewBinding.imgWifiLogo.setImageResource(R.mipmap.icon_ctrl_wifi_green);
stopWorkProgressUpdate();
updateProgressView(0);
}
viewBinding.tvDeviceUpdateTime.setText(getString(R.string.update_time) + entity.getPowerLevelUpdatatime());
}
private void initListener() {
setClick(viewBinding.includedTitle.imgBack);
setClick(viewBinding.includedTitle.imgSetting);
@@ -77,6 +181,7 @@ public class DeviceControlActivity extends BaseActivity<DeviceControlLayoutBindi
setClick(viewBinding.linearWifi);
setClick(viewBinding.linearBat);
setClick(viewBinding.tvConfirm);
setClick(viewBinding.tvWorkDuartion);
}
private void setClick(View view) {
@@ -90,11 +195,27 @@ public class DeviceControlActivity extends BaseActivity<DeviceControlLayoutBindi
break;
case R.id.img_setting:
goTo(mContext, DeviceSettingsActivity.class,null);
Bundle bundle = new Bundle();
bundle.putSerializable("device_entity",mDeviceEntity);
goToForResult(mContext, DeviceSettingsActivity.class,REQUEST_EVENT_CODE_10002,bundle);
break;
case R.id.linear_bt:
showDialogtext(null, false, false, true,
getString(R.string.tip_text),
getString(R.string.reset_device_provision),
getString(R.string.cancel_text),
getString(R.string.confirm_text),
new BaseFragment.OnDialogClick() {
@Override
public void onCancelClick() {
}
@Override
public void onConfirmClick() {
}
});
break;
case R.id.linear_wifi:
@@ -107,16 +228,210 @@ public class DeviceControlActivity extends BaseActivity<DeviceControlLayoutBindi
.isDestroyOnDismiss(true) //对于只使用一次的弹窗,推荐设置这个
.atView(viewBinding.linearBat)
.hasShadowBg(false) // 去掉半透明背景
.asCustom(new CustomBubbleAttachPopup(mContext,"设备电量:40% 更新时间: 05- 11 18:00"))
.asCustom(new CustomBubbleAttachPopup(mContext,getString(R.string.device_battery)+
mDeviceEntity.getPowerLevel() + "%" +
getString(R.string.update_time) + mDeviceEntity.getPowerLevelUpdatatime()))
.show();
break;
case R.id.tv_work_duartion:
//此时处于工作中,不可点击
if(mDeviceEntity.getWorkStatus().equals("1")){
ToastUtil.showShort(mContext,getString(R.string.tips_device_working_stop_task));
return;
}
inputDuration(mCustomDuration);
break;
case R.id.tv_confirm:
showCenterTipDialog(R.mipmap.icon_dialog_success,"开始浇水");
handleDeviceSwitch();
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;
}
/**
* 处理设备开关操作
*/
private void handleDeviceSwitch() {
if (mDeviceEntity == null) {
return;
}
// 检查设备在线状态
if (!mDeviceEntity.getStatus().equals("1") && !mDeviceEntity.getStatus().equals("2")) {
ToastUtil.showShort(mContext, getString(R.string.device_offline_tip));
return;
}
boolean isCurrentlyWorking = mDeviceEntity.getWorkStatus().equals("1");
// 开启浇水时,必须选择时长
if (!isCurrentlyWorking) {
if (mCustomDuration <= 0 && mAdapter.getmSelPostion() < 0) {
ToastUtil.showShort(mContext, getString(R.string.select_watering_duration));
return;
}
}
// 防止快速点击
if (!FastClickUtils.onclickTimes()) {
return;
}
// 显示加载状态
showLoadingDialog();
String newStatus = isCurrentlyWorking ? "0" : "1";
int duration = 0;
// 只有开启浇水时才计算时长
if (!isCurrentlyWorking) {
duration = mCustomDuration > 0 ? mCustomDuration
: convertToMinutes(mTimeList.get(mAdapter.getmSelPostion()));
}
mPresenter.switchDeviceWork(mDeviceEntity, newStatus, duration);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(resultCode == RESULT_OK){
switch (requestCode){
case REQUEST_EVENT_CODE_10002:
mDeviceEntity = (WaterDeviceEntity) data.getSerializableExtra("device_entity");
viewBinding.includedTitle.tvTitle.setText(mDeviceEntity.getDeviceName());
break;
}
}
}
@Subscribe(threadMode = ThreadMode.MainThread)
public void onGetMessage(EventMessage message) {
switch (message.msg){
case EventMessage.ACTIVITY_FINISH:
finish();
break;
}
}
/**
* 获取设备详情成功
* @param bean
*/
@Override
public void getDeviceDetailInfo(WaterDeviceEntity bean) {
hideDialogLoading();
viewBinding.imgBatStatus.setImageResource(PowerIconManage.getInstance().
getPowerVerIcon(Integer.parseInt(bean.getPowerLevel())));
mDeviceEntity = bean;
updateDeviceStatus( bean);
}
/**
* 开关设备成功
* @param bean
* @param status
*/
@Override
public void turnOnOffDeviceSuccess(BaseBean bean, String status) {
hideDialogLoading();
mDeviceEntity.setWorkStatus(status);
mCustomDuration = 0;
mAdapter.setOnSelPosition(-1);
viewBinding.tvWorkDuartion.setText("");
if(status.equals("0")){
showCenterTipDialog(R.mipmap.icon_dialog_success,getString(R.string.end_watering));
viewBinding.tvConfirm.setText(getString(R.string.start_watering));
stopWorkProgressUpdate();
updateProgressView(0);
}else{
showCenterTipDialog(R.mipmap.icon_dialog_success,getString(R.string.start_watering));
viewBinding.tvConfirm.setText(getString(R.string.end_watering));
mPresenter.getDeviceWorkProgress(mDeviceEntity.getDeviceNo());
}
//开关设备之后,更新一下当前设备状态
mPresenter.getDeviceDetailInfo(mDeviceEntity.getDeviceNo());
}
@Override
public int getLayoutResId(Bundle savedInstanceState) {
return 0;
@@ -124,11 +439,152 @@ public class DeviceControlActivity extends BaseActivity<DeviceControlLayoutBindi
@Override
public void showError(String message) {
hideDialogLoading();
ToastUtil.showShort(mContext,message);
}
/**
* 查询到设备目前的工作时间,控制进度条的展示
* @param bean
*/
@Override
public void queryDeviceWorkTimeSuccess(WaterDeviceEntity bean) {
if (bean == null || TextUtils.isEmpty(bean.getStartTime()) || TextUtils.isEmpty(bean.getEndTime())) {
stopWorkProgressUpdate();
updateProgressView(0);
return;
}
mWorkStartTime = parseWorkTime(bean.getStartTime());
mWorkEndTime = parseWorkTime(bean.getEndTime());
mHasRefreshAfterWorkFinished = false;
if (mWorkStartTime <= 0 || mWorkEndTime <= mWorkStartTime) {
stopWorkProgressUpdate();
updateProgressView(0);
return;
}
updateWorkProgress();
//"startTime": "2026-07-06 16:35:00",
//"endTime": "2026-07-06 16:37:00"
//bean.getStartTime(),bean.getEndTime(),这两个字段。
//Todo 根据以上开始时间和结束时间以及当前时间计算进度条的进度viewBinding.progressWork,做到实时更新。
}
private long parseWorkTime(String time) {
try {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.CHINA);
Date date = format.parse(time);
return date == null ? 0L : date.getTime();
} catch (ParseException e) {
e.printStackTrace();
}
return 0L;
}
private void updateWorkProgress() {
long now = System.currentTimeMillis();
int progress;
if (now <= mWorkStartTime) {
progress = 0;
} else if (now >= mWorkEndTime) {
progress = 100;
} else {
progress = (int) ((now - mWorkStartTime) * 100 / (mWorkEndTime - mWorkStartTime));
}
updateProgressView(progress);
mProgressHandler.removeCallbacks(mWorkProgressRunnable);
if (progress < 100 && mDeviceEntity != null && "1".equals(mDeviceEntity.getWorkStatus())) {
mProgressHandler.postDelayed(mWorkProgressRunnable, 1000);
} else if (progress >= 100 && !mHasRefreshAfterWorkFinished
&& mDeviceEntity != null && !TextUtils.isEmpty(mDeviceEntity.getDeviceNo())) {
mHasRefreshAfterWorkFinished = true;
ToastUtil.showShort(mContext,"任务结束");
showLoadingDialog();
mProgressHandler.postDelayed(new Runnable() {
@Override
public void run() {
if (mDeviceEntity != null && !TextUtils.isEmpty(mDeviceEntity.getDeviceNo())) {
mPresenter.getDeviceDetailInfo(mDeviceEntity.getDeviceNo());
}
}
}, 1500);
}
}
private void updateProgressView(int progress) {
viewBinding.progressWork.setProgress(progress);
viewBinding.tvProgressPercent.setText(getString(R.string.device_work_progress_title) + progress + "%");
}
private void stopWorkProgressUpdate() {
mProgressHandler.removeCallbacksAndMessages(null);
mWorkStartTime = 0L;
mWorkEndTime = 0L;
}
private void startDeviceDetailPolling() {
mIsDeviceDetailPolling = true;
mDeviceDetailPollHandler.removeCallbacks(mDeviceDetailPollRunnable);
mDeviceDetailPollHandler.postDelayed(mDeviceDetailPollRunnable, DEVICE_DETAIL_POLL_INTERVAL_MS);
}
private void stopDeviceDetailPolling() {
mIsDeviceDetailPolling = false;
mDeviceDetailPollHandler.removeCallbacks(mDeviceDetailPollRunnable);
}
@Override
protected void onResume() {
super.onResume();
startDeviceDetailPolling();
}
@Override
protected void onPause() {
stopDeviceDetailPolling();
super.onPause();
}
@Override
protected void onDestroy() {
stopWorkProgressUpdate();
stopDeviceDetailPolling();
super.onDestroy();
}
@Override
public void logOut(BaseBean bean) {
}
@Override
public void addDeviceSuccess(WaterDeviceEntity bean) {
}
@Override
public void editDeviceInfoSuccess(BaseBean bean) {
}
@Override
public void deleteDeviceSuccess(BaseBean bean) {
}
@Override
public void queryDeviceBindStatusSuccess(WaterDeviceEntity bean) {
}
}

View File

@@ -9,7 +9,9 @@ import com.ckkj.water.databinding.DeviceInfoLayoutBinding;
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.ThemeManager;
import com.gyf.immersionbar.ImmersionBar;
/**
@@ -17,6 +19,9 @@ import com.gyf.immersionbar.ImmersionBar;
*/
public class DeviceInfoActivity extends BaseActivity<DeviceInfoLayoutBinding, DevicePresenter>
implements DeviceContract.DeviceView {
private WaterDeviceEntity mDeviceEntity;
@Override
public DevicePresenter createPresenter() {
return new DevicePresenter(new DeviceImpl());
@@ -25,10 +30,17 @@ public class DeviceInfoActivity extends BaseActivity<DeviceInfoLayoutBinding, De
@Override
public void initData(Bundle savedInstanceState) {
super.initData(savedInstanceState);
Bundle bundle = getIntent().getExtras();
if(bundle != null){
mDeviceEntity = (WaterDeviceEntity) bundle.getSerializable("device_entity");
viewBinding.tvDevCode.setText(mDeviceEntity.getDeviceNo());
viewBinding.tvDevMod.setText(mDeviceEntity.getDeviceEm());
viewBinding.tvDeviceSerial.setText(mDeviceEntity.getDeviceSn());
viewBinding.tvDevVersion.setText(mDeviceEntity.getFwVer());
}
ImmersionBar.with(this)
.statusBarColor(R.color.white) // 设置状态栏颜色
.statusBarDarkFont(true) // 设置状态栏字体为黑色
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext)) // 白色状态栏图标
.navigationBarColor(R.color.mainBackgroundColor)
.titleBar(viewBinding.rlTitle)
.init();
viewBinding.includedTitle.tvTitle.setText(R.string.device_setting_info);
@@ -65,4 +77,39 @@ public class DeviceInfoActivity extends BaseActivity<DeviceInfoLayoutBinding, De
public void logOut(BaseBean bean) {
}
@Override
public void addDeviceSuccess(WaterDeviceEntity 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, String status) {
}
@Override
public void queryDeviceBindStatusSuccess(WaterDeviceEntity bean) {
}
@Override
public void queryDeviceWorkTimeSuccess(WaterDeviceEntity bean) {
}
}

View File

@@ -1,16 +1,21 @@
package com.ckkj.water.device;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.view.View;
import com.ckkj.water.ConstantUtil;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity;
import com.ckkj.water.base.BaseFragment;
import com.ckkj.water.databinding.SetDeviceNameLayoutBinding;
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.show.ToastUtil;
import com.gyf.immersionbar.ImmersionBar;
@@ -19,6 +24,11 @@ import com.gyf.immersionbar.ImmersionBar;
*/
public class DeviceReNameActivity extends BaseActivity<SetDeviceNameLayoutBinding, DevicePresenter>
implements DeviceContract.DeviceView {
private String mWifiName;
private String mWifiPassword;
private WaterDeviceEntity mBleDeviceEntity;
@Override
public DevicePresenter createPresenter() {
return new DevicePresenter(new DeviceImpl());
@@ -27,6 +37,15 @@ public class DeviceReNameActivity extends BaseActivity<SetDeviceNameLayoutBindin
@Override
public void initData(Bundle savedInstanceState) {
super.initData(savedInstanceState);
Bundle bundle = getIntent().getExtras();
if (bundle != null) {
mBleDeviceEntity = (WaterDeviceEntity) bundle.getSerializable("ble_device_entity");
if(mBleDeviceEntity != null){
mWifiName = mBleDeviceEntity.getWifiName();
mWifiPassword = mBleDeviceEntity.getWifiPassword();
}
}
ImmersionBar.with(this)
.statusBarColor(R.color.color_F9F9F9) // 设置状态栏颜色
.statusBarDarkFont(true) // 设置状态栏字体为黑色
@@ -56,12 +75,41 @@ public class DeviceReNameActivity extends BaseActivity<SetDeviceNameLayoutBindin
ToastUtil.showShort(mContext,getString(R.string.input_device_name_hint));
return;
}
ToastUtil.showShort(mContext,"名称保存成功");
goTo(mContext,DeviceControlActivity.class,null);
String deviceName = viewBinding.etDeviceName.getText().toString().trim();
// ToastUtil.showShort(mContext,"名称保存成功");
// goTo(mContext,DeviceControlActivity.class,null);
showLoadingDialog();
mPresenter.addDevice(deviceName,mWifiName,mWifiPassword,mBleDeviceEntity);
break;
}
}
/**
* 添加设备成功
* @param bean
*/
@Override
public void addDeviceSuccess(WaterDeviceEntity bean) {
hideDialogLoading();
ToastUtil.showShort(mContext,getString(R.string.device_save_success));
ActivityManager.removeActivityByTag(ConstantUtil.ADD_DEVICE_FLAG);
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Bundle bundle = new Bundle();
bundle.putSerializable("device_info",bean);
goTo(mContext,DeviceControlActivity.class,bundle);
finish();
}
},800);
}
@Override
public void getDeviceDetailInfo(WaterDeviceEntity bean) {
}
@Override
public int getLayoutResId(Bundle savedInstanceState) {
return 0;
@@ -69,11 +117,52 @@ public class DeviceReNameActivity extends BaseActivity<SetDeviceNameLayoutBindin
@Override
public void showError(String message) {
hideDialogLoading();
showDialogtext(null, false, false, false,
getString(R.string.tip_text),
message,
null,
getString(R.string.confirm_text),
new BaseFragment.OnDialogClick() {
@Override
public void onCancelClick() {
}
@Override
public void onConfirmClick() {
finish();
}
});
}
@Override
public void logOut(BaseBean bean) {
}
@Override
public void editDeviceInfoSuccess(BaseBean bean) {
}
@Override
public void deleteDeviceSuccess(BaseBean bean) {
}
@Override
public void turnOnOffDeviceSuccess(BaseBean bean, String status) {
}
@Override
public void queryDeviceBindStatusSuccess(WaterDeviceEntity bean) {
}
@Override
public void queryDeviceWorkTimeSuccess(WaterDeviceEntity bean) {
}
}

View File

@@ -2,11 +2,13 @@ package com.ckkj.water.device;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import androidx.annotation.Nullable;
import com.ckkj.water.ConstantUtil;
import com.ckkj.water.EventMessage;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity;
import com.ckkj.water.base.BaseFragment;
@@ -14,15 +16,21 @@ import com.ckkj.water.databinding.DeviceSettingsLayoutBinding;
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.ThemeManager;
import com.ckkj.water.utils.show.ToastUtil;
import com.gyf.immersionbar.ImmersionBar;
import de.greenrobot.event.EventBus;
/**
* 设备控制页面
*/
public class DeviceSettingsActivity extends BaseActivity<DeviceSettingsLayoutBinding, DevicePresenter>
implements DeviceContract.DeviceView {
private WaterDeviceEntity mDeviceEntity;
@Override
public DevicePresenter createPresenter() {
return new DevicePresenter(new DeviceImpl());
@@ -31,10 +39,16 @@ public class DeviceSettingsActivity extends BaseActivity<DeviceSettingsLayoutBin
@Override
public void initData(Bundle savedInstanceState) {
super.initData(savedInstanceState);
Bundle bundle = getIntent().getExtras();
if(bundle != null){
mDeviceEntity = (WaterDeviceEntity) bundle.getSerializable("device_entity");
viewBinding.tvDeviceName.setText(mDeviceEntity.getDeviceName());
viewBinding.tvWifiName.setText(mDeviceEntity.getWifiName());
}
ImmersionBar.with(this)
.statusBarColor(R.color.white) // 设置状态栏颜色
.statusBarDarkFont(true) // 设置状态栏字体为黑色
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext)) // 白色状态栏图标
.navigationBarColor(R.color.mainBackgroundColor)
.titleBar(viewBinding.rlTitle)
.init();
viewBinding.includedTitle.tvTitle.setText(R.string.settings_title);
@@ -56,15 +70,21 @@ public class DeviceSettingsActivity extends BaseActivity<DeviceSettingsLayoutBin
public void onClick(View v){
switch (v.getId()){
case R.id.img_back:
Intent intent = new Intent();
intent.putExtra("device_entity",mDeviceEntity);
setResult(RESULT_OK,intent);
finish();
break;
case R.id.rl_dev_name:
showEditDialog(getString(R.string.device_setting_name),
getString(R.string.input_device_name_hint),
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() {
}
@@ -76,6 +96,8 @@ public class DeviceSettingsActivity extends BaseActivity<DeviceSettingsLayoutBin
@Override
public void onConfirmClisk(String content) {
viewBinding.tvDeviceName.setText(content);
mDeviceEntity.setDeviceName(content);
mPresenter.updateDeviceInfo(mDeviceEntity);
}
@Override
@@ -85,13 +107,31 @@ public class DeviceSettingsActivity extends BaseActivity<DeviceSettingsLayoutBin
break;
case R.id.rl_dev_info:
goTo(mContext, DeviceInfoActivity.class,null);
Bundle bundleDev = new Bundle();
bundleDev.putSerializable("device_entity",mDeviceEntity);
goTo(mContext, DeviceInfoActivity.class,bundleDev);
break;
case R.id.rl_dev_wifi_manager:
Bundle bundle = new Bundle();
bundle.putInt(ConstantUtil.ACTIVITY_NAVIGATE_FLAG,200);
goToForResult(mContext, SelectWifiActivity.class,REQUEST_EVENT_CODE_10001,bundle);
showDialogtext(null, false, false, true,
getString(R.string.tip_text),
getString(R.string.reset_device_provision),
getString(R.string.cancel_text),
getString(R.string.confirm_text),
new BaseFragment.OnDialogClick() {
@Override
public void onCancelClick() {
}
@Override
public void onConfirmClick() {
Bundle bundle = new Bundle();
bundle.putInt(ConstantUtil.ACTIVITY_NAVIGATE_FLAG,100);
bundle.putSerializable("ble_device_entity",mDeviceEntity);
goToForResult(mContext, SelectWifiActivity.class,REQUEST_EVENT_CODE_10001,bundle);
}
});
break;
case R.id.rl_dev_unbind:
@@ -104,7 +144,7 @@ public class DeviceSettingsActivity extends BaseActivity<DeviceSettingsLayoutBin
@Override
public void onConfirmClick() {
ToastUtil.showShort(mContext,"确定");
mPresenter.unBindDevice(mDeviceEntity);
}
});
break;
@@ -112,6 +152,14 @@ public class DeviceSettingsActivity extends BaseActivity<DeviceSettingsLayoutBin
}
}
@Override
public void onBackPressed() {
Intent intent = new Intent();
intent.putExtra("device_entity",mDeviceEntity);
setResult(RESULT_OK,intent);
super.onBackPressed();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
@@ -119,13 +167,46 @@ public class DeviceSettingsActivity extends BaseActivity<DeviceSettingsLayoutBin
if (resultCode == RESULT_OK) {
switch (requestCode) {
case REQUEST_EVENT_CODE_10001:
ToastUtil.showShort(mContext,data.getStringExtra("wifi_name"));
viewBinding.tvWifiName.setText(data.getStringExtra("wifi_name"));
mDeviceEntity.setWifiName(data.getStringExtra("wifi_name"));
mDeviceEntity.setWifiPassword(data.getStringExtra("wifi_psd"));
mPresenter.updateDeviceInfo(mDeviceEntity);
break;
}
}
}
}
/**
* 修改设备信息成功
* @param bean
*/
@Override
public void editDeviceInfoSuccess(BaseBean bean) {
hideDialogLoading();
}
@Override
public void finish() {
super.finish();
}
@Override
public void deleteDeviceSuccess(BaseBean bean) {
hideDialogLoading();
ToastUtil.showShort(mContext, getString(R.string.unbind_success));
EventBus.getDefault().post(new EventMessage(EventMessage.ACTIVITY_FINISH));
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
finish();
}
},500);
}
@Override
public int getLayoutResId(Bundle savedInstanceState) {
return 0;
@@ -140,4 +221,33 @@ public class DeviceSettingsActivity extends BaseActivity<DeviceSettingsLayoutBin
public void logOut(BaseBean bean) {
}
@Override
public void addDeviceSuccess(WaterDeviceEntity bean) {
}
@Override
public void getDeviceDetailInfo(WaterDeviceEntity bean) {
}
@Override
public void turnOnOffDeviceSuccess(BaseBean bean, String status) {
}
@Override
public void queryDeviceBindStatusSuccess(WaterDeviceEntity bean) {
}
@Override
public void queryDeviceWorkTimeSuccess(WaterDeviceEntity bean) {
}
}

View File

@@ -1,49 +1,88 @@
package com.ckkj.water.device;
import android.Manifest;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import androidx.core.app.ActivityCompat;
import androidx.recyclerview.widget.DividerItemDecoration;
import android.Manifest;
import android.content.Intent;
import android.graphics.drawable.AnimationDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.text.InputType;
import android.text.TextUtils;
import android.text.method.PasswordTransformationMethod;
import android.util.Log;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import com.ckkj.water.ConstantUtil;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity;
import com.ckkj.water.databinding.DeviceResetWifiBinding;
import com.ckkj.water.device.adapter.WifiListAdapter;
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.espBle.EspProvisionManager;
import com.ckkj.water.espBle.ProvisionCallback;
import com.espressif.provisioning.WiFiAccessPoint;
import com.espressif.provisioning.listeners.WiFiScanListener;
import com.ckkj.water.index.entity.WaterDeviceEntity;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.permission.PermissionActivity;
import com.ckkj.water.utils.manager.ActivityManager;
import com.ckkj.water.utils.manager.ThemeManager;
import com.ckkj.water.utils.show.ToastUtil;
import com.ckkj.water.utils.text.TextUtil;
import com.gyf.immersionbar.ImmersionBar;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
/**
* 配置wifi
*/
public class SelectWifiActivity extends BaseActivity<DeviceResetWifiBinding, DevicePresenter>
implements DeviceContract.DeviceView {
private WifiManager wifiManager;
private static final long BLE_CONNECT_TIMEOUT = 40 * 1000L;
//当前密码显示/隐藏
private boolean isPasswordVisible = false;
private WifiListAdapter wifiListAdapter;
private WifiScanReceiver wifiScanReceiver;
//100 来自设备添加, 200 来自设备信息
private int WITCH_FROM_CODE = 100;
private String mWifiName = "";
private WaterDeviceEntity mBleDeviceEntity;
private WaterDeviceEntity mRemoteDeviceEntity;
private EspProvisionManager espProvisionManager;
private boolean isBleConnecting = false;
private boolean isBleConnected = false;
private boolean keepBleConnectionForProvision = false;
private boolean isBleConnectTimedOut = false;
private boolean isBleConnectTimeoutRunning = false;
private Handler bleConnectHandler = new Handler(Looper.getMainLooper());
private AnimationDrawable animationDrawable;
private Handler handler = new Handler(Looper.getMainLooper());
private final Runnable bleConnectTask = new Runnable() {
@Override
public void run() {
connectBleDevice();
}
};
private final Runnable bleConnectTimeoutTask = new Runnable() {
@Override
public void run() {
handleBleConnectTimeout();
}
};
@Override
public DevicePresenter createPresenter() {
return new DevicePresenter(new DeviceImpl());
@@ -52,21 +91,17 @@ public class SelectWifiActivity extends BaseActivity<DeviceResetWifiBinding, Dev
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
wifiManager = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
wifiScanReceiver = new WifiScanReceiver();
ActivityManager.addActivity(this,getClass().getSimpleName() + "_" + ConstantUtil.ADD_DEVICE_FLAG);
}
@Override
protected void onResume() {
super.onResume();
registerReceiver(wifiScanReceiver, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
}
@Override
protected void onPause() {
super.onPause();
unregisterReceiver(wifiScanReceiver);
protected void onDestroy() {
super.onDestroy();
cancelBleConnectTask();
if (WITCH_FROM_CODE == 100 && espProvisionManager != null && !keepBleConnectionForProvision) {
espProvisionManager.disconnect();
espProvisionManager.setCallback(null);
}
}
@Override
@@ -75,11 +110,12 @@ public class SelectWifiActivity extends BaseActivity<DeviceResetWifiBinding, Dev
Bundle bundle = getIntent().getExtras();
if(bundle != null){
WITCH_FROM_CODE = bundle.getInt(ConstantUtil.ACTIVITY_NAVIGATE_FLAG);
mBleDeviceEntity = (WaterDeviceEntity) bundle.getSerializable("ble_device_entity");
}
ImmersionBar.with(this)
.statusBarColor(R.color.color_F9F9F9)
.statusBarDarkFont(true)
.statusBarColor(R.color.color_gray_theme)
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext))
.titleBar(viewBinding.rlTitle)
.init();
if(WITCH_FROM_CODE == 200){
@@ -89,10 +125,39 @@ public class SelectWifiActivity extends BaseActivity<DeviceResetWifiBinding, Dev
initListener();
initWifiList();
if (WITCH_FROM_CODE == 100) {
initEspProvision();
scheduleBleConnect();
}
}
private void startConnectAnimation() {
if (animationDrawable != null && animationDrawable.isRunning()) {
animationDrawable.stop();
}
viewBinding.ivConnectAnimation.setImageResource(R.drawable.anim_connect_ble_device);
animationDrawable = (AnimationDrawable) viewBinding.ivConnectAnimation.getDrawable();
if (animationDrawable != null) {
animationDrawable.start();
}
}
private void stopConnectAnimation() {
handler.removeCallbacksAndMessages(null);
if (animationDrawable != null) {
animationDrawable.stop();
animationDrawable = null;
}
viewBinding.linearConnectBle.setVisibility(GONE);
viewBinding.linearWifiContent.setVisibility(VISIBLE);
}
private void initWifiList() {
wifiListAdapter = new WifiListAdapter();
wifiListAdapter = new WifiListAdapter(mContext);
viewBinding.rvWifiList.setAdapter(wifiListAdapter);
wifiListAdapter.setOnWifiItemClickListener(wifiName -> {
mWifiName = wifiName;
@@ -107,6 +172,7 @@ public class SelectWifiActivity extends BaseActivity<DeviceResetWifiBinding, Dev
setClick(viewBinding.includedTitle.imgBack);
setClick(viewBinding.tvSelectWifi);
setClick(viewBinding.tvNext);
setClick(viewBinding.imgPsdShowHide);
}
private void setClick(View view) {
@@ -120,6 +186,14 @@ public class SelectWifiActivity extends BaseActivity<DeviceResetWifiBinding, Dev
break;
case R.id.tv_select_wifi:
String[] permission = {
Manifest.permission.ACCESS_FINE_LOCATION
};
boolean hasPermission = hasRecordPermission(permission);
if(!hasPermission){
goTo(mContext, PermissionActivity.class,null);
return;
}
scanWifi();
break;
@@ -132,36 +206,250 @@ public class SelectWifiActivity extends BaseActivity<DeviceResetWifiBinding, Dev
ToastUtil.showShort(mContext,getString(R.string.toast_select_wifi_psd_tip));
return;
}
String wifiPassword = viewBinding.etInputPsd.getText().toString();
if(WITCH_FROM_CODE == 100){
goTo(mContext,DeviceConnectActivity.class,null);
goToDeviceConnect(wifiPassword);
}else{
Intent intent = new Intent();
intent.putExtra("wifi_name",mWifiName);
intent.putExtra("wifi_psd",viewBinding.etInputPsd.getText().toString());
intent.putExtra("wifi_psd",wifiPassword);
setResult(RESULT_OK,intent);
finish();
}
break;
case R.id.img_psd_show_hide:
togglePasswordVisibility();
break;
}
}
private void togglePasswordVisibility() {
EditText etPassword = viewBinding.etInputPsd;
ImageView ivToggle = viewBinding.imgPsdShowHide;
if (etPassword == null || ivToggle == null) {
return;
}
if (isPasswordVisible) {
// 隐藏密码
etPassword.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
etPassword.setTransformationMethod(PasswordTransformationMethod.getInstance());
ivToggle.setImageResource(R.mipmap.icon_psd_hide);
} else {
// 显示密码
etPassword.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
etPassword.setTransformationMethod(null);
ivToggle.setImageResource(R.mipmap.icon_psd_show);
}
// 保持光标位置
etPassword.setSelection(etPassword.getText().length());
isPasswordVisible = !isPasswordVisible;
}
private void goToDeviceConnect(String wifiPassword) {
if (espProvisionManager == null) {
initEspProvision();
}
if (mBleDeviceEntity == null) {
ToastUtil.showShort(mContext, getString(R.string.device_not_initialized));
return;
}
isBleConnected = espProvisionManager != null && espProvisionManager.isConnected();
if (!isBleConnected) {
if (!isBleConnecting) {
scheduleBleConnect();
}
ToastUtil.showShort(mContext, getString(R.string.device_connecting_please_wait));
return;
}
Bundle bundle = new Bundle();
bundle.putString("wifi_name", mWifiName);
bundle.putString("wifi_password", wifiPassword);
bundle.putSerializable("ble_device_entity", mBleDeviceEntity);
keepBleConnectionForProvision = true;
cancelBleConnectTask();
goTo(mContext, DeviceConnectActivity.class, bundle);
finish();
}
private void initEspProvision() {
espProvisionManager = EspProvisionManager.init(mContext).setCallback(new ProvisionCallback() {
@Override
public void onConnecting() {
isBleConnecting = true;
isBleConnected = false;
startBleConnectTimeout();
Log.i("TEST","onConnecting");
}
@Override
public void onConnected() {
isBleConnecting = false;
isBleConnected = true;
cancelBleConnectTask();
Log.i("TEST","onConnected设备连接成功");
stopConnectAnimation();
ToastUtil.showShort(mContext, getString(R.string.device_connected_success));
//Tips 蓝牙扫描添加的设备不同于二维码扫描连接的设备由于无法拿到deviceNo所以需要请求接口获取
mPresenter.bindDeviceStatus(mBleDeviceEntity);
}
@Override
public void onProvisioning(int progress) {
Log.i("TEST","WIFI连接进度 == " + progress);
mProgressLoadingDialog.setProgress(progress);
}
@Override
public void onSuccess(String deviceName, String deviceType) {
Log.i("TEST","deviceName" + deviceName + "连接成功");
ToastUtil.showShort(mContext, getString(R.string.connection_success));
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
hideDialogLoading();
}
}, 800);
}
@Override
public void onFailed(int errorCode, String message) {
cancelBleConnectTask();
isBleConnecting = false;
if (errorCode != 0) {
isBleConnected = espProvisionManager != null && espProvisionManager .isConnected();
}
Log.i("TEST","errorCode == " + errorCode + " message == " + message);
ToastUtil.showShort(mContext, message);
stopConnectAnimation();
finish();
}
@Override
public void onDisconnected() {
isBleConnecting = false;
isBleConnected = false;
cancelBleConnectTask();
stopConnectAnimation();
Log.i("TEST","onDisconnected");
if (isBleConnectTimedOut) {
return;
}
ToastUtil.showShort(mContext, getString(R.string.device_disconnected));
}
@Override
public void onWifiListReceived(String[] wifiList) {
Log.i("TEST","onWifiListReceived-Msg==" + wifiList.toString());
}
@Override
public void onWifiListFailed(String message) {
Log.i("TEST","onWifiListFailed-Msg==" + message);
}
@Override
public void onProvisioningRejected() {
Log.i("TEST","onProvisioningRejected");
}
});
}
/**
* 连接设备
*/
private void scheduleBleConnect() {
startConnectAnimation();
startBleConnectTimeout();
bleConnectHandler.removeCallbacks(bleConnectTask);
bleConnectHandler.postDelayed(bleConnectTask, 1000);
}
private void connectBleDevice() {
if (WITCH_FROM_CODE != 100 || mBleDeviceEntity == null || espProvisionManager == null) {
return;
}
if (isBleConnected || isBleConnecting) {
return;
}
espProvisionManager.connect(mBleDeviceEntity.getMacAddress().toUpperCase(Locale.ROOT),
mBleDeviceEntity.getDeviceName());
}
private void startBleConnectTimeout() {
if (isBleConnected || keepBleConnectionForProvision) {
return;
}
if (isBleConnectTimeoutRunning) {
return;
}
isBleConnectTimedOut = false;
isBleConnectTimeoutRunning = true;
bleConnectHandler.removeCallbacks(bleConnectTimeoutTask);
bleConnectHandler.postDelayed(bleConnectTimeoutTask, BLE_CONNECT_TIMEOUT);
}
private void cancelBleConnectTask() {
isBleConnectTimeoutRunning = false;
bleConnectHandler.removeCallbacks(bleConnectTask);
bleConnectHandler.removeCallbacks(bleConnectTimeoutTask);
}
private void handleBleConnectTimeout() {
if (WITCH_FROM_CODE != 100 || isBleConnected || keepBleConnectionForProvision) {
return;
}
isBleConnectTimedOut = true;
isBleConnecting = false;
cancelBleConnectTask();
if (espProvisionManager != null) {
espProvisionManager.disconnect();
}
stopConnectAnimation();
ToastUtil.showShort(mContext, getString(R.string.ble_connect_timeout));
finish();
}
private void scanWifi() {
showLoadingDialog();
viewBinding.tvNext.setVisibility(View.GONE);
if (wifiManager != null && wifiManager.isWifiEnabled()) {
wifiManager.startScan();
} else {
ToastUtil.showShort(mContext, getString(R.string.wifi_not_enabled));
if (espProvisionManager == null || !espProvisionManager.isConnected()) {
ToastUtil.showShort(mContext, getString(R.string.device_not_connected_retry));
hideDialogLoading();
return;
}
espProvisionManager.scanWifiFromDevice(new WiFiScanListener() {
@Override
public void onWifiListReceived(ArrayList<WiFiAccessPoint> wifiList) {
runOnUiThread(() -> displayWifiListFromDevice(wifiList));
}
@Override
public void onWiFiScanFailed(Exception e) {
runOnUiThread(() -> {
ToastUtil.showShort(mContext, getString(R.string.scan_failed) + e.getMessage());
hideDialogLoading();
});
}
});
}
private void displayWifiList(List<ScanResult> scanResults) {
private void displayWifiListFromDevice(ArrayList<WiFiAccessPoint> wifiNetworks) {
Set<String> wifiSet = new HashSet<>();
List<String> wifiNameList = new ArrayList<>();
for (ScanResult result : scanResults) {
String wifiName = result.SSID;
for (WiFiAccessPoint network : wifiNetworks) {
String wifiName = network.getWifiName();
if (wifiName != null && !wifiName.isEmpty() && !wifiSet.contains(wifiName)) {
wifiSet.add(wifiName);
wifiNameList.add(wifiName);
@@ -180,31 +468,43 @@ public class SelectWifiActivity extends BaseActivity<DeviceResetWifiBinding, Dev
}
}
private class WifiScanReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (WifiManager.SCAN_RESULTS_AVAILABLE_ACTION.equals(intent.getAction())) {
boolean success = intent.getBooleanExtra(WifiManager.EXTRA_RESULTS_UPDATED, false);
if (success) {
if (ActivityCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
List<ScanResult> results = wifiManager.getScanResults();
displayWifiList(results);
} else {
ToastUtil.showShort(mContext, getString(R.string.wifi_scan_failed));
}
}
}
@Override
public void queryDeviceBindStatusSuccess(WaterDeviceEntity bean) {
mBleDeviceEntity = bean.getBindDevice();
}
@Override
public void queryDeviceWorkTimeSuccess(WaterDeviceEntity bean) {
}
@Override
public void addDeviceSuccess(WaterDeviceEntity 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, String status) {
}
@Override
public int getLayoutResId(Bundle savedInstanceState) {
return 0;

View File

@@ -0,0 +1,31 @@
package com.ckkj.water.device.adapter;
import android.content.Context;
import android.text.TextUtils;
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, TextUtils.isEmpty(item.getDeviceName())? "未知设备" : item.getDeviceName());
}
}

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;
@@ -32,7 +41,7 @@ public class DeviceTimeSelectAdapter extends BaseQuickAdapter<String, BaseViewHo
helper.setTextColor(R.id.tv_itme,mContext.getResources().getColor(R.color.white));
}else{
relTextBg.setBackground(mContext.getResources().getDrawable(R.drawable.shape_f7f7f7_line_button_bg));
helper.setTextColor(R.id.tv_itme,mContext.getResources().getColor(R.color.color_838080));
helper.setTextColor(R.id.tv_itme,mContext.getResources().getColor(R.color.textPrimaryColor));
}
}

View File

@@ -1,5 +1,6 @@
package com.ckkj.water.device;
package com.ckkj.water.device.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -10,6 +11,8 @@ import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity;
import com.ckkj.water.utils.manager.ThemeManager;
import java.util.ArrayList;
import java.util.List;
@@ -18,6 +21,10 @@ public class WifiListAdapter extends RecyclerView.Adapter<WifiListAdapter.WifiVi
private List<String> wifiList = new ArrayList<>();
private OnWifiItemClickListener listener;
private Context mContext;
public WifiListAdapter(BaseActivity mContext) {
this.mContext = mContext;
}
public interface OnWifiItemClickListener {
void onWifiItemClick(String wifiName);
@@ -49,6 +56,9 @@ public class WifiListAdapter extends RecyclerView.Adapter<WifiListAdapter.WifiVi
listener.onWifiItemClick(wifiName);
}
});
holder.ivWifiIcon.setImageResource(ThemeManager.isAppThemeLight(mContext) ?
R.mipmap.icon_wifi :
R.mipmap.icon_white_wifi );
}
@Override

View File

@@ -0,0 +1,18 @@
package com.ckkj.water.device.manage;
import android.content.Context;
import androidx.annotation.NonNull;
import no.nordicsemi.android.ble.BleManager;
/**
* @author fengxiaoyang
* @date 2026/5/30
* @description TODO: 描述该类的作用
*/
public class DeviceBleManager extends BleManager {
public DeviceBleManager(@NonNull Context context) {
super(context);
}
}

View File

@@ -1,6 +1,13 @@
package com.ckkj.water.device.mvp;
import com.ckkj.water.base.IView;
import com.ckkj.water.index.entity.WaterDeviceEntity;
import com.ckkj.water.index.mvp.IndexContract;
import com.ckkj.water.network.BaseBean;
import org.json.JSONObject;
import java.util.Map;
public class DeviceContract {
public interface CallBack<T> {
@@ -8,8 +15,35 @@ public class DeviceContract {
void fail(String msg);
}
public interface DataModel{
//查询设备信息
void getDeviceInfomation(Map<String,String> map, IndexContract.CallBack callBack);
//添加设备
void addDevice(JSONObject json, IndexContract.CallBack callBack);
//修改设备信息
void editDeviceInfo(JSONObject json, IndexContract.CallBack callBack);
//删除设备信息
void deleteDevice(String id, IndexContract.CallBack callBack);
//手动开启关闭设备
void turnOnOffDevice(JSONObject json, IndexContract.CallBack callBack);
//设备当前工作的开始时间和结束时间
void queryDeviceWorkTime(Map<String,String> map , IndexContract.CallBack callBack);
}
public interface DeviceView extends IView {
//添加设备成功g
void addDeviceSuccess(WaterDeviceEntity bean);
void getDeviceDetailInfo(WaterDeviceEntity bean);
void editDeviceInfoSuccess(BaseBean bean);
void deleteDeviceSuccess(BaseBean bean);
void turnOnOffDeviceSuccess(BaseBean bean, String status);
void queryDeviceBindStatusSuccess(WaterDeviceEntity bean);
//查询设备目前工作状态
void queryDeviceWorkTimeSuccess(WaterDeviceEntity bean);
}
}

View File

@@ -1,5 +1,244 @@
package com.ckkj.water.device.mvp;
import com.ckkj.water.index.entity.WaterDeviceEntity;
import com.ckkj.water.index.mvp.IndexContract;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.network.NetWorkManager;
import com.ckkj.water.plan.entity.WaterPlanWeekEntity;
import org.json.JSONObject;
import java.util.List;
import java.util.Map;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
public class DeviceImpl implements DeviceContract.DataModel{
@Override
public void getDeviceInfomation(Map<String, String> map, IndexContract.CallBack callBack) {
String deviceID = map.get("deviceNo");
NetWorkManager.getRequest().getDeviceDetailInfo(NetWorkManager.getToken(), deviceID)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean<WaterDeviceEntity>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean<WaterDeviceEntity> bean) {
if (bean.getCode() == 200) {
callBack.success(bean.getData(),0);
}
}
@Override
public void onError(@NonNull Throwable e) {
callBack.fail(e.getMessage());
}
@Override
public void onComplete() {
}
});
}
/**
* 添加设备
* @param json
* @param callBack
*/
@Override
public void addDevice(JSONObject json, IndexContract.CallBack callBack) {
try {
NetWorkManager.getRequest().addDevice(NetWorkManager.getToken(), NetWorkManager.toRequestBody(json))
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean<WaterDeviceEntity>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean<WaterDeviceEntity> bean) {
if (bean.getCode() == 200) {
callBack.success(bean.getData(),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();
}
}
/**
* 修改设备信息
* @param json
* @param callBack
*/
@Override
public void editDeviceInfo(JSONObject json, IndexContract.CallBack callBack) {
try {
NetWorkManager.getRequest().updateDeviceInfo(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();
}
}
/**
* 删除设备
* @param callBack
*/
@Override
public void deleteDevice(String deviceNo, IndexContract.CallBack callBack) {
try {
NetWorkManager.getRequest().unBindDevice(NetWorkManager.getToken(), deviceNo)
.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();
}
}
/**
* 手动开启关闭设备
* @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();
}
}
/**
* 查询设备工作时间
* @param map
* @param callBack
*/
@Override
public void queryDeviceWorkTime(Map<String, String> map, IndexContract.CallBack callBack) {
NetWorkManager.getRequest().queryDeviceWorkTime(NetWorkManager.getToken(),map)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean<WaterDeviceEntity>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean<WaterDeviceEntity> bean) {
if (bean.getCode() == 200) {
callBack.success(bean.getData(),0);
}
}
@Override
public void onError(@NonNull Throwable e) {
callBack.fail(e.getMessage());
}
@Override
public void onComplete() {
}
});
}
}

View File

@@ -1,6 +1,18 @@
package com.ckkj.water.device.mvp;
import com.ckkj.water.base.BasePresenter;
import com.ckkj.water.index.entity.WaterDeviceEntity;
import com.ckkj.water.index.mvp.IndexContract;
import com.ckkj.water.index.mvp.IndexImpl;
import com.ckkj.water.index.mvp.IndexPresenter;
import com.ckkj.water.network.BaseBean;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
public class DevicePresenter extends BasePresenter<DeviceContract.DeviceView> {
private DeviceImpl impl;
@@ -8,4 +20,207 @@ public class DevicePresenter extends BasePresenter<DeviceContract.DeviceView> {
public DevicePresenter(DeviceImpl impl) {
this.impl = impl;
}
/**
* 添加设备
* @param mWifiName
* @param mWifiPassword
* @param mBleDeviceEntity
*/
public void addDevice(String deviceName,String mWifiName, String mWifiPassword, WaterDeviceEntity mBleDeviceEntity) {
try {
JSONObject json = new JSONObject();
json.put("deviceName",deviceName);
json.put("wifiName",mWifiName);
json.put("wifiPassword",mWifiPassword);
json.put("macAddress",mBleDeviceEntity.getMacAddress());
json.put("deviceNo", mBleDeviceEntity.getDeviceNo());
// json.put("deviceEm","213123123123");
// json.put("deviceSn","sdfsd32423gdfgdf3");
// json.put("fwVer","fwVer-56565644");
impl.addDevice(json, new IndexContract.CallBack<WaterDeviceEntity>() {
@Override
public void success(WaterDeviceEntity bean,int total) {
if(DevicePresenter.this.getView() != null){
DevicePresenter.this.getView().addDeviceSuccess(bean);
}
}
@Override
public void fail(String msg) {
if(DevicePresenter.this.getView() != null){
DevicePresenter.this.getView().showError( msg);
}
}
});
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
/**
* 获取设备详情信息
* @param deviceNo
*/
public void getDeviceDetailInfo(String deviceNo) {
Map<String,String> map = new HashMap<>();
map.put("deviceNo",deviceNo);
impl.getDeviceInfomation(map, new IndexContract.CallBack<WaterDeviceEntity>() {
@Override
public void success(WaterDeviceEntity bean, int total) {
if(DevicePresenter.this.getView() != null){
DevicePresenter.this.getView().getDeviceDetailInfo(bean);
}
}
@Override
public void fail(String msg) {
if(DevicePresenter.this.getView() != null){
DevicePresenter.this.getView().showError( msg);
}
}
});
}
/**
* 修改设备信息
* @param mDeviceEntity
*/
public void updateDeviceInfo(WaterDeviceEntity mDeviceEntity) {
try {
JSONObject json = new JSONObject();
json.put("deviceNo",mDeviceEntity.getDeviceNo());
json.put("deviceName",mDeviceEntity.getDeviceName());
json.put("wifiName",mDeviceEntity.getWifiName());
json.put("wifiPassword",mDeviceEntity.getWifiPassword());
impl.editDeviceInfo(json, new IndexContract.CallBack<BaseBean>() {
@Override
public void success(BaseBean bean,int total) {
if(DevicePresenter.this.getView() != null){
DevicePresenter.this.getView().editDeviceInfoSuccess(bean);
}
}
@Override
public void fail(String msg) {
if(DevicePresenter.this.getView() != null){
DevicePresenter.this.getView().showError( msg);
}
}
});
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
/**
* 解绑设备
* @param deviceEntity
*/
public void unBindDevice(WaterDeviceEntity deviceEntity) {
impl.deleteDevice(deviceEntity.getDeviceNo(), new IndexContract.CallBack<BaseBean>() {
@Override
public void success(BaseBean bean, int total) {
if (DevicePresenter.this.getView() != null) {
DevicePresenter.this.getView().deleteDeviceSuccess(bean);
}
}
@Override
public void fail(String msg) {
if (DevicePresenter.this.getView() != null) {
DevicePresenter.this.getView().showError(msg);
}
}
});
}
/**
* 开启/关闭浇水
* @param deviceEntity
* @param status
* @param duration
*/
public void switchDeviceWork(WaterDeviceEntity deviceEntity, String status, int duration) {
try {
JSONObject json = new JSONObject();
json.put("deviceNo",deviceEntity.getDeviceNo());
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,status);
}
}
@Override
public void fail(String msg) {
if(DevicePresenter.this.getView() != null){
DevicePresenter.this.getView().showError( msg);
}
}
});
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
/**
* 绑定设备状态
* @param entity
*/
public void bindDeviceStatus(WaterDeviceEntity entity) {
try {
JSONObject json = new JSONObject();
json.put("macAddress",
entity.getMacAddress() == null ? "" : entity.getMacAddress().toLowerCase());
new IndexImpl().queryDeviceBindStatus(json, new IndexContract.CallBack<WaterDeviceEntity>() {
@Override
public void success(WaterDeviceEntity bean, int total) {
bean.setMacAddress(entity.getMacAddress());
bean.setDeviceName(entity.getDeviceName());
if(DevicePresenter.this.getView() != null){
DevicePresenter.this.getView().queryDeviceBindStatusSuccess(bean);
}
}
@Override
public void fail(String msg) {
if(DevicePresenter.this.getView() != null){
DevicePresenter.this.getView().showError(msg);
}
}
});
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
/**
* 查询设备工作的开始时间和结束时间,计算进度
* @param deviceNo
*/
public void getDeviceWorkProgress(String deviceNo) {
Map<String,String> map = new HashMap<>();
map.put("deviceNo",deviceNo);
impl.queryDeviceWorkTime(map, new IndexContract.CallBack<WaterDeviceEntity>() {
@Override
public void success(WaterDeviceEntity bean, int total) {
if(DevicePresenter.this.getView() != null){
DevicePresenter.this.getView().queryDeviceWorkTimeSuccess(bean);
}
}
@Override
public void fail(String msg) {
if(DevicePresenter.this.getView() != null){
DevicePresenter.this.getView().showError( msg);
}
}
});
}
}

View File

@@ -19,6 +19,7 @@ import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;
import android.widget.TextView;
@@ -48,8 +49,10 @@ public class DialogAppUpdate {
public static View layoutView;
private Dialog mDialog;
private Activity mContext;
private TextView tvTitle,tvContent,tvConfirm,tvCancel;
private TextView tvTitle,tvContent,tvConfirm,tvCancel,tvVersionName;
private TextView tvProgressPercent;
private RelativeLayout rlUpdateContent;
private LinearLayout llProgressContainer,llButton;
private ImageView imgClose,imgClear;
private String startTime = "";
private String endTime = "";
@@ -84,7 +87,7 @@ public class DialogAppUpdate {
this.mContext = context;
this.mIsForceUpdate = isForceUpdate;
this.mVersionBean = bean;
mDwonLoadUrl = bean.getVersionUrl();
mDwonLoadUrl = bean.getDownloadUrl();
mDialog = new Dialog(context, R.style.ActionSheetDialogStyle);
//填充对话框的布局
layoutView = LayoutInflater.from(context).inflate(layoutId, null);
@@ -122,29 +125,28 @@ public class DialogAppUpdate {
dialogWindow.setAttributes(lp);
}
tvTitle = layoutView.findViewById(R.id.tv_title);
tvVersionName = layoutView.findViewById(R.id.tv_version);
tvContent = layoutView.findViewById(R.id.tv_update_content);
tvConfirm = layoutView.findViewById(R.id.tv_update);
tvCancel = layoutView.findViewById(R.id.tv_cencel);
tvCancel = layoutView.findViewById(R.id.tv_cancel);
progressBar = layoutView.findViewById(R.id.progress_bar);
rlUpdateContent = layoutView.findViewById(R.id.rel_file_content);
llProgressContainer = layoutView.findViewById(R.id.ll_progress_container);
llButton = layoutView.findViewById(R.id.Linear_button);
tvConfirm.setOnClickListener(onclick);
tvCancel.setOnClickListener(onclick);
if(isForceUpdate){
tvCancel.setVisibility(View.GONE);
}
// 初始化 DownloadManager
downloadManager = (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
notificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
tvVersionName.setText(bean.getLatestVersion());
// 解析更新说明支持纯文本和HTML格式
String releaseNotes = bean.getReleaseNotes();
releaseNotes = releaseNotes.replace("\n", "<br>");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
tvContent.setText(Html.fromHtml(bean.getVersionDescription(), Html.FROM_HTML_MODE_LEGACY));
tvContent.setText(Html.fromHtml(releaseNotes, Html.FROM_HTML_MODE_LEGACY));
} else {
tvContent.setText(Html.fromHtml(releaseNotes));
}
String [] desStr = bean.getVersionDescription().split("#");
String desLine = "";
for(String str : desStr){
desLine = desLine + str + "\n";
}
tvContent.setText(desLine);
tvContent.setLineSpacing(0, 1.2f);
}
@@ -163,9 +165,18 @@ public class DialogAppUpdate {
return;
}
}
if (!mIsForceUpdate && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.POST_NOTIFICATIONS)
!= PackageManager.PERMISSION_GRANTED) {
if(mListener != null){
mListener.callBackGetPermisson(Manifest.permission.POST_NOTIFICATIONS);
}
return;
}
}
startUpdateApp();
break;
case R.id.tv_cencel:
case R.id.tv_cancel:
dismiss();
AppConfig.USER_CONFIRM_UPDATE_APP = true;
break;
@@ -176,8 +187,8 @@ public class DialogAppUpdate {
public void startUpdateApp() {
//强制更新不需要隐藏dialog
if(mIsForceUpdate){
progressBar.setVisibility(View.VISIBLE);
rlUpdateContent.setVisibility(View.GONE);
llProgressContainer.setVisibility(View.VISIBLE);
llButton.setVisibility(View.GONE);
progressBar.setMax(100);
}else{
ToastUtil.showShort(mContext,"可在任务栏查看下载进度");
@@ -188,7 +199,6 @@ public class DialogAppUpdate {
File apkFile = new File(downloadDir, fileName);
String apkPath = apkFile.getAbsolutePath();
DownLoadApk downloadTask = new DownLoadApk(mContext,mIsForceUpdate);
downloadTask.startDownload(mDwonLoadUrl, fileName,apkPath);
downloadTask.setDownLoadCallback(new DownLoadApk.DownLoadCallback() {
@Override
public void updateProgress(long currentProgress, long total) {
@@ -209,6 +219,7 @@ public class DialogAppUpdate {
}
}
});
downloadTask.startDownload(mDwonLoadUrl, fileName,apkPath);
}
public void show() {
@@ -224,4 +235,4 @@ public class DialogAppUpdate {
}
}
}

View File

@@ -0,0 +1,94 @@
package com.ckkj.water.dialog;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.view.WindowManager;
import android.view.animation.LinearInterpolator;
import android.widget.ImageView;
import android.widget.TextView;
import com.ckkj.water.R;
import com.timqi.sectorprogressview.ColorfulRingProgressView;
import java.util.Objects;
/**
* author:
* time: 2019/9/18 9:34
* desc:
* version: 1.0.0
*/
public class LoadingProgressDialog extends Dialog {
private Context context;
private int mProgress = 0;
private ColorfulRingProgressView mCircleLoading;
public LoadingProgressDialog(Context context) {
super(context); // 应用透明背景样式
this.context=context;
}
public LoadingProgressDialog(Context context, String s) {
super(context);
this.context=context;
}
public void setProgress(int progress) {
mProgress = progress;
if(mCircleLoading != null){
mCircleLoading.setPercent( progress);
}
}
public LoadingProgressDialog(Context context, int theme) {
super(context, theme);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
init();
}
@SuppressLint({"ResourceAsColor", "MissingInflatedId"})
private void init() {
// 设置对话框背景为透明
getWindow().setBackgroundDrawable(context.getResources().getDrawable(R.color.transparent));
getWindow().setDimAmount(0f);
setCancelable(true);
setCanceledOnTouchOutside(false);
setContentView(R.layout.dialog_progress_layout);//loading的xml文件
mCircleLoading = findViewById(R.id.circle_loading);
mCircleLoading.setStartAngle(0);
mCircleLoading.setPercent(mProgress);
//加载动画资源
WindowManager.LayoutParams params = Objects.requireNonNull(getWindow()).getAttributes();
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.height = WindowManager.LayoutParams.MATCH_PARENT;
getWindow().setAttributes(params);
// 可选: 设置窗口大小
getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.MATCH_PARENT);
}
@Override
public void show() {//开启
// if (context==null){
// return;
// }
super.show();
}
@Override
public void dismiss() {//关闭
// if (context==null){
// return;
// }
super.dismiss();
}
}

View File

@@ -0,0 +1,646 @@
package com.ckkj.water.espBle;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import android.text.TextUtils;
import com.ckkj.water.AppConfig;
import com.espressif.provisioning.DeviceConnectionEvent;
import com.espressif.provisioning.ESPConstants;
import com.espressif.provisioning.ESPDevice;
import com.espressif.provisioning.WiFiAccessPoint;
import com.espressif.provisioning.listeners.ProvisionListener;
import com.espressif.provisioning.listeners.WiFiScanListener;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.Set;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;
/**
* @author fengxiaoyang
* @date 2026/6/4
* @description 鎿嶄綔钃濈墮绠$悊绫?
*/
public class EspProvisionManager {
private static volatile EspProvisionManager instance;
private final Context context;
private final Handler mainHandler;
private final com.espressif.provisioning.ESPProvisionManager provisionManager;
private static final long CONNECT_COOLDOWN = 1200L;
private static final long QUICK_DISCONNECT_WINDOW = 3000L;
private static final long RETRY_DELAY = 1200L;
private static final long PROGRESS_UPDATE_INTERVAL = 500L;
private static final int MAX_CONNECT_RETRY = 2;
private static final int MAX_AUTO_PROGRESS = 99;
private ESPDevice espDevice;
private ProvisionCallback callback;
private boolean connecting;
private boolean connected;
private boolean provisioning;
private int provisionProgress;
private int provisionTargetProgress;
private boolean eventBusRegistered;
private boolean ignoreNextDisconnectEvent;
private boolean expectDisconnected; // 标记是否预期断开(配网成功后)
private String currentDeviceName;
private String currentDeviceType = ProvisionConfig.TRANSPORT_BLE;
private BluetoothDevice pendingBluetoothDevice;
private String pendingDeviceName;
private String pendingServiceUuid;
private String pendingProofOfPossession;
private String pendingUserName;
private int pendingSecurityType;
private int connectRetryCount;
private long lastDisconnectTime;
private long connectStartTime;
private final Runnable connectionTimeoutTask = new Runnable() {
@Override
public void run() {
if (connecting && !connected) {
connecting = false;
disconnectDeviceOnly();
lastDisconnectTime = System.currentTimeMillis();
if (retryConnectIfAllowed()) {
return;
}
resetState(true);
clearPendingConnect();
notifyFailed(ProvisionConfig.ERROR_CONNECT_TIMEOUT, "蓝牙连接超时");
disconnectDeviceOnly();
}
}
};
private final Runnable provisionTimeoutTask = new Runnable() {
@Override
public void run() {
if (provisioning) {
finishProvisionFailed(ProvisionConfig.ERROR_PROVISION_TIMEOUT, "配网超时");
}
}
};
private final Runnable connectRetryTask = new Runnable() {
@Override
public void run() {
startPendingConnect(false);
}
};
private final Runnable provisionProgressTask = new Runnable() {
@Override
public void run() {
if (!provisioning) {
return;
}
if (provisionProgress < provisionTargetProgress) {
notifyProvisioning(provisionProgress + 1);
}
if (provisionProgress < MAX_AUTO_PROGRESS) {
mainHandler.postDelayed(provisionProgressTask, PROGRESS_UPDATE_INTERVAL);
}
}
};
private EspProvisionManager(Context context) {
this.context = context.getApplicationContext();
this.mainHandler = new Handler(Looper.getMainLooper());
this.provisionManager = com.espressif.provisioning.ESPProvisionManager.getInstance(this.context);
ensureEventBusRegistered();
}
public static EspProvisionManager init(Context context) {
if (instance == null) {
synchronized (EspProvisionManager.class) {
if (instance == null) {
instance = new EspProvisionManager(context);
}
}
}
return instance;
}
public static EspProvisionManager get() {
if (instance == null) {
throw new RuntimeException("Please call init()");
}
return instance;
}
public EspProvisionManager setCallback(ProvisionCallback callback) {
this.callback = callback;
return this;
}
public ESPDevice getEspDevice() {
return espDevice;
}
public boolean isConnected() {
return connected;
}
public boolean isProvisioning() {
return provisioning;
}
public int getProvisionProgress() {
return provisionProgress;
}
public void connect(String deviceAddress, String deviceName) {
connect(deviceAddress, deviceName, AppConfig.BLUETOOTH_DEVICE_UUID);
}
public void connect(String deviceAddress, String deviceName, String serviceUuid) {
connect(deviceAddress, deviceName, serviceUuid, "", ProvisionConfig.DEFAULT_SECURITY);
}
public void connect(String deviceAddress, String deviceName, String serviceUuid,
String proofOfPossession, int securityType) {
BluetoothAdapter bluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
if (bluetoothAdapter == null || TextUtils.isEmpty(deviceAddress)) {
notifyFailed(ProvisionConfig.ERROR_INVALID_DEVICE, "蓝牙设备无效");
return;
}
try {
BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(deviceAddress);
connect(bluetoothDevice, deviceName, serviceUuid, proofOfPossession, securityType);
} catch (IllegalArgumentException e) {
notifyFailed(ProvisionConfig.ERROR_INVALID_DEVICE, "蓝牙设备地址无效");
}
}
public void connect(BluetoothDevice bluetoothDevice, String serviceUuid) {
connect(bluetoothDevice, null, serviceUuid, "", ProvisionConfig.DEFAULT_SECURITY);
}
public void connect(BluetoothDevice bluetoothDevice, String deviceName, String serviceUuid,
String proofOfPossession, int securityType) {
connect(bluetoothDevice, deviceName, serviceUuid, proofOfPossession, "", securityType);
}
public void connect(BluetoothDevice bluetoothDevice, String deviceName, String serviceUuid,
String proofOfPossession, String userName, int securityType) {
if (bluetoothDevice == null) {
notifyFailed(ProvisionConfig.ERROR_INVALID_DEVICE, "蓝牙设备无效");
return;
}
if (connecting && pendingBluetoothDevice != null
&& TextUtils.equals(pendingBluetoothDevice.getAddress(), bluetoothDevice.getAddress())) {
return;
}
pendingBluetoothDevice = bluetoothDevice;
pendingDeviceName = deviceName;
pendingServiceUuid = TextUtils.isEmpty(serviceUuid) ? ProvisionConfig.SERVICE_UUID : serviceUuid;
pendingProofOfPossession = proofOfPossession == null ? "" : proofOfPossession;
pendingUserName = userName == null ? "" : userName;
pendingSecurityType = securityType;
connectRetryCount = 0;
startPendingConnect(true);
}
private void startPendingConnect(boolean respectCooldown) {
if (pendingBluetoothDevice == null) {
notifyFailed(ProvisionConfig.ERROR_INVALID_DEVICE, "蓝牙设备无效");
return;
}
if (provisioning) {
return;
}
mainHandler.removeCallbacks(connectRetryTask);
long elapsedSinceDisconnect = System.currentTimeMillis() - lastDisconnectTime;
if (respectCooldown && lastDisconnectTime > 0 && elapsedSinceDisconnect < CONNECT_COOLDOWN) {
mainHandler.postDelayed(connectRetryTask, CONNECT_COOLDOWN - elapsedSinceDisconnect);
return;
}
String displayName = !TextUtils.isEmpty(pendingDeviceName)
? pendingDeviceName : pendingBluetoothDevice.getName();
currentDeviceName = displayName;
currentDeviceType = ProvisionConfig.TRANSPORT_BLE;
boolean hadActiveDevice = espDevice != null && (connecting || connected || provisioning);
ignoreNextDisconnectEvent = hadActiveDevice;
disconnectDeviceOnly();
ensureEventBusRegistered();
espDevice = provisionManager.createESPDevice(
ESPConstants.TransportType.TRANSPORT_BLE,
parseSecurityType(pendingSecurityType)
);
espDevice.setBluetoothDevice(pendingBluetoothDevice);
espDevice.setPrimaryServiceUuid(pendingServiceUuid);
espDevice.setDeviceName(displayName);
espDevice.setProofOfPossession(pendingProofOfPossession);
espDevice.setUserName(pendingUserName);
connecting = true;
connected = false;
provisioning = false;
connectStartTime = System.currentTimeMillis();
mainHandler.removeCallbacks(connectionTimeoutTask);
mainHandler.postDelayed(connectionTimeoutTask, ProvisionConfig.CONNECTION_TIMEOUT);
notifyConnecting();
espDevice.connectBLEDevice(pendingBluetoothDevice, pendingServiceUuid);
}
private boolean retryConnectIfAllowed() {
if (pendingBluetoothDevice == null || provisioning || connected) {
return false;
}
if (connectRetryCount >= MAX_CONNECT_RETRY) {
return false;
}
connectRetryCount++;
resetState(true);
mainHandler.removeCallbacks(connectRetryTask);
mainHandler.postDelayed(connectRetryTask, RETRY_DELAY * connectRetryCount);
return true;
}
private void clearPendingConnect() {
pendingBluetoothDevice = null;
pendingDeviceName = null;
pendingServiceUuid = null;
pendingProofOfPossession = null;
pendingUserName = null;
pendingSecurityType = 0;
connectRetryCount = 0;
connectStartTime = 0;
}
public void provisionWifi(String ssid, String password) {
if (!checkConnected()) {
notifyFailed(ProvisionConfig.ERROR_NOT_CONNECTED, "设备未连接");
return;
}
if (TextUtils.isEmpty(ssid)) {
notifyFailed(ProvisionConfig.ERROR_WIFI_CONFIG_FAILED, "WiFi 名称不能为空");
return;
}
provisioning = true;
provisionProgress = 0;
provisionTargetProgress = 29;
mainHandler.removeCallbacks(provisionTimeoutTask);
mainHandler.postDelayed(provisionTimeoutTask, ProvisionConfig.PROVISION_TIMEOUT);
notifyProvisioning(0);
startProvisionProgressTask();
//wifi 密码 回调
espDevice.provision(ssid, password == null ? "" : password, new ProvisionListener() {
@Override
public void createSessionFailed(Exception e) {
finishProvisionFailed(ProvisionConfig.ERROR_SESSION_FAILED, getErrorMessage(e, "创建配网会话失败"));
}
@Override
public void wifiConfigSent() {
advanceProvisionProgress(30, 59);
}
@Override
public void wifiConfigFailed(Exception e) {
notifyProvisioningRejected();
finishProvisionFailed(ProvisionConfig.ERROR_WIFI_CONFIG_FAILED, getErrorMessage(e, "发送 WiFi 信息失败"));
}
@Override
public void wifiConfigApplied() {
advanceProvisionProgress(60, MAX_AUTO_PROGRESS);
}
@Override
public void wifiConfigApplyFailed(Exception e) {
finishProvisionFailed(ProvisionConfig.ERROR_WIFI_APPLY_FAILED, getErrorMessage(e, "应用 WiFi 信息失败"));
}
@Override
public void provisioningFailedFromDevice(ESPConstants.ProvisionFailureReason failureReason) {
finishProvisionFailed(ProvisionConfig.ERROR_PROVISION_FAILED, getProvisionFailureMessage(failureReason));
}
@Override
public void deviceProvisioningSuccess() {
finishProvisionSuccess();
}
@Override
public void onProvisioningFailed(Exception e) {
finishProvisionFailed(ProvisionConfig.ERROR_PROVISION_FAILED, getErrorMessage(e, "配网失败"));
}
});
}
/**
* 通过蓝牙请求 ESP32 扫描周围 WiFi
* @param scanListener 扫描结果监听器
*/
public void scanWifiFromDevice(WiFiScanListener scanListener) {
if (!checkConnected()) {
if (scanListener != null) {
scanListener.onWiFiScanFailed(new Exception("设备未连接"));
}
return;
}
if (espDevice == null) {
if (scanListener != null) {
scanListener.onWiFiScanFailed(new Exception("设备实例为空"));
}
return;
}
espDevice.scanNetworks(scanListener);
}
public void disconnect() {
boolean shouldNotify = connected || connecting || provisioning;
mainHandler.removeCallbacks(connectRetryTask);
disconnectDeviceOnly();
lastDisconnectTime = System.currentTimeMillis();
resetState(true);
clearPendingConnect();
if (shouldNotify) {
notifyDisconnected();
}
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void onDeviceConnectionEvent(DeviceConnectionEvent event) {
if (event == null) {
return;
}
switch (event.getEventType()) {
case ESPConstants.EVENT_DEVICE_CONNECTED:
mainHandler.removeCallbacks(connectionTimeoutTask);
mainHandler.removeCallbacks(connectRetryTask);
connecting = false;
connected = true;
connectRetryCount = 0;
notifyConnected();
break;
case ESPConstants.EVENT_DEVICE_CONNECTION_FAILED:
mainHandler.removeCallbacks(connectionTimeoutTask);
mainHandler.removeCallbacks(provisionTimeoutTask);
boolean wasConnecting = connecting;
connecting = false;
connected = false;
provisioning = false;
lastDisconnectTime = System.currentTimeMillis();
if (wasConnecting && retryConnectIfAllowed()) {
break;
}
if (wasConnecting) {
resetState(true);
clearPendingConnect();
notifyFailed(ProvisionConfig.ERROR_CONNECT_FAILED, "蓝牙连接失败");
} else {
notifyFailed(ProvisionConfig.ERROR_PROVISION_FAILED, "设备通信失败");
}
break;
case ESPConstants.EVENT_DEVICE_DISCONNECTED:
if (ignoreNextDisconnectEvent) {
ignoreNextDisconnectEvent = false;
return;
}
mainHandler.removeCallbacks(connectionTimeoutTask);
mainHandler.removeCallbacks(provisionTimeoutTask);
boolean shouldNotify = connected || connecting || provisioning;
boolean expectedDisconnect = expectDisconnected;
boolean wasConnecting2 = connecting;
long connectDuration = System.currentTimeMillis() - connectStartTime;
lastDisconnectTime = System.currentTimeMillis();
resetState(true);
if (wasConnecting2 && !expectedDisconnect
&& connectDuration < QUICK_DISCONNECT_WINDOW
&& retryConnectIfAllowed()) {
break;
}
if (wasConnecting2 && !expectedDisconnect) {
clearPendingConnect();
notifyFailed(ProvisionConfig.ERROR_CONNECT_FAILED, "蓝牙连接失败");
break;
}
if (shouldNotify) {
// 如果是配网成功后的预期断开,不发送断开回调
// 因为 onSuccess 已经通知过了
if (!expectedDisconnect) {
notifyDisconnected();
}
}
break;
default:
break;
}
}
private boolean checkConnected() {
return espDevice != null && connected;
}
private void ensureEventBusRegistered() {
if (!eventBusRegistered) {
EventBus.getDefault().register(this);
eventBusRegistered = true;
}
}
private void disconnectDeviceOnly() {
if (espDevice != null) {
try {
espDevice.disconnectDevice();
} catch (Exception ignored) {
}
}
}
private void resetState(boolean clearDevice) {
mainHandler.removeCallbacks(connectionTimeoutTask);
mainHandler.removeCallbacks(provisionTimeoutTask);
stopProvisionProgressTask();
connecting = false;
connected = false;
provisioning = false;
provisionProgress = 0;
provisionTargetProgress = 0;
ignoreNextDisconnectEvent = false;
expectDisconnected = false;
if (clearDevice) {
espDevice = null;
}
}
private ESPConstants.SecurityType parseSecurityType(int securityType) {
if (securityType == ProvisionConfig.SECURITY_0) {
return ESPConstants.SecurityType.SECURITY_0;
} else if (securityType == ProvisionConfig.SECURITY_1) {
return ESPConstants.SecurityType.SECURITY_1;
} else {
return ESPConstants.SecurityType.SECURITY_2;
}
}
private void finishProvisionSuccess() {
mainHandler.removeCallbacks(provisionTimeoutTask);
stopProvisionProgressTask();
provisioning = false;
expectDisconnected = true; // 标记为预期断开
notifyProvisioning(100);
notifySuccess();
}
private void finishProvisionFailed(int errorCode, String message) {
mainHandler.removeCallbacks(provisionTimeoutTask);
stopProvisionProgressTask();
provisioning = false;
notifyFailed(errorCode, message);
}
private void advanceProvisionProgress(int minProgress, int targetProgress) {
int nextProgress = Math.max(provisionProgress, minProgress);
provisionTargetProgress = Math.max(nextProgress, Math.min(targetProgress, MAX_AUTO_PROGRESS));
notifyProvisioning(nextProgress);
startProvisionProgressTask();
}
private void startProvisionProgressTask() {
mainHandler.removeCallbacks(provisionProgressTask);
mainHandler.postDelayed(provisionProgressTask, PROGRESS_UPDATE_INTERVAL);
}
private void stopProvisionProgressTask() {
mainHandler.removeCallbacks(provisionProgressTask);
}
private String getProvisionFailureMessage(ESPConstants.ProvisionFailureReason failureReason) {
if (failureReason == ESPConstants.ProvisionFailureReason.AUTH_FAILED) {
return "WiFi 密码错误";
} else if (failureReason == ESPConstants.ProvisionFailureReason.NETWORK_NOT_FOUND) {
return "未找到指定 WiFi";
} else if (failureReason == ESPConstants.ProvisionFailureReason.DEVICE_DISCONNECTED) {
return "设备已断开连接";
} else {
return "设备配网失败";
}
}
private String getErrorMessage(Exception e, String defaultMessage) {
if (e != null && !TextUtils.isEmpty(e.getMessage())) {
return e.getMessage();
}
return defaultMessage;
}
private void runOnMainThread(Runnable runnable) {
if (Looper.myLooper() == Looper.getMainLooper()) {
runnable.run();
} else {
mainHandler.post(runnable);
}
}
private void notifyConnecting() {
runOnMainThread(new Runnable() {
@Override
public void run() {
if (callback != null) {
callback.onConnecting();
}
}
});
}
private void notifyConnected() {
runOnMainThread(new Runnable() {
@Override
public void run() {
if (callback != null) {
callback.onConnected();
}
}
});
}
private void notifyProvisioning(final int progress) {
provisionProgress = Math.max(0, Math.min(progress, 100));
runOnMainThread(new Runnable() {
@Override
public void run() {
provisionProgress = Math.max(0, Math.min(progress, 100));
if (callback != null) {
callback.onProvisioning(provisionProgress);
}
}
});
}
private void notifySuccess() {
runOnMainThread(new Runnable() {
@Override
public void run() {
if (callback != null) {
callback.onSuccess(currentDeviceName, currentDeviceType);
}
}
});
}
private void notifyFailed(final int errorCode, final String message) {
runOnMainThread(new Runnable() {
@Override
public void run() {
if (callback != null) {
callback.onFailed(errorCode, message);
}
}
});
}
private void notifyDisconnected() {
runOnMainThread(new Runnable() {
@Override
public void run() {
if (callback != null) {
callback.onDisconnected();
}
}
});
}
private void notifyProvisioningRejected() {
runOnMainThread(new Runnable() {
@Override
public void run() {
if (callback != null) {
callback.onProvisioningRejected();
}
}
});
}
}

View File

@@ -0,0 +1,61 @@
package com.ckkj.water.espBle;
/**
* @author fengxiaoyang
* @date 2026/6/4
* @description 蓝牙设备配网回调接口
*/
public interface ProvisionCallback {
/**
* 正在连接设备
*/
void onConnecting();
/**
* 设备连接成功
*/
void onConnected();
/**
* 正在配网中
* @param progress 配网进度 (0-100)
*/
void onProvisioning(int progress);
/**
* 配网成功
* @param deviceName 设备名称
* @param deviceType 设备类型
*/
void onSuccess(String deviceName, String deviceType);
/**
* 配网失败
* @param errorCode 错误码
* @param message 错误信息
*/
void onFailed(int errorCode, String message);
/**
* 设备断开连接
*/
void onDisconnected();
/**
* WiFi列表获取成功
* @param wifiList WiFi列表
*/
void onWifiListReceived(String[] wifiList);
/**
* 获取WiFi列表失败
* @param message 错误信息
*/
void onWifiListFailed(String message);
/**
* 配网被拒绝(设备端拒绝配网请求)
*/
void onProvisioningRejected();
}

View File

@@ -0,0 +1,135 @@
package com.ckkj.water.espBle;
import android.os.Build;
import com.espressif.provisioning.ESPConstants;
/**
* @author fengxiaoyang
* @date 2026/6/4
* @description 蓝牙配网配置管理类
*/
public class ProvisionConfig {
/**
* ESP BLE Service UUID
*/
public static final String SERVICE_UUID =
"0000ffff-0000-1000-8000-00805f9b34fb";
/**
* ESP BLE Characteristic UUID for writes
*/
public static final String CHARACTERISTIC_UUID =
"0000ff01-0000-1000-8000-00805f9b34fb";
/**
* ESP BLE Characteristic UUID for notifications
*/
public static final String NOTIFY_UUID =
"0000ff02-0000-1000-8000-00805f9b34fb";
/**
* 设备传输的协议类型
*/
public static final String TRANSPORT_BLE = "ble";
/**
* 设备前缀 (用于过滤设备)
*/
public static final String DEVICE_PREFIX = "PROV_";
/**
* 安全类型 - SECURITY_0 (无安全)
*/
public static final int SECURITY_0 = ESPConstants.SecurityType.SECURITY_0.ordinal();
/**
* 安全类型 - SECURITY_1 (推荐)
*/
public static final int SECURITY_1 = ESPConstants.SecurityType.SECURITY_1.ordinal();
/**
* 默认安全类型
*/
public static final int DEFAULT_SECURITY = SECURITY_1;
/**
* 默认 PoP需和 ESP 固件保持一致
*/
public static final String DEFAULT_PROOF_OF_POSSESSION = "abcd1234";
/**
* Security2 使用的默认用户名
*/
public static final String DEFAULT_USER_NAME = "";
/**
* 连接超时时间 (毫秒)
*/
public static final int CONNECTION_TIMEOUT = 30000;
/**
* 配网超时时间 (毫秒)
*/
public static final int PROVISION_TIMEOUT = 120000;
public static final int ERROR_NOT_INITIALIZED = 1000;
public static final int ERROR_INVALID_DEVICE = 1001;
public static final int ERROR_CONNECT_TIMEOUT = 1002;
public static final int ERROR_CONNECT_FAILED = 1003;
public static final int ERROR_NOT_CONNECTED = 1004;
public static final int ERROR_SESSION_FAILED = 1005;
public static final int ERROR_WIFI_CONFIG_FAILED = 1006;
public static final int ERROR_WIFI_APPLY_FAILED = 1007;
public static final int ERROR_PROVISION_FAILED = 1008;
public static final int ERROR_PROVISION_TIMEOUT = 1009;
public static final int ERROR_WIFI_SCAN_FAILED = 1010;
/**
* 配网版本 (可根据实际固件调整)
*/
public static final String VERSION = "v1.1";
/**
* 生成蓝牙扫描过滤器
* @return 服务UUID数组
*/
public static String[] getScanServiceUuids() {
return new String[]{SERVICE_UUID};
}
/**
* 生成设备前缀过滤器 (用于过滤非ESP设备)
* @return 设备前缀
*/
public static String getDevicePrefix() {
return DEVICE_PREFIX;
}
/**
* 获取当前Android版本号
* @return Android版本号
*/
public static int getAndroidVersion() {
return Build.VERSION.SDK_INT;
}
/**
* 检查是否为Android 10及以上版本
* @return true 表示支持
*/
public static boolean isAndroid10OrAbove() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q;
}
}

View File

@@ -1,52 +0,0 @@
package com.ckkj.water.fate.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.fate.entity.FateHistoryEntity;
import com.ckkj.water.utils.color.FateDataUtil;
import java.util.List;
/**
* @author fengxiaoyang
* @date 2025/7/24
* @description TODO: 描述该类的作用
*/
public class FateHistoryAdapter extends BaseQuickAdapter<FateHistoryEntity.HistoryList, BaseViewHolder> {
private Context mContext;
public FateHistoryAdapter(Context context,int layoutResId, @Nullable List<FateHistoryEntity.HistoryList> data) {
super(layoutResId, data);
this.mContext = context;
}
@Override
protected void convert(BaseViewHolder holder, FateHistoryEntity.HistoryList item) {
holder.setText(R.id.tv_date,item.getRecordDate());
holder.setText(R.id.tv_week,item.getDayOfWeek());
if(item.isToday()){
holder.setVisible(R.id.tv_today,true);
}else{
holder.setVisible(R.id.tv_today,false);
}
holder.setText(R.id.tv_name,item.getName());
holder.setText(R.id.tv_location,item.getCurrentPlace());
holder.setText(R.id.tv_weather,item.getWeatherCondition());
holder.setText(R.id.tv_star,item.getConstellation());
holder.setText(R.id.tv_score,item.getOverallScore() + "");
holder.setText(R.id.tv_syy_score,item.getCareerScore() + "");
holder.setText(R.id.tv_gqy_score,item.getLoveScore() + "");
holder.setText(R.id.tv_jky_score,item.getHealthScore() + "");
holder.setText(R.id.tv_cfy_score,item.getWealthScore() + "");
holder.setTextColor(R.id.tv_score, FateDataUtil.getScoreColor(item.getOverallScore()));
holder.setTextColor(R.id.tv_syy_score, FateDataUtil.getScoreColor(item.getCareerScore()));
holder.setTextColor(R.id.tv_gqy_score, FateDataUtil.getScoreColor(item.getLoveScore()));
holder.setTextColor(R.id.tv_jky_score, FateDataUtil.getScoreColor(item.getHealthScore()));
holder.setTextColor(R.id.tv_cfy_score, FateDataUtil.getScoreColor(item.getWealthScore()));
}
}

View File

@@ -1,50 +0,0 @@
package com.ckkj.water.fate.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.fate.entity.CityDataEntity;
import java.util.List;
/**
* @author fengxiaoyang
* @date 2025/7/18
* @description TODO: 描述该类的作用
*/
public class SelectCityAdapter extends BaseQuickAdapter<CityDataEntity, BaseViewHolder> {
private Context mContext;
private int mSelPos = -1;
public SelectCityAdapter(Context context,int layoutResId, @Nullable List<CityDataEntity> data) {
super(layoutResId, data);
this.mContext = context;
}
@Override
protected void convert(BaseViewHolder helper, CityDataEntity item) {
helper.setText(R.id.tv_city_name,item.getCityName());
if(mSelPos == helper.getLayoutPosition()){
helper.setVisible(R.id.img_select,true);
}else{
helper.setVisible(R.id.img_select,false);
}
}
public void setSelPos(int pos){
this.mSelPos = pos;
notifyDataSetChanged();
}
public int getmSelPos() {
return mSelPos;
}
public void setmSelPos(int mSelPos) {
this.mSelPos = mSelPos;
}
}

View File

@@ -1,203 +0,0 @@
package com.ckkj.water.fate.entity;
import java.io.Serializable;
/**
* @author fengxiaoyang
* @date 2025/7/18
* @description TODO: 描述该类的作用
*/
public class CityDataEntity implements Serializable {
private int id;
private String cityCode;
private String cityName;
private String cityNameEn;
private String provinceCode;
private String provinceName;
private int cityLevel;
private String cityType;
private double longitude;
private double latitude;
private String population;
private String area;
private String gdp;
private String zipCode;
private String phoneCode;
private int isCapital;
private int isActive;
private int sortOrder;
private String remark;
private String createdTime;
private String updatedTime;
private String capitalCity;
public String getCapitalCity() {
return capitalCity;
}
public void setCapitalCity(String capitalCity) {
this.capitalCity = capitalCity;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getCityCode() {
return cityCode;
}
public void setCityCode(String cityCode) {
this.cityCode = cityCode;
}
public String getCityName() {
return cityName;
}
public void setCityName(String cityName) {
this.cityName = cityName;
}
public String getCityNameEn() {
return cityNameEn;
}
public void setCityNameEn(String cityNameEn) {
this.cityNameEn = cityNameEn;
}
public String getProvinceCode() {
return provinceCode;
}
public void setProvinceCode(String provinceCode) {
this.provinceCode = provinceCode;
}
public String getProvinceName() {
return provinceName;
}
public void setProvinceName(String provinceName) {
this.provinceName = provinceName;
}
public int getCityLevel() {
return cityLevel;
}
public void setCityLevel(int cityLevel) {
this.cityLevel = cityLevel;
}
public String getCityType() {
return cityType;
}
public void setCityType(String cityType) {
this.cityType = cityType;
}
public double getLongitude() {
return longitude;
}
public void setLongitude(double longitude) {
this.longitude = longitude;
}
public double getLatitude() {
return latitude;
}
public void setLatitude(double latitude) {
this.latitude = latitude;
}
public String getPopulation() {
return population;
}
public void setPopulation(String population) {
this.population = population;
}
public String getArea() {
return area;
}
public void setArea(String area) {
this.area = area;
}
public String getGdp() {
return gdp;
}
public void setGdp(String gdp) {
this.gdp = gdp;
}
public String getZipCode() {
return zipCode;
}
public void setZipCode(String zipCode) {
this.zipCode = zipCode;
}
public String getPhoneCode() {
return phoneCode;
}
public void setPhoneCode(String phoneCode) {
this.phoneCode = phoneCode;
}
public int getIsCapital() {
return isCapital;
}
public void setIsCapital(int isCapital) {
this.isCapital = isCapital;
}
public int getIsActive() {
return isActive;
}
public void setIsActive(int isActive) {
this.isActive = isActive;
}
public int getSortOrder() {
return sortOrder;
}
public void setSortOrder(int sortOrder) {
this.sortOrder = sortOrder;
}
public String getRemark() {
return remark;
}
public void setRemark(String remark) {
this.remark = remark;
}
public void setCreatedTime(String createdTime) {
this.createdTime = createdTime;
}
public void setUpdatedTime(String updatedTime) {
this.updatedTime = updatedTime;
}
}

View File

@@ -1,139 +0,0 @@
package com.ckkj.water.fate.entity;
import java.io.Serializable;
import java.util.List;
/**
* @author fengxiaoyang
* @date 2025/7/25
* @description TODO: 描述该类的作用
*/
public class FateHistoryEntity {
private List<HistoryList> list;
private int total;
public void setList(List<HistoryList> list) {
this.list = list;
}
public List<HistoryList> getList() {
return list;
}
public void setTotal(int total) {
this.total = total;
}
public int getTotal() {
return total;
}
public static class HistoryList implements Serializable{
private String recordDate;
private String dayOfWeek;
private String name;
private String currentPlace;
private String weatherCondition;
private String constellation;
private int overallScore;
private int careerScore;
private int loveScore;
private int healthScore;
private int wealthScore;
private boolean isToday;
public String getRecordDate() {
return recordDate;
}
public void setRecordDate(String recordDate) {
this.recordDate = recordDate;
}
public String getDayOfWeek() {
return dayOfWeek;
}
public void setDayOfWeek(String dayOfWeek) {
this.dayOfWeek = dayOfWeek;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getCurrentPlace() {
return currentPlace;
}
public void setCurrentPlace(String currentPlace) {
this.currentPlace = currentPlace;
}
public String getWeatherCondition() {
return weatherCondition;
}
public void setWeatherCondition(String weatherCondition) {
this.weatherCondition = weatherCondition;
}
public String getConstellation() {
return constellation;
}
public void setConstellation(String constellation) {
this.constellation = constellation;
}
public int getOverallScore() {
return overallScore;
}
public void setOverallScore(int overallScore) {
this.overallScore = overallScore;
}
public int getCareerScore() {
return careerScore;
}
public void setCareerScore(int careerScore) {
this.careerScore = careerScore;
}
public int getLoveScore() {
return loveScore;
}
public void setLoveScore(int loveScore) {
this.loveScore = loveScore;
}
public int getHealthScore() {
return healthScore;
}
public void setHealthScore(int healthScore) {
this.healthScore = healthScore;
}
public int getWealthScore() {
return wealthScore;
}
public void setWealthScore(int wealthScore) {
this.wealthScore = wealthScore;
}
public boolean isToday() {
return isToday;
}
public void setToday(boolean today) {
isToday = today;
}
}
}

View File

@@ -1,66 +0,0 @@
package com.ckkj.water.fate.entity;
import java.io.Serializable;
import java.util.List;
/**
* @author fengxiaoyang
* @date 2025/7/16
* @description TODO: 描述该类的作用
*/
public class LunarYearEntity implements Serializable {
public String year; // 年份
public List<LunarMonth> month; // 月份列表
// Gson需要无参构造
public LunarYearEntity() {}
public String getYear() {
return year;
}
public void setYear(String year) {
this.year = year;
}
public List<LunarMonth> getMonth() {
return month;
}
public void setMonth(List<LunarMonth> month) {
this.month = month;
}
public static class LunarMonth implements Serializable{
public String name; // 月份数字(字符串形式)
public int isRun; // 是否闰月0=否1=是)
public int isAllDay; // 是否包含三十0=29天1=30天
// Gson需要无参构造
public LunarMonth() {}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getIsRun() {
return isRun;
}
public void setIsRun(int isRun) {
this.isRun = isRun;
}
public int getIsAllDay() {
return isAllDay;
}
public void setIsAllDay(int isAllDay) {
this.isAllDay = isAllDay;
}
}
}

View File

@@ -1,526 +0,0 @@
package com.ckkj.water.fate.entity;
import com.ckkj.water.base.entity.UserInfoEntity;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* @author fengxiaoyang
* @date 2025/7/21
* @description TODO: 描述该类的作用
*/
public class TodayFateEntity implements Serializable {
private FortuneAnalysis fortuneAnalysis;
private UserInfoEntity userInfo;
private TodayInfo todayInfo;
private String weatherCondition;
private String weatherTemperature;
private int todayVisitCount;
private int totalVisitDays;
public void setFortuneAnalysis(FortuneAnalysis fortuneAnalysis) {
this.fortuneAnalysis = fortuneAnalysis;
}
public FortuneAnalysis getFortuneAnalysis() {
return fortuneAnalysis;
}
public void setUserInfo(UserInfoEntity userInfo) {
this.userInfo = userInfo;
}
public UserInfoEntity getUserInfo() {
return userInfo;
}
public void setTodayInfo(TodayInfo todayInfo) {
this.todayInfo = todayInfo;
}
public TodayInfo getTodayInfo() {
return todayInfo;
}
public void setWeatherCondition(String weatherCondition) {
this.weatherCondition = weatherCondition;
}
public String getWeatherCondition() {
return weatherCondition;
}
public void setWeatherTemperature(String weatherTemperature) {
this.weatherTemperature = weatherTemperature;
}
public String getWeatherTemperature() {
return weatherTemperature;
}
public void setTodayVisitCount(int todayVisitCount) {
this.todayVisitCount = todayVisitCount;
}
public int getTodayVisitCount() {
return todayVisitCount;
}
public void setTotalVisitDays(int totalVisitDays) {
this.totalVisitDays = totalVisitDays;
}
public int getTotalVisitDays() {
return totalVisitDays;
}
/**
* Copyright 2025 bejson.com
*/
/**
* Auto-generated: 2025-07-21 17:23:17
*
* @author bejson.com (i@bejson.com)
* @website http://www.bejson.com/java2pojo/
*/
public static class TodayInfo implements Serializable{
private Date date;
private String weekDay;
private int year;
private int month;
private int day;
public void setDate(Date date) {
this.date = date;
}
public Date getDate() {
return date;
}
public void setWeekDay(String weekDay) {
this.weekDay = weekDay;
}
public String getWeekDay() {
return weekDay;
}
public void setYear(int year) {
this.year = year;
}
public int getYear() {
return year;
}
public void setMonth(int month) {
this.month = month;
}
public int getMonth() {
return month;
}
public void setDay(int day) {
this.day = day;
}
public int getDay() {
return day;
}
}
/**
* Copyright 2025 bejson.com
*/
/**
* Auto-generated: 2025-07-21 17:23:17
*
* @author bejson.com (i@bejson.com)
* @website http://www.bejson.com/java2pojo/
*/
public static class FortuneAnalysis implements Serializable{
private int overallScore;
private String overallDescription;
private String destiny;
private String todayGanzhi;
private BaziAnalysis baziAnalysis;
private Weather weather;
private AstrologyInfluence astrologyInfluence;
private LuckyInfo luckyInfo;
private int careerScore;
private String careerDescription;
private int loveScore;
private String loveDescription;
private int healthScore;
private String healthDescription;
private int wealthScore;
private String wealthDescription;
private Suggestions suggestions;
private String fiveElementsAnalysis;
public void setOverallScore(int overallScore) {
this.overallScore = overallScore;
}
public int getOverallScore() {
return overallScore;
}
public void setOverallDescription(String overallDescription) {
this.overallDescription = overallDescription;
}
public String getOverallDescription() {
return overallDescription;
}
public void setDestiny(String destiny) {
this.destiny = destiny;
}
public String getDestiny() {
return destiny;
}
public void setTodayGanzhi(String todayGanzhi) {
this.todayGanzhi = todayGanzhi;
}
public String getTodayGanzhi() {
return todayGanzhi;
}
public void setBaziAnalysis(BaziAnalysis baziAnalysis) {
this.baziAnalysis = baziAnalysis;
}
public BaziAnalysis getBaziAnalysis() {
return baziAnalysis;
}
public void setWeather(Weather weather) {
this.weather = weather;
}
public Weather getWeather() {
return weather;
}
public void setAstrologyInfluence(AstrologyInfluence astrologyInfluence) {
this.astrologyInfluence = astrologyInfluence;
}
public AstrologyInfluence getAstrologyInfluence() {
return astrologyInfluence;
}
public void setLuckyInfo(LuckyInfo luckyInfo) {
this.luckyInfo = luckyInfo;
}
public LuckyInfo getLuckyInfo() {
return luckyInfo;
}
public void setCareerScore(int careerScore) {
this.careerScore = careerScore;
}
public int getCareerScore() {
return careerScore;
}
public void setCareerDescription(String careerDescription) {
this.careerDescription = careerDescription;
}
public String getCareerDescription() {
return careerDescription;
}
public void setLoveScore(int loveScore) {
this.loveScore = loveScore;
}
public int getLoveScore() {
return loveScore;
}
public void setLoveDescription(String loveDescription) {
this.loveDescription = loveDescription;
}
public String getLoveDescription() {
return loveDescription;
}
public void setHealthScore(int healthScore) {
this.healthScore = healthScore;
}
public int getHealthScore() {
return healthScore;
}
public void setHealthDescription(String healthDescription) {
this.healthDescription = healthDescription;
}
public String getHealthDescription() {
return healthDescription;
}
public void setWealthScore(int wealthScore) {
this.wealthScore = wealthScore;
}
public int getWealthScore() {
return wealthScore;
}
public void setWealthDescription(String wealthDescription) {
this.wealthDescription = wealthDescription;
}
public String getWealthDescription() {
return wealthDescription;
}
public void setSuggestions(Suggestions suggestions) {
this.suggestions = suggestions;
}
public Suggestions getSuggestions() {
return suggestions;
}
public void setFiveElementsAnalysis(String fiveElementsAnalysis) {
this.fiveElementsAnalysis = fiveElementsAnalysis;
}
public String getFiveElementsAnalysis() {
return fiveElementsAnalysis;
}
}
/**
* Copyright 2025 bejson.com
*/
/**
* Auto-generated: 2025-07-21 17:23:17
*
* @author bejson.com (i@bejson.com)
* @website http://www.bejson.com/java2pojo/
*/
public static class Suggestions implements Serializable{
private String positive;
private String negative;
public void setPositive(String positive) {
this.positive = positive;
}
public String getPositive() {
return positive;
}
public void setNegative(String negative) {
this.negative = negative;
}
public String getNegative() {
return negative;
}
}
/**
* Copyright 2025 bejson.com
*/
/**
* Auto-generated: 2025-07-21 17:23:17
*
* @author bejson.com (i@bejson.com)
* @website http://www.bejson.com/java2pojo/
*/
public static class LuckyInfo implements Serializable{
private String[] numbers;
private String color;
private String direction;
private List<LuckyTimes> luckyTimes;
private String flower;
private String food;
public String[] getNumbers() {
return numbers;
}
public void setNumbers(String[] numbers) {
this.numbers = numbers;
}
public void setColor(String color) {
this.color = color;
}
public String getColor() {
return color;
}
public void setDirection(String direction) {
this.direction = direction;
}
public String getDirection() {
return direction;
}
public void setLuckyTimes(List<LuckyTimes> luckyTimes) {
this.luckyTimes = luckyTimes;
}
public List<LuckyTimes> getLuckyTimes() {
return luckyTimes;
}
public void setFlower(String flower) {
this.flower = flower;
}
public String getFlower() {
return flower;
}
public void setFood(String food) {
this.food = food;
}
public String getFood() {
return food;
}
}
/**
* Copyright 2025 bejson.com
*/
/**
* Auto-generated: 2025-07-21 17:23:17
*
* @author bejson.com (i@bejson.com)
* @website http://www.bejson.com/java2pojo/
*/
public static class LuckyTimes implements Serializable{
private String time;
private String description;
public void setTime(String time) {
this.time = time;
}
public String getTime() {
return time;
}
public void setDescription(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
/**
* Copyright 2025 bejson.com
*/
/**
* Auto-generated: 2025-07-21 17:23:17
*
* @author bejson.com (i@bejson.com)
* @website http://www.bejson.com/java2pojo/
*/
public static class AstrologyInfluence implements Serializable{
private String constellation;
private String value;
private String description;
public void setConstellation(String constellation) {
this.constellation = constellation;
}
public String getConstellation() {
return constellation;
}
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public void setDescription(String description) {
this.description = description;
}
public String getDescription() {
return description;
}
}
/**
* Copyright 2025 bejson.com
*/
/**
* Auto-generated: 2025-07-21 17:23:17
*
* @author bejson.com (i@bejson.com)
* @website http://www.bejson.com/java2pojo/
*/
public static class Weather implements Serializable{
private String condition;
private String temperature;
public void setCondition(String condition) {
this.condition = condition;
}
public String getCondition() {
return condition;
}
public void setTemperature(String temperature) {
this.temperature = temperature;
}
public String getTemperature() {
return temperature;
}
}
/**
* Copyright 2025 bejson.com
*/
/**
* Auto-generated: 2025-07-21 17:23:17
*
* @author bejson.com (i@bejson.com)
* @website http://www.bejson.com/java2pojo/
*/
public static class BaziAnalysis implements Serializable{
private String nianzhu;
private String yuezhu;
private String rizhu;
private String shizhu;
private String todayInfluence;
public void setNianzhu(String nianzhu) {
this.nianzhu = nianzhu;
}
public String getNianzhu() {
return nianzhu;
}
public void setYuezhu(String yuezhu) {
this.yuezhu = yuezhu;
}
public String getYuezhu() {
return yuezhu;
}
public void setRizhu(String rizhu) {
this.rizhu = rizhu;
}
public String getRizhu() {
return rizhu;
}
public void setShizhu(String shizhu) {
this.shizhu = shizhu;
}
public String getShizhu() {
return shizhu;
}
public void setTodayInfluence(String todayInfluence) {
this.todayInfluence = todayInfluence;
}
public String getTodayInfluence() {
return todayInfluence;
}
}
}

View File

@@ -1,56 +0,0 @@
package com.ckkj.water.fate.mvp;
import com.ckkj.water.base.IView;
import com.ckkj.water.base.entity.MineUserInfoBean;
import com.ckkj.water.fate.entity.CityDataEntity;
import com.ckkj.water.fate.entity.FateHistoryEntity;
import com.ckkj.water.fate.entity.TodayFateEntity;
import com.ckkj.water.network.BaseBean;
import org.json.JSONObject;
import java.util.List;
import java.util.Map;
/**
* @author fengxiaoyang
* @date 2025/7/15
* @description TODO: 描述该类的作用
*/
public class FateContract {
public interface CallBack<T> {
void success(T msg);
void fail(String msg);
}
public interface DataModel{
//查询所有城市
void getAllCityDatas(Map<String,Object> map, CallBack callBack);
//保存用户生日信息
void saveUserBirthInfo(JSONObject json, CallBack callBack);
//用户修改生日信息
void saveUserEditBirthInfo(JSONObject json, CallBack callBack);
//查询用户今日运势
void getUserTodayFateDatas(Map<String,Object> map, CallBack callBack);
//查询历史运势
void getUserFateHistory(Map<String,Object> map, CallBack callBack);
}
public interface FateView extends IView{
//查询所有城市成功
void getAllCitySuccess(List<CityDataEntity> list);
//获取今日运势成功
void getUserTodayFateDataSuccess(TodayFateEntity entity);
//获取历史运势成功
void getFateHistorySuccess(FateHistoryEntity entity);
//用户保存生辰八字成功
void userSaveBirthInfoSuccess(TodayFateEntity bean);
//用户信息
void getUserInfoSuccess(MineUserInfoBean bean);
//提交举报反馈成功
void commitReportSuccess(BaseBean bean);
}
}

View File

@@ -1,232 +0,0 @@
package com.ckkj.water.fate.mvp;
import com.ckkj.water.fate.entity.CityDataEntity;
import com.ckkj.water.fate.entity.FateHistoryEntity;
import com.ckkj.water.fate.entity.TodayFateEntity;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.network.NetWorkManager;
import org.json.JSONObject;
import java.util.List;
import java.util.Map;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
/**
* @author fengxiaoyang
* @date 2025/7/15
* @description TODO: 描述该类的作用
*/
public class FateImpl implements FateContract.DataModel{
/**
* 所有城市
* @param map
* @param callBack
*/
@Override
public void getAllCityDatas(Map<String, Object> map, FateContract.CallBack callBack) {
try {
NetWorkManager.getRequest().getCityDatas(NetWorkManager.getToken(),map)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean<List<CityDataEntity>>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean<List<CityDataEntity>> bean) {
if(bean.getCode() == 200){
callBack.success(bean.getData());
}else{
callBack.fail(bean.getMessage());
}
}
@Override
public void onError(@NonNull Throwable e) {
callBack.fail(e.getMessage());
}
@Override
public void onComplete() {
}
});
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 保存用户生辰信息
* @param json
* @param callBack
*/
@Override
public void saveUserBirthInfo(JSONObject json, FateContract.CallBack callBack) {
try {
NetWorkManager.getRequest().saveUserBirthInfo(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);
}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();
}
}
/**
* 用户修改生辰八字
* @param json
* @param callBack
*/
@Override
public void saveUserEditBirthInfo(JSONObject json, FateContract.CallBack callBack) {
try {
NetWorkManager.getRequest().saveUserEditBirthInfo(NetWorkManager.getToken(),NetWorkManager.toRequestBody(json))
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean<TodayFateEntity>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean<TodayFateEntity> bean) {
if(bean.getCode() == 200){
callBack.success(bean.getData());
}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();
}
}
/**
* 获取今日运势
* @param map
* @param callBack
*/
@Override
public void getUserTodayFateDatas(Map<String, Object> map, FateContract.CallBack callBack) {
try {
NetWorkManager.getRequest().getUserTodayFateDatas(NetWorkManager.getToken())
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean<TodayFateEntity>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean<TodayFateEntity> bean) {
if(bean.getCode() == 200){
callBack.success(bean.getData());
}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();
}
}
/**
* 获取历史数据
* @param map
* @param callBack
*/
@Override
public void getUserFateHistory(Map<String, Object> map, FateContract.CallBack callBack) {
try {
NetWorkManager.getRequest().getUserFateHistory(NetWorkManager.getToken())
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean<FateHistoryEntity>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean<FateHistoryEntity> bean) {
if(bean.getCode() == 200){
callBack.success(bean.getData());
}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

@@ -1,191 +0,0 @@
package com.ckkj.water.fate.mvp;
import com.ckkj.water.base.BasePresenter;
import com.ckkj.water.base.entity.MineUserInfoBean;
import com.ckkj.water.chat.mvp.IndexContract;
import com.ckkj.water.chat.mvp.IndexImpl;
import com.ckkj.water.fate.entity.CityDataEntity;
import com.ckkj.water.fate.entity.FateHistoryEntity;
import com.ckkj.water.fate.entity.TodayFateEntity;
import com.ckkj.water.login.mvp.LoginContract;
import com.ckkj.water.mine.mvp.MineImpl;
import com.ckkj.water.network.BaseBean;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author fengxiaoyang
* @date 2025/7/15
* @description TODO: 描述该类的作用
*/
public class FatePresenter extends BasePresenter<FateContract.FateView> {
private FateImpl impl;
public FatePresenter(FateImpl impl) {
this.impl = impl;
}
/**
* 获取城市列表
*/
public void getCityDatas() {
Map<String, Object> map = new HashMap<>();
impl.getAllCityDatas(map, new FateContract.CallBack<List<CityDataEntity>>() {
@Override
public void success(List<CityDataEntity> list) {
if(FatePresenter.this.getView() != null){
FatePresenter.this.getView().getAllCitySuccess(list);
}
}
@Override
public void fail(String msg) {
if(FatePresenter.this.getView() != null){
FatePresenter.this.getView().showError(msg);
}
}
});
}
/**
* 保存用户生辰
* @param json
*/
public void saveUserBirthInfo(JSONObject json) {
impl.saveUserBirthInfo(json, new FateContract.CallBack<BaseBean>() {
@Override
public void success(BaseBean bean) {
if(FatePresenter.this.getView() != null){
FatePresenter.this.getView().userSaveBirthInfoSuccess(null);
}
}
@Override
public void fail(String msg) {
if(FatePresenter.this.getView() != null){
FatePresenter.this.getView().showError(msg);
}
}
});
}
/**
* 获取今日运势
*/
public void getTodayFateDatas() {
impl.getUserTodayFateDatas(new HashMap<>(), new FateContract.CallBack<TodayFateEntity>() {
@Override
public void success(TodayFateEntity bean) {
if(FatePresenter.this.getView() != null){
FatePresenter.this.getView().getUserTodayFateDataSuccess(bean);
}
}
@Override
public void fail(String msg) {
if(FatePresenter.this.getView() != null){
FatePresenter.this.getView().showError(msg);
}
}
});
}
/**
* 获取历史运势
* @param page
*/
public void getHistoryDatas(int page) {
Map<String, Object> map = new HashMap<>();
map.put("pageNum", page);
map.put("pageSize", 10);
impl.getUserFateHistory(map, new FateContract.CallBack<FateHistoryEntity>() {
@Override
public void success(FateHistoryEntity entity) {
if(FatePresenter.this.getView() != null){
FatePresenter.this.getView().getFateHistorySuccess(entity);
}
}
@Override
public void fail(String msg) {
if(FatePresenter.this.getView() != null){
FatePresenter.this.getView().showError(msg);
}
}
});
}
/**
* 用户修改的生辰八字保存
* @param json
*/
public void saveEditBirthInfo(JSONObject json) {
impl.saveUserEditBirthInfo(json, new FateContract.CallBack<TodayFateEntity>() {
@Override
public void success(TodayFateEntity bean) {
if(FatePresenter.this.getView() != null){
FatePresenter.this.getView().userSaveBirthInfoSuccess(bean);
}
}
@Override
public void fail(String msg) {
if(FatePresenter.this.getView() != null){
FatePresenter.this.getView().showError(msg);
}
}
});
}
/**
* 获取个人信息
*/
public void getUserInfo() {
Map<String,Object> map = new HashMap<>();
// new MineImpl().getUserInfo(map, new LoginContract.CallBack<MineUserInfoBean>() {
// @Override
// public void success(MineUserInfoBean bean) {
// if( FatePresenter.this.getView() != null){
// FatePresenter.this.getView().getUserInfoSuccess(bean);
// }
// }
//
// @Override
// public void fail(String msg) {
// if( FatePresenter.this.getView() != null){
// FatePresenter.this.getView().showError(msg);
// }
// }
// });
}
/**
* 举报
* @param reason
*/
public void commitReport(String reason) {
Map<String, Object> map = new HashMap<>();
map.put("content", reason);
new IndexImpl().commitReport(map, new IndexContract.CallBack<BaseBean>() {
@Override
public void success(BaseBean bean) {
if (FatePresenter.this.getView() != null) {
FatePresenter.this.getView().commitReportSuccess(bean);
}
}
@Override
public void fail(String msg) {
if (FatePresenter.this.getView() != null) {
FatePresenter.this.getView().showError(msg);
}
}
});
}
}

View File

@@ -1,75 +0,0 @@
package com.ckkj.water.fate.util;
import android.content.Context;
import androidx.annotation.Nullable;
import org.json.JSONArray;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
public class LunarDateLoader {
// 缓存静态变量
private static JSONArray cachedLunarJsonArray = null;
/**
* 初始化缓存(异步调用,建议在 Application 中调用)
*/
public static void preloadLunarJson(final Context context) {
if (cachedLunarJsonArray != null) return; // 已加载过则跳过
new Thread(() -> {
try {
InputStream inputStream = context.getAssets().open("lunar_1899_2099.json");
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
inputStream.close();
String jsonStr = outputStream.toString("UTF-8");
cachedLunarJsonArray = new JSONArray(jsonStr);
} catch (Exception e) {
e.printStackTrace();
}
}).start();
}
/**
* 获取缓存的农历 JSON 数据(同步获取)
* 可在页面中直接调用
*/
@Nullable
public static JSONArray getCachedLunarJson() {
return cachedLunarJsonArray;
}
/**
* 同步加载(兜底)
*/
@Nullable
public static JSONArray loadJsonFromAssets(Context context, String fileName) {
try {
InputStream inputStream = context.getAssets().open(fileName);
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len = inputStream.read(buffer)) != -1) {
outputStream.write(buffer, 0, len);
}
inputStream.close();
String jsonStr = outputStream.toString("UTF-8");
return new JSONArray(jsonStr);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}

View File

@@ -1,96 +0,0 @@
package com.ckkj.water.fate.util;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import com.ckkj.water.fate.entity.LunarYearEntity;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;
public class LunarDatePicker {
private List<LunarYearEntity> lunarYears; // 所有年份数据
private List<LunarYearEntity.LunarMonth> currentYearMonths; // 当前选中年的月份数据
// 月份名称映射(数字->中文)
private static final String[] MONTH_NAMES = {
"正月", "二月", "三月", "四月", "五月", "六月",
"七月", "八月", "九月", "十月", "冬月", "腊月"
};
// 日期名称列表(初一到三十)
private static final String[] DAY_NAMES = {
"初一", "初二", "初三", "初四", "初五", "初六", "初七", "初八", "初九", "初十",
"十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九", "二十",
"廿一", "廿二", "廿三", "廿四", "廿五", "廿六", "廿七", "廿八", "廿九", "三十"
};
public LunarDatePicker(String jsonData) {
// 解析JSON数据
parseLunarData(jsonData);
}
// 解析JSON数据
private void parseLunarData(String jsonData) {
Gson gson = new Gson();
Type type = new TypeToken<List<LunarYearEntity>>(){}.getType();
lunarYears = gson.fromJson(jsonData, type);
}
// 获取年份列表用于年的WheelView
public List<String> getYearList() {
List<String> years = new ArrayList<>();
for (LunarYearEntity year : lunarYears) {
years.add(year.getYear());
}
return years;
}
// 根据选中的年份索引获取月份列表用于月的WheelView
public List<String> getMonthList(int yearIndex) {
if (yearIndex < 0 || yearIndex >= lunarYears.size()) {
return new ArrayList<>();
}
LunarYearEntity selectedYear = lunarYears.get(yearIndex);
currentYearMonths = selectedYear.getMonth(); // 保存当前年的月份数据
List<String> monthList = new ArrayList<>();
for (LunarYearEntity.LunarMonth month : selectedYear.month) {
int monthNum = Integer.parseInt(month.name);
String monthName = MONTH_NAMES[monthNum - 1]; // 数组索引从0开始
// 处理闰月
if (month.isRun == 1) {
monthName = "" + monthName;
}
monthList.add(monthName);
}
return monthList;
}
// 根据选中的月份索引获取日期列表用于日的WheelView
public List<String> getDayList(int monthIndex) {
if (currentYearMonths == null || monthIndex < 0 || monthIndex >= currentYearMonths.size()) {
return new ArrayList<>();
}
LunarYearEntity.LunarMonth selectedMonth = currentYearMonths.get(monthIndex);
int days = selectedMonth.isAllDay == 1 ? 30 : 29; // 确定天数
List<String> dayList = new ArrayList<>();
for (int i = 0; i < days; i++) {
dayList.add(DAY_NAMES[i]);
}
return dayList;
}
// 获取选中的完整日期(年、月、日索引)
public String getSelectedDate(int yearIndex, int monthIndex, int dayIndex) {
String year = lunarYears.get(yearIndex).year + "";
String month = getMonthList(yearIndex).get(monthIndex);
String day = getDayList(monthIndex).get(dayIndex);
return year + month + day;
}
}

View File

@@ -1,758 +0,0 @@
package com.ckkj.water.fate.view;
import static android.app.Activity.RESULT_OK;
import static com.qweather.sdk.QWeather.instance;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.content.Intent;
import android.graphics.Rect;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.amap.api.services.core.AMapException;
import com.amap.api.services.core.LatLonPoint;
import com.amap.api.services.geocoder.GeocodeAddress;
import com.amap.api.services.geocoder.GeocodeQuery;
import com.amap.api.services.geocoder.GeocodeResult;
import com.amap.api.services.geocoder.GeocodeSearch;
import com.google.android.material.progressindicator.CircularProgressIndicator;
import com.google.android.material.progressindicator.LinearProgressIndicator;
import com.ckkj.water.AppConfig;
import com.ckkj.water.ConstantUtil;
import com.ckkj.water.EventMessage;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseFragment;
import com.ckkj.water.base.entity.MineUserInfoBean;
import com.ckkj.water.custom.view.CustomRefreshHeader;
import com.ckkj.water.databinding.FateLayoutBinding;
import com.ckkj.water.dialog.ContentReportDialog;
import com.ckkj.water.fate.entity.CityDataEntity;
import com.ckkj.water.fate.entity.FateHistoryEntity;
import com.ckkj.water.fate.entity.TodayFateEntity;
import com.ckkj.water.fate.mvp.FateContract;
import com.ckkj.water.fate.mvp.FateImpl;
import com.ckkj.water.fate.mvp.FatePresenter;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.utils.WeatherIconUtil;
import com.ckkj.water.utils.click.ClickUtil;
import com.ckkj.water.utils.color.FateDataUtil;
import com.ckkj.water.utils.dataUtil.SharePreUtil;
import com.ckkj.water.utils.manager.MingDengUserInfoManager;
import com.ckkj.water.utils.number.NumberDecimalFilter;
import com.ckkj.water.utils.show.ToastUtil;
import com.ckkj.water.utils.time.ZYTimeUtils;
import com.qweather.sdk.Callback;
import com.qweather.sdk.JWTGenerator;
import com.qweather.sdk.QWeather;
import com.qweather.sdk.basic.Lang;
import com.qweather.sdk.basic.Unit;
import com.qweather.sdk.parameter.weather.WeatherParameter;
import com.qweather.sdk.response.error.ErrorResponse;
import com.qweather.sdk.response.geo.GeoPoiResponse;
import com.qweather.sdk.response.weather.WeatherNowResponse;
import com.scwang.smart.refresh.layout.api.RefreshHeader;
import java.util.List;
import java.util.Calendar;
import android.os.Handler;
import android.os.Looper;
import de.greenrobot.event.Subscribe;
import de.greenrobot.event.ThreadMode;
import pub.devrel.easypermissions.EasyPermissions;
/**
* @author fengxiaoyang
* @date 2025/7/7
* @description TODO: 描述该类的作用
*/
public class FateFragment extends BaseFragment<FateLayoutBinding, FatePresenter>
implements FateContract.FateView , EasyPermissions.PermissionCallbacks{
private TodayFateEntity mTodayEntity;
// 添加动画执行标志位
private boolean isCareerAnimPlayed = false;
private boolean isLoveAnimPlayed = false;
private boolean isHealthAnimPlayed = false;
private boolean isWealthAnimPlayed = false;
private boolean isOverallAnimPlayed = false;
//是否已经加载过
private boolean hasLoaded = false;
// 添加Toast相关的成员变量
private Handler toastHandler = new Handler(Looper.getMainLooper());
private Runnable toastRunnable;
private boolean isToastTaskRunning = false;
private int currentToastIndex = 0;
private String[] toastMessages = {
"正在查看您的今日能量值",
"让我帮你看看今天的星座运势",
"正在生成您的幸运元素",
"别着急,让我来想想今天的事宜"
};
@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
super.setUserVisibleHint(isVisibleToUser);
}
@Override
protected void onResumeInitData() {
super.onResumeInitData();
// 确保在Fragment可见时检查动画
viewBinding.getRoot().postDelayed(() -> {
checkAndAnimateProgressBars();
}, 300);
if(AppConfig.USER_LOGIN){
mPresenter.getUserInfo();
}else{
viewBinding.llInput.setVisibility(View.VISIBLE);
}
if (getUserVisibleHint() && !hasLoaded) {
if(AppConfig.USER_LOGIN){
showLoadingDialog();
// 调用接口并启动延迟Toast任务
startFateDataRequest();
hasLoaded = true;
}
}
}
@Override
public void initData(Bundle state) {
//请求定位权限
// String permission = Manifest.permission.ACCESS_FINE_LOCATION;
// if(!hasRecordPermission(permission)){
// getPermissionRequst(permission,"定位权限申请","叮叮车需要您的授权,获取您当前所在的城市",REQUEST_EVENT_CODE_10002);
// }else{
// AMapLocationClient.updatePrivacyAgree(mContext, true);
// initLocationInfo();
// }
// 设置当前日期和星期几
setCurrentDate();
//和风天气 init
try {
JWTGenerator jwt = new JWTGenerator(
"-----BEGIN PRIVATE KEY-----\n" +
"MC4CAQAwBQYDK2VwBCIEIFH+qB/0rER1mEpgAIwpBiAShW9pnSAj8OipGY/eFLRb\n" +
"-----END PRIVATE KEY-----",//密钥
"2A89MF5WVU",//项目 ID
"K5PU3M4P2A");//凭据 ID
instance = QWeather.getInstance(mContext, "mr3597qqpv.re.qweatherapi.com")
.setTokenGenerator(jwt)
.setLogEnable(true);
Log.d(TAG,"和风天气初始化成功");
} catch (Throwable e) {
Log.d(TAG,"和风天气初始化异常");
throw new RuntimeException(e);
}
// 添加视图可见性监听
setupProgressAnimations();
ClickUtil.addClickPart(viewBinding.imgHistory,60);
// 设置自定义刷新头
RefreshHeader header = new CustomRefreshHeader(mContext);
viewBinding.refresh.setRefreshHeader(header);
// viewBinding.refresh.setRefreshFooter(new ClassicsFooter(mContext));
viewBinding.refresh.setOnRefreshListener(layout -> {
if(AppConfig.USER_LOGIN){
showLoadingDialog();
// 调用接口并启动延迟Toast任务
startFateDataRequest();
hasLoaded = true;
}
});
}
/**
* 设置当前日期和星期几
*/
private void setCurrentDate() {
Calendar calendar = Calendar.getInstance();
int month = calendar.get(Calendar.MONTH) + 1; // Calendar.MONTH 从0开始所以要+1
int day = calendar.get(Calendar.DAY_OF_MONTH);
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
// 将数字星期转换为中文
String[] weekDays = {"星期日", "星期一", "星期二", "星期三", "星期四", "星期五", "星期六"};
String weekDayStr = weekDays[dayOfWeek - 1]; // Calendar.DAY_OF_WEEK 从1开始星期日=1
// 格式化日期字符串:例如 "9 月 9 日 星期三"
String dateStr = month + "" + day + "" + weekDayStr;
// 设置到TextView
viewBinding.tvDate.setText(dateStr);
}
private void setupProgressAnimations() {
// 监听父滚动视图假设外层是NestedScrollView
View scrollView = viewBinding.getRoot().findViewById(R.id.nesd_scrollview); // 替换为你的NestedScrollView id
if (scrollView != null) {
scrollView.getViewTreeObserver().addOnScrollChangedListener(() -> {
checkAndAnimateProgressBars();
});
}
// 初始检查(如果视图已经可见)
viewBinding.getRoot().postDelayed(() -> {
checkAndAnimateProgressBars();
}, 300); // 延迟300ms确保布局完成
}
private void checkAndAnimateProgressBars() {
if(mTodayEntity == null){
return;
}
// 圆形进度条(运势分数)
if (isViewVisible(viewBinding.includedTodayFateDatas.progressIndicator) && !isOverallAnimPlayed) {
animateCircularProgress(
viewBinding.includedTodayFateDatas.progressIndicator,
mTodayEntity.getFortuneAnalysis().getOverallScore()
);
isOverallAnimPlayed = true;
}
// 检查每个进度条是否需要执行动画
if (isViewVisible(viewBinding.includedSyy.proSyy) && !isCareerAnimPlayed) {
animateProgress(viewBinding.includedSyy.proSyy,
mTodayEntity.getFortuneAnalysis().getCareerScore());
isCareerAnimPlayed = true;
}
if (isViewVisible(viewBinding.includedSyy.proGqy) && !isLoveAnimPlayed) {
animateProgress(viewBinding.includedSyy.proGqy,
mTodayEntity.getFortuneAnalysis().getLoveScore());
isLoveAnimPlayed = true;
}
if (isViewVisible(viewBinding.includedSyy.proJky) && !isHealthAnimPlayed) {
animateProgress(viewBinding.includedSyy.proJky,
mTodayEntity.getFortuneAnalysis().getHealthScore());
isHealthAnimPlayed = true;
}
if (isViewVisible(viewBinding.includedSyy.proCfy) && !isWealthAnimPlayed) {
animateProgress(viewBinding.includedSyy.proCfy,
mTodayEntity.getFortuneAnalysis().getWealthScore());
isWealthAnimPlayed = true;
}
}
// 判断视图是否可见
private boolean isViewVisible(View view) {
if (view == null) return false;
Rect scrollBounds = new Rect();
View scrollView = viewBinding.getRoot().findViewById(R.id.nesd_scrollview);
if (scrollView != null) {
scrollView.getHitRect(scrollBounds);
return view.getLocalVisibleRect(scrollBounds);
}
return false;
}
// 执行进度条动画
private void animateProgress(LinearProgressIndicator progressBar, int targetProgress) {
// 保存原始进度值
final int originalProgress = progressBar.getProgress();
// 先重置为0
progressBar.setProgress(0);
// 创建动画
ObjectAnimator animator = ObjectAnimator.ofInt(progressBar, "progress", 0, targetProgress);
animator.setDuration(800); // 动画时长800ms
animator.setInterpolator(new AccelerateDecelerateInterpolator());
// 添加脉冲效果
animator.addUpdateListener(animation -> {
float fraction = animation.getAnimatedFraction();
float scale = 1 + 0.2f * (float) Math.sin(fraction * Math.PI);
progressBar.setScaleX(scale);
progressBar.setScaleY(scale);
});
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// 恢复原始大小
progressBar.animate()
.scaleX(1f)
.scaleY(1f)
.setDuration(200)
.start();
}
});
animator.start();
}
// 执行圆形进度条动画(带旋转效果)
private void animateCircularProgress(CircularProgressIndicator progressBar, int targetProgress) {
// 保存原始进度值
final int originalProgress = progressBar.getProgress();
// 先重置为0
progressBar.setProgress(0);
// 创建进度动画
ValueAnimator progressAnimator = ValueAnimator.ofInt(0, targetProgress);
progressAnimator.setDuration(1000); // 动画时长1000ms
progressAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
// 创建旋转动画
ObjectAnimator rotationAnimator = ObjectAnimator.ofFloat(progressBar, "rotation", 0f, 360f);
rotationAnimator.setDuration(800);
rotationAnimator.setInterpolator(new AccelerateDecelerateInterpolator());
// 组合动画
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(progressAnimator, rotationAnimator);
// 添加脉冲缩放效果
progressAnimator.addUpdateListener(animation -> {
int progress = (int) animation.getAnimatedValue();
progressBar.setProgress(progress);
// 脉冲效果:在动画过程中轻微缩放
float fraction = animation.getAnimatedFraction();
float scale = 1 + 0.12f * (float) Math.sin(fraction * Math.PI);
progressBar.setScaleX(scale);
progressBar.setScaleY(scale);
});
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// 恢复原始大小
progressBar.animate()
.scaleX(1f)
.scaleY(1f)
.setDuration(200)
.start();
}
});
animatorSet.start();
}
@Override
public FatePresenter createPresenter() {
return new FatePresenter(new FateImpl());
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(data != null){
if(resultCode == RESULT_OK){
switch (requestCode){
case REQUEST_EVENT_CODE_10002:
break;
case REQUEST_EVENT_CODE_10001:
break;
}
}
}
}
@Subscribe(threadMode = ThreadMode.MainThread)
public void onGetMessage(EventMessage message) {
switch (message.msg){
case EventMessage.DATA_REFRESH_EVENT:
showLoadingDialog();
startFateDataRequest();
break;
case EventMessage.USER_LOGIN_SUCCESS:
startFateDataRequest();
break;
}
}
/**
* 启动今日运势数据请求并开始延迟Toast任务
*/
private void startFateDataRequest() {
// 停止之前的Toast任务如果有的话
stopToastTask();
// 调用接口
mPresenter.getTodayFateDatas();
// 启动延迟Toast任务
startDelayedToastTask();
}
/**
* 启动延迟Toast任务
*/
private void startDelayedToastTask() {
// 5秒后开始显示Toast
toastHandler.postDelayed(() -> {
if (!isToastTaskRunning) {
isToastTaskRunning = true;
currentToastIndex = 0;
startToastLoop();
}
}, 5000);
}
/**
* 开始Toast循环显示
*/
private void startToastLoop() {
if (!isToastTaskRunning) {
return;
}
toastRunnable = new Runnable() {
@Override
public void run() {
if (isToastTaskRunning && currentToastIndex < toastMessages.length) {
// 显示当前Toast
ToastUtil.showLong(mContext, toastMessages[currentToastIndex]);
currentToastIndex++;
// 如果还有下一条消息5秒后显示
if (currentToastIndex < toastMessages.length && isToastTaskRunning) {
toastHandler.postDelayed(this, 5000);
} else {
// 所有消息显示完毕,重新开始循环
if (isToastTaskRunning) {
currentToastIndex = 0;
toastHandler.postDelayed(this, 5000);
}
}
}
}
};
// 立即执行第一次
toastRunnable.run();
}
/**
* 停止Toast任务
*/
private void stopToastTask() {
isToastTaskRunning = false;
if (toastRunnable != null) {
toastHandler.removeCallbacks(toastRunnable);
}
// 移除所有待执行的任务
toastHandler.removeCallbacksAndMessages(null);
}
/**
* 今日运势请求成功
* @param entity
*/
@Override
public void getUserTodayFateDataSuccess(TodayFateEntity entity) {
// 立即停止Toast任务
stopToastTask();
hideDialogLoading();
mTodayEntity = entity;
getLatLngByCityName(entity.getUserInfo().getCurrentPlace());
setDataShow();
viewBinding.refresh.finishRefresh(300);
}
@Override
public void getFateHistorySuccess(FateHistoryEntity entity) {
}
@Override
public void userSaveBirthInfoSuccess(TodayFateEntity bean) {
}
@Override
public void getUserInfoSuccess(MineUserInfoBean bean) {
if(bean != null){
MingDengUserInfoManager.getInstance().setUserInfo(bean.getUserInfo());
SharePreUtil.putBoolean(ConstantUtil.SHARE_PRE_NAME,mContext,ConstantUtil.USER_HAS_SET_BIRTH,bean.isHasEightCharacters());
AppConfig.USER_SET_BIRTH_INFO = bean.isHasEightCharacters();
//是否设置过生辰
if(AppConfig.USER_SET_BIRTH_INFO){
viewBinding.llInput.setVisibility(View.GONE);
viewBinding.refresh.setVisibility(View.VISIBLE);
}else{
viewBinding.llInput.setVisibility(View.VISIBLE);
viewBinding.refresh.setVisibility(View.GONE);
}
}
}
/**
* 提交举报成功
* @param bean
*/
@Override
public void commitReportSuccess(BaseBean bean) {
ToastUtil.showShort(mContext, "星语明灯感谢您的反馈");
hideDialogLoading();
}
/**
* 设置显示内容
*/
private void setDataShow() {
//出生日期
String birthDay = mTodayEntity.getUserInfo().getBirthTime();
viewBinding.includedBirthInfo.tvBirthdayTime.setText(ZYTimeUtils.formatDateString(birthDay));
//真太阳时
String realSunTime = mTodayEntity.getUserInfo().getTrueSolarTime();
viewBinding.includedBirthInfo.tvZhentaiyangValue.setText(TextUtils.isEmpty(realSunTime) ?
"未知":ZYTimeUtils.formatToChineseDate(realSunTime));
//命格
viewBinding.includedBirthInfo.tvMingge.setText(TextUtils.isEmpty(mTodayEntity.getFortuneAnalysis().getDestiny()) ?
"未知": mTodayEntity.getFortuneAnalysis().getDestiny());
//经纬度
String lng = NumberDecimalFilter.truncateToTwoDecimal(Double.parseDouble(mTodayEntity.getUserInfo().getLongitude()));
String lat = NumberDecimalFilter.truncateToTwoDecimal(Double.parseDouble(mTodayEntity.getUserInfo().getLatitude()));
viewBinding.includedBirthInfo.tvLonglat.setText(lng + "," + lat);
//出生地
viewBinding.includedBirthInfo.tvBirthAddress.setText(mTodayEntity.getUserInfo().getBirthPlace());
//星座
viewBinding.includedBirthInfo.tvXingzuo.setText(mTodayEntity.getUserInfo().getConstellation());
//运势分数
int todayScore = mTodayEntity.getFortuneAnalysis().getOverallScore();
int color = FateDataUtil.getScoreColor(todayScore);
viewBinding.includedTodayFateDatas.progressIndicator.setIndicatorColor(color);
// viewBinding.includedTodayFateDatas.progressIndicator.setProgress(todayScore,true);
viewBinding.includedTodayFateDatas.tvFeenValue.setText(String.valueOf(todayScore));
viewBinding.includedTodayFateDatas.tvFeenValue.setTextColor(color);
//运势内容
viewBinding.includedTodayFateDatas.tvYunshiContent.setText(TextUtils.isEmpty(mTodayEntity.getFortuneAnalysis().getOverallDescription())?
"暂无内容" : mTodayEntity.getFortuneAnalysis().getOverallDescription());
//今日吉时
viewBinding.includedTodayFateDatas.tvTimeZc.setText(mTodayEntity.getFortuneAnalysis().getLuckyInfo().getLuckyTimes().get(0).getTime());
viewBinding.includedTodayFateDatas.tvThingZc.setText(mTodayEntity.getFortuneAnalysis().getLuckyInfo().getLuckyTimes().get(0).getDescription());
viewBinding.includedTodayFateDatas.tvTimeZw.setText(mTodayEntity.getFortuneAnalysis().getLuckyInfo().getLuckyTimes().get(1).getTime());
viewBinding.includedTodayFateDatas.tvThingZw.setText(mTodayEntity.getFortuneAnalysis().getLuckyInfo().getLuckyTimes().get(1).getDescription());
viewBinding.includedTodayFateDatas.tvTimeXw.setText(mTodayEntity.getFortuneAnalysis().getLuckyInfo().getLuckyTimes().get(2).getTime());
viewBinding.includedTodayFateDatas.tvThingXw.setText(mTodayEntity.getFortuneAnalysis().getLuckyInfo().getLuckyTimes().get(2).getDescription());
//天象星座
viewBinding.includedXingzuo.tvXingzuo.setText(mTodayEntity.getUserInfo().getConstellation());
viewBinding.includedXingzuo.tvWendu.setText(mTodayEntity.getWeatherTemperature());
viewBinding.includedXingzuo.tvTianqi.setText(mTodayEntity.getWeatherCondition());
viewBinding.includedXingzuo.tvXzType.setText(FateDataUtil.getConstellationType(mTodayEntity.getUserInfo().getConstellation()));
viewBinding.includedXingzuo.tvXingzuoYingxiang.setText(mTodayEntity.getFortuneAnalysis().getAstrologyInfluence().getDescription());
//事业运
viewBinding.includedSyy.tvSyyScore.setText(String.valueOf(mTodayEntity.getFortuneAnalysis().getCareerScore()));
// viewBinding.includedSyy.proSyy.setProgress(mTodayEntity.getFortuneAnalysis().getCareerScore());
viewBinding.includedSyy.tvSyyContent.setText(mTodayEntity.getFortuneAnalysis().getCareerDescription());
//事业运
viewBinding.includedSyy.tvGqyScore.setText(String.valueOf(mTodayEntity.getFortuneAnalysis().getLoveScore()));
// viewBinding.includedSyy.proGqy.setProgress(mTodayEntity.getFortuneAnalysis().getLoveScore());
viewBinding.includedSyy.tvGqyContent.setText(mTodayEntity.getFortuneAnalysis().getLoveDescription());
//事业运
viewBinding.includedSyy.tvJkyScore.setText(String.valueOf(mTodayEntity.getFortuneAnalysis().getHealthScore()));
// viewBinding.includedSyy.proJky.setProgress(mTodayEntity.getFortuneAnalysis().getHealthScore());
viewBinding.includedSyy.tvJkyContent.setText(mTodayEntity.getFortuneAnalysis().getHealthDescription());
//事业运
viewBinding.includedSyy.tvCfyScore.setText(String.valueOf(mTodayEntity.getFortuneAnalysis().getWealthScore()));
// viewBinding.includedSyy.proCfy.setProgress(mTodayEntity.getFortuneAnalysis().getWealthScore());
viewBinding.includedSyy.tvCfyContent.setText(mTodayEntity.getFortuneAnalysis().getWealthDescription());
//幸运数字
String [] numbers = mTodayEntity.getFortuneAnalysis().getLuckyInfo().getNumbers();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < numbers.length; i++) {
sb.append(numbers[i]);
if (i < numbers.length - 1) {
sb.append("");
}
}
viewBinding.includeXingyun.tvLuckNum.setText(sb.toString());
//幸运颜色
viewBinding.includeXingyun.tvLuckColor.setText(mTodayEntity.getFortuneAnalysis().getLuckyInfo().getColor());
//幸运方位
viewBinding.includeXingyun.tvLuckFw.setText(mTodayEntity.getFortuneAnalysis().getLuckyInfo().getDirection());
//幸运时间
viewBinding.includeXingyun. tvLuckTime.setText(mTodayEntity.getFortuneAnalysis().getLuckyInfo().getLuckyTimes().get(0).getTime());
//幸运花卉
viewBinding.includeXingyun.tvLuckFlower.setText(mTodayEntity.getFortuneAnalysis().getLuckyInfo().getFlower());
//幸运方位
viewBinding.includeXingyun.tvLuckFood.setText(mTodayEntity.getFortuneAnalysis().getLuckyInfo().getFood());
//宜 忌
String positive = mTodayEntity.getFortuneAnalysis().getSuggestions().getPositive();
String negative = mTodayEntity.getFortuneAnalysis().getSuggestions().getNegative();
viewBinding.includeYsjy.tvPositive.setText(positive);
viewBinding.includeYsjy.tvNegative.setText(negative);
//五行分析
viewBinding.includeYsjy.tvFive.setText(mTodayEntity.getFortuneAnalysis().getFiveElementsAnalysis());
// 重置动画标志位
resetAnimationFlags();
}
/**
* 通过城市逆地理编码,获取经纬度
* @param cityName
*/
private void getLatLngByCityName(String cityName) {
try {
GeocodeSearch geocodeSearch = new GeocodeSearch(mContext);
geocodeSearch.setOnGeocodeSearchListener(new GeocodeSearch.OnGeocodeSearchListener() {
@Override
public void onRegeocodeSearched(com.amap.api.services.geocoder.RegeocodeResult regeocodeResult, int i) {
// Not used here
}
@Override
public void onGeocodeSearched(GeocodeResult geocodeResult, int rCode) {
if (rCode == AMapException.CODE_AMAP_SUCCESS) {
List<GeocodeAddress> addressList = geocodeResult.getGeocodeAddressList();
if (addressList != null && !addressList.isEmpty()) {
GeocodeAddress address = addressList.get(0);
LatLonPoint latLonPoint = address.getLatLonPoint();
String lat = String.valueOf(NumberDecimalFilter.roundToTwoDecimalPlaces(latLonPoint.getLatitude()));
String lng = String.valueOf(NumberDecimalFilter.roundToTwoDecimalPlaces(latLonPoint.getLongitude()));
WeatherParameter parameter = new WeatherParameter(lng + "," + lat)
.lang(Lang.ZH_HANS)
.unit(Unit.METRIC);
instance.weatherNow(parameter, new Callback<WeatherNowResponse>() {
@Override
public void onSuccess(WeatherNowResponse response) {
mContext.runOnUiThread(new Runnable() {
@Override
public void run() {
try {
WeatherIconUtil.loadWeatherIcon(mContext,viewBinding.includedBirthInfo.imgWeather,response.getNow().getIcon());
viewBinding.includedBirthInfo.tvTianqi.setText(response.getNow().getText() + " " +response.getNow().getTemp() + "");
} catch (Exception e) {
throw new RuntimeException(e);
}
}
});
}
@Override
public void onFailure(ErrorResponse errorResponse) {
Log.d(TAG,"调用和风 API--onFailure" + errorResponse.toString());
}
@Override
public void onException(Throwable e) {
Log.d(TAG,"调用和风 API--onException" + e.getMessage());
e.printStackTrace();
}
});
} else {
Log.d("GeoUtil", "未找到城市: " + cityName);
}
} else {
Log.e("GeoUtil", "查询失败,错误码: " + rCode);
}
}
});
// 创建 GeocodeQuery第一个参数是城市名第二个参数可指定省份可选
GeocodeQuery query = new GeocodeQuery(cityName, null);
geocodeSearch.getFromLocationNameAsyn(query); // 异步查询
} catch (AMapException e) {
throw new RuntimeException(e);
}
}
// 重置动画标志位
private void resetAnimationFlags() {
isOverallAnimPlayed = false;
isCareerAnimPlayed = false;
isLoveAnimPlayed = false;
isHealthAnimPlayed = false;
isWealthAnimPlayed = false;
}
@Override
public void onPause() {
super.onPause();
// 停止Toast任务
stopToastTask();
// 重置标志位以便下次可见时重新动画
resetAnimationFlags();
}
@Override
public void onDestroy() {
super.onDestroy();
// 清理资源
stopToastTask();
}
@Override
public void onNetChange(int netModel) {
}
@Override
public void showError(String message) {
// 停止Toast任务
stopToastTask();
hideDialogLoading();
ToastUtil.showShort(mContext,message);
}
@Override
public void logOut(BaseBean bean) {
}
@Override
public void getAllCitySuccess(List<CityDataEntity> list) {
}
private void getCurrentCityWeather(GeoPoiResponse response) {
}
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
}
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
hidePermissionDialog();
if(requestCode == REQUEST_EVENT_CODE_10002){
if(grantResults[0] == 0){
ToastUtil.showShort(mContext,"定位获取成功");
}
}
}
}

View File

@@ -25,6 +25,7 @@ import com.ckkj.water.ConstantUtil;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity;
import com.ckkj.water.databinding.GuideActivityLayoutBinding;
import com.ckkj.water.login.LoginActivity;
import com.ckkj.water.login.entity.LoginEntity;
import com.ckkj.water.login.mvp.LoginContract;
import com.ckkj.water.login.mvp.LoginImpl;
@@ -42,9 +43,6 @@ public class GuideActivity extends BaseActivity<GuideActivityLayoutBinding, Logi
private List<Drawable> mImgUrls = new ArrayList<>();
private List<ImageView> mPointList = new ArrayList<>();
private boolean mHasEnterAppFlag = false;
private String desc[] = {"探索你的个性特征",
"一键生成,智能排盘工具",
"贴心提供每日生活指南"};
private int[] guideDrawableIds = {
R.mipmap.img_guide_1,
@@ -98,8 +96,11 @@ public class GuideActivity extends BaseActivity<GuideActivityLayoutBinding, Logi
viewBinding.tvStart.setOnClickListener(v -> {
SharePreUtil.putBoolean(ConstantUtil.SHARE_PRE_NAME, mContext, ConstantUtil.FIRST_ENTER_APP_FLAG, true);
Intent intent = new Intent(mContext, MainActivity.class);
startActivity(intent);
if(AppConfig.USER_LOGIN){
goTo(mContext, MainActivity.class,null);
}else{
goTo(mContext, LoginActivity.class,null);
}
finish();
});
}
@@ -122,6 +123,11 @@ public class GuideActivity extends BaseActivity<GuideActivityLayoutBinding, Logi
super.initWidget();
}
@Override
public void registerSuccess(BaseBean bean) {
}
@Override
public void getPhoneCodeSuccess(BaseBean bean) {
}
@@ -130,6 +136,11 @@ public class GuideActivity extends BaseActivity<GuideActivityLayoutBinding, Logi
public void loginBySmsCodeSuccess(LoginEntity entity) {
}
@Override
public void updateUserPasswordSuccess(BaseBean bean) {
}
class MyAdapter extends PagerAdapter {
@Override
public int getCount() {

View File

@@ -1,17 +1,20 @@
package com.ckkj.water.guide;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import com.ckkj.water.AppConfig;
import com.ckkj.water.login.LoginActivity;
import com.gyf.immersionbar.ImmersionBar;
import com.ckkj.water.ConstantUtil;
import com.ckkj.water.MyApplication;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity;
import com.ckkj.water.base.entity.MineUserInfoBean;
import com.ckkj.water.mine.entity.MineUserInfoBean;
import com.ckkj.water.databinding.GuideLayoutBinding;
import com.ckkj.water.main.MainContract;
import com.ckkj.water.main.MainImpl;
@@ -23,8 +26,9 @@ import com.ckkj.water.network.BaseBean;
import com.ckkj.water.utils.dataUtil.SharePreUtil;
import com.ckkj.water.utils.manager.ActivityManager;
import java.util.Locale;
import me.jessyan.autosize.internal.CancelAdapt;
import me.jessyan.autosize.internal.CustomAdapt;
/**
* @author fengxiaoyang
@@ -41,6 +45,7 @@ public class LaunchActivity extends BaseActivity<GuideLayoutBinding, MainPresent
super.initData(savedInstanceState);
viewBinding.includeTitle.imgBack.setVisibility(View.GONE);
viewBinding.includeTitle.tvTitle.setVisibility(View.GONE);
viewBinding.includeTitle.relativeTitle.setBackgroundColor(mContext.getColor(R.color.white));
ImmersionBar.with(this)
.statusBarColor(R.color.white)
.statusBarDarkFont(true)
@@ -52,8 +57,13 @@ public class LaunchActivity extends BaseActivity<GuideLayoutBinding, MainPresent
//首次进入,弹框提示隐私政策
if (mIsFirstEnterApp) {
//首次进入
showDialogtext("温馨提示", mContext.getResources().getString(R.string.tips_yinsizhengce_dialog),
"同意", "拒绝", "用户隐私协议", new OnDialogClick() {
showDialogtext(
getSystemLanguageString(R.string.tip_text),
getSystemLanguageString(R.string.tips_yinsizhengce_dialog),
getSystemLanguageString(R.string.agree),
getSystemLanguageString(R.string.reject),
getSystemLanguageString(R.string.user_agreement)
, new OnDialogClick() {
@Override
public void onCancelClick() {
@@ -87,9 +97,15 @@ public class LaunchActivity extends BaseActivity<GuideLayoutBinding, MainPresent
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = new Intent(mContext, MainActivity.class);
startActivity(intent);
finish();
if(AppConfig.USER_LOGIN){
Intent intent = new Intent(mContext, MainActivity.class);
startActivity(intent);
finish();
}else{
goTo(mContext, LoginActivity.class,null);
finish();
}
}
}, 2000);
@@ -106,6 +122,16 @@ public class LaunchActivity extends BaseActivity<GuideLayoutBinding, MainPresent
finish();
}
private String getSystemLanguageString(int resId) {
Locale systemLocale = Resources.getSystem().getConfiguration().getLocales().get(0);
Locale targetLocale = Locale.CHINESE.getLanguage().equals(systemLocale.getLanguage())
? Locale.SIMPLIFIED_CHINESE
: Locale.ENGLISH;
Configuration configuration = new Configuration(getResources().getConfiguration());
configuration.setLocale(targetLocale);
return createConfigurationContext(configuration).getString(resId);
}
@Override
public MainPresenter createPresenter() {

View File

@@ -10,12 +10,13 @@ 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.WaterStatisticsEntity;
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;
import com.ckkj.water.plan.BindDeviceFragment;
import com.ckkj.water.plan.WaterConfigFragment;
import com.ckkj.water.plate.adapter.ViewPagerAdapter;
import com.ckkj.water.plan.adapter.ViewPagerAdapter;
import com.ckkj.water.utils.manager.ThemeManager;
import com.gyf.immersionbar.ImmersionBar;
import java.util.ArrayList;
@@ -31,8 +32,8 @@ public class HistoryFragment extends BaseFragment<HistoryFragmentLayoutBinding,
@Override
public void initData(Bundle state) {
ImmersionBar.with(mContext)
.statusBarColor(R.color.white)
.statusBarDarkFont(true)
.statusBarColor(R.color.mainBackgroundColor)
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext))
.titleBar(viewBinding.rlTitle)
.init();
viewBinding.includedTitle.imgBack.setVisibility(INVISIBLE);
@@ -96,4 +97,19 @@ public class HistoryFragment extends BaseFragment<HistoryFragmentLayoutBinding,
public void logOut(BaseBean bean) {
}
@Override
public void getHistoryListSuccess(List<WateringRecord> msg, int total) {
}
@Override
public void deleteHistorySuccess(BaseBean msg, int position) {
}
@Override
public void getHistoryStatisticsSuccess(WaterStatisticsEntity entity) {
}
}

View File

@@ -4,25 +4,129 @@ import android.os.Bundle;
import com.ckkj.water.base.BaseFragment;
import com.ckkj.water.databinding.WaterCountFragmentLayoutBinding;
import com.ckkj.water.history.entity.WaterStatisticsEntity;
import com.ckkj.water.history.entity.WateringRecord;
import com.ckkj.water.history.mvp.HistoryContract;
import com.ckkj.water.history.mvp.HistoryImpl;
import com.ckkj.water.history.mvp.HistoryPresenter;
import com.ckkj.water.index.entity.WaterDeviceEntity;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.plan.entity.WaterPlanWeekEntity;
import com.ckkj.water.plan.mvp.WaterPlanContract;
import com.ckkj.water.plan.mvp.WaterPlanImpl;
import com.ckkj.water.plan.mvp.WaterPlanPresenter;
import com.ckkj.water.utils.show.ToastUtil;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
/**
* @author fengxiaoyang
* @date 2026/5/16
* @description TODO: 描述该类的作用
* @description TODO: 统计界面
*/
public class StatisticFragment extends BaseFragment<WaterCountFragmentLayoutBinding, WaterPlanPresenter>
implements WaterPlanContract.WaterPlanView {
public class StatisticFragment extends BaseFragment<WaterCountFragmentLayoutBinding, HistoryPresenter>
implements HistoryContract.HistoryView {
@Override
protected void onResumeInitData() {
super.onResumeInitData();
showLoadingDialog();
mPresenter.getWaterStatistic();
}
@Override
public void initData(Bundle state) {
}
@Override
public WaterPlanPresenter createPresenter() {
return null;
public void getHistoryStatisticsSuccess(WaterStatisticsEntity entity) {
hideDialogLoading();
viewBinding.tvTotalValue.setText(entity.getCount()+"");
viewBinding.tvThisWeekValue.setText(entity.getWeekCount()+"");
viewBinding.tvThisMonthValue.setText(entity.getMonCount()+"");
viewBinding.tvPlanValue.setText(entity.getSchedule()+"");
viewBinding.tvManuallyValue.setText(entity.getManualNum()+"");
setTotalDuration(entity.getAllTime());
setAverageDuration(entity.getAverageTime());
setWeekAndMonthDate();
// viewBinding.tvTotalValue.setText(entity.getCount()+"");
}
private void setTotalDuration(int totalMinutes) {
if (totalMinutes < 60) {
viewBinding.tvTotalDurationValue.setText(totalMinutes + "");
viewBinding.tvTotalDurationHourUnit.setText("min");
viewBinding.tvTotalDurationMinValue.setVisibility(android.view.View.GONE);
viewBinding.tvTotalDurationMinUnit.setVisibility(android.view.View.GONE);
} else {
int hours = totalMinutes / 60;
int minutes = totalMinutes % 60;
viewBinding.tvTotalDurationHourUnit.setText("h");
viewBinding.tvTotalDurationValue.setText(hours + "");
if (minutes == 0) {
viewBinding.tvTotalDurationMinValue.setVisibility(android.view.View.GONE);
viewBinding.tvTotalDurationMinUnit.setVisibility(android.view.View.GONE);
} else {
viewBinding.tvTotalDurationMinValue.setVisibility(android.view.View.VISIBLE);
viewBinding.tvTotalDurationMinUnit.setVisibility(android.view.View.VISIBLE);
viewBinding.tvTotalDurationMinValue.setText(minutes + "");
}
}
}
private void setAverageDuration(int avgMinutes) {
if (avgMinutes < 60) {
viewBinding.tvAverageHourValue.setText(avgMinutes + "");
viewBinding.tvAverageHourUnit.setText("min");
viewBinding.tvAverageMinValue.setVisibility(android.view.View.GONE);
viewBinding.tvAverageMinUnit.setVisibility(android.view.View.GONE);
} else {
int hours = avgMinutes / 60;
int minutes = avgMinutes % 60;
viewBinding.tvAverageHourUnit.setText("h");
viewBinding.tvAverageHourValue.setText(hours + "");
if (minutes == 0) {
viewBinding.tvAverageMinValue.setVisibility(android.view.View.GONE);
viewBinding.tvAverageMinUnit.setVisibility(android.view.View.GONE);
} else {
viewBinding.tvAverageMinValue.setVisibility(android.view.View.VISIBLE);
viewBinding.tvAverageMinUnit.setVisibility(android.view.View.VISIBLE);
viewBinding.tvAverageMinValue.setText(minutes + "");
}
}
}
private void setWeekAndMonthDate() {
SimpleDateFormat monthFormat = new SimpleDateFormat("M月", Locale.CHINA);
SimpleDateFormat dayFormat = new SimpleDateFormat("M.d", Locale.CHINA);
Calendar calendar = Calendar.getInstance();
// 设置当月日期显示
viewBinding.tvThisMonthDate.setVisibility(android.view.View.VISIBLE);
viewBinding.tvThisMonthDate.setText(monthFormat.format(calendar.getTime()));
// 计算本周的开始日期(周一)和结束日期(周日)
int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);
int mondayOffset = (dayOfWeek == Calendar.SUNDAY) ? -6 : Calendar.MONDAY - dayOfWeek;
calendar.add(Calendar.DAY_OF_MONTH, mondayOffset);
Date weekStart = calendar.getTime();
calendar.add(Calendar.DAY_OF_MONTH, 6);
Date weekEnd = calendar.getTime();
// 设置本周日期显示
viewBinding.tvWeekDate.setVisibility(android.view.View.VISIBLE);
viewBinding.tvWeekDate.setText(dayFormat.format(weekStart) + " - " + dayFormat.format(weekEnd));
}
@Override
public HistoryPresenter createPresenter() {
return new HistoryPresenter(new HistoryImpl());
}
@Override
@@ -32,11 +136,24 @@ public class StatisticFragment extends BaseFragment<WaterCountFragmentLayoutBind
@Override
public void showError(String message) {
hideDialogLoading();
ToastUtil.showShort(mContext,message);
}
@Override
public void logOut(BaseBean bean) {
}
@Override
public void getHistoryListSuccess(List<WateringRecord> msg, int total) {
}
@Override
public void deleteHistorySuccess(BaseBean msg, int position) {
}
}

View File

@@ -8,19 +8,25 @@ 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.WaterStatisticsEntity;
import com.ckkj.water.history.entity.WateringRecord;
import com.ckkj.water.history.mvp.HistoryContract;
import com.ckkj.water.history.mvp.HistoryImpl;
import com.ckkj.water.history.mvp.HistoryPresenter;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.plan.entity.WaterPlanWeekEntity;
import com.ckkj.water.utils.manager.ThemeManager;
import com.ckkj.water.utils.show.ToastUtil;
import com.google.gson.Gson;
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;
@@ -34,28 +40,70 @@ 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;
private String mFilterStartTime = "";
private String mFilterEndTime = "";
private int mFilterType = -1; //0-排程 1-手动
@Override
protected void onResumeInitData() {
super.onResumeInitData();
showLoadingDialog();
getWaterHistoryList();
}
@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();
RefreshHeader header = new CustomRefreshHeader(mContext);
viewBinding.refresh.setRefreshHeader(header);
viewBinding.refresh.setRefreshFooter(new ClassicsFooter(mContext));
viewBinding.refresh.setOnRefreshListener(layout -> {
mCurrentPage = 1;
mFilterStartTime = "";
mFilterEndTime = "";
mFilterType = -1;
viewBinding.refresh.setNoMoreData(false);
getWaterHistoryList();
});
viewBinding.refresh.setOnLoadMoreListener(layout -> {
mCurrentPage++;
getWaterHistoryList();
});
viewBinding.imgTimeFilter.setImageResource(
ThemeManager.isAppThemeLight(mContext) ?
R.mipmap.icon_triangle_down : R.mipmap.icon_white_triangle_down);
viewBinding.imgTypeFilter.setImageResource(
ThemeManager.isAppThemeLight(mContext) ?
R.mipmap.icon_triangle_down : R.mipmap.icon_white_triangle_down);
}
private void getWaterHistoryList(){
showLoadingDialog();
mPresenter.getWaterHistoryList(mCurrentPage,mFilterStartTime,mFilterEndTime,mFilterType);
}
private void initListener() {
setClick(viewBinding.linearTimeFilter);
setClick(viewBinding.linearTypeFilter);
setClick(viewBinding.includedNoresult.btnRefresh);
mAdapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
@Override
public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
switch (view.getId()){
case R.id.img_delete:
showDialogtext(null, false, false, "提示",
"确定删除该记录", "取消", "删除",
showDialogtext(null, false, false,
getString(R.string.tip_text),
getString(R.string.confirm_delete_record),
getString(R.string.cancel_text),
getString(R.string.confirm_text),
new OnDialogClick() {
@Override
public void onCancelClick() {
@@ -63,7 +111,8 @@ public class TabHistoryFragment extends BaseFragment<TabHistoryFragmentLayoutBin
@Override
public void onConfirmClick() {
mAdapter.remove(position);
showLoadingDialog();
mPresenter.deleteWaterHistory(recordList.get(position).getId(),position);
}
});
break;
@@ -93,14 +142,18 @@ public class TabHistoryFragment extends BaseFragment<TabHistoryFragmentLayoutBin
}
@Override
public void onDismiss(BasePopupView popupView) {
viewBinding.tvTimeFilter.setTextColor(mContext.getColor(R.color.color_222222));
viewBinding.imgTimeFilter.setImageResource(R.mipmap.icon_triangle_down);
viewBinding.tvTimeFilter.setTextColor(mContext.getColor(R.color.textPrimaryColor));
viewBinding.imgTimeFilter.setImageResource(
ThemeManager.isAppThemeLight(mContext) ?
R.mipmap.icon_triangle_down : R.mipmap.icon_white_triangle_down);
}
})
.asCustom(new CustomTimeFilterPopupView(getContext(), new CustomTimeFilterPopupView.OnClickListener() {
@Override
public void callBack(String startTime, String endTime) {
ToastUtil.showShort(mContext,startTime + "---" + endTime);
mFilterStartTime = startTime;
mFilterEndTime = endTime;
getWaterHistoryList();
}
}));
popupView.show();
@@ -121,25 +174,98 @@ public class TabHistoryFragment extends BaseFragment<TabHistoryFragmentLayoutBin
}
@Override
public void onDismiss(BasePopupView popupView) {
viewBinding.tvTypeFilter.setTextColor(mContext.getColor(R.color.color_222222));
viewBinding.imgTypeFilter.setImageResource(R.mipmap.icon_triangle_down);
viewBinding.tvTypeFilter.setTextColor(mContext.getColor(R.color.textPrimaryColor));
viewBinding.imgTypeFilter.setImageResource(
ThemeManager.isAppThemeLight(mContext) ?
R.mipmap.icon_triangle_down : R.mipmap.icon_white_triangle_down);
}
})
.asCustom(new CustomTypeFilterPopupView(getContext(), new CustomTypeFilterPopupView.OnClickListener() {
@Override
public void callBack(boolean mSelManual, boolean mSelAuto) {
if (mSelManual) {
mFilterType = 1;
} else if (mSelAuto) {
mFilterType = 0;
} else {
mFilterType = -1;
}
getWaterHistoryList();
}
}));
}, mFilterType));
typePopupView.show();
break;
case R.id.btn_refresh:
mCurrentPage = 1;
mFilterStartTime = "";
mFilterEndTime = "";
mFilterType = -1;
getWaterHistoryList();
break;
}
}
/**
* 获取浇水记录成功
* @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);
}
}
/**
* 删除历史记录成功
* @param bean
* @param position
*/
@Override
public void deleteHistorySuccess(BaseBean bean, int position) {
hideDialogLoading();
recordList.remove(position);
mAdapter.notifyItemRemoved(position);
}
@Override
public void getHistoryStatisticsSuccess(WaterStatisticsEntity entity) {
}
@Override
public HistoryPresenter createPresenter() {
return null;
return new HistoryPresenter(new HistoryImpl());
}
@Override
@@ -149,11 +275,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

@@ -8,6 +8,7 @@ import com.chad.library.adapter.base.BaseQuickAdapter;
import com.chad.library.adapter.base.BaseViewHolder;
import com.ckkj.water.R;
import com.ckkj.water.history.entity.WateringRecord;
import com.ckkj.water.utils.time.ZYTimeUtils;
import java.util.List;
@@ -26,13 +27,16 @@ 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());
+ ":" + ZYTimeUtils.timeWithoutMillis(item.getStartTime())
+ "-" + ZYTimeUtils.timeWithoutMillis(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());
+ ":" + (item.getTriggerType().equals("0") ?
mContext.getString(R.string.history_filter_auto) :
mContext.getString(R.string.history_filter_manual)));
helper.addOnClickListener(R.id.img_delete);
}
}

View File

@@ -0,0 +1,34 @@
package com.ckkj.water.history.entity;
import java.io.Serializable;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author fengxiaoyang
* @date 2026/6/2
* @description 历史记录数据统计
*/
@NoArgsConstructor
@Data
public class WaterStatisticsEntity implements Serializable {
/**
* schedule : 0
* manualNum : 2
* averageTime : 2
* count : 2
* weekCount : 1
* monCount : 1
* allTime : 5
*/
private int schedule;//排程次数
private int manualNum;//手动浇水次数
private int averageTime;//平均浇水时长
private int count;//总次数
private int weekCount;//本周浇水次数
private int monCount;//本月浇水次数
private int allTime;//总浇水时长
}

View File

@@ -8,7 +8,7 @@ import lombok.NoArgsConstructor;
/**
* @author fengxiaoyang
* @date 2026/5/15
* @description TODO: 描述该类的作用
* @description 浇水历史记录
*/
@NoArgsConstructor
@Data
@@ -29,4 +29,37 @@ 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 String scheduleId;
private String durationMin;
private String zones;
private String createTime;
/**
* commandId : null
* scheduleId : 2062827585485807618
* zones : null
* remark : 设备离线,按排程自动补记
*/
private String commandId;
private String scheduleIdX;
private String zonesX;
private String remark;
}

View File

@@ -1,16 +1,34 @@
package com.ckkj.water.history.mvp;
import com.ckkj.water.base.IView;
import com.ckkj.water.history.entity.WaterStatisticsEntity;
import com.ckkj.water.history.entity.WateringRecord;
import com.ckkj.water.network.BaseBean;
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{
//历史列表
void getHistoryList(Map<String, String> map, CallBack callBack);
//删除某一历史记录
void deleteHistory(String logID, CallBack callBack);
//浇水的数据统计
void getWateringStatistics(Map<String, String> map, CallBack callBack);
}
public interface HistoryView extends IView {
//历史记录成功
void getHistoryListSuccess(List<WateringRecord> msg, int total);
//删除历史记录成功
void deleteHistorySuccess(BaseBean msg, int position);
//数据统计成功
void getHistoryStatisticsSuccess(WaterStatisticsEntity entity);
}
}

View File

@@ -1,5 +1,127 @@
package com.ckkj.water.history.mvp;
import com.ckkj.water.history.entity.WaterStatisticsEntity;
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;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
public class HistoryImpl implements HistoryContract.DataModel{
/**
* 查询浇水历史
* @param map
* @param callBack
*/
@Override
public void getHistoryList(Map<String, String> map, HistoryContract.CallBack callBack) {
NetWorkManager.getRequest().waterLogList(NetWorkManager.getToken(), map)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean<List<WateringRecord>>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean<List<WateringRecord>> bean) {
if (bean.getCode() == 200) {
callBack.success(bean.getRows(),bean.getTotal());
}
}
@Override
public void onError(@NonNull Throwable e) {
callBack.fail(e.getMessage());
}
@Override
public void onComplete() {
}
});
}
/**
* 删除某一历史记录
* @param logID
* @param callBack
*/
@Override
public void deleteHistory(String logID, HistoryContract.CallBack callBack) {
try {
NetWorkManager.getRequest().deleteWaterLog(NetWorkManager.getToken(), logID)
.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();
}
}
/**
* 浇水的数据统计
* @param map
* @param callBack
*/
@Override
public void getWateringStatistics(Map<String, String> map, HistoryContract.CallBack callBack) {
NetWorkManager.getRequest().getDataStatistics(NetWorkManager.getToken())
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean<WaterStatisticsEntity>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean<WaterStatisticsEntity> bean) {
if (bean.getCode() == 200) {
callBack.success(bean.getData(),0);
}
}
@Override
public void onError(@NonNull Throwable e) {
callBack.fail(e.getMessage());
}
@Override
public void onComplete() {
}
});
}
}

View File

@@ -1,6 +1,15 @@
package com.ckkj.water.history.mvp;
import android.text.TextUtils;
import com.ckkj.water.base.BasePresenter;
import com.ckkj.water.history.entity.WaterStatisticsEntity;
import com.ckkj.water.history.entity.WateringRecord;
import com.ckkj.water.network.BaseBean;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class HistoryPresenter extends BasePresenter<HistoryContract.HistoryView> {
private HistoryImpl impl;
@@ -8,4 +17,88 @@ public class HistoryPresenter extends BasePresenter<HistoryContract.HistoryView>
public HistoryPresenter(HistoryImpl impl) {
this.impl = impl;
}
/**
* 查询历史列表
* @param mCurrentPage
* @param mFilterStartTime
* @param mFilterEndTime
* @param mFilterType
*/
public void getWaterHistoryList(int mCurrentPage, String mFilterStartTime, String mFilterEndTime, int mFilterType) {
Map<String,String> map = new HashMap<>();
map.put("pageNum",mCurrentPage+"");
if(!TextUtils.isEmpty(mFilterStartTime)){
map.put("params[beginTime]", mFilterStartTime);
}
if(!TextUtils.isEmpty(mFilterStartTime)){
map.put("params[endTime]", mFilterEndTime);
}
if(mFilterType != -1){
map.put("triggerType",mFilterType+"");
}
impl.getHistoryList(map, new HistoryContract.CallBack<List<WateringRecord>>() {
@Override
public void success(List<WateringRecord> list,int total) {
if(HistoryPresenter.this.getView() != null){
HistoryPresenter.this.getView().getHistoryListSuccess(list,total);
}
}
@Override
public void fail(String msg) {
if(HistoryPresenter.this.getView() != null){
HistoryPresenter.this.getView().showMessage(msg);
}
}
});
}
/**
* 删除历史记录
* @param id
* @param position
*/
public void deleteWaterHistory(String id, int position) {
impl.deleteHistory(id, new HistoryContract.CallBack<BaseBean>() {
@Override
public void success(BaseBean bean, int total) {
if(HistoryPresenter.this.getView() != null){
HistoryPresenter.this.getView().deleteHistorySuccess(bean,position);
}
}
@Override
public void fail(String msg) {
if(HistoryPresenter.this.getView() != null){
HistoryPresenter.this.getView().showMessage(msg);
}
}
});
}
/**
* 获取浇水统计
*/
public void getWaterStatistic() {
impl.getWateringStatistics(null, new HistoryContract.CallBack<WaterStatisticsEntity>() {
@Override
public void success(WaterStatisticsEntity entity, int total) {
if(HistoryPresenter.this.getView() != null){
HistoryPresenter.this.getView().getHistoryStatisticsSuccess(entity);
}
}
@Override
public void fail(String msg) {
if(HistoryPresenter.this.getView() != null){
HistoryPresenter.this.getView().showMessage(msg);
}
}
});
}
}

View File

@@ -1,27 +1,45 @@
package com.ckkj.water.index;
import android.Manifest;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothManager;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import com.ckkj.water.AppConfig;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseFragment;
import com.ckkj.water.custom.view.CustomRefreshHeader;
import com.ckkj.water.databinding.IndexFragmentLayoutBinding;
import com.ckkj.water.ConstantUtil;
import com.ckkj.water.device.AddDeviceActivity;
import com.ckkj.water.device.DeviceControlActivity;
import com.ckkj.water.device.DeviceReNameActivity;
import com.ckkj.water.device.SelectWifiActivity;
import com.ckkj.water.index.adapter.DeviceAdapter;
import com.ckkj.water.index.entity.IndexDeviceEntity;
import com.ckkj.water.index.entity.WaterDeviceEntity;
import com.ckkj.water.index.mvp.IndexContract;
import com.ckkj.water.index.mvp.IndexImpl;
import com.ckkj.water.index.mvp.IndexPresenter;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.permission.PermissionActivity;
import com.ckkj.water.utils.show.ToastUtil;
import com.maning.mlkitscanner.scan.MNScanManager;
import com.maning.mlkitscanner.scan.callback.act.MNScanCallback;
import com.scwang.smart.refresh.footer.ClassicsFooter;
import com.scwang.smart.refresh.layout.api.RefreshHeader;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.ArrayList;
import java.util.List;
@@ -32,22 +50,45 @@ public class IndexFragment extends BaseFragment<IndexFragmentLayoutBinding, Inde
implements IndexContract.IndexView , EasyPermissions.PermissionCallbacks{
private DeviceAdapter deviceAdapter;
private List<IndexDeviceEntity> devList = new ArrayList<>();
private List<WaterDeviceEntity> devList = new ArrayList<>();
private int mCurrentPage = 1;
// 扫码待配网设备
private WaterDeviceEntity pendingDeviceEntity = null;
// 蓝牙相关
private static final int REQUEST_EVENT_CODE_BLUETOOTH_PERMISSION = 10002;
private static final int REQUEST_ENABLE_BT = 10003;
private BluetoothAdapter bluetoothAdapter;
private boolean bluetoothReady = false;
@Override
protected void initArgs(Bundle bundle) {
}
@Override
protected void onResumeInitData() {
super.onResumeInitData();
if(AppConfig.USER_LOGIN){
showLoadingDialog();
mPresenter.getDevicesList(mCurrentPage);
}
}
@Override
public void initData(@Nullable Bundle savedInstanceState) {
IndexDeviceEntity entity = new IndexDeviceEntity("智能浇水设备A1","YA226641000101","工作中",100);
IndexDeviceEntity entity2 = new IndexDeviceEntity("智能浇水设备B1","YA22664100000","工作中",80);
IndexDeviceEntity entity3 = new IndexDeviceEntity("智能浇水设备C5","YA234534000101","工作中",70);
IndexDeviceEntity entity4 = new IndexDeviceEntity("智能浇水设备D6","YA2266678543101","工作中",50);
devList.add(entity);
devList.add(entity2);
devList.add(entity3);
devList.add(entity4);
// 设置自定义刷新头
RefreshHeader header = new CustomRefreshHeader(mContext);
viewBinding.refresh.setRefreshHeader(header);
viewBinding.refresh.setRefreshFooter(new ClassicsFooter(mContext));
viewBinding.refresh.setOnRefreshListener(layout -> {
mCurrentPage = 1;
mPresenter.getDevicesList(mCurrentPage);
});
viewBinding.refresh.setOnLoadMoreListener(layout -> {
mCurrentPage++;
mPresenter.getDevicesList(mCurrentPage);
});
}
@Override
@@ -65,6 +106,7 @@ public class IndexFragment extends BaseFragment<IndexFragmentLayoutBinding, Inde
private void initListener() {
setClick(viewBinding.imgScan);
setClick(viewBinding.imgAdd);
setClick(viewBinding.includedNoresult.btnRefresh);
}
private void setClick(View view) {
@@ -75,18 +117,35 @@ public class IndexFragment extends BaseFragment<IndexFragmentLayoutBinding, Inde
switch (v.getId()){
case R.id.img_scan:
//检测有无相机权限
String readPermission = Manifest.permission.CAMERA;
if(!hasRecordPermission(readPermission)){
getPermissionRequst(readPermission,"权限申请",
"智能浇水需要您的授权访问相机,并使用扫一扫",REQUEST_EVENT_CODE_10001);
if(checkBluetoothPermissions()){
//检测有无相机权限
String readPermission = Manifest.permission.CAMERA;
if(!hasRecordPermission(readPermission)){
getPermissionRequst(readPermission,
getString(R.string.permission_request_title),
getString(R.string.camera_permission_scan_desc),
REQUEST_EVENT_CODE_10001);
}else{
scanQrcode();
}
}else{
scanQrcode();
goTo(mContext, PermissionActivity.class,null);
}
break;
case R.id.img_add:
goTo(mContext, AddDeviceActivity.class,null);
if(checkBluetoothPermissions()){
goTo(mContext, AddDeviceActivity.class,null);
}else{
goTo(mContext, PermissionActivity.class,null);
}
break;
case R.id.btn_refresh:
mCurrentPage = 1;
mPresenter.getDevicesList(mCurrentPage);
break;
}
}
@@ -99,15 +158,48 @@ public class IndexFragment extends BaseFragment<IndexFragmentLayoutBinding, Inde
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());
if (results == null || results.isEmpty()) {
ToastUtil.showShort(mContext, getString(R.string.qr_content_empty));
return;
}
String scanContent = results.get(0);
if (TextUtils.isEmpty(scanContent)) {
ToastUtil.showShort(mContext, getString(R.string.qr_content_empty));
return;
}
JSONObject json;
try {
json = new JSONObject(scanContent);
} catch (JSONException e) {
ToastUtil.showShort(mContext, getString(R.string.qr_format_error));
return;
}
try {
WaterDeviceEntity entity = new WaterDeviceEntity();
if(TextUtils.isEmpty(json.getString("mac"))
|| TextUtils.isEmpty(json.getString("name"))){
ToastUtil.showShort(mContext,getString(R.string.scan_correct_qr));
return;
}
entity.setMacAddress(json.getString("mac"));
entity.setDeviceName(json.getString("name"));
if(entity != null){
//这里拿到Mac地址需要去服务器查询当前Mac的绑定状态
mPresenter.bindDeviceStatus(entity);
}
} catch (JSONException e) {
ToastUtil.showShort(mContext, getString(R.string.scan_correct_qr));
return;
}
break;
case MNScanManager.RESULT_FAIL:
String resultError = data.getStringExtra(MNScanManager.INTENT_KEY_RESULT_ERROR);
break;
case MNScanManager.RESULT_CANCLE:
ToastUtil.showShort(mContext,"取消扫码");
ToastUtil.showShort(mContext,getString(R.string.cancel_scan));
break;
}
}
@@ -118,10 +210,185 @@ public class IndexFragment extends BaseFragment<IndexFragmentLayoutBinding, Inde
deviceAdapter = new DeviceAdapter(mContext,R.layout.index_device_list_item,devList);
viewBinding.rvDevices.setAdapter(deviceAdapter);
deviceAdapter.setOnItemClickListener((adapter, view, position) -> {
goTo(mContext, DeviceControlActivity.class,null);
Bundle bundle = new Bundle();
bundle.putSerializable("device_info",devList.get(position));
goTo(mContext, DeviceControlActivity.class,bundle);
});
}
/**
* 获取设备列表成功
*/
@Override
public void getDeviceListSuccess(List<WaterDeviceEntity> list, int total) {
hideDialogLoading();
List<WaterDeviceEntity> pageList = list == null ? new ArrayList<>() : list;
if (mCurrentPage == 1) {
viewBinding.refresh.finishRefresh(300);
devList.clear(); // 只在刷新时清除
devList = pageList;
} else {
viewBinding.refresh.finishLoadMore(300);
devList.addAll(pageList);
}
deviceAdapter.setNewData(devList);
// 4. 判断是否还有更多数据
if (deviceAdapter.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 (deviceAdapter.getItemCount() != 0) {
viewBinding.refresh.setVisibility(View.VISIBLE);
viewBinding.relNoresult.setVisibility(View.GONE);
} else {
viewBinding.refresh.setVisibility(View.GONE);
viewBinding.relNoresult.setVisibility(View.VISIBLE);
}
}
/**
* 查询当前设备绑定状态
* @param entity
*/
@Override
public void queryDeviceBindStatusSuccess(WaterDeviceEntity entity) {
pendingDeviceEntity = entity.getBindDevice();
switch (entity.getBindDeviceStatus()){
case 200:
//该设备目前已经绑定了当前用户,直接跳转到设备控制页面
Bundle bundle = new Bundle();
bundle.putSerializable("device_info",entity.getBindDevice());
goTo(mContext, DeviceControlActivity.class,bundle);
break;
case 302:
case 305:
// 这是一台没有在mqtt中注册的设备需要通过蓝牙连接配网,(可理解为新设备,走蓝牙,配网)
// 先检查蓝牙权限和开启状态
checkBluetoothAndProceed();
break;
case 303:
//目前该设备,不属于该用户,需要提示解绑
showDialogtext(null,
false,
false,
getString(R.string.tip_text),
getString(R.string.device_binding_conflict_message),
"",
"",
new OnDialogClick() {
@Override
public void onCancelClick() {
}
@Override
public void onConfirmClick() {
}
});
break;
case 304:
//该设备目前在线已经在mqtt注册但是没有绑定用户需要跳转到绑定页面
Bundle bundleNoBind = new Bundle();
bundleNoBind.putSerializable("ble_device_entity", entity.getBindDevice());
goTo(mContext, DeviceReNameActivity.class, bundleNoBind);
break;
}
}
/**
* 检查蓝牙权限和开启状态,然后跳转配网
*/
private void checkBluetoothAndProceed() {
// 1. 检查蓝牙权限
if (!checkBluetoothPermissions()) {
return;
}
// 2. 检查蓝牙是否开启
if (!initBluetooth()) {
return;
}
// 3. 权限和状态都正常,跳转配网页面
goToDeviceProvisioning();
}
/**
* 检查蓝牙权限
*/
private boolean checkBluetoothPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
String[] permissions = {
Manifest.permission.BLUETOOTH_SCAN,
Manifest.permission.BLUETOOTH_CONNECT,
Manifest.permission.ACCESS_FINE_LOCATION
};
if (!hasRecordPermission(permissions)) {
return false;
}
} else {
// Android 12 以下需要位置权限
if (ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION)
!= PackageManager.PERMISSION_GRANTED) {
return false;
}
}
return true;
}
/**
* 初始化蓝牙适配器并检查蓝牙是否开启
* @return true 表示蓝牙已开启false 表示未开启或不支持
*/
private boolean initBluetooth() {
BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService(Context.BLUETOOTH_SERVICE);
if (bluetoothManager != null) {
bluetoothAdapter = bluetoothManager.getAdapter();
}
if (bluetoothAdapter == null) {
ToastUtil.showShort(mContext, "设备不支持蓝牙");
return false;
}
if (!bluetoothAdapter.isEnabled()) {
// 蓝牙未开启,请求开启
Intent enableBtIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
startActivityForResult(enableBtIntent, REQUEST_ENABLE_BT);
return false;
}
bluetoothReady = true;
return true;
}
/**
* 跳转设备配网页面
*/
private void goToDeviceProvisioning() {
if (pendingDeviceEntity == null) {
ToastUtil.showShort(mContext, "设备信息异常");
return;
}
Bundle bundle = new Bundle();
bundle.putInt(ConstantUtil.ACTIVITY_NAVIGATE_FLAG, 100); // 100 = 来自扫码添加
bundle.putSerializable("ble_device_entity", pendingDeviceEntity);
goTo(mContext, SelectWifiActivity.class, bundle);
pendingDeviceEntity = null;
}
@Override
public void onDestroyView() {
super.onDestroyView();
@@ -133,19 +400,80 @@ public class IndexFragment extends BaseFragment<IndexFragmentLayoutBinding, Inde
@Override
public void showError(String message) {
hideDialogLoading();
ToastUtil.showShort(mContext,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();
// } else if (requestCode == REQUEST_EVENT_CODE_BLUETOOTH_PERMISSION) {
// // 蓝牙权限获取成功,检查蓝牙状态
// if (initBluetooth()) {
// goToDeviceProvisioning();
// }
// }
}
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
hidePermissionDialog();
if (requestCode == REQUEST_EVENT_CODE_BLUETOOTH_PERMISSION) {
ToastUtil.showShort(mContext, getString(R.string.bluetooth_permission_required));
}
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
hidePermissionDialog();
if (requestCode == REQUEST_EVENT_CODE_10001) {
// 相机权限回调
scanQrcode();
} else if (requestCode == REQUEST_EVENT_CODE_BLUETOOTH_PERMISSION) {
// 蓝牙权限回调
boolean allGranted = true;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allGranted = false;
break;
}
}
if (allGranted) {
// 权限获取成功,检查蓝牙状态
if (initBluetooth()) {
goToDeviceProvisioning();
}
} else {
ToastUtil.showShort(mContext, getString(R.string.bluetooth_permission_required));
}
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_ENABLE_BT) {
if (resultCode == -1) { // RESULT_OK
ToastUtil.showShort(mContext, getString(R.string.bluetooth_enabled));
bluetoothReady = true;
if (pendingDeviceEntity != null) {
goToDeviceProvisioning();
}
} else {
ToastUtil.showShort(mContext, getString(R.string.enable_bluetooth_to_add_device));
}
}
}
}

View File

@@ -1,36 +1,64 @@
package com.ckkj.water.index.adapter;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
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.IndexDeviceEntity;
import com.ckkj.water.index.entity.WaterDeviceEntity;
import com.ckkj.water.utils.manager.PowerIconManage;
import com.esafirm.imagepicker.model.Image;
import java.util.List;
public class DeviceAdapter extends BaseQuickAdapter<IndexDeviceEntity, BaseViewHolder> {
public class DeviceAdapter extends BaseQuickAdapter<WaterDeviceEntity, BaseViewHolder> {
public DeviceAdapter(Context context, int layoutResId, @Nullable List<IndexDeviceEntity> data) {
public DeviceAdapter(Context context, int layoutResId, @Nullable List<WaterDeviceEntity> data) {
super(layoutResId, data);
this.mContext = context;
this.mData = data;
}
@Override
protected void convert(BaseViewHolder holder, IndexDeviceEntity item) {
holder.setText(R.id.tv_device_name,item.getDeviceNames());
holder.setText(R.id.tv_device_serial,item.getDeviceSerials());
holder.setText(R.id.tv_device_status,item.getDeviceStatuses());
holder.setText(R.id.tv_battery_level,item.getBatteryLevels() + "");
protected void convert(BaseViewHolder holder, WaterDeviceEntity item) {
ImageView imgPower = holder.getView(R.id.img_battery);
View viewStatus = holder.getView(R.id.view_status_dot);
holder.setText(R.id.tv_device_name,item.getDeviceName());
holder.setText(R.id.tv_device_serial,item.getDeviceSn());
//设备联网在线
if(item.getStatus().equals("1")){
switch (item.getWorkStatus()){
case "0"://待机
holder.setText(R.id.tv_device_status,mContext.getString(R.string.device_status_not_working));
holder.setTextColor(R.id.tv_device_status,mContext.getResources().getColor(R.color.color_999999));
viewStatus.setBackground(mContext.getResources().getDrawable(R.drawable.shape_status_dot_999999));
break;
case "1"://工作中
holder.setText(R.id.tv_device_status,mContext.getString(R.string.device_status_working));
holder.setTextColor(R.id.tv_device_status,mContext.getResources().getColor(R.color.color_24B326));
viewStatus.setBackground(mContext.getResources().getDrawable(R.drawable.shape_status_dot_green));
break;
}
}else{
holder.setText(R.id.tv_device_status,mContext.getString(R.string.device_status_offline));
holder.setTextColor(R.id.tv_device_status,mContext.getResources().getColor(R.color.color_E60000));
viewStatus.setBackground(mContext.getResources().getDrawable(R.drawable.shape_status_dot_red));
}
// if(item.getStatus().equals("1") || item.getStatus().equals("2")){
// }else{
// holder.setText(R.id.tv_device_status,mContext.getString(R.string.device_status_offline));
// holder.setTextColor(R.id.tv_device_status,mContext.getResources().getColor(R.color.color_999999));
// viewStatus.setBackground(mContext.getResources().getDrawable(R.drawable.shape_status_dot_999999));
// }
holder.setText(R.id.tv_battery_level,item.getPowerLevel());
imgPower.setImageResource(PowerIconManage.getInstance().getPowerIcon(Integer.parseInt(item.getPowerLevel())));
}

View File

@@ -1,49 +0,0 @@
package com.ckkj.water.index.entity;
import java.io.Serializable;
public class IndexDeviceEntity implements Serializable {
private String deviceNames;
private String deviceSerials;
private String deviceStatuses;
private int batteryLevels;
public IndexDeviceEntity(String deviceNames, String deviceSerials, String deviceStatuses, int batteryLevels) {
this.deviceNames = deviceNames;
this.deviceSerials = deviceSerials;
this.deviceStatuses = deviceStatuses;
this.batteryLevels = batteryLevels;
}
public String getDeviceNames() {
return deviceNames;
}
public void setDeviceNames(String deviceNames) {
this.deviceNames = deviceNames;
}
public String getDeviceSerials() {
return deviceSerials;
}
public void setDeviceSerials(String deviceSerials) {
this.deviceSerials = deviceSerials;
}
public String getDeviceStatuses() {
return deviceStatuses;
}
public void setDeviceStatuses(String deviceStatuses) {
this.deviceStatuses = deviceStatuses;
}
public int getBatteryLevels() {
return batteryLevels;
}
public void setBatteryLevels(int batteryLevels) {
this.batteryLevels = batteryLevels;
}
}

View File

@@ -0,0 +1,84 @@
package com.ckkj.water.index.entity;
import java.io.Serializable;
import java.util.List;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
public class WaterDeviceEntity implements Serializable {
/**
* id : 2056638783822123010
* deviceNo : 1779175872972
* userId : 2056543507514097665
* deviceName : 50号设备
* deviceImg : null
* status : 0
* powerLevel : null
* powerLevelUpdatatime : null
* wifiName : 橙空科技WiFi
* wifiPassword : 12321212
* deviceEm : 213123123123
* deviceSn : sdfsd32423gdfgdf3
* fwVer : fwVer-56565644
* macAddress : 2E:8H:00:KK:N7
* expirationTime : null
*/
private String id;
private String deviceNo;
private String userId;
private String deviceName;
private Object deviceImg;
private String status;
private String workStatus;
private String powerLevel;
private String powerLevelUpdatatime;
private String wifiName;
private String wifiPassword;
private String deviceEm;
private String deviceSn;
private String fwVer;
private String macAddress;
private Object expirationTime;
private List<DeviceListBean> deviceList;
private int bindDeviceStatus;
private String bindDeviceStatusName;
private WaterDeviceEntity bindDevice;
private String startTime;
private String endTime;
@Data
@NoArgsConstructor
public static class DeviceListBean implements Serializable{
private String id;
private String deviceNo;
private String userId;
private String deviceName;
private Object deviceImg;
private String status;
private String workStatus;
private String powerLevel;
private Object powerLevelUpdatatime;
private String wifiName;
private String wifiPassword;
private String deviceEm;
private String deviceSn;
private String fwVer;
private String macAddress;
private Object expirationTime;
private int createDept;
private int createBy;
private String createTime;
private int updateBy;
private String updateTime;
private Object bindTokenHash;
private Object deviceImgX;
private String powerLevelUpdatatimeX;
private Object expirationTimeX;
private String checkStatus = "0";
}
}

View File

@@ -1,11 +1,13 @@
package com.ckkj.water.index.mvp;
import com.ckkj.water.base.IView;
import com.ckkj.water.login.entity.LoginEntity;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.index.entity.WaterDeviceEntity;
import org.json.JSONObject;
import java.util.List;
import java.util.Map;
/**
* @author fengxiaoyang
* @date 2025/7/26
@@ -14,12 +16,21 @@ import org.json.JSONObject;
public class IndexContract {
public interface CallBack<T> {
void success(T msg);
void success(T msg,int total);
void fail(String msg);
}
public interface DataModel{
//获取设备列表
void getDeviceList(Map<String,String> map,CallBack callBack);
//查询二维码当前设备绑定状态
void queryDeviceBindStatus(JSONObject json, CallBack callBack);
}
public interface IndexView extends IView{
void getDeviceListSuccess(List<WaterDeviceEntity> list, int total);
//查询二维码当前设备绑定状态
void queryDeviceBindStatusSuccess(WaterDeviceEntity entity);
}
}

View File

@@ -1,5 +1,94 @@
package com.ckkj.water.index.mvp;
import com.ckkj.water.index.entity.WaterDeviceEntity;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.network.NetWorkManager;
import org.json.JSONObject;
import java.util.List;
import java.util.Map;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
public class IndexImpl implements IndexContract.DataModel{
/**
* 设备列表
* @param map
* @param callBack
*/
@Override
public void getDeviceList(Map<String, String> map, IndexContract.CallBack callBack) {
NetWorkManager.getRequest().getIndexDeviceList(NetWorkManager.getToken())
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean<List<WaterDeviceEntity>>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean<List<WaterDeviceEntity>> bean) {
if (bean.getCode() == 200) {
callBack.success(bean.getRows(),bean.getTotal());
}
}
@Override
public void onError(@NonNull Throwable e) {
callBack.fail(e.getMessage());
}
@Override
public void onComplete() {
}
});
}
/**
* 查询当前设备绑定状态
* @param json
* @param callBack
*/
@Override
public void queryDeviceBindStatus(JSONObject json, IndexContract.CallBack callBack) {
try {
NetWorkManager.getRequest().bindDeviceStatus(NetWorkManager.getToken(),NetWorkManager.toRequestBody(json))
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Observer<BaseBean<WaterDeviceEntity>>() {
@Override
public void onSubscribe(@NonNull Disposable d) {
}
@Override
public void onNext(@NonNull BaseBean<WaterDeviceEntity> bean) {
if (bean.getCode() == 200) {
callBack.success(bean.getData(),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

@@ -1,9 +1,15 @@
package com.ckkj.water.index.mvp;
import com.ckkj.water.base.BasePresenter;
import com.ckkj.water.login.mvp.LoginImpl;
import com.ckkj.water.index.entity.WaterDeviceEntity;
import com.ckkj.water.network.BaseBean;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.HashMap;
import java.util.List;
public class IndexPresenter extends BasePresenter<IndexContract.IndexView>{
private IndexImpl mImpl;
@@ -12,4 +18,57 @@ public class IndexPresenter extends BasePresenter<IndexContract.IndexView>{
this.mImpl = mImpl;
}
/**
* 查询设备列表
*/
public void getDevicesList(int mCurrentPage) {
mImpl.getDeviceList(new HashMap<>(), new IndexContract.CallBack<List<WaterDeviceEntity>>() {
@Override
public void success(List<WaterDeviceEntity> list, int total) {
if(IndexPresenter.this.getView() != null){
IndexPresenter.this.getView().getDeviceListSuccess(list,total);
}
}
@Override
public void fail(String msg) {
if(IndexPresenter.this.getView() != null){
IndexPresenter.this.getView().showError(msg);
}
}
});
}
/**
* 绑定设备状态
* @param entity
*/
public void bindDeviceStatus(WaterDeviceEntity entity) {
try {
JSONObject json = new JSONObject();
json.put("macAddress",
entity.getMacAddress() == null ? "" : entity.getMacAddress().toLowerCase());
mImpl.queryDeviceBindStatus(json, new IndexContract.CallBack<WaterDeviceEntity>() {
@Override
public void success(WaterDeviceEntity bean, int total) {
bean.setMacAddress(entity.getMacAddress());
bean.setDeviceName(entity.getDeviceName());
if(IndexPresenter.this.getView() != null){
IndexPresenter.this.getView().queryDeviceBindStatusSuccess(bean);
}
}
@Override
public void fail(String msg) {
if(IndexPresenter.this.getView() != null){
IndexPresenter.this.getView().showError(msg);
}
}
});
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -2,8 +2,12 @@ package com.ckkj.water.login;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.text.InputType;
import android.text.TextUtils;
import android.text.method.PasswordTransformationMethod;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity;
@@ -13,6 +17,7 @@ import com.ckkj.water.login.mvp.LoginContract;
import com.ckkj.water.login.mvp.LoginImpl;
import com.ckkj.water.login.mvp.LoginPresenter;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.utils.manager.ThemeManager;
import com.ckkj.water.utils.number.PhoneNumberUtils;
import com.ckkj.water.utils.show.ToastUtil;
import com.gyf.immersionbar.ImmersionBar;
@@ -22,6 +27,10 @@ import com.gyf.immersionbar.ImmersionBar;
*/
public class ForgetPasswordActivity extends BaseActivity<RegisterLayoutBinding, LoginPresenter>
implements LoginContract.LoginView {
private boolean isPasswordVisible = false;
private boolean isConfirmPasswordVisible = false;
@Override
public LoginPresenter createPresenter() {
return new LoginPresenter(new LoginImpl());
@@ -36,8 +45,8 @@ public class ForgetPasswordActivity extends BaseActivity<RegisterLayoutBinding,
public void initData(Bundle savedInstanceState) {
super.initData(savedInstanceState);
ImmersionBar.with(this)
.statusBarColor(R.color.color_F9F9F9) // 设置状态栏颜色
.statusBarDarkFont(true) // 设置状态栏字体为黑色
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext)) // 白色状态栏图标
.navigationBarColor(R.color.color_gray_theme)
.titleBar(viewBinding.rlTitle)
.init();
viewBinding.includedTitle.tvTitle.setText(R.string.reset_psd_title);
@@ -49,6 +58,8 @@ public class ForgetPasswordActivity extends BaseActivity<RegisterLayoutBinding,
setClick(viewBinding.includedTitle.imgBack);
setClick(viewBinding.tvGetVerifiCode);
setClick(viewBinding.tvCommit);
setClick(viewBinding.imgPsdShowHide);
setClick(viewBinding.imgPsdShowHideAgin);
}
private void setClick(View view) {
@@ -71,17 +82,78 @@ public class ForgetPasswordActivity extends BaseActivity<RegisterLayoutBinding,
ToastUtil.showShort(mContext, getString(R.string.login_error_phone_invalid));
return;
}
cdTimer.start();
mPresenter.getPhoneCode(viewBinding.etAccount.getText().toString(),"");
// cdTimer.start();
break;
case R.id.tv_commit:
goTo(mContext, RegisterSuccessActivity.class,null);
if (TextUtils.isEmpty(viewBinding.etAccount.getText().toString())) {
ToastUtil.showShort(mContext, getString(R.string.login_error_phone_empty));
return;
}
if (!PhoneNumberUtils.checkPhoneNum(viewBinding.etAccount.getText().toString().trim()) &&
!PhoneNumberUtils.checkEmail(viewBinding.etAccount.getText().toString().trim())) {
ToastUtil.showShort(mContext, getString(R.string.login_error_phone_invalid));
return;
}
if (TextUtils.isEmpty(viewBinding.etVerfiCode.getText().toString())) {
ToastUtil.showShort(mContext, getString(R.string.login_error_code_empty));
return;
}
if (TextUtils.isEmpty(viewBinding.etInputPsd.getText().toString())) {
ToastUtil.showShort(mContext, getString(R.string.login_error_psd_empty));
return;
}
if (TextUtils.isEmpty(viewBinding.etInputPsdAgin.getText().toString())) {
ToastUtil.showShort(mContext, getString(R.string.login_error_psd_agin_empty));
return;
}
if(!(viewBinding.etInputPsd.getText().toString()).equals(
viewBinding.etInputPsdAgin.getText().toString())){
ToastUtil.showShort(mContext, getString(R.string.hint_input_psd_not_match));
return;
}
showLoadingDialog();
mPresenter.updatePsdByVerifyCode(viewBinding.etAccount.getText().toString(),
viewBinding.etVerfiCode.getText().toString(),
viewBinding.etInputPsd.getText().toString());
break;
case R.id.img_psd_show_hide:
togglePasswordVisibility(viewBinding.etInputPsd, viewBinding.imgPsdShowHide, isPasswordVisible);
isPasswordVisible = !isPasswordVisible;
break;
case R.id.img_psd_show_hide_agin:
togglePasswordVisibility(viewBinding.etInputPsdAgin, viewBinding.imgPsdShowHideAgin, isConfirmPasswordVisible);
isConfirmPasswordVisible = !isConfirmPasswordVisible;
break;
}
}
private void togglePasswordVisibility(EditText editText, ImageView imageView, boolean isVisible) {
if (editText == null || imageView == null) {
return;
}
private CountDownTimer cdTimer = new CountDownTimer(6000, 1000) {
if (isVisible) {
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
editText.setTransformationMethod(PasswordTransformationMethod.getInstance());
imageView.setImageResource(R.mipmap.icon_psd_hide);
} else {
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
editText.setTransformationMethod(null);
imageView.setImageResource(R.mipmap.icon_psd_show);
}
editText.setSelection(editText.getText().length());
}
private CountDownTimer cdTimer = new CountDownTimer(60000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
viewBinding.tvGetVerifiCode.setText("(" + millisUntilFinished / 1000 + ") " + getString(R.string.login_resend));
@@ -101,10 +173,27 @@ public class ForgetPasswordActivity extends BaseActivity<RegisterLayoutBinding,
}
};
/**
* 修改密码成功
* @param bean
*/
@Override
public void updateUserPasswordSuccess(BaseBean bean) {
hideDialogLoading();
ToastUtil.showShort(mContext,getString(R.string.hint_update_psd_success));
finish();
}
@Override
public void registerSuccess(BaseBean bean) {
}
@Override
public void getPhoneCodeSuccess(BaseBean bean) {
hideDialogLoading();
ToastUtil.showLong(mContext, getString(R.string.login_code_sent));
cdTimer.start();
}
@Override
@@ -112,6 +201,7 @@ public class ForgetPasswordActivity extends BaseActivity<RegisterLayoutBinding,
}
@Override
public void showError(String message) {
ToastUtil.showShort(mContext,message);

View File

@@ -2,26 +2,32 @@ package com.ckkj.water.login;
import static android.view.View.GONE;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import android.graphics.Color;
import android.graphics.Typeface;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.text.InputType;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.method.LinkMovementMethod;
import android.text.method.PasswordTransformationMethod;
import android.text.style.ClickableSpan;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.core.content.ContextCompat;
import com.ckkj.water.custom.view.PhotoViewActivity;
import com.ckkj.water.utils.DownFileUtil;
import com.ckkj.water.mine.WebviewUserAgreement;
import com.ckkj.water.utils.manager.LanguageManager;
import com.google.gson.Gson;
import com.gyf.immersionbar.ImmersionBar;
import com.ckkj.water.AppConfig;
@@ -34,11 +40,10 @@ import com.ckkj.water.login.mvp.LoginContract;
import com.ckkj.water.login.mvp.LoginImpl;
import com.ckkj.water.login.mvp.LoginPresenter;
import com.ckkj.water.main.activity.MainActivity;
import com.ckkj.water.mine.WebviewUserAgreement;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.utils.click.ClickUtil;
import com.ckkj.water.utils.dataUtil.SharePreUtil;
import com.ckkj.water.utils.manager.MingDengUserInfoManager;
import com.ckkj.water.utils.manager.WaterUserInfoManager;
import com.ckkj.water.utils.number.PhoneNumberUtils;
import com.ckkj.water.utils.show.ToastUtil;
import com.lxj.xpopup.XPopup;
@@ -49,7 +54,8 @@ public class LoginActivity extends BaseActivity<LoginLayoutBinding, LoginPresent
implements LoginContract.LoginView {
private boolean mYinsiConfirm = false;
//当前登录方式 验证码100 密码 200
private boolean isPasswordVisible = false;
//当前登录方式 验证码100 密码 200..
private static int LOGIN_TYPE_STATUS_CODE = 100;
@Override
@@ -79,6 +85,10 @@ public class LoginActivity extends BaseActivity<LoginLayoutBinding, LoginPresent
setClick(viewBinding.tvGoRegister);
setClick(viewBinding.tvLanguage);
setClick(viewBinding.tvResetPsd);
setClick(viewBinding.checkboxRead);
setClick(viewBinding.imgPsdShowHide);
setClick(viewBinding.btn133);
setClick(viewBinding.btn151);
}
private void setClick(View view) {
@@ -86,59 +96,119 @@ public class LoginActivity extends BaseActivity<LoginLayoutBinding, LoginPresent
}
private void setupAgreementText() {
String prefix = getString(R.string.login_agreement_prefix);
String privacy = getString(R.string.login_privacy_policy);
String and = getString(R.string.login_and);
String userAgreement = getString(R.string.login_user_agreement);
String content = prefix + privacy + and + userAgreement;
ClickUtil.addClickPart(viewBinding.checkboxRead, 100);
TextView tvAgreement = findViewById(R.id.tv_agreement_full);
String content = getString(R.string.agree_user_policy);
String userAgreementText = getString(R.string.user_agreement);
String privacyPolicyText = getString(R.string.privacy_policy);
SpannableString spannableString = new SpannableString(content);
int privacyStart = prefix.length();
int privacyEnd = privacyStart + privacy.length();
ClickableSpan privacyPolicySpan = new ClickableSpan() {
addClickableSpan(
spannableString,
content,
userAgreementText,
() -> {
// 跳转用户协议
Bundle bundle = new Bundle();
bundle.putInt("from_requse_code", WebviewUserAgreement.AGREEMENT_CODE);
goToWihoutNoPresent(mContext, WebviewUserAgreement.class,bundle);
}
);
addClickableSpan(
spannableString,
content,
privacyPolicyText,
() -> {
// 跳转隐私政策
Bundle bundleYS = new Bundle();
bundleYS.putInt("from_requse_code",WebviewUserAgreement.PRIVACY_CODE);
goToWihoutNoPresent(mContext, WebviewUserAgreement.class,bundleYS);
}
);
tvAgreement.setText(spannableString);
tvAgreement.setMovementMethod(LinkMovementMethod.getInstance());
tvAgreement.setHighlightColor(Color.TRANSPARENT);
}
private void addClickableSpan(
SpannableString spannableString,
String content,
String target,
Runnable action) {
int start = content.indexOf(target);
if (start < 0) {
return;
}
int end = start + target.length();
ClickableSpan clickableSpan = new ClickableSpan() {
@Override
public void onClick(@NonNull View widget) {
Bundle bundle = new Bundle();
bundle.putInt("from_requse_code", WebviewUserAgreement.PRIVACY_CODE);
goToWihoutNoPresent(mContext, WebviewUserAgreement.class, bundle);
action.run();
}
@Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
ds.setColor(ContextCompat.getColor(mContext, R.color.color_zhu));
ds.setColor(
ContextCompat.getColor(
mContext,
R.color.color_zhu
)
);
ds.setUnderlineText(false);
}
};
spannableString.setSpan(privacyPolicySpan, privacyStart, privacyEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
int userStart = privacyEnd + and.length();
int userEnd = userStart + userAgreement.length();
ClickableSpan userAgreementSpan = new ClickableSpan() {
@Override
public void onClick(@NonNull View widget) {
Bundle bundle = new Bundle();
bundle.putInt("from_requse_code", WebviewUserAgreement.AGREEMENT_CODE);
goToWihoutNoPresent(mContext, WebviewUserAgreement.class, bundle);
}
@Override
public void updateDrawState(@NonNull TextPaint ds) {
super.updateDrawState(ds);
ds.setColor(ContextCompat.getColor(mContext, R.color.color_zhu));
ds.setUnderlineText(false);
}
};
spannableString.setSpan(userAgreementSpan, userStart, userEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
// viewBinding.tvAgreement.setText(spannableString);
// viewBinding.tvAgreement.setMovementMethod(LinkMovementMethod.getInstance());
spannableString.setSpan(
clickableSpan,
start,
end,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE
);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_133:
showLoadingDialog();
mPresenter.loginBySmsCode("password",
"13305376054",
"",
"000000");
break;
case R.id.btn_151:
showLoadingDialog();
mPresenter.loginBySmsCode("password",
"15192412330",
"",
"000000");
break;
case R.id.checkbox_read:
if(mYinsiConfirm){
mYinsiConfirm = false;
viewBinding.checkboxRead.setChecked(false);
}else{
mYinsiConfirm = true;
viewBinding.checkboxRead.setChecked(true);
}
break;
case R.id.tv_go_register:
goTo(mContext, RegisterActivity.class,null);
break;
@@ -151,7 +221,7 @@ public class LoginActivity extends BaseActivity<LoginLayoutBinding, LoginPresent
viewBinding.tvLoginByPsd.setTextColor(mContext.getResources().getColor(R.color.color_999999));
viewBinding.tvLoginByPsd.setTypeface(Typeface.DEFAULT);
viewBinding.viewLinePsd.setVisibility(GONE);
viewBinding.viewLinePsd.setVisibility(INVISIBLE);
viewBinding.linearVerfiCode.setVisibility(VISIBLE);
viewBinding.linearPsd.setVisibility(GONE);
@@ -166,7 +236,7 @@ public class LoginActivity extends BaseActivity<LoginLayoutBinding, LoginPresent
viewBinding.tvLoginByCode.setTextColor(mContext.getResources().getColor(R.color.color_999999));
viewBinding.tvLoginByCode.setTypeface(Typeface.DEFAULT);
viewBinding.viewLineCode.setVisibility(GONE);
viewBinding.viewLineCode.setVisibility(INVISIBLE);
viewBinding.linearVerfiCode.setVisibility(GONE);
viewBinding.linearPsd.setVisibility(VISIBLE);
@@ -183,71 +253,88 @@ public class LoginActivity extends BaseActivity<LoginLayoutBinding, LoginPresent
ToastUtil.showShort(mContext, getString(R.string.login_error_phone_invalid));
return;
}
cdTimer.start();
// showLoadingDialog();
// mPresenter.getPhoneCode(
// viewBinding.edPhoneInput.getText().toString().trim(),
// AppConfig.SMS_CODE_TYPE_LOGIN);
// cdTimer.start();
showLoadingDialog();
//发送短信
mPresenter.getPhoneCode(viewBinding.etAccount.getText().toString(),"");
break;
case R.id.tv_language:
new XPopup.Builder(mContext).asBottomList(
"选择语言",
new String[]{"中文", "English"},
new int[]{R.mipmap.ic_launcher, R.mipmap.ic_launcher},
0,
new OnSelectListener() {
@Override
public void onSelect(int position, String text) {
ToastUtil.showShort(mContext,text);
}
},
R.layout.select_language_layout,
R.layout.select_language_item
).show();
new XPopup.Builder(mContext)
.isDestroyOnDismiss(true)
.dismissOnBackPressed(true)
.dismissOnTouchOutside(true)
.asBottomList(
getString(R.string.change_language),
LanguageManager.getLanguageDisplayNames(mContext),
LanguageManager.getLanguageIcons(),
LanguageManager.getCurrentLanguageIndex(),
new OnSelectListener() {
@Override
public void onSelect(int position, String text) {
LanguageManager.switchLanguageByIndex(position, LoginActivity.this);
}
},
R.layout.select_language_layout,
R.layout.select_language_item
).show();
break;
// case R.id.checkbox_read:
// if (mYinsiConfirm) {
// mYinsiConfirm = false;
// viewBinding.checkboxRead.setChecked(false);
// } else {
// mYinsiConfirm = true;
// viewBinding.checkboxRead.setChecked(true);
// }
// break;
// case R.id.tv_login_confirm:
// if (TextUtils.isEmpty(viewBinding.edPhoneInput.getText().toString())) {
// ToastUtil.showShort(mContext, getString(R.string.login_error_phone_empty));
// return;
// }
// if (!PhoneNumberUtils.checkPhoneNum(viewBinding.edPhoneInput.getText().toString().trim())) {
// ToastUtil.showShort(mContext, getString(R.string.login_error_phone_invalid));
// return;
// }
// if (TextUtils.isEmpty(viewBinding.edCodeInput.getText().toString())) {
// ToastUtil.showShort(mContext, getString(R.string.login_error_code_empty));
// return;
// }
// if (viewBinding.edCodeInput.getText().toString().trim().length() < 6) {
// ToastUtil.showShort(mContext, getString(R.string.login_error_code_invalid));
// return;
// }
// if (!mYinsiConfirm) {
// ToastUtil.showShort(mContext, getString(R.string.login_error_agreement));
// return;
// }
// showLoadingDialog();
// mPresenter.loginBySmsCode(
// viewBinding.edPhoneInput.getText().toString().trim(),
// viewBinding.edCodeInput.getText().toString().trim());
case R.id.tv_reset_psd ://重置密码
goTo(mContext, ForgetPasswordActivity.class,null);
break;
case R.id.tv_login:
if (TextUtils.isEmpty(viewBinding.etAccount.getText().toString())) {
ToastUtil.showShort(mContext, getString(R.string.login_error_phone_empty));
return;
}
if (!PhoneNumberUtils.checkPhoneNum(viewBinding.etAccount.getText().toString().trim()) &&
!PhoneNumberUtils.checkEmail(viewBinding.etAccount.getText().toString().trim())) {
ToastUtil.showShort(mContext, getString(R.string.login_error_phone_invalid));
return;
}
String loginType = "sms";
if(LOGIN_TYPE_STATUS_CODE == 100){//验证码登录
loginType = "sms";
if (TextUtils.isEmpty(viewBinding.edCodeInput.getText().toString())) {
ToastUtil.showShort(mContext, getString(R.string.login_error_code_empty));
return;
}
if (viewBinding.edCodeInput.getText().toString().trim().length() < 6) {
ToastUtil.showShort(mContext, getString(R.string.login_error_code_invalid));
return;
}
}else{//密码登录
loginType = "password";
if (TextUtils.isEmpty(viewBinding.etInputPsd.getText().toString())) {
ToastUtil.showShort(mContext, getString(R.string.login_error_psd_empty));
return;
}
}
if (!mYinsiConfirm) {
ToastUtil.showShort(mContext, getString(R.string.login_error_agreement));
return;
}
showLoadingDialog();
mPresenter.loginBySmsCode(loginType,
viewBinding.etAccount.getText().toString().trim(),
viewBinding.edCodeInput.getText().toString().trim(),
viewBinding.etInputPsd.getText().toString());
// new Handler().postDelayed(() -> hideDialogLoading(), 1000);
// break;
break;
case R.id.img_psd_show_hide:
togglePasswordVisibility();
break;
}
}
private CountDownTimer cdTimer = new CountDownTimer(6000, 1000) {
private CountDownTimer cdTimer = new CountDownTimer(60000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
viewBinding.tvGetVerifiCode.setText("(" + millisUntilFinished / 1000 + ") " + getString(R.string.login_resend));
@@ -267,10 +354,40 @@ public class LoginActivity extends BaseActivity<LoginLayoutBinding, LoginPresent
}
};
private void togglePasswordVisibility() {
EditText etPassword = viewBinding.etInputPsd;
ImageView ivToggle = viewBinding.imgPsdShowHide;
if (etPassword == null || ivToggle == null) {
return;
}
if (isPasswordVisible) {
// 隐藏密码
etPassword.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
etPassword.setTransformationMethod(PasswordTransformationMethod.getInstance());
ivToggle.setImageResource(R.mipmap.icon_psd_hide);
} else {
// 显示密码
etPassword.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
etPassword.setTransformationMethod(null);
ivToggle.setImageResource(R.mipmap.icon_psd_show);
}
// 保持光标位置
etPassword.setSelection(etPassword.getText().length());
isPasswordVisible = !isPasswordVisible;
}
@Override
public void registerSuccess(BaseBean bean) {
}
@Override
public void getPhoneCodeSuccess(BaseBean bean) {
hideDialogLoading();
ToastUtil.showShort(mContext, getString(R.string.login_code_sent));
ToastUtil.showLong(mContext, getString(R.string.login_code_sent));
cdTimer.start();
}
@@ -280,22 +397,23 @@ public class LoginActivity extends BaseActivity<LoginLayoutBinding, LoginPresent
ToastUtil.showShort(mContext, getString(R.string.login_success));
SharePreUtil.putBoolean(ConstantUtil.SHARE_PRE_NAME, mContext, ConstantUtil.USER_LOGIN, true);
SharePreUtil.putString(ConstantUtil.SHARE_PRE_NAME, mContext, ConstantUtil.USER_TOKEN, entity.getAccess_token());
SharePreUtil.putString(ConstantUtil.SHARE_PRE_NAME, mContext, ConstantUtil.USER_NICKNAME, entity.getUser_info().getNickname());
SharePreUtil.putString(ConstantUtil.SHARE_PRE_NAME, mContext, ConstantUtil.USER_ID, String.valueOf(entity.getUser_info().getUserId()));
MingDengUserInfoManager.getInstance().setEntity(entity);
MingDengUserInfoManager.getInstance().setUserInfo(entity.getUser_info());
Gson gson = new Gson();
String userInfoJson = gson.toJson(entity);
SharePreUtil.putString(ConstantUtil.SHARE_PRE_NAME, mContext, ConstantUtil.USER_INFO_JSON_CONTENT, userInfoJson);
WaterUserInfoManager.getInstance().setEntity(entity);
// Gson gson = new Gson();
// String userInfoJson = gson.toJson(entity);
// SharePreUtil.putString(ConstantUtil.SHARE_PRE_NAME, mContext, ConstantUtil.USER_INFO_JSON_CONTENT, userInfoJson);
AppConfig.USER_LOGIN = true;
AppConfig.USER_TOKEN = entity.getAccess_token();
AppConfig.USER_ID = String.valueOf(entity.getUser_info().getUserId());
new Handler().postDelayed(() -> {
goTo(mContext, MainActivity.class, null);
finish();
}, 200);
}
@Override
public void updateUserPasswordSuccess(BaseBean bean) {
}
@Override
public int getLayoutResId(Bundle savedInstanceState) {
return 0;

View File

@@ -2,8 +2,12 @@ package com.ckkj.water.login;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.text.InputType;
import android.text.TextUtils;
import android.text.method.PasswordTransformationMethod;
import android.view.View;
import android.widget.EditText;
import android.widget.ImageView;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity;
@@ -13,6 +17,8 @@ import com.ckkj.water.login.mvp.LoginContract;
import com.ckkj.water.login.mvp.LoginImpl;
import com.ckkj.water.login.mvp.LoginPresenter;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.utils.FastClickUtils;
import com.ckkj.water.utils.manager.ThemeManager;
import com.ckkj.water.utils.number.PhoneNumberUtils;
import com.ckkj.water.utils.show.ToastUtil;
import com.gyf.immersionbar.ImmersionBar;
@@ -21,6 +27,10 @@ import me.jessyan.autosize.internal.CustomAdapt;
public class RegisterActivity extends BaseActivity<RegisterLayoutBinding, LoginPresenter>
implements LoginContract.LoginView {
private boolean isPasswordVisible = false;
private boolean isConfirmPasswordVisible = false;
@Override
public LoginPresenter createPresenter() {
return new LoginPresenter(new LoginImpl());
@@ -35,8 +45,8 @@ public class RegisterActivity extends BaseActivity<RegisterLayoutBinding, LoginP
public void initData(Bundle savedInstanceState) {
super.initData(savedInstanceState);
ImmersionBar.with(this)
.statusBarColor(R.color.color_F9F9F9) // 设置状态栏颜色
.statusBarDarkFont(true) // 设置状态栏字体为黑色
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext)) // 白色状态栏图标
.navigationBarColor(R.color.color_gray_theme)
.titleBar(viewBinding.rlTitle)
.init();
viewBinding.includedTitle.tvTitle.setText(R.string.register_title);
@@ -47,6 +57,8 @@ public class RegisterActivity extends BaseActivity<RegisterLayoutBinding, LoginP
setClick(viewBinding.includedTitle.imgBack);
setClick(viewBinding.tvGetVerifiCode);
setClick(viewBinding.tvCommit);
setClick(viewBinding.imgPsdShowHide);
setClick(viewBinding.imgPsdShowHideAgin);
}
private void setClick(View view) {
@@ -60,6 +72,11 @@ public class RegisterActivity extends BaseActivity<RegisterLayoutBinding, LoginP
break;
case R.id.tv_get_verifi_code:
// 防止快速点击
if (!FastClickUtils.onclickTimes()) {
return;
}
if (TextUtils.isEmpty(viewBinding.etAccount.getText().toString())) {
ToastUtil.showShort(mContext, getString(R.string.login_error_phone_empty));
return;
@@ -69,17 +86,65 @@ public class RegisterActivity extends BaseActivity<RegisterLayoutBinding, LoginP
ToastUtil.showShort(mContext, getString(R.string.login_error_phone_invalid));
return;
}
cdTimer.start();
//发送短信
mPresenter.getPhoneCode(viewBinding.etAccount.getText().toString(),"");
break;
case R.id.tv_commit:
goTo(mContext, RegisterSuccessActivity.class,null);
// 防止快速点击
if (!FastClickUtils.onclickTimes()) {
return;
}
if (TextUtils.isEmpty(viewBinding.etAccount.getText().toString())) {
ToastUtil.showShort(mContext, getString(R.string.login_error_phone_empty));
return;
}
if (!PhoneNumberUtils.checkPhoneNum(viewBinding.etAccount.getText().toString().trim()) &&
!PhoneNumberUtils.checkEmail(viewBinding.etAccount.getText().toString().trim())) {
ToastUtil.showShort(mContext, getString(R.string.login_error_phone_invalid));
return;
}
if (TextUtils.isEmpty(viewBinding.etVerfiCode.getText().toString())) {
ToastUtil.showShort(mContext, getString(R.string.login_error_code_empty));
return;
}
if (TextUtils.isEmpty(viewBinding.etInputPsd.getText().toString())) {
ToastUtil.showShort(mContext, getString(R.string.login_error_psd_empty));
return;
}
if (TextUtils.isEmpty(viewBinding.etInputPsdAgin.getText().toString())) {
ToastUtil.showShort(mContext, getString(R.string.login_error_psd_agin_empty));
return;
}
if(!(viewBinding.etInputPsd.getText().toString()).equals(
viewBinding.etInputPsdAgin.getText().toString())){
ToastUtil.showShort(mContext, getString(R.string.hint_input_psd_not_match));
return;
}
showLoadingDialog();
mPresenter.registerAccount(viewBinding.etAccount.getText().toString(),
viewBinding.etVerfiCode.getText().toString(),
viewBinding.etInputPsd.getText().toString());
break;
case R.id.img_psd_show_hide:
togglePasswordVisibility(viewBinding.etInputPsd, viewBinding.imgPsdShowHide, isPasswordVisible);
isPasswordVisible = !isPasswordVisible;
break;
case R.id.img_psd_show_hide_agin:
togglePasswordVisibility(viewBinding.etInputPsdAgin, viewBinding.imgPsdShowHideAgin, isConfirmPasswordVisible);
isConfirmPasswordVisible = !isConfirmPasswordVisible;
break;
}
}
private CountDownTimer cdTimer = new CountDownTimer(6000, 1000) {
private CountDownTimer cdTimer = new CountDownTimer(60000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
viewBinding.tvGetVerifiCode.setText("(" + millisUntilFinished / 1000 + ") " + getString(R.string.login_resend));
@@ -100,9 +165,40 @@ public class RegisterActivity extends BaseActivity<RegisterLayoutBinding, LoginP
};
private void togglePasswordVisibility(EditText editText, ImageView imageView, boolean isVisible) {
if (editText == null || imageView == null) {
return;
}
if (isVisible) {
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
editText.setTransformationMethod(PasswordTransformationMethod.getInstance());
imageView.setImageResource(R.mipmap.icon_psd_hide);
} else {
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD);
editText.setTransformationMethod(null);
imageView.setImageResource(R.mipmap.icon_psd_show);
}
editText.setSelection(editText.getText().length());
}
@Override
public void registerSuccess(BaseBean bean) {
hideDialogLoading();
ToastUtil.showShort(mContext,getString(R.string.register_success_title));
finish();
}
/**
* 获取验证码成功
* @param bean
*/
@Override
public void getPhoneCodeSuccess(BaseBean bean) {
hideDialogLoading();
ToastUtil.showShort(mContext,getString(R.string.verification_code_sent));
cdTimer.start();
}
@Override
@@ -110,8 +206,18 @@ public class RegisterActivity extends BaseActivity<RegisterLayoutBinding, LoginP
}
/**
* 修改密码成功
* @param bean
*/
@Override
public void updateUserPasswordSuccess(BaseBean bean) {
hideDialogLoading();
}
@Override
public void showError(String message) {
hideDialogLoading();
ToastUtil.showShort(mContext,message);
}

View File

@@ -13,6 +13,7 @@ import com.ckkj.water.login.entity.LoginEntity;
import com.ckkj.water.login.mvp.LoginContract;
import com.ckkj.water.login.mvp.LoginImpl;
import com.ckkj.water.login.mvp.LoginPresenter;
import com.ckkj.water.main.activity.MainActivity;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.permission.PermissionActivity;
import com.ckkj.water.utils.show.ToastUtil;
@@ -45,7 +46,7 @@ public class RegisterSuccessActivity extends BaseActivity<RegisterSuccessLayoutB
viewBinding.tvCommit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
goTo(mContext, PermissionActivity.class,null);
goTo(mContext, MainActivity.class,null);
}
});
}
@@ -79,6 +80,11 @@ public class RegisterSuccessActivity extends BaseActivity<RegisterSuccessLayoutB
return 0;
}
@Override
public void registerSuccess(BaseBean bean) {
}
@Override
public void getPhoneCodeSuccess(BaseBean bean) {
@@ -89,6 +95,11 @@ public class RegisterSuccessActivity extends BaseActivity<RegisterSuccessLayoutB
}
@Override
public void updateUserPasswordSuccess(BaseBean bean) {
}
@Override
public void showError(String message) {

View File

@@ -1,41 +1,35 @@
package com.ckkj.water.login.entity;
import com.ckkj.water.base.entity.UserInfoEntity;
import java.io.Serializable;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @author fengxiaoyang
* @date 2025/7/28
* @description TODO: 描述该类的作用
*/
@NoArgsConstructor
@Data
public class LoginEntity implements Serializable {
/**
* scope : null
* openid : null
* access_token : eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsb2dpblR5cGUiOiJsb2dpbiIsImxvZ2luSWQiOiJhcHBfdXNlcjoyMDU2NTQzNTA3NTE0MDk3NjY1Iiwicm5TdHIiOiJFOVE4a1VzT2dJbjRCZWtGY0pTRXZGdkljNjhFOG1FQSIsImNsaWVudGlkIjoiNDI4YTgzMTBjZDQ0Mjc1N2FlNjk5ZGY1ZDg5NGYwNTEiLCJ0ZW5hbnRJZCI6IjAwMDAwMCIsInVzZXJJZCI6MjA1NjU0MzUwNzUxNDA5NzY2NX0.nh38mcgK2JOtuBR4ogQlcnQbMTiLJ_TXln_BGFhlUkU
* refresh_token : null
* expire_in : 60480000
* refresh_expire_in : null
* client_id : 428a8310cd442757ae699df5d894f051
*/
private Object scope;
private Object openid;
private String access_token;
private UserInfoEntity user_info;
private long expires_in;
public String getAccess_token() {
return access_token;
}
public void setAccess_token(String access_token) {
this.access_token = access_token;
}
public UserInfoEntity getUser_info() {
return user_info;
}
public void setUser_info(UserInfoEntity user_info) {
this.user_info = user_info;
}
public long getExpires_in() {
return expires_in;
}
public void setExpires_in(long expires_in) {
this.expires_in = expires_in;
}
private Object refresh_token;
private int expire_in;
private Object refresh_expire_in;
private String client_id;
}

View File

@@ -6,6 +6,8 @@ import com.ckkj.water.network.BaseBean;
import org.json.JSONObject;
import java.util.Map;
/**
* @author fengxiaoyang
* @date 2025/7/26
@@ -18,16 +20,29 @@ public class LoginContract {
void fail(String msg);
}
public interface DataModel{
//手机验证码登录
void register(JSONObject json, CallBack callBack);
//获取手机验证码
void getPhoneCode(JSONObject json, CallBack callBack);
void getPhoneCode(Map<String,Object> map, CallBack callBack);
//手机验证码登录
void loginBySmsCode(JSONObject json, CallBack callBack);
//通过账户+验证码 修改密码
void updatePsdByVerifyCode(JSONObject json, CallBack callBack);
//退出登录
void logout(JSONObject json, CallBack callBack);
//注销账号--发送验证码
void logoffAccountCode(Map<String,Object> map, CallBack callBack);
//注销账号--确认注销
void logoffAccount(JSONObject json, CallBack callBack);
}
public interface LoginView extends IView{
void registerSuccess(BaseBean bean);
//获取验证码
void getPhoneCodeSuccess(BaseBean bean);
//验证码登录成功
void loginBySmsCodeSuccess(LoginEntity entity);
//通过验证码修改密码成功
void updateUserPasswordSuccess(BaseBean bean);
}
}

View File

@@ -7,6 +7,8 @@ import com.ckkj.water.network.NetWorkManager;
import org.json.JSONObject;
import java.util.Map;
import io.reactivex.Observer;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
@@ -21,15 +23,57 @@ import io.reactivex.schedulers.Schedulers;
public class LoginImpl implements LoginContract.DataModel {
/**
* 获取验证码
*
* 注册
* @param json
* @param callBack
*/
@Override
public void getPhoneCode(JSONObject json, LoginContract.CallBack callBack) {
public void register(JSONObject json, LoginContract.CallBack callBack) {
try {
NetWorkManager.getRequest().sendPhoneCode(NetWorkManager.getToken(), NetWorkManager.toRequestBody(json))
NetWorkManager.getRequest().register(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.getData());
// 登录成功时调用
LoginStateManager.reset();
} 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();
}
}
/**
* 获取验证码
*
* @param map
* @param callBack
*/
@Override
public void getPhoneCode(Map<String,Object> map, LoginContract.CallBack callBack) {
try {
NetWorkManager.getRequest().sendPhoneCode(NetWorkManager.getToken(), map)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
@@ -61,6 +105,93 @@ public class LoginImpl implements LoginContract.DataModel {
}
}
/**
* 注销账号--发送验证码
* @param map
* @param callBack
*/
@Override
public void logoffAccountCode(Map<String, Object> map, LoginContract.CallBack callBack) {
try {
NetWorkManager.getRequest().logoffAccountCode(NetWorkManager.getToken())
.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);
} 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();
}
}
/**
* 注销账号--确认
* @param json
* @param callBack
*/
@Override
public void logoffAccount(JSONObject json, LoginContract.CallBack callBack) {
try {
NetWorkManager.getRequest().confirmLogiffAccount(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);
} 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();
}
}
/**
* 使用验证码登录
* @param json
* @param callBack
*/
@Override
public void loginBySmsCode(JSONObject json, LoginContract.CallBack callBack) {
try {
@@ -97,4 +228,86 @@ public class LoginImpl implements LoginContract.DataModel {
e.printStackTrace();
}
}
/**
* 通过验证码修改密码
* @param json
* @param callBack
*/
@Override
public void updatePsdByVerifyCode(JSONObject json, LoginContract.CallBack callBack) {
try {
NetWorkManager.getRequest().updatePasswordByVerifyCode(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);
} 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();
}
}
/**
* 退出登录
* @param json
* @param callBack
*/
@Override
public void logout(JSONObject json, LoginContract.CallBack callBack) {
try {
NetWorkManager.getRequest().logout(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.getData());
} 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

@@ -1,16 +1,11 @@
package com.ckkj.water.login.mvp;
import com.ckkj.water.AppConfig;
import com.ckkj.water.ConstantUtil;
import com.ckkj.water.EventMessage;
import com.ckkj.water.MyApplication;
import com.ckkj.water.base.BasePresenter;
import com.ckkj.water.base.entity.MineUserInfoBean;
import com.ckkj.water.login.entity.LoginEntity;
import com.ckkj.water.mine.mvp.MineImpl;
import com.ckkj.water.mine.mvp.MineContract;
import com.ckkj.water.mine.mvp.MinePresenter;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.utils.dataUtil.SharePreUtil;
import com.ckkj.water.utils.manager.MingDengUserInfoManager;
import org.json.JSONException;
import org.json.JSONObject;
@@ -18,8 +13,6 @@ import org.json.JSONObject;
import java.util.HashMap;
import java.util.Map;
import de.greenrobot.event.EventBus;
/**
* @author fengxiaoyang
* @date 2025/7/26
@@ -33,16 +26,26 @@ public class LoginPresenter extends BasePresenter<LoginContract.LoginView> {
this.mImpl = mImpl;
}
public void getPhoneCode(String phone, String codeType) {
/**
* 注册
* @param account
* @param code
* @param psd
*/
public void registerAccount(String account, String code, String psd) {
try {
JSONObject json = new JSONObject();
json.put("mobile",phone);
json.put("type",codeType);
mImpl.getPhoneCode(json, new LoginContract.CallBack<BaseBean>() {
json.put("clientId",AppConfig.APP_CLIENT_ID);
json.put("username",account);
json.put("code",code);
json.put("password",psd);
json.put("grantType",psd);
json.put("userType",AppConfig.APP_USER_TYPE);
mImpl.register(json, new LoginContract.CallBack<BaseBean>() {
@Override
public void success(BaseBean bean) {
if(LoginPresenter.this.getView() != null){
LoginPresenter.this.getView().getPhoneCodeSuccess(bean);
LoginPresenter.this.getView().registerSuccess(bean);
}
}
@@ -53,27 +56,57 @@ public class LoginPresenter extends BasePresenter<LoginContract.LoginView> {
}
}
});
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
public void getPhoneCode(String phone, String codeType) {
Map<String,Object> map = new HashMap<>();
map.put("username",phone);
mImpl.getPhoneCode(map, new LoginContract.CallBack<BaseBean>() {
@Override
public void success(BaseBean bean) {
if(LoginPresenter.this.getView() != null){
LoginPresenter.this.getView().getPhoneCodeSuccess(bean);
}
}
@Override
public void fail(String msg) {
if(LoginPresenter.this.getView() != null){
LoginPresenter.this.getView().showError(msg);
}
}
});
}
/**
* 通过手机验证码登录
* @param phone
* @param account
* @param code
*/
public void loginBySmsCode(String phone, String code) {
public void loginBySmsCode(String loginType,String account, String code,String psd) {
try {
JSONObject json = new JSONObject();
json.put("mobile",phone);
json.put("code",code);
json.put("grantType",loginType);
if(loginType.equals("sms")){
json.put("phonenumber",account);
json.put("smsCode",code);
}else{
json.put("username",account);
json.put("password",psd);
}
json.put("clientId",AppConfig.APP_CLIENT_ID);
json.put("userType",AppConfig.APP_USER_TYPE);
mImpl.loginBySmsCode(json, new LoginContract.CallBack<LoginEntity>() {
@Override
public void success(LoginEntity bean) {
if(LoginPresenter.this.getView() != null){
LoginPresenter.this.getView().loginBySmsCodeSuccess(bean);
EventBus.getDefault().post(new EventMessage(EventMessage.USER_LOGIN_SUCCESS));
// EventBus.getDefault().post(new EventMessage(EventMessage.USER_LOGIN_SUCCESS));
//获取个人信息
// getUserInfo();
}
@@ -113,4 +146,37 @@ public class LoginPresenter extends BasePresenter<LoginContract.LoginView> {
// }
// });
}
/**
* 通过验证码修改密码
* @param account
* @param code
* @param psd
*/
public void updatePsdByVerifyCode(String account, String code, String psd) {
try {
JSONObject json = new JSONObject();
json.put("userName", account);
json.put("code", code);
json.put("password", psd);
mImpl.updatePsdByVerifyCode(json, new LoginContract.CallBack<BaseBean>() {
@Override
public void success(BaseBean bean) {
if(LoginPresenter.this.getView() != null){
LoginPresenter.this.getView().updateUserPasswordSuccess(bean);
}
}
@Override
public void fail(String msg) {
if(LoginPresenter.this.getView() != null){
LoginPresenter.this.getView().showError(msg);
}
}
});
} catch (JSONException e) {
throw new RuntimeException(e);
}
}
}

View File

@@ -1,7 +1,7 @@
package com.ckkj.water.main;
import com.ckkj.water.base.IView;
import com.ckkj.water.base.entity.MineUserInfoBean;
import com.ckkj.water.mine.entity.MineUserInfoBean;
import com.ckkj.water.main.entity.CheckVersionEntity;
import com.ckkj.water.network.BaseBean;
@@ -12,7 +12,7 @@ import java.util.Map;
public interface MainContract {
interface DataModel{
//新版本检测
void checkAppVersionCode(JSONObject json, CallBack callBack);
void checkAppVersionCode(Map<String,String> map, CallBack callBack);
//运势
void getYunshiData(Map<String,Object> map, CallBack callBack);
}

View File

@@ -20,13 +20,13 @@ public class MainImpl implements MainContract.DataModel{
/**
* 检测新版本
* @param jsonObject
* @param map
* @param callBack
*/
@Override
public void checkAppVersionCode(JSONObject jsonObject, MainContract.CallBack callBack) {
public void checkAppVersionCode(Map<String,String> map, MainContract.CallBack callBack) {
try {
NetWorkManager.getRequest().checkVersionCode(NetWorkManager.getToken(),NetWorkManager.toRequestBody(jsonObject))
NetWorkManager.getRequest().checkVersionCode(NetWorkManager.getToken(),map)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())

View File

@@ -1,11 +1,8 @@
package com.ckkj.water.main;
import com.ckkj.water.BuildConfig;
import com.ckkj.water.base.BasePresenter;
import com.ckkj.water.base.entity.MineUserInfoBean;
import com.ckkj.water.login.mvp.LoginContract;
import com.ckkj.water.main.entity.CheckVersionEntity;
import com.ckkj.water.mine.mvp.MineImpl;
import com.ckkj.water.network.BaseBean;
import org.json.JSONObject;
@@ -24,8 +21,10 @@ public class MainPresenter extends BasePresenter<MainContract.MainView> {
* 获取用户信息
*/
public void checkNewVersion() {
JSONObject json = new JSONObject();
mainImpl.checkAppVersionCode(json, new MainContract.CallBack<CheckVersionEntity>() {
Map<String,String> map = new HashMap<>();
map.put("platform","android");
map.put("currentVersion", BuildConfig.APP_VERSION_NAME);
mainImpl.checkAppVersionCode(map, new MainContract.CallBack<CheckVersionEntity>() {
@Override
public void success(CheckVersionEntity bean) {
if( MainPresenter.this.getView() != null){

View File

@@ -1,5 +1,9 @@
package com.ckkj.water.main.activity;
import static com.ckkj.water.ConstantUtil.POST_NOTIFICATIONS_PERMISSION_CODE;
import static com.ckkj.water.ConstantUtil.WRITE_FILE_PERMISSON_CPDE;
import android.Manifest;
import android.content.Intent;
import android.os.Bundle;
import android.view.KeyEvent;
@@ -16,29 +20,24 @@ import com.ckkj.water.history.HistoryFragment;
import com.ckkj.water.index.IndexFragment;
import com.ckkj.water.mine.MineCenterFragment;
import com.ckkj.water.plan.WaterPlanFragment;
import com.ckkj.water.utils.manager.ThemeManager;
import com.gyf.immersionbar.ImmersionBar;
import com.ckkj.water.AppConfig;
import com.ckkj.water.ConstantUtil;
import com.ckkj.water.EventMessage;
import com.ckkj.water.R;
import com.ckkj.water.adapter.ViewPageAdapter;
import com.ckkj.water.base.BaseActivity;
import com.ckkj.water.base.entity.MineUserInfoBean;
import com.ckkj.water.chat.ChatFragment;
import com.ckkj.water.mine.entity.MineUserInfoBean;
import com.ckkj.water.databinding.MainActivityLayoutBinding;
import com.ckkj.water.dialog.DialogAppUpdate;
import com.ckkj.water.fate.view.FateFragment;
import com.ckkj.water.main.MainContract;
import com.ckkj.water.main.MainImpl;
import com.ckkj.water.main.MainPresenter;
import com.ckkj.water.main.entity.CheckVersionEntity;
import com.ckkj.water.mine.MineFragment;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.plate.PlateFragment;
import com.ckkj.water.utils.AppInfoUtil;
import com.ckkj.water.utils.dataUtil.SharePreUtil;
import com.ckkj.water.utils.manager.ActivityManager;
import com.ckkj.water.utils.manager.MingDengUserInfoManager;
import com.ckkj.water.utils.manager.WaterUserInfoManager;
import com.ckkj.water.utils.show.ToastUtil;
import org.jetbrains.annotations.NotNull;
@@ -78,8 +77,8 @@ public class MainActivity extends BaseActivity<MainActivityLayoutBinding, MainPr
super.onCreate(savedInstanceState);
ImmersionBar.with(this)
.transparentStatusBar() // 透明状态栏
.statusBarDarkFont(false) // 白色状态栏图标
.navigationBarColor(R.color.white)
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext)) // 白色状态栏图标
.navigationBarColor(R.color.mainBackgroundColor)
.keyboardEnable(false)
.init();
}
@@ -146,32 +145,31 @@ public class MainActivity extends BaseActivity<MainActivityLayoutBinding, MainPr
// IndexFragment 需要透明状态栏
ImmersionBar.with(MainActivity.this)
.transparentStatusBar()
.statusBarDarkFont(false)
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext))
.init();
break;
case 1:
viewBinding.navView.getMenu().getItem(position).setChecked(true);
viewBinding.navView.setSelectedItemId(R.id.navigation_plan);
// 其他页面恢复白色状态栏
ImmersionBar.with(MainActivity.this)
.statusBarColor(R.color.white)
.statusBarDarkFont(true)
.statusBarColor(R.color.mainBackgroundColor)
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext))
.init();
break;
case 2:
viewBinding.navView.getMenu().getItem(position).setChecked(true);
viewBinding.navView.setSelectedItemId(R.id.navigation_history);
ImmersionBar.with(MainActivity.this)
.statusBarColor(R.color.white)
.statusBarDarkFont(true)
.statusBarColor(R.color.mainBackgroundColor)
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext))
.init();
break;
case 3:
viewBinding.navView.getMenu().getItem(position).setChecked(true);
viewBinding.navView.setSelectedItemId(R.id.navigation_mine);
ImmersionBar.with(MainActivity.this)
.statusBarColor(R.color.white)
.statusBarDarkFont(true)
.statusBarColor(R.color.mainBackgroundColor)
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext))
.init();
break;
}
@@ -186,28 +184,31 @@ public class MainActivity extends BaseActivity<MainActivityLayoutBinding, MainPr
viewBinding.navViewpage2.setCurrentItem(0);
ImmersionBar.with(MainActivity.this)
.transparentStatusBar()
.statusBarDarkFont(false)
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext))
.init();
return true;
case R.id.navigation_plan:
viewBinding.navViewpage2.setCurrentItem(1);
ImmersionBar.with(MainActivity.this)
.statusBarColor(R.color.mainBackgroundColor)
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext))
.init();
return true;
case R.id.navigation_history:
viewBinding.navViewpage2.setCurrentItem(2);
ImmersionBar.with(MainActivity.this)
.statusBarColor(R.color.white)
.statusBarDarkFont(true)
.statusBarColor(R.color.mainBackgroundColor)
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext))
.init();
return true;
case R.id.navigation_mine:
viewBinding.navViewpage2.setCurrentItem(3);
ImmersionBar.with(MainActivity.this)
.statusBarColor(R.color.white)
.statusBarDarkFont(true)
.statusBarColor(R.color.mainBackgroundColor)
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext))
.init();
return true;
}
@@ -230,8 +231,6 @@ public class MainActivity extends BaseActivity<MainActivityLayoutBinding, MainPr
// 设置底部导航栏选中状态
viewBinding.navView.setSelectedItemId(R.id.navigation_index);
// 将参数传递给IndexFragment
passBundleToIndexFragment(extras);
});
}
}
@@ -250,28 +249,10 @@ public class MainActivity extends BaseActivity<MainActivityLayoutBinding, MainPr
// 设置底部导航栏选中状态
viewBinding.navView.setSelectedItemId(R.id.navigation_index);
// 将参数传递给IndexFragment
passBundleToIndexFragment(extras);
});
}
}
/**
* 将Bundle参数传递给IndexFragment
*/
private void passBundleToIndexFragment(Bundle bundle) {
// 获取IndexFragment实例
Fragment indexFragment = mFragmentList.get(1);
if (indexFragment instanceof ChatFragment) {
// 设置参数到Fragment
indexFragment.setArguments(bundle);
// 如果Fragment已经创建可以调用其方法来处理参数
if (indexFragment.isAdded()) {
((ChatFragment) indexFragment).handleBirthInfoBundle(bundle);
}
}
}
@Override
@@ -286,9 +267,7 @@ public class MainActivity extends BaseActivity<MainActivityLayoutBinding, MainPr
@Override
public void getUserInfoSuccess(MineUserInfoBean bean) {
if(bean != null){
MingDengUserInfoManager.getInstance().setUserInfo(bean.getUserInfo());
SharePreUtil.putBoolean(ConstantUtil.SHARE_PRE_NAME,mContext,ConstantUtil.USER_HAS_SET_BIRTH,bean.isHasEightCharacters());
AppConfig.USER_SET_BIRTH_INFO = bean.isHasEightCharacters();
WaterUserInfoManager.getInstance().setUserInfo(bean);
}
}
@@ -333,9 +312,25 @@ public class MainActivity extends BaseActivity<MainActivityLayoutBinding, MainPr
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 == WRITE_FILE_PERMISSON_CPDE){
if(mUpdateDialog != null && grantResults.length > 0 && grantResults[0] == android.content.pm.PackageManager.PERMISSION_GRANTED){
mUpdateDialog.startUpdateApp();
}
}else if(requestCode == POST_NOTIFICATIONS_PERMISSION_CODE){
if(mUpdateDialog != null && grantResults.length > 0 && grantResults[0] == android.content.pm.PackageManager.PERMISSION_GRANTED){
mUpdateDialog.startUpdateApp();
}
}
}
@Override
public void onPermissionsGranted(int requestCode, @NonNull @NotNull List<String> perms) {
}
@Override
protected void onDestroy() {
super.onDestroy();
@@ -359,7 +354,7 @@ public class MainActivity extends BaseActivity<MainActivityLayoutBinding, MainPr
private void exitApp() {
if ((System.currentTimeMillis() - exitTime) > 2000) {
ToastUtil.showShort(getApplicationContext(), "再按一次退出");
ToastUtil.showShort(getApplicationContext(), getString(R.string.press_again_to_exit));
exitTime = System.currentTimeMillis();
} else {
//结束所有activity
@@ -381,24 +376,34 @@ public class MainActivity extends BaseActivity<MainActivityLayoutBinding, MainPr
if(bean == null){
return;
}
if(bean.getVersionCoercion().equals("0")){//不强制
if(!bean.isForceUpdate()){//不强制
isForceUpdate = false;
}else{
isForceUpdate = true;
}
//前期强制更新
// isForceUpdate = true;
if(bean.getVersionNumber() > versionCode){
if(bean.getVersionCode() > versionCode){
//App更新dialog
mUpdateDialog = new DialogAppUpdate(mContext,R.layout.dialog_app_update,isForceUpdate,bean);
mUpdateDialog = new DialogAppUpdate(mContext,R.layout.dialog_app_update_water,isForceUpdate,bean);
mUpdateDialog.show();
mUpdateDialog.setOnDialogListener(new DialogAppUpdate.DialogListener() {
@Override
public void callBackGetPermisson(String permission) {
if (!hasRecordPermission(permission)) {
if (Manifest.permission.POST_NOTIFICATIONS.equals(permission)) {
getPermissionRequst(permission,
"通知权限申请",
"渥田氏需要通知权限来显示应用下载进度",
POST_NOTIFICATIONS_PERMISSION_CODE);
return;
}
// 没有权限只在ACTION_DOWN时去申请
getPermissionRequst(permission,"文件存储权限申请",
"星语明灯需要您的授权,获取存储权限来更新您的应用",PICK_IMAGE_REQUEST_300);
getPermissionRequst(permission,
"文件存储权限申请",
"渥田氏需要您的授权,获取存储权限来更新您的应用",
WRITE_FILE_PERMISSON_CPDE);
}else{
@@ -438,14 +443,6 @@ public class MainActivity extends BaseActivity<MainActivityLayoutBinding, MainPr
@Override
public void onPermissionsGranted(int requestCode, @NonNull @NotNull List<String> perms) {
if (requestCode == ConstantUtil.WRITE_FILE_PERMISSON_CPDE) {
if(mUpdateDialog != null){
mUpdateDialog.startUpdateApp();
}
}
}
@Override
public void onPermissionsDenied(int requestCode, @NonNull @NotNull List<String> perms) {

View File

@@ -1,41 +1,32 @@
package com.ckkj.water.main.entity;
import com.google.gson.annotations.SerializedName;
import java.io.Serializable;
import lombok.Data;
import lombok.NoArgsConstructor;
@NoArgsConstructor
@Data
@NoArgsConstructor
public class CheckVersionEntity implements Serializable {
@SerializedName("versionNumber")
private int versionNumber;
@SerializedName("versionEnd")
private String versionEnd;
@SerializedName("versionCoercion")
private String versionCoercion;
@SerializedName("versionDescription")
private String versionDescription;
@SerializedName("versionName")
private String versionName;
@SerializedName("versionUrl")
private String versionUrl;
@SerializedName("versionHide1")
private String versionHide1;
@SerializedName("versionHide2")
private String versionHide2;
@SerializedName("versionHide3")
private String versionHide3;
@SerializedName("versionHide4")
private String versionHide4;
@SerializedName("versionHide5")
private Object versionHide5;
@SerializedName("versionHide6")
private Object versionHide6;
@SerializedName("paipanApiKey")
private String paipanApiKey;
/**
* platform : android
* currentVersion : 1.0.1
* latestVersion : 1.0.0
* versionCode : 1
* updateAvailable : true
* forceUpdate : false
* downloadUrl : null
* releaseNotes : null
*/
private String platform;
private String currentVersion;
private String latestVersion;
private int versionCode;
private boolean updateAvailable;
private boolean forceUpdate;
private String downloadUrl;
private String releaseNotes;
}

View File

@@ -0,0 +1,146 @@
package com.ckkj.water.mine;
import android.os.Bundle;
import android.view.View;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity;
import com.ckkj.water.main.entity.CheckVersionEntity;
import com.ckkj.water.mine.entity.MineUserInfoBean;
import com.ckkj.water.databinding.AppThemeChangeLayoutBinding;
import com.ckkj.water.mine.mvp.MineContract;
import com.ckkj.water.mine.mvp.MineImpl;
import com.ckkj.water.mine.mvp.MinePresenter;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.utils.manager.ThemeManager;
import com.gyf.immersionbar.ImmersionBar;
/**
* @author fengxiaoyang
* @date 2026/5/17
* @description TODO: 切换主题
*/
public class AppThemActivity extends BaseActivity<AppThemeChangeLayoutBinding, MinePresenter>
implements MineContract.MineView {
@Override
public void initData(Bundle savedInstanceState) {
super.initData(savedInstanceState);
ImmersionBar.with(this)
.statusBarColor(R.color.transparent) // 设置状态栏颜色
.statusBarDarkFont(true) // 设置状态栏字体为黑色
.titleBar(viewBinding.rlTitle)
.init();
viewBinding.includedTitle.tvTitle.setText(R.string.register_title);
initListener();
int currentTheme = ThemeManager.getCurrentTheme(mContext);
if(currentTheme == ThemeManager.MODE_LIGHT){
viewBinding.checkNight.setChecked(false);
viewBinding.checkLight.setChecked(true);
}else{
viewBinding.checkNight.setChecked(true);
viewBinding.checkLight.setChecked(false);
}
}
private void initListener() {
setClick(viewBinding.includedTitle.imgBack);
setClick(viewBinding.linearLight);
setClick(viewBinding.linearNight);
setClick(viewBinding.imgLight);
setClick(viewBinding.imgNight);
}
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.linear_light:
case R.id.img_light:
switchTheme(ThemeManager.MODE_LIGHT);
viewBinding.checkNight.setChecked(false);
viewBinding.checkLight.setChecked(true);
break;
case R.id.linear_night:
case R.id.img_night:
switchTheme(ThemeManager.MODE_DARK);
viewBinding.checkNight.setChecked(true);
viewBinding.checkLight.setChecked(false);
break;
}
}
private void switchTheme(int themeMode) {
ThemeManager.setCurrentTheme(this, themeMode);
ThemeManager.applyTheme(themeMode);
recreate(); // 需要重新创建Activity使主题生效
}
@Override
public MinePresenter createPresenter() {
return new MinePresenter(new MineImpl());
}
@Override
public int getLayoutResId(Bundle savedInstanceState) {
return 0;
}
@Override
public void showError(String message) {
}
@Override
public void logOut(BaseBean bean) {
}
@Override
public void getUserInfoSuccess(MineUserInfoBean bean) {
}
@Override
public void updateUserInfoSuccess(BaseBean bean) {
}
@Override
public void updateUserPasswordSuccess(BaseBean bean) {
}
@Override
public void logOutSuccess(BaseBean bean) {
}
@Override
public void uploadImageSuccess(MineUserInfoBean bean) {
}
@Override
public void getAppVersionSuccess(CheckVersionEntity bean) {
}
@Override
public void getAccountLogoffCodeSuccess(BaseBean bean) {
}
@Override
public void confirmAccountLogoffCodeSuccess(BaseBean bean) {
}
}

View File

@@ -2,30 +2,22 @@ package com.ckkj.water.mine;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Handler;
import android.text.TextUtils;
import android.view.View;
import com.ckkj.water.main.entity.CheckVersionEntity;
import com.ckkj.water.mine.entity.MineUserInfoBean;
import com.gyf.immersionbar.ImmersionBar;
import com.ckkj.water.AppConfig;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity;
import com.ckkj.water.base.entity.MineUserInfoBean;
import com.ckkj.water.databinding.ChangePhoneActivityBinding;
import com.ckkj.water.login.LoginActivity;
import com.ckkj.water.main.entity.CheckVersionEntity;
import com.ckkj.water.mine.entity.MemberComboEntity;
import com.ckkj.water.mine.mvp.MineContract;
import com.ckkj.water.mine.mvp.MineImpl;
import com.ckkj.water.mine.mvp.MinePresenter;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.utils.manager.ActivityManager;
import com.ckkj.water.utils.manager.MingDengUserInfoManager;
import com.ckkj.water.utils.number.PhoneNumberUtils;
import com.ckkj.water.utils.show.ToastUtil;
import java.util.List;
/**
* @Description TODO
@@ -54,7 +46,6 @@ public class ChangePhoneActivity extends BaseActivity<ChangePhoneActivityBinding
.titleBar(viewBinding.rlTitle)
.init();
viewBinding.includeTitle.tvTitle.setText("更换手机号");
viewBinding.tvUserPhone.setText(MingDengUserInfoManager.getInstance().getUserInfo().getMobile());
initListener();
}
@@ -140,4 +131,44 @@ public class ChangePhoneActivity extends BaseActivity<ChangePhoneActivityBinding
public void logOut(BaseBean bean) {
}
@Override
public void getUserInfoSuccess(MineUserInfoBean bean) {
}
@Override
public void updateUserInfoSuccess(BaseBean bean) {
}
@Override
public void updateUserPasswordSuccess(BaseBean bean) {
}
@Override
public void logOutSuccess(BaseBean bean) {
}
@Override
public void uploadImageSuccess(MineUserInfoBean bean) {
}
@Override
public void getAppVersionSuccess(CheckVersionEntity bean) {
}
@Override
public void getAccountLogoffCodeSuccess(BaseBean bean) {
}
@Override
public void confirmAccountLogoffCodeSuccess(BaseBean bean) {
}
}

View File

@@ -1,28 +1,35 @@
package com.ckkj.water.mine;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import com.bumptech.glide.Glide;
import com.ckkj.water.AppConfig;
import com.ckkj.water.ConstantUtil;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseFragment;
import com.ckkj.water.base.entity.MineUserInfoBean;
import com.ckkj.water.databinding.MineFragmentBinding;
import com.ckkj.water.databinding.MineFragmentLayoutBinding;
import com.ckkj.water.main.entity.CheckVersionEntity;
import com.ckkj.water.mine.entity.MemberComboEntity;
import com.ckkj.water.mine.entity.MineUserInfoBean;
import com.ckkj.water.databinding.MineFragmentLayoutBinding;
import com.ckkj.water.login.LoginActivity;
import com.ckkj.water.mine.mvp.MineContract;
import com.ckkj.water.mine.mvp.MineImpl;
import com.ckkj.water.mine.mvp.MinePresenter;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.utils.show.ToastUtil;
import com.ckkj.water.utils.dataUtil.SharePreUtil;
import com.ckkj.water.utils.manager.ActivityManager;
import com.ckkj.water.utils.manager.LanguageManager;
import com.ckkj.water.utils.manager.ThemeManager;
import com.ckkj.water.utils.manager.WaterUserInfoManager;
import com.google.gson.Gson;
import com.gyf.immersionbar.ImmersionBar;
import com.lxj.xpopup.XPopup;
import com.lxj.xpopup.interfaces.OnSelectListener;
import java.util.List;
/**
* @author fengxiaoyang
* @date 2026/5/16
@@ -30,22 +37,35 @@ import java.util.List;
*/
public class MineCenterFragment extends BaseFragment<MineFragmentLayoutBinding, MinePresenter>
implements MineContract.MineView {
private MineUserInfoBean mUserBean;
@Override
public void initData(Bundle state) {
ImmersionBar.with(mContext)
.statusBarColor(R.color.white)
.statusBarDarkFont(true)
.statusBarColor(R.color.mainBackgroundColor)
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext))
.titleBar(viewBinding.rlTitle)
.init();
viewBinding.includedTitle.imgBack.setVisibility(INVISIBLE);
viewBinding.includedTitle.imgSetting.setVisibility(VISIBLE);
viewBinding.includedTitle.tvTitle.setText(getString(R.string.mine_center_title));
initListener();
}
@Override
protected void onResumeInitData() {
super.onResumeInitData();
if(AppConfig.USER_LOGIN){
mPresenter.getUserinfo();
}
}
private void initListener() {
setClick(viewBinding.imgAvatar);
setClick(viewBinding.linearInfo);
setClick(viewBinding.linearLanguage);
setClick(viewBinding.linearPsdManage);
setClick(viewBinding.linearTheme);
setClick(viewBinding.btnDelete);
setClick(viewBinding.includedTitle.imgSetting);
}
private void setClick(View view) {
@@ -54,20 +74,35 @@ public class MineCenterFragment extends BaseFragment<MineFragmentLayoutBinding,
public void onClick(View v){
switch (v.getId()){
case R.id.img_avatar:
goTo(mContext, MineInfomationActivity.class,null);
case R.id.img_setting:
goTo(mContext, SettingsActivity.class,null);
break;
case R.id.linear_info:
Bundle bundle = new Bundle();
bundle.putSerializable(ConstantUtil.USER_INFO_JSON_CONTENT,mUserBean);
goTo(mContext, MineInfomationActivity.class,bundle);
break;
case R.id.linear_theme:
goTo(mContext, AppThemActivity.class,null);
break;
case R.id.linear_language:
new XPopup.Builder(mContext).asBottomList(
"选择语言",
new String[]{"中文", "English"},
new int[]{R.mipmap.ic_launcher, R.mipmap.ic_launcher},
0,
new XPopup.Builder(mContext)
.isDestroyOnDismiss(true)
.dismissOnBackPressed(true)
.dismissOnTouchOutside(true)
.asBottomList(
getString(R.string.change_language),
LanguageManager.getLanguageDisplayNames(mContext),
LanguageManager.getLanguageIcons(),
LanguageManager.getCurrentLanguageIndex(),
new OnSelectListener() {
@Override
public void onSelect(int position, String text) {
ToastUtil.showShort(mContext,text);
LanguageManager.switchLanguageByIndex(position, getActivity());
}
},
R.layout.select_language_layout,
@@ -78,9 +113,98 @@ public class MineCenterFragment extends BaseFragment<MineFragmentLayoutBinding,
case R.id.linear_psd_manage:
goTo(mContext, PsdManageActivity.class,null);
break;
case R.id.btn_delete:
showDialogtext(null,
false,
false,
getString(R.string.tip_text),
getString(R.string.logout_tips),
getString(R.string.cancel_text),
getString(R.string.confirm_text),
new OnDialogClick() {
@Override
public void onCancelClick() {
}
@Override
public void onConfirmClick() {
mPresenter.logout();
}
});
break;
}
}
/**
* 获取个人信息
* @param bean
*/
@Override
public void getUserInfoSuccess(MineUserInfoBean bean) {
Gson gson = new Gson();
String userInfoJson = gson.toJson(bean);
SharePreUtil.putString(ConstantUtil.SHARE_PRE_NAME,mContext,ConstantUtil.USER_INFO_JSON_CONTENT,userInfoJson);
WaterUserInfoManager.getInstance().setUserInfo(bean);
mUserBean = bean;
viewBinding.tvUserName.setText(bean.getUser().getNickName());
if(TextUtils.isEmpty(bean.getUser().getPhonenumber())){
viewBinding.tvUserAccount.setText(bean.getUser().getEmail());
}else{
viewBinding.tvUserAccount.setText(bean.getUser().getPhonenumber());
}
if(!TextUtils.isEmpty(bean.getUser().getAvatar())){
// 刷新头像显示
Glide.with(mContext)
.load(bean.getUser().getAvatar())
.error(mContext.getDrawable(R.mipmap.icon_mine_user_head_defaul))
.into(viewBinding.imgAvatar);
}
}
/**
* 退出登录
* @param bean
*/
@Override
public void logOutSuccess(BaseBean bean) {
WaterUserInfoManager.getInstance().clearUser(mContext);
ActivityManager.removeAllActivity();
goTo(mContext, LoginActivity.class,null);
}
@Override
public void uploadImageSuccess(MineUserInfoBean bean) {
}
@Override
public void getAppVersionSuccess(CheckVersionEntity bean) {
}
@Override
public void getAccountLogoffCodeSuccess(BaseBean bean) {
}
@Override
public void confirmAccountLogoffCodeSuccess(BaseBean bean) {
}
@Override
public void updateUserInfoSuccess(BaseBean bean) {
}
@Override
public void updateUserPasswordSuccess(BaseBean bean) {
}
@Override
public MinePresenter createPresenter() {
return new MinePresenter(new MineImpl());
@@ -101,4 +225,5 @@ public class MineCenterFragment extends BaseFragment<MineFragmentLayoutBinding,
public void logOut(BaseBean bean) {
}
}

View File

@@ -1,269 +0,0 @@
package com.ckkj.water.mine;
import static android.app.Activity.RESULT_OK;
import android.Manifest;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.text.TextUtils;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.alibaba.sdk.android.oss.ClientException;
import com.alibaba.sdk.android.oss.ServiceException;
import com.bumptech.glide.Glide;
import com.esafirm.imagepicker.features.ImagePicker;
import com.esafirm.imagepicker.model.Image;
import com.ckkj.water.AppConfig;
import com.ckkj.water.EventMessage;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseFragment;
import com.ckkj.water.base.entity.MineUserInfoBean;
import com.ckkj.water.base.inter.UploadCallback;
import com.ckkj.water.databinding.MineFragmentBinding;
import com.ckkj.water.login.LoginActivity;
import com.ckkj.water.main.entity.CheckVersionEntity;
import com.ckkj.water.mine.entity.MemberComboEntity;
import com.ckkj.water.mine.mvp.MineContract;
import com.ckkj.water.mine.mvp.MineImpl;
import com.ckkj.water.mine.mvp.MinePresenter;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.utils.LubanCompressUtil;
import com.ckkj.water.utils.PreviewFileUtil;
import com.ckkj.water.utils.click.ClickUtil;
import com.ckkj.water.utils.dataUtil.FileUtil;
import com.ckkj.water.utils.manager.MingDengUserInfoManager;
import com.ckkj.water.utils.oss.OssUploadHelper;
import com.ckkj.water.utils.show.ToastUtil;
import com.ckkj.water.utils.time.ZYTimeUtils;
import com.lxj.xpopup.XPopup;
import com.lxj.xpopup.interfaces.OnSelectListener;
import com.yalantis.ucrop.UCrop;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import de.greenrobot.event.EventBus;
import pub.devrel.easypermissions.EasyPermissions;
/**
* @author fengxiaoyang 新分支
* @date 2025/7/7
* @description TODO: 描述该类的作用
*/
public class MineFragment extends BaseFragment<MineFragmentBinding, MinePresenter>
implements MineContract.MineView {
@Override
public void initData(Bundle state) {
viewBinding.includedTitle.imgBack.setVisibility(View.GONE);
viewBinding.includedTitle.imgSetting.setVisibility(View.VISIBLE);
ClickUtil.addClickPart(viewBinding.includedTitle.imgSetting, 50);
ClickUtil.addClickPart(viewBinding.imgEdit, 50);
viewBinding.includedTitle.tvTitle.setText("个人中心");
}
@Override
protected void onResumeInitData() {
super.onResumeInitData();
if (AppConfig.USER_LOGIN) {
// mPresenter.getUserinfo();
} else {
viewBinding.tvDefaulBirthtime.setVisibility(View.VISIBLE);
viewBinding.tvDefaulStar.setVisibility(View.VISIBLE);
viewBinding.tvUserBirthTime.setVisibility(View.GONE);
viewBinding.tvUserStar.setVisibility(View.GONE);
}
}
@Override
public MinePresenter createPresenter() {
return new MinePresenter(new MineImpl());
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.img_user_head:
if (!AppConfig.USER_LOGIN) {
goTo(mContext, LoginActivity.class, null);
return;
}
new XPopup.Builder(mContext).asBottomList("头像", new String[]{"上传头像", "查看大图"}, new OnSelectListener() {
@Override
public void onSelect(int position, String text) {
switch (position) {
case 0:
String readPermission = Manifest.permission.READ_EXTERNAL_STORAGE;
if (!hasRecordPermission(readPermission)) {
// 没有权限只在ACTION_DOWN时去申请
getPermissionRequst(readPermission, "相机、文件访问权限申请",
"星语明灯需要您的授权,获取文件访问权限来修改头像", REQUEST_CODE_100);
return;
} else {
imagePicker(1, REQUEST_CODE_100, MineFragment.this);
}
break;
case 1:
String suffix = FileUtil.getFileSuffixWithQuestion(MingDengUserInfoManager.getInstance().getUserInfo().getAvatar());
PreviewFileUtil.previewFiles(mContext, suffix, MingDengUserInfoManager.getInstance().getUserInfo().getAvatar());
break;
}
}
}).show();
break;
case R.id.img_setting:
goTo(mContext, SettingsActivity.class, null);
break;
case R.id.img_edit://编辑出生日期
if (!AppConfig.USER_LOGIN) {
goTo(mContext, LoginActivity.class, null);
return;
}
// 发送EventBus消息让MainActivity切换到FateFragment
EventBus.getDefault().post(new EventMessage(EventMessage.SWITCH_TO_FATE_FRAGMENT));
break;
case R.id.linear_tobe_vip:
case R.id.lienar_share_be_vip:
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case 100://头像
List<Image> images = ImagePicker.getImages(data);
Uri sourceUri = Uri.fromFile(new File(images.get(0).getPath()));
Uri destinationUri = Uri.fromFile(new File(mContext.getCacheDir(), "cropped_image.jpg"));
UCrop.of(sourceUri, destinationUri)
.withAspectRatio(1, 1) // 设置裁剪框比例1:1 为正方形
.withMaxResultSize(1000, 1000) // 设置裁剪后的最大图片尺寸
.start(requireContext(), this);
break;
case UCrop.REQUEST_CROP:
showLoadingDialog();
Uri resultUri = UCrop.getOutput(data);
String filePath = resultUri.getPath();
if (!TextUtils.isEmpty(filePath)) {
getCompressFile(new File(filePath));
}
break;
}
}
}
private void getCompressFile(File file) {
LubanCompressUtil.getInstance().getCompressFile(mContext, file, new LubanCompressUtil.CompressCallback() {
@Override
public void success(File file) {
if (file != null) {
if (file.exists()) {
long fileSizeInBytes = file.length(); // 获取文件大小(字节)
long fileSizeInMB = fileSizeInBytes / (1024 * 1024); // 转换为MB
String fileName = file.getName();
if (fileSizeInMB > 5) {
hideDialogLoading();
ToastUtil.showShort(mContext, "文件大小超出5M");
return;
}
if (!fileName.toLowerCase().endsWith(".png") &&
!fileName.toLowerCase().endsWith(".jpg") &&
!fileName.toLowerCase().endsWith(".jpeg")) {
ToastUtil.showShort(mContext, "文件格式不正确");
hideDialogLoading();
return;
}
List<String> filePath = new ArrayList<>();
filePath.add(file.getPath());
OssUploadHelper helper = new OssUploadHelper(mContext);
helper.upload(AppConfig.UPLOAD_TYPE_200, fileName, filePath, new UploadCallback() {
@Override
public void onSuccess(List<String> urls) {
mContext.runOnUiThread(new Runnable() {
@Override
public void run() {
if (!TextUtils.isEmpty(urls.get(0))) {
// mPresenter.updateUserAvatar(urls.get(0));
}
}
});
}
@Override
public void onFailure(ClientException clientException, ServiceException serviceException) {
}
});
} else {
ToastUtil.showShort(mContext, "文件不存在");
}
}
}
@Override
public void fail(String failMsg) {
}
});
}
@Override
public void onNetChange(int netModel) {
}
@Override
public void showError(String message) {
hideDialogLoading();
ToastUtil.showShort(mContext, message);
}
@Override
public void logOut(BaseBean bean) {
}
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
}
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
hidePermissionDialog();
if (requestCode == REQUEST_CODE_100) {
imagePicker(1, REQUEST_CODE_100, this);
}
}
}

View File

@@ -9,15 +9,13 @@ import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.alibaba.sdk.android.oss.ClientException;
import com.alibaba.sdk.android.oss.ServiceException;
import com.bumptech.glide.Glide;
import com.ckkj.water.ConstantUtil;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity;
import com.ckkj.water.base.entity.MineUserInfoBean;
import com.ckkj.water.databinding.MineInformationLayoutBinding;
import com.ckkj.water.main.entity.CheckVersionEntity;
import com.ckkj.water.mine.entity.MemberComboEntity;
import com.ckkj.water.mine.entity.MineUserInfoBean;
import com.ckkj.water.databinding.MineInformationLayoutBinding;
import com.ckkj.water.mine.mvp.MineContract;
import com.ckkj.water.mine.mvp.MineImpl;
import com.ckkj.water.mine.mvp.MinePresenter;
@@ -25,7 +23,9 @@ import com.ckkj.water.network.BaseBean;
import com.ckkj.water.utils.LubanCompressUtil;
import com.ckkj.water.utils.PreviewFileUtil;
import com.ckkj.water.utils.dataUtil.FileUtil;
import com.ckkj.water.utils.manager.MingDengUserInfoManager;
import com.ckkj.water.utils.dataUtil.SharePreUtil;
import com.ckkj.water.utils.manager.ThemeManager;
import com.ckkj.water.utils.manager.WaterUserInfoManager;
import com.ckkj.water.utils.show.ToastUtil;
import com.esafirm.imagepicker.features.ImagePicker;
import com.esafirm.imagepicker.model.Image;
@@ -34,7 +34,6 @@ import com.lxj.xpopup.XPopup;
import com.lxj.xpopup.interfaces.OnSelectListener;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import pub.devrel.easypermissions.EasyPermissions;
@@ -46,6 +45,7 @@ import pub.devrel.easypermissions.EasyPermissions;
*/
public class MineInfomationActivity extends BaseActivity<MineInformationLayoutBinding, MinePresenter>
implements MineContract.MineView, EasyPermissions.PermissionCallbacks{
private MineUserInfoBean mUserBean;
@Override
public MinePresenter createPresenter() {
return new MinePresenter(new MineImpl());
@@ -54,10 +54,26 @@ public class MineInfomationActivity extends BaseActivity<MineInformationLayoutBi
@Override
public void initData(Bundle savedInstanceState) {
super.initData(savedInstanceState);
Bundle bundle = getIntent().getExtras();
if(bundle != null){
mUserBean = (MineUserInfoBean) bundle.getSerializable(ConstantUtil.USER_INFO_JSON_CONTENT);
viewBinding.tvNicknameValue.setText(mUserBean.getUser().getNickName());
if(!TextUtils.isEmpty(mUserBean.getUser().getAvatar())){
// 刷新头像显示
Glide.with(mContext)
.load(mUserBean.getUser().getAvatar())
.error(mContext.getDrawable(R.mipmap.icon_mine_user_head_defaul))
.into(viewBinding.imgAvatar);
}
if(TextUtils.isEmpty(mUserBean.getUser().getEmail())){
viewBinding.tvBindPhoneValue.setText(mUserBean.getUser().getPhonenumber());
}else{
viewBinding.tvBindPhoneValue.setText(mUserBean.getUser().getEmail());
}
}
ImmersionBar.with(this)
.statusBarColor(R.color.white) // 设置状态栏颜色
.statusBarDarkFont(true) // 设置状态栏字体为黑色
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext)) // 白色状态栏图标
.navigationBarColor(R.color.mainBackgroundColor)
.titleBar(viewBinding.rlTitle)
.init();
viewBinding.includedTitle.tvTitle.setText(R.string.mine_infomation_title);
@@ -68,6 +84,7 @@ public class MineInfomationActivity extends BaseActivity<MineInformationLayoutBi
setClick(viewBinding.includedTitle.imgBack);
setClick(viewBinding.imgAvatar);
setClick(viewBinding.imgCamera);
setClick(viewBinding.rlNickname);
}
private void setClick(View view) {
@@ -82,7 +99,11 @@ public class MineInfomationActivity extends BaseActivity<MineInformationLayoutBi
case R.id.img_avatar:
case R.id.img_camera:
new XPopup.Builder(mContext).asBottomList("选择头像", new String[]{"上传头像", "查看大图"}, new OnSelectListener() {
new XPopup.Builder(mContext).asBottomList(
getString(R.string.avatar),
new String[]{
getString(R.string.upload_avatar),
getString(R.string.view_full_image)}, new OnSelectListener() {
@Override
public void onSelect(int position, String text) {
switch (position) {
@@ -90,21 +111,56 @@ public class MineInfomationActivity extends BaseActivity<MineInformationLayoutBi
String readPermission = Manifest.permission.READ_EXTERNAL_STORAGE;
if (!hasRecordPermission(readPermission)) {
// 没有权限只在ACTION_DOWN时去申请
getPermissionRequst(readPermission,"相册权限申请",
"智能浇水需要您的授权,获取相册修改头像",REQUEST_EVENT_CODE_10001);
getPermissionRequst(readPermission,
getString(R.string.request_camera_storage_permission),
getString(R.string.avatar_permission_tips),
REQUEST_EVENT_CODE_10001);
return;
}else{
imagePicker(1,PICK_IMAGE_REQUEST_100);
}
break;
case 1:
String suffix = FileUtil.getFileSuffixWithQuestion(MingDengUserInfoManager.getInstance().getUserInfo().getAvatar());
PreviewFileUtil.previewFiles(mContext, suffix, MingDengUserInfoManager.getInstance().getUserInfo().getAvatar());
String suffix = FileUtil.getFileSuffixWithQuestion(mUserBean.getUser().getAvatar());
PreviewFileUtil.previewFiles(mContext, suffix, mUserBean.getUser().getAvatar());
break;
}
}
}).show();
break;
case R.id.rl_nickname:
showEditDialog(
getString(R.string.edit_nickname),
getString(R.string.input_nickname),
mUserBean.getUser().getNickName(),
true,
getString(R.string.cancel_text),
getString(R.string.confirm_text),
INPUT_TYPE_NORMAL,
new OnDialogClick() {
@Override
public void onCancelClick() {
}
@Override
public void onConfirmClisk() {
}
@Override
public void onConfirmClisk(String content) {
showLoadingDialog();
mPresenter.updateUserInfo(content,null);
mUserBean.getUser().setNickName(content);
viewBinding.tvNicknameValue.setText(content);
}
@Override
public void onView1Click() {
}
});
break;
}
}
@@ -131,6 +187,69 @@ public class MineInfomationActivity extends BaseActivity<MineInformationLayoutBi
}
}
/**
* 获取个人信息成功
* @param bean
*/
@Override
public void updateUserInfoSuccess(BaseBean bean) {
hideDialogLoading();
}
@Override
public void updateUserPasswordSuccess(BaseBean bean) {
}
@Override
public void logOutSuccess(BaseBean bean) {
}
@Override
public void uploadImageSuccess(MineUserInfoBean bean) {
hideDialogLoading();
// 后端约定data 为上传后的头像访问URLString
String avatarUrl = bean.getUrl();
if (TextUtils.isEmpty(avatarUrl)) {
ToastUtil.showShort(mContext, getString(R.string.avatar_update_success));
return;
}
// 更新内存中的用户信息
if (mUserBean != null && mUserBean.getUser() != null) {
mUserBean.getUser().setAvatar(avatarUrl);
WaterUserInfoManager.getInstance().setUserInfo(mUserBean);
}
// 缓存头像地址到本地
SharePreUtil.putString(ConstantUtil.SHARE_PRE_NAME, mContext,
ConstantUtil.USER_INFO_AVATAR, avatarUrl);
// 刷新头像显示
Glide.with(mContext)
.load(avatarUrl)
.error(mContext.getDrawable(R.mipmap.icon_mine_user_head_defaul))
.into(viewBinding.imgAvatar);
ToastUtil.showShort(mContext, getString(R.string.avatar_update_success));
}
@Override
public void getAppVersionSuccess(CheckVersionEntity bean) {
}
@Override
public void getAccountLogoffCodeSuccess(BaseBean bean) {
}
@Override
public void confirmAccountLogoffCodeSuccess(BaseBean bean) {
}
private void getCompressFile(File file) {
LubanCompressUtil.getInstance().getCompressFile(mContext, file, new LubanCompressUtil.CompressCallback() {
@@ -143,26 +262,30 @@ public class MineInfomationActivity extends BaseActivity<MineInformationLayoutBi
String fileName = file.getName();
if (fileSizeInMB > 5) {
hideDialogLoading();
ToastUtil.showShort(mContext, "文件大小超出5M");
ToastUtil.showShort(mContext, getString(R.string.file_size_exceeds_5m));
return;
}
if (!fileName.toLowerCase().endsWith(".png") &&
!fileName.toLowerCase().endsWith(".jpg") &&
!fileName.toLowerCase().endsWith(".jpeg")) {
ToastUtil.showShort(mContext, "文件格式不正确");
ToastUtil.showShort(mContext, getString(R.string.invalid_file_format));
hideDialogLoading();
return;
}
hideDialogLoading();
ToastUtil.showShort(mContext,"头像修改成功");
// 上传图片
mPresenter.uploadImage(file);
Glide.with(mContext)
.load(file)
.error(mContext.getDrawable(R.mipmap.icon_mine_user_head_defaul))
.into(viewBinding.imgAvatar);
// hideDialogLoading();
// ToastUtil.showShort(mContext,getString(R.string.avatar_update_success));
//
// Glide.with(mContext)
// .load(file)
// .error(mContext.getDrawable(R.mipmap.icon_mine_user_head_defaul))
// .into(viewBinding.imgAvatar);
} else {
ToastUtil.showShort(mContext, "文件不存在");
ToastUtil.showShort(mContext, getString(R.string.file_not_exist));
}
}
}
@@ -182,7 +305,8 @@ public class MineInfomationActivity extends BaseActivity<MineInformationLayoutBi
@Override
public void showError(String message) {
hideDialogLoading();
ToastUtil.showShort(mContext,message);
}
@Override
@@ -211,4 +335,11 @@ public class MineInfomationActivity extends BaseActivity<MineInformationLayoutBi
}
}
}
@Override
public void getUserInfoSuccess(MineUserInfoBean bean) {
}
}

View File

@@ -5,12 +5,15 @@ import android.view.View;
import com.ckkj.water.R;
import com.ckkj.water.base.BaseActivity;
import com.ckkj.water.main.entity.CheckVersionEntity;
import com.ckkj.water.mine.entity.MineUserInfoBean;
import com.ckkj.water.databinding.PasswordManLayoutBinding;
import com.ckkj.water.login.ForgetPasswordActivity;
import com.ckkj.water.mine.mvp.MineContract;
import com.ckkj.water.mine.mvp.MineImpl;
import com.ckkj.water.mine.mvp.MinePresenter;
import com.ckkj.water.network.BaseBean;
import com.ckkj.water.utils.manager.ThemeManager;
import com.gyf.immersionbar.ImmersionBar;
/**
@@ -26,11 +29,11 @@ public class PsdManageActivity extends BaseActivity<PasswordManLayoutBinding, Mi
super.initData(savedInstanceState);
ImmersionBar.with(this)
.statusBarColor(R.color.white) // 设置状态栏颜色
.statusBarDarkFont(true) // 设置状态栏字体为黑色
.statusBarDarkFont(ThemeManager.isAppThemeLight(mContext)) // 白色状态栏图标
.navigationBarColor(R.color.mainBackgroundColor)
.titleBar(viewBinding.rlTitle)
.init();
viewBinding.includedTitle.tvTitle.setText(R.string.register_title);
viewBinding.includedTitle.tvTitle.setText(R.string.mine_psd_manager);
initListener();
}
@@ -79,4 +82,44 @@ public class PsdManageActivity extends BaseActivity<PasswordManLayoutBinding, Mi
public void logOut(BaseBean bean) {
}
@Override
public void getUserInfoSuccess(MineUserInfoBean bean) {
}
@Override
public void updateUserInfoSuccess(BaseBean bean) {
}
@Override
public void updateUserPasswordSuccess(BaseBean bean) {
}
@Override
public void logOutSuccess(BaseBean bean) {
}
@Override
public void uploadImageSuccess(MineUserInfoBean bean) {
}
@Override
public void getAppVersionSuccess(CheckVersionEntity bean) {
}
@Override
public void getAccountLogoffCodeSuccess(BaseBean bean) {
}
@Override
public void confirmAccountLogoffCodeSuccess(BaseBean bean) {
}
}

Some files were not shown because too many files have changed in this diff Show More