Compare commits

..

60 Commits

Author SHA1 Message Date
513607e1ef fix: 增加日志 2025-11-07 18:03:11 +08:00
6cc00ca348 fix: 增加调试页面 2025-10-29 18:02:55 +08:00
21aa3adf7b fix: 调整页面是默认选中为锁通通 2025-10-27 17:35:41 +08:00
f6e900aaad fix: 增加日志 2025-10-27 09:27:37 +08:00
41aea1d1bf fix: 增加调试页面 2025-10-24 09:17:22 +08:00
5ed7e4013e fix: 调整列表左滑无法删除的问题 2025-10-23 09:49:21 +08:00
a4892633a3 fix: 增加日志 2025-10-22 15:52:53 +08:00
f73943785f fix: 修复添加完锁之后立马蓝牙开锁无法打开的问题 2025-10-22 14:38:55 +08:00
baeb3c139a fix: 修复在列表无法直接删除锁的问题 2025-10-22 09:59:55 +08:00
e1011688de fix: 调整第三方平台设置 2025-10-22 09:37:26 +08:00
4f1b1dd02f fix: 恢复分支 2025-10-20 15:29:28 +08:00
ae176ffb6c fix: 增加网关配网时的参数 2025-10-20 14:48:13 +08:00
2bfbac52fa fix: 停用友盟,格式化部分代码 2025-10-15 16:52:04 +08:00
10c9b896aa fix: 停用友盟,格式化部分代码 2025-10-09 11:38:47 +08:00
ddb71668c4 fix: 增加网络信号信息 2025-10-09 11:37:38 +08:00
d3cd4adce8 fix: 增加ai助理、调整第三方协议 2025-10-09 11:37:24 +08:00
b5dfd10baa fix: 调整注册账户时国外用户默认使用邮箱,并给出提示 2025-09-28 09:31:13 +08:00
2e85975c41 fix: 调整蓝牙开锁时出现重复响应,增加防抖处理。调整动画控制器在初始化之前进行赋值 2025-09-26 18:49:41 +08:00
8a5de7d442 fix: 调整蓝牙开锁时出现重复响应,增加防抖处理。调整动画控制器在初始化之前进行赋值 2025-09-25 18:23:35 +08:00
8fd0a5243e fix: 测试ci
Some checks failed
Flutter CI - Basic Setup / 🏗️ Build Flutter Artifacts (pull_request) Failing after 3m45s
Flutter CI - Basic Setup / 🔧 Basic Setup (pull_request) Successful in 1m31s
2025-09-25 10:57:01 +08:00
bdaad63b11 fix: 测试ci
Some checks failed
Flutter CI - Basic Setup / 🔧 Basic Setup (pull_request) Successful in 3m21s
Flutter CI - Basic Setup / 🏗️ Build Flutter Artifacts (pull_request) Failing after 5m34s
2025-09-25 10:38:36 +08:00
0ee37e8fda fix: 测试ci
Some checks failed
Flutter CI - Basic Setup / 🔧 Basic Setup (pull_request) Successful in 3m13s
Flutter CI - Basic Setup / 🏗️ Build Flutter Artifacts (pull_request) Failing after 7m9s
2025-09-25 10:18:11 +08:00
670e6e8b26 fix: 测试ci 2025-09-25 10:16:02 +08:00
68b244a527 fix: 测试ci 2025-09-25 09:56:28 +08:00
47b5e7b06e fix: 测试ci(增加构建制品) 2025-09-25 09:55:37 +08:00
736854c959 fix: 测试ci(增加构建制品) 2025-09-25 09:55:10 +08:00
7aef630e1b fix: 测试ci(增加构建制品) 2025-09-25 09:54:04 +08:00
3eee6d663a fix: 测试ci(增加构建制品)
Some checks failed
Flutter CI - Basic Setup / 🔧 Basic Setup (push) Successful in 3m38s
Flutter CI - Basic Setup / 🏗️ Build Flutter Artifacts (push) Has been cancelled
2025-09-25 09:43:46 +08:00
fc4429d828 fix: 测试ci
Some checks failed
Flutter CI - Basic Setup / 🔧 Basic Setup (push) Failing after 1m30s
2025-09-25 09:41:33 +08:00
9c14338c53 fix: 测试ci(计算下一位版本号)
All checks were successful
Flutter CI - Basic Setup / 🔧 Basic Setup (push) Successful in 3m11s
2025-09-25 09:32:03 +08:00
9b69c65bf8 fix: 测试ci
All checks were successful
Flutter CI - Basic Setup / 🔧 Basic Setup (push) Successful in 3m14s
2025-09-25 09:22:12 +08:00
398c45ade1 fix: 测试ci
Some checks failed
Flutter CI - Basic Setup / 🔧 Basic Setup (push) Failing after 3m32s
2025-09-25 09:12:47 +08:00
ac157e8b93 fix: 测试ci
Some checks failed
Flutter Build - Sky Develop / 📦 Build AAB (Develop) (push) Failing after 2m30s
Flutter Build - Sky Develop / 🍎 Build iOS (Develop) (push) Failing after 1m31s
Flutter Build - Sky Develop / 🏷️ Create Tag & Notify (push) Has been skipped
Flutter Build - Sky Develop / 🧮 Determine Version (push) Successful in 4m11s
Flutter Build - Sky Develop / 📦 Build APK (Develop) (push) Failing after 2m35s
2025-09-23 11:07:09 +08:00
d49359bdf7 fix: 测试ci
Some checks failed
Flutter Build - Sky Develop / 🧮 Determine Version (push) Successful in 3m24s
Flutter Build - Sky Develop / 📦 Build APK (Develop) (push) Failing after 3m11s
Flutter Build - Sky Develop / 📦 Build AAB (Develop) (push) Failing after 2m39s
Flutter Build - Sky Develop / 🍎 Build iOS (Develop) (push) Failing after 5m28s
Flutter Build - Sky Develop / 🏷️ Create Tag & Notify (push) Has been skipped
2025-09-23 10:43:51 +08:00
2ab18a1425 fix: 测试ci
Some checks failed
Flutter Build - Sky Develop / 🧮 Determine Version (push) Has been cancelled
Flutter Build - Sky Develop / 📦 Build APK (Develop) (push) Has been cancelled
Flutter Build - Sky Develop / 📦 Build AAB (Develop) (push) Has been cancelled
Flutter Build - Sky Develop / 🍎 Build iOS (Develop) (push) Has been cancelled
Flutter Build - Sky Develop / 🏷️ Create Tag & Notify (push) Has been cancelled
2025-09-23 10:42:47 +08:00
c41e1ebd9d fix: 测试ci
Some checks failed
Flutter Build - Sky / 🧮 Determine Version (push) Failing after 1m30s
Flutter Build - Sky / 📦 Build APK (push) Has been skipped
Flutter Build - Sky / 📦 Build AAB (push) Has been skipped
Flutter Build - Sky / 🍎 Build iOS (push) Has been skipped
Flutter Build - Sky / 🏷️ Create Tag & Notify (push) Has been skipped
2025-09-23 10:27:05 +08:00
13af3c02a2 fix: 测试ci
Some checks failed
Flutter Build - Sky Dev / Build Android for Sky (dev) (push) Failing after 8m47s
2025-09-23 10:11:29 +08:00
2674cf2554 fix: 测试ci
Some checks failed
Flutter Build - Sky Dev / Build Android for Sky (dev) (push) Failing after 6m16s
2025-09-23 09:42:50 +08:00
1f107b4be7 fix: 测试ci
Some checks failed
Flutter Build - Sky Dev / Build Android for Sky (dev) (push) Failing after 1m48s
2025-09-23 09:16:36 +08:00
74826b9acb fix: 测试ci
Some checks failed
Flutter Build - Sky Dev / Build Android for Sky (dev) (push) Failing after 2m23s
2025-09-23 09:11:59 +08:00
c122bfa637 fix: 测试ci
Some checks failed
Flutter Build - Sky Dev / Build Android for Sky (dev) (push) Failing after 1m46s
2025-09-22 18:37:42 +08:00
462e464f8a fix: 测试ci
Some checks failed
Flutter Build - Sky Dev / Build Android for Sky (dev) (push) Failing after 1m51s
2025-09-22 18:32:24 +08:00
7c346a118e fix: 测试ci
Some checks failed
Flutter Build - Sky Dev / Build Android for Sky (dev) (push) Has been cancelled
2025-09-22 18:31:50 +08:00
01d60d2d49 fix: 测试ci
Some checks failed
Flutter CI - Build iOS & Android / Build Flutter App (push) Failing after 2m53s
2025-09-22 18:25:50 +08:00
73548e45b6 fix: 测试ci
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 1m32s
2025-09-22 18:23:59 +08:00
004a0b89a9 fix: 测试ci
Some checks are pending
Flutter CI - Develop Sky / Build iOS & Android (push) Waiting to run
2025-09-22 18:07:29 +08:00
4cbaa92593 fix: 测试ci
Some checks are pending
Flutter CI - Develop Sky / Build iOS & Android (push) Waiting to run
2025-09-22 18:03:43 +08:00
47a1bfb914 Merge pull request 'develop_sky_liyi' (#2) from develop_sky_liyi into develop_sky
Some checks are pending
Flutter CI/CD Pipeline / Print Environment (push) Waiting to run
Flutter CI/CD Pipeline / Generate Git Tag (push) Blocked by required conditions
Flutter CI/CD Pipeline / Generate Next Version (push) Blocked by required conditions
Flutter CI/CD Pipeline / Build Android APK/AAB (push) Blocked by required conditions
Flutter CI/CD Pipeline / Build iOS IPA (push) Blocked by required conditions
Flutter CI/CD Pipeline / Create Release & Upload Assets (push) Blocked by required conditions
Flutter CI/CD Pipeline / Notify on Success (push) Blocked by required conditions
Flutter CI/CD Pipeline / Notify on Failure (push) Blocked by required conditions
Flutter CI/CD Pipeline / Clean Workspace (push) Blocked by required conditions
Reviewed-on: #2
2025-09-22 17:49:44 +08:00
7759267d0b Merge pull request 'develop_sky' (#1) from develop_sky into develop_sky_liyi
Some checks failed
Flutter CI/CD Pipeline / Print Environment (pull_request) Has been cancelled
Flutter CI/CD Pipeline / Generate Git Tag (pull_request) Has been cancelled
Flutter CI/CD Pipeline / Generate Next Version (pull_request) Has been cancelled
Flutter CI/CD Pipeline / Build Android APK/AAB (pull_request) Has been cancelled
Flutter CI/CD Pipeline / Build iOS IPA (pull_request) Has been cancelled
Flutter CI/CD Pipeline / Create Release & Upload Assets (pull_request) Has been cancelled
Flutter CI/CD Pipeline / Notify on Success (pull_request) Has been cancelled
Flutter CI/CD Pipeline / Notify on Failure (pull_request) Has been cancelled
Flutter CI/CD Pipeline / Clean Workspace (pull_request) Has been cancelled
Reviewed-on: #1
2025-09-22 17:48:19 +08:00
a8cc945d61 fix: 测试ci 2025-09-22 17:46:41 +08:00
725ef31640 fix: 测试ci
All checks were successful
Gitea Actions Demo / Explore-Gitea-Actions (push) Successful in 1m40s
2025-09-22 17:38:39 +08:00
1b70bd8b92 fix: 测试ci
Some checks are pending
Gitea Actions Demo / Explore-Gitea-Actions (push) Waiting to run
2025-09-22 17:33:51 +08:00
31123e503a fix: 测试ci 2025-09-22 17:28:58 +08:00
e8b2cf4660 fix: 调整插件版本 2025-09-22 17:17:00 +08:00
279b329a1e Merge branch 'develop_sky_liyi' into 'develop_sky'
fix: 调整插件版本

See merge request StarlockTeam/app-starlock!285
2025-09-22 07:35:42 +00:00
30283fe513 fix: 调整插件版本 2025-09-22 15:35:15 +08:00
8728c4f697 Merge branch 'develop_sky_liyi' into 'develop_sky'
fix: 调整插件版本

See merge request StarlockTeam/app-starlock!284
2025-09-22 07:29:21 +00:00
766dd88770 fix: 调整插件版本 2025-09-22 15:27:45 +08:00
5118aed3ca Merge branch 'develop_sky_liyi' into 'develop_sky'
fix: 调整开锁在同步数据完成之后断开连接

See merge request StarlockTeam/app-starlock!283
2025-09-22 07:03:09 +00:00
987fd4770a fix: 调整开锁在同步数据完成之后断开连接 2025-09-22 15:01:44 +08:00
94 changed files with 44056 additions and 42653 deletions

251
.gitea/workflows/ci.yml Normal file
View File

@ -0,0 +1,251 @@
name: Flutter CI - Basic Setup
on:
push:
branches:
- master_sky
pull_request:
branches:
- master_sky
jobs:
# 基础设置任务:检出代码、提取版本号
basic-setup:
name: 🔧 Basic Setup
runs-on: sky
steps:
# 1. 检出代码
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 1
# 2. 提取版本号
- name: Extract Version
id: version
run: |
# 获取最新的tag按版本号排序匹配vX.X.X_sky格式
LATEST_TAG=$(git tag --list "v*.*.*_sky" --sort=-version:refname | head -1)
# 如果没有找到tag使用默认值
if [ -z "$LATEST_TAG" ]; then
LATEST_TAG="v1.0.0_sky"
echo "📌 No tags found, using default: $LATEST_TAG"
else
echo "📌 Latest tag found: $LATEST_TAG"
fi
# 提取基础版本号去除_sky后缀
BASE_VERSION=$(echo "$LATEST_TAG" | grep -oE 'v[0-9]+\.[0-9]+\.[0-9]+' | head -1)
[ -z "$BASE_VERSION" ] && BASE_VERSION="v1.0.0"
echo "📌 Base version: $BASE_VERSION"
# 解析版本号各部分
MAJOR=$(echo $BASE_VERSION | cut -d'.' -f1 | sed 's/v//')
MINOR=$(echo $BASE_VERSION | cut -d'.' -f2)
PATCH=$(echo $BASE_VERSION | cut -d'.' -f3)
echo "📌 Version components: Major=$MAJOR, Minor=$MINOR, Patch=$PATCH"
# 计算下一个版本号
echo "📊 Calculating next version..."
# 获取当前提交与最新tag之间的所有提交消息
COMMIT_MESSAGES=$(git log --oneline --format=%s $LATEST_TAG..HEAD 2>/dev/null || echo "")
# 统计需要递增的提交次数(过滤重复的提交消息)
INCREMENT_COUNT=0
if [ -n "$COMMIT_MESSAGES" ]; then
# 使用awk过滤重复的提交消息并计数
UNIQUE_MESSAGES=$(echo "$COMMIT_MESSAGES" | awk '!seen[$0]++')
INCREMENT_COUNT=$(echo "$UNIQUE_MESSAGES" | wc -l)
echo "📝 Found $INCREMENT_COUNT unique commit(s) since last tag"
else
echo "📝 No new commits since last tag"
fi
# 计算新的版本号
NEW_PATCH=$((PATCH + INCREMENT_COUNT))
NEW_MINOR=$MINOR
NEW_MAJOR=$MAJOR
# 处理版本号进位逻辑
if [ $NEW_PATCH -ge 1000 ]; then
NEW_MINOR=$((NEW_MINOR + NEW_PATCH / 1000))
NEW_PATCH=$((NEW_PATCH % 1000))
echo "🔄 Patch version overflow, incrementing minor version"
fi
if [ $NEW_MINOR -ge 10 ]; then
NEW_MAJOR=$((NEW_MAJOR + NEW_MINOR / 10))
NEW_MINOR=$((NEW_MINOR % 10))
echo "🔄 Minor version overflow, incrementing major version"
fi
# 生成下一个版本号
NEXT_VERSION="v${NEW_MAJOR}.${NEW_MINOR}.${NEW_PATCH}"
NEXT_TAG="${NEXT_VERSION}_sky"
echo "🚀 Next version: $NEXT_VERSION"
echo "🏷️ Next tag: $NEXT_TAG"
echo "📈 Increment count: $INCREMENT_COUNT"
# 设置输出变量供后续任务使用Gitea Actions格式
echo "NEXT_VERSION=$NEXT_VERSION" >> $GITEA_OUTPUT
echo "NEXT_TAG=$NEXT_TAG" >> $GITEA_OUTPUT
echo "INCREMENT_COUNT=$INCREMENT_COUNT" >> $GITEA_OUTPUT
# 输出版本信息
echo "✅ Version extraction completed"
# 5. 任务完成通知
- name: Task Completion
run: |
echo "🎉 Basic CI setup completed successfully!"
echo ""
echo "📋 Tasks executed:"
echo " ✅ Code checkout"
echo " ✅ Version extraction"
echo ""
echo "🚀 Next steps: Building Flutter artifacts..."
# 构建Flutter制品任务
build-artifacts:
name: 🏗️ Build Flutter Artifacts
runs-on: sky
needs: basic-setup
steps:
# 1. 检出代码
- name: Checkout Code
uses: actions/checkout@v4
with:
fetch-depth: 1
# 4. 构建APK文件
- name: Build APK
run: |
echo "🏗️ Building APK artifact..."
# 生成当前时间作为build-number格式YYYYMMDDHH
BUILD_NUMBER=$(date +%Y%m%d%H)
echo "📅 Build number: $BUILD_NUMBER"
# 获取版本信息从basic-setup任务传递
echo "🔍 Getting version info from basic-setup job..."
# 设置默认版本号,如果环境变量为空
if [ -z "${{ needs.basic-setup.outputs.NEXT_VERSION }}" ]; then
VERSION_FOR_FILENAME="1-0-0"
echo "⚠️ Version not found, using default: $VERSION_FOR_FILENAME"
else
# 格式化版本号用于文件名
VERSION_FOR_FILENAME=$(echo "${{ needs.basic-setup.outputs.NEXT_VERSION }}" | sed 's/v//g' | sed 's/\./-/g')
echo "✅ Version found: $VERSION_FOR_FILENAME"
fi
# 设置APK文件名
APK_FILENAME="sky-star-lock-release-$VERSION_FOR_FILENAME.apk"
echo "📁 APK filename: $APK_FILENAME"
# 构建APK使用新的构建参数
flutter build apk --no-tree-shake-icons --release --flavor sky -t lib/main_sky_full.dart --build-number=$BUILD_NUMBER --build-name="sky-star-lock-release-$VERSION_FOR_FILENAME.apk"
# 重命名APK文件
mv build/app/outputs/flutter-apk/app-sky-release.apk "$APK_FILENAME"
echo "✅ APK build completed: $APK_FILENAME"
# 5. 构建AAB文件
- name: Build AAB
run: |
echo "🏗️ Building AAB artifact..."
# 生成当前时间作为build-number格式YYYYMMDDHH
BUILD_NUMBER=$(date +%Y%m%d%H)
echo "📅 Build number: $BUILD_NUMBER"
# 获取版本信息从basic-setup任务传递
echo "🔍 Getting version info from basic-setup job..."
# 设置默认版本号,如果环境变量为空
if [ -z "${{ needs.basic-setup.outputs.NEXT_VERSION }}" ]; then
VERSION_FOR_FILENAME="1-0-0"
echo "⚠️ Version not found, using default: $VERSION_FOR_FILENAME"
else
# 格式化版本号用于文件名
VERSION_FOR_FILENAME=$(echo "${{ needs.basic-setup.outputs.NEXT_VERSION }}" | sed 's/v//g' | sed 's/\./-/g')
echo "✅ Version found: $VERSION_FOR_FILENAME"
fi
# 设置AAB文件名
AAB_FILENAME="sky-star-lock-release-$VERSION_FOR_FILENAME.aab"
echo "📁 AAB filename: $AAB_FILENAME"
# 构建AAB使用新的构建参数
flutter build appbundle --no-tree-shake-icons --release --flavor sky -t lib/main_sky_full.dart --build-number=$BUILD_NUMBER --build-name="sky-star-lock-release-$VERSION_FOR_FILENAME.aab"
# 重命名AAB文件
mv build/app/outputs/bundle/skyRelease/app-sky-release.aab "$AAB_FILENAME"
echo "✅ AAB build completed: $AAB_FILENAME"
# 6. 构建iOS IPA文件如果支持iOS构建
- name: Build iOS IPA
if: runner.os == 'macos'
run: |
echo "🏗️ Building iOS IPA artifact..."
# 生成当前时间作为build-number格式YYYYMMDDHH
BUILD_NUMBER=$(date +%Y%m%d%H)
echo "📅 Build number: $BUILD_NUMBER"
# 获取版本信息从basic-setup任务传递
echo "🔍 Getting version info from basic-setup job..."
# 设置默认版本号,如果环境变量为空
if [ -z "${{ needs.basic-setup.outputs.NEXT_VERSION }}" ]; then
VERSION_FOR_FILENAME="1-0-0"
echo "⚠️ Version not found, using default: $VERSION_FOR_FILENAME"
else
# 格式化版本号用于文件名
VERSION_FOR_FILENAME=$(echo "${{ needs.basic-setup.outputs.NEXT_VERSION }}" | sed 's/v//g' | sed 's/\./-/g')
echo "✅ Version found: $VERSION_FOR_FILENAME"
fi
# 设置IPA文件名
IPA_FILENAME="sky-star-lock-release-$VERSION_FOR_FILENAME.ipa"
echo "📁 IPA filename: $IPA_FILENAME"
# 配置iOS自动签名CI环境使用自动签名
echo "🔧 Configuring iOS automatic code signing for CI environment..."
# 构建iOS IPA使用自动签名模式
flutter build ipa --no-tree-shake-icons --release --flavor sky -t lib/main_sky_full.dart --build-number=$BUILD_NUMBER --build-name="sky-star-lock-release-$VERSION_FOR_FILENAME.ipa" --codesign
# 重命名IPA文件
mv build/ios/ipa/*.ipa "$IPA_FILENAME"
echo "✅ iOS IPA build completed: $IPA_FILENAME"
# 7. 上传制品
- name: Upload Artifacts
uses: actions/upload-artifact@v4
with:
name: flutter-artifacts-release
path: |
sky-star-lock-release-*.apk
sky-star-lock-release-*.aab
sky-star-lock-release-*.ipa
retention-days: 30
# 8. 构建完成通知
- name: Build Completion
run: |
echo "🎉 Flutter artifacts build completed successfully!"
echo ""
echo "📦 Artifacts generated:"
echo " ✅ APK: sky-star-lock-release-*.apk"
echo " ✅ AAB: sky-star-lock-release-*.aab"
if [ "${{ runner.os }}" == "macos" ]; then
echo " ✅ IPA: sky-star-lock-release-*.ipa"
fi
echo ""
echo "🏷️ Version: ${{ needs.basic-setup.outputs.NEXT_VERSION }}"
echo "📁 Files available in artifacts section"

View File

@ -61,7 +61,7 @@ keytool -list -v -keystore android/app/sky.jks
```
输入密码在android/app/build.gradle:38可以看到
测试ci
一般需要的是:证书指纹-SHA1 看起来像 95:6B:***********共59个字符
## 编译

BIN
images/other/2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

BIN
images/other/ai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

BIN
images/other/matter.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
images/other/tuya.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

View File

@ -1167,5 +1167,11 @@
"服务,图像视频信息随心存!": "معلومات الخدمة والصور والفيديو في قلبك!",
"图像": "صورة",
"视频": "فيديو",
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "حاليا ، لا تدعم الدولة تسجيل رمز التحقق من الهاتف المحمول ، يرجى استخدام عنوان بريدك الإلكتروني للتسجيل"
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "حاليا ، لا تدعم الدولة تسجيل رمز التحقق من الهاتف المحمول ، يرجى استخدام عنوان بريدك الإلكتروني للتسجيل",
"注:": "ملاحظة:",
"必需在开始时间24小时内使用一次否则将失效": "يجب استخدامه مرة واحدة خلال 24 ساعة من وقت البدء، وإلا سيصبح غير صالح",
"这是单次密码,只能使用一次": "هذا رمز مرور لمرة واحدة فقط، ولا يمكن استخدامه سوى مرة واحدة",
"您好": "مرحبًا",
"您的开门密码是": "رمز فتح الباب الخاص بك هو",
"开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标": "عند الفتح، قم أولاً بتنشيط لوحة مفاتيح القفل، ثم أدخل الرمز وكلّمه بـ #، ويقع مفتاح # في الزاوية السفلى اليمنى من لوحة المفاتيح وقد يكون مُميّزًا برمز آخر"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1174,5 +1174,11 @@
"服务,图像视频信息随心存!": "Service, image and video information are at your heart!",
"图像": "image",
"视频": "Video",
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "Currently, the country does not support mobile phone verification code registration, please use your email address to register"
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "Currently, the country does not support mobile phone verification code registration, please use your email address to register",
"注:": "Note:",
"必需在开始时间24小时内使用一次否则将失效": "Must be used once within 24 hours of the start time, otherwise it will expire",
"这是单次密码,只能使用一次": "This is a one-time password and can only be used once",
"您好": "Hello",
"您的开门密码是": "Your door access code is",
"开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标": "To unlock, first activate the lock keypad, then enter the password and end with #. The # key is located at the bottom right corner of the keypad and may have a different icon"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1043,7 +1043,6 @@
"一键开锁": "一鍵解鎖",
"已开通": "打開",
"英文": "English",
"英文": "English",
"简体中文": "简体中文",
"繁体中文": "繁體中文",
"法语": "Français",
@ -1169,5 +1168,11 @@
"服务,图像视频信息随心存!": "服務,圖像視頻資訊隨心存!",
"图像": "圖像",
"视频": "視頻",
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "當前國家不支援手機驗證碼註冊,請使用郵箱進行註冊"
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "當前國家不支援手機驗證碼註冊,請使用郵箱進行註冊",
"注:": "註:",
"必需在开始时间24小时内使用一次否则将失效": "必須在開始時間24小時內使用一次否則將失效",
"这是单次密码,只能使用一次": "這是單次密碼,只能使用一次",
"您好": "您好",
"您的开门密码是": "您的開門密碼是",
"开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标": "開鎖時,先啟動鎖鍵盤,再輸入密碼,以#號結束,#號鍵在鍵盤右下角,有可能是其他圖標"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1179,5 +1179,11 @@
"服务,图像视频信息随心存!": "服务,图像视频信息随心存!",
"图像": "图像",
"视频": "视频",
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "当前国家不支持手机验证码注册,请使用邮箱进行注册"
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "当前国家不支持手机验证码注册,请使用邮箱进行注册",
"注:":"注:",
"必需在开始时间24小时内使用一次否则将失效": "必需在开始时间24小时内使用一次否则将失效",
"这是单次密码,只能使用一次": "这是单次密码,只能使用一次",
"您好": "您好",
"您的开门密码是": "您的开门密码是",
"开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标": "开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1173,5 +1173,11 @@
"服务,图像视频信息随心存!": "Informações de serviço, imagem e vídeo estão no seu coração!",
"图像": "imagem",
"视频": "Vídeo",
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "Atualmente, o país não suporta o registro do código de verificação do telefone celular, use seu endereço de e-mail para se registrar"
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "Atualmente, o país não suporta o registro do código de verificação do telefone celular, use seu endereço de e-mail para se registrar",
"注:": "Nota:",
"必需在开始时间24小时内使用一次否则将失效": "Deve ser usado uma vez dentro de 24 horas após o horário de início, caso contrário expirará",
"这是单次密码,只能使用一次": "Esta é uma senha de uso único e só pode ser usada uma vez",
"您好": "Olá",
"您的开门密码是": "Seu código de acesso à porta é",
"开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标": "Para desbloquear, primeiro ative o teclado do cadeado e depois digite a senha terminando com #. A tecla # está no canto inferior direito do teclado e pode ter um ícone diferente"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1168,5 +1168,11 @@
"服务,图像视频信息随心存!": "Сервис , слике и видео информације су у вашем срцу!",
"图像": "Слика",
"视频": "Пријава",
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "Тренутно , земља не подржава регистрацију кода за верификацију мобилног телефона, молимо вас да користите своју адресу е-поште за регистрацију"
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "Тренутно , земља не подржава регистрацију кода за верификацију мобилног телефона, молимо вас да користите своју адресу е-поште за регистрацију",
"注:": "Напомена:",
"必需在开始时间24小时内使用一次否则将失效": "Мора се користити једном у року од 24 сата од времена почетка, иначе истиче",
"这是单次密码,只能使用一次": "Ово је једнократна лозинка која се може користити само једном",
"您好": "Здраво",
"您的开门密码是": "Ваш код за отварање врата је",
"开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标": "Да бисте откључали, прво активирајте тастатуру браве, затим унесите лозинку и завршите са #. Тастер # се налази у доњем десном углу тастатуре и може имати другу икону"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1168,5 +1168,11 @@
"服务,图像视频信息随心存!": "服務,圖像視頻資訊隨心存!",
"图像": "圖像",
"视频": "視頻",
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "當前國家不支援手機驗證碼註冊,請使用郵箱進行註冊"
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "當前國家不支援手機驗證碼註冊,請使用郵箱進行註冊",
"注:": "註:",
"必需在开始时间24小时内使用一次否则将失效": "必須在開始時間24小時內使用一次否則將失效",
"这是单次密码,只能使用一次": "這是單次密碼,只能使用一次",
"您好": "您好",
"您的开门密码是": "您的開門密碼是",
"开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标": "開鎖時,先啟動鎖鍵盤,再輸入密碼,以#號結束,#號鍵在鍵盤右下角,有可能是其他圖標"
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -1180,5 +1180,11 @@
"服务,图像视频信息随心存!": "服务,图像视频信息随心存!",
"图像": "图像",
"视频": "视频",
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "当前国家不支持手机验证码注册,请使用邮箱进行注册"
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "当前国家不支持手机验证码注册,请使用邮箱进行注册",
"注:":"注:",
"必需在开始时间24小时内使用一次否则将失效": "必需在开始时间24小时内使用一次否则将失效",
"这是单次密码,只能使用一次": "这是单次密码,只能使用一次",
"您好": "您好",
"您的开门密码是": "您的开门密码是",
"开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标": "开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标"
}

View File

@ -1,5 +1,5 @@
//<cn>
import 'package:umeng_common_sdk/umeng_common_sdk.dart';
// import 'package:umeng_common_sdk/umeng_common_sdk.dart';
import '../../flavors.dart';
class UmengHelper {
@ -15,27 +15,24 @@ class UmengHelper {
return _instance!;
}
Future<void> initApp() async {
}
Future<void> initApp() async {}
Future<void> initSdk() async {
UmengCommonSdk.initCommon(
F.umengKey.androidKey, F.umengKey.iosKey, F.umengKey.channel);
UmengCommonSdk.setPageCollectionModeManual();
// UmengCommonSdk.initCommon(
// F.umengKey.androidKey, F.umengKey.iosKey, F.umengKey.channel);
// UmengCommonSdk.setPageCollectionModeManual();
}
Future<void> login(String userId) async {
UmengCommonSdk.onProfileSignIn(userId);
// UmengCommonSdk.onProfileSignIn(userId);
}
Future<void> trackEvent(
String eventName, Map<String, Object> parameters) async {
UmengCommonSdk.onEvent(eventName, parameters);
Future<void> trackEvent(String eventName, Map<String, Object> parameters) async {
// UmengCommonSdk.onEvent(eventName, parameters);
}
Future<void> logout() async {
UmengCommonSdk.onProfileSignOff();
// UmengCommonSdk.onProfileSignOff();
}
}
//</cn>

View File

@ -18,6 +18,7 @@ import 'package:star_lock/main/lockDetail/iris/addIris/addIris_page.dart';
import 'package:star_lock/main/lockDetail/iris/addIrisType/addIrisTypeManage/addIrisTypeManage_page.dart';
import 'package:star_lock/main/lockDetail/iris/irisList/irisList_page.dart';
import 'package:star_lock/main/lockDetail/lockDetail/lockDetail_main_page.dart';
import 'package:star_lock/main/lockDetail/lockSet/aiAssistant/ai_assistant_page.dart';
import 'package:star_lock/main/lockDetail/lockSet/catEyeSet/catEyeCustomMode/catEyeCustomMode_page.dart';
import 'package:star_lock/main/lockDetail/lockSet/catEyeSet/catEyeSet/catEyeSet_page.dart';
import 'package:star_lock/main/lockDetail/lockSet/catEyeSet/videoSlot/videoSlot_page.dart';
@ -532,6 +533,7 @@ abstract class Routers {
static const String permissionGuidancePage =
'/permissionGuidancePage'; //
static const String lockVoiceSettingPage = '/lockVoiceSetting'; //
static const String aiAssistant = '/aiAssistant';
}
abstract class AppRouters {
@ -1224,6 +1226,9 @@ abstract class AppRouters {
GetPage<dynamic>(
name: Routers.thirdPartyPlatformPage,
page: () => ThirdPartyPlatformPage()),
GetPage<dynamic>(
name: Routers.aiAssistant,
page: () => AiAssistantPage()),
//
// GetPage<dynamic>(name: Routers.h264View, page: () => H264WebView()), // webview播放页面
];

View File

@ -46,7 +46,7 @@ class BlueManage {
StreamSubscription<BluetoothConnectionState>? _connectionStateSubscription;
StreamSubscription<int>? _mtuSubscription;
int? _mtuSize = 20;
int? _mtuSize = 30;
//
String connectDeviceName = '';
@ -119,8 +119,7 @@ class BlueManage {
_connectionStateSubscription?.cancel();
_connectionStateSubscription = null;
_connectionStateSubscription =
bluetoothConnectDevice!.connectionState.listen((BluetoothConnectionState state) async {
_connectionStateSubscription = bluetoothConnectDevice!.connectionState.listen((BluetoothConnectionState state) async {
bluetoothConnectionState = state;
AppLog.log('蓝牙连接回调状态:$state');
});
@ -159,26 +158,20 @@ class BlueManage {
// AppLog.log('startScanSingle 蓝牙状态 系统蓝牙状态:$_adapterState 蓝牙连接状态:$bluetoothConnectionState');
if (_adapterState == BluetoothAdapterState.on) {
try {
BuglyTool.uploadException(
message: '开始指定设备名称的扫描蓝牙设备', detail: '调用方法是:startScanSingle 指定设备名称是:$deviceName', upload: false);
BuglyTool.uploadException(message: '开始指定设备名称的扫描蓝牙设备', detail: '调用方法是:startScanSingle 指定设备名称是:$deviceName', upload: false);
//android 3
final int divisor = Platform.isAndroid ? 3 : 1;
FlutterBluePlus.startScan(
continuousDivisor: divisor,
continuousUpdates: true,
withKeywords: <String>[deviceName],
timeout: Duration(seconds: timeout));
continuousDivisor: divisor, continuousUpdates: true, withKeywords: <String>[deviceName], timeout: Duration(seconds: timeout));
final Completer<dynamic> completer = Completer<dynamic>();
final StreamSubscription<List<ScanResult>> subscription =
FlutterBluePlus.scanResults.listen((List<ScanResult> results) {
final bool isExit = results.any((ScanResult element) =>
(element.device.platformName == deviceName) || (element.advertisementData.advName == deviceName));
final StreamSubscription<List<ScanResult>> subscription = FlutterBluePlus.scanResults.listen((List<ScanResult> results) {
final bool isExit = results
.any((ScanResult element) => (element.device.platformName == deviceName) || (element.advertisementData.advName == deviceName));
final int milliseconds = DateTime.now().millisecondsSinceEpoch - start.millisecondsSinceEpoch;
AppLog.log('扫描到的设备数:${results.length} 是否查找到 $isExit 以查找$milliseconds毫秒');
BuglyTool.uploadException(
message: '指定设备名称的扫描蓝牙设备 监听扫描结果',
detail:
'startScanSingle$deviceName 监听扫描结果 是否查找到 $isExit 以查找$milliseconds毫秒 扫描到的设备数:${results.length} results:$results',
detail: 'startScanSingle$deviceName 监听扫描结果 是否查找到 $isExit 以查找$milliseconds毫秒 扫描到的设备数:${results.length} results:$results',
upload: false);
if (isExit) {
for (final ScanResult scanResult in results) {
@ -215,8 +208,7 @@ class BlueManage {
completer.complete();
}
}, onError: (e) {
BuglyTool.uploadException(
message: '指定设备名称的扫描蓝牙设备 监听扫描结果失败', detail: '打印失败问题 e${e.toString()}', upload: false);
BuglyTool.uploadException(message: '指定设备名称的扫描蓝牙设备 监听扫描结果失败', detail: '打印失败问题 e${e.toString()}', upload: false);
AppLog.log('扫描失败:$e');
});
FlutterBluePlus.cancelWhenScanComplete(subscription);
@ -224,8 +216,7 @@ class BlueManage {
scanDevicesCallBack(scanDevices);
subscription.cancel();
} catch (e) {
BuglyTool.uploadException(
message: '指定设备名称的扫描蓝牙设备 内部逻辑整形失败', detail: 'tartScanSingle内部逻辑整形失败 e:${e.toString()}', upload: false);
BuglyTool.uploadException(message: '指定设备名称的扫描蓝牙设备 内部逻辑整形失败', detail: 'tartScanSingle内部逻辑整形失败 e:${e.toString()}', upload: false);
AppLog.log('扫描失败');
}
} else {
@ -242,16 +233,14 @@ class BlueManage {
}
///
Future<void> startScan(int timeout, DeviceType deviceType, ScanDevicesCallBack scanDevicesCallBack,
{List<Guid>? idList}) async {
Future<void> startScan(int timeout, DeviceType deviceType, ScanDevicesCallBack scanDevicesCallBack, {List<Guid>? idList}) async {
FlutterBluePlus.isSupported.then((bool isAvailable) async {
if (isAvailable) {
// AppLog.log('startScan 蓝牙状态 系统蓝牙状态:$_adapterState 蓝牙连接状态:$bluetoothConnectionState');
AppLog.log('startScan 蓝牙状态 系统蓝牙状态:$_adapterState 蓝牙连接状态:$bluetoothConnectionState');
if (_adapterState == BluetoothAdapterState.on) {
try {
FlutterBluePlus.startScan(timeout: Duration(seconds: timeout));
final StreamSubscription<List<ScanResult>> subscription =
FlutterBluePlus.scanResults.listen((List<ScanResult> results) {
final StreamSubscription<List<ScanResult>> subscription = FlutterBluePlus.scanResults.listen((List<ScanResult> results) {
scanDevices.clear();
for (final ScanResult scanResult in results) {
if (scanResult.advertisementData.serviceUuids.isNotEmpty) {
@ -394,8 +383,11 @@ class BlueManage {
}
/// List senderData,
Future<void> blueSendData(String deviceName, ConnectStateCallBack stateCallBack,
{bool isAddEquipment = false}) async {
Future<void> blueSendData(
String deviceName,
ConnectStateCallBack stateCallBack, {
bool isAddEquipment = false,
}) async {
FlutterBluePlus.isSupported.then((bool isAvailable) async {
if (isAvailable) {
// AppLog.log('蓝牙状态 系统蓝牙状态:$_adapterState 蓝牙连接状态:$bluetoothConnectionState');
@ -406,22 +398,24 @@ class BlueManage {
message: '点击按钮 蓝牙未连接 下一步扫描连接蓝牙',
detail: 'blueSendData 蓝牙连接状态 bluetoothConnectionState$bluetoothConnectionState deviceName:$deviceName',
upload: false);
_connect(deviceName, (BluetoothConnectionState state) {
stateCallBack(bluetoothConnectionState!);
}, isAddEquipment: isAddEquipment);
//
_connect(
deviceName,
(BluetoothConnectionState state) {
stateCallBack(bluetoothConnectionState!);
},
isAddEquipment: isAddEquipment,
);
} else {
BuglyTool.uploadException(
message: '点击按钮 蓝牙已经连接 下一步扫描连接蓝牙',
detail:
'blueSendData 直接回调状态 蓝牙连接状态bluetoothConnectionState$bluetoothConnectionState deviceName:$deviceName',
detail: 'blueSendData 直接回调状态 蓝牙连接状态bluetoothConnectionState$bluetoothConnectionState deviceName:$deviceName',
upload: false);
stateCallBack(bluetoothConnectionState!);
}
} else {
BuglyTool.uploadException(
message: '点击按钮 蓝牙未打开',
detail: 'blueSendData 蓝牙未打开--_adapterState:${BluetoothAdapterState.on} deviceName:$deviceName',
upload: false);
message: '点击按钮 蓝牙未打开', detail: 'blueSendData 蓝牙未打开--_adapterState:${BluetoothAdapterState.on} deviceName:$deviceName', upload: false);
try {
stateCallBack(BluetoothConnectionState.disconnected);
openBlue();
@ -434,8 +428,7 @@ class BlueManage {
}
}
} else {
BuglyTool.uploadException(
message: '点击按钮 蓝牙状态不可用', detail: 'blueSendData 蓝牙状态不可用--isAvailable:$isAvailable', upload: false);
BuglyTool.uploadException(message: '点击按钮 蓝牙状态不可用', detail: 'blueSendData 蓝牙状态不可用--isAvailable:$isAvailable', upload: false);
stateCallBack(BluetoothConnectionState.disconnected);
AppLog.log('开始扫描 蓝牙不可用,不能进行蓝牙操作');
}
@ -443,8 +436,7 @@ class BlueManage {
}
///
Future<void> _connect(String deviceName, ConnectStateCallBack connectStateCallBack,
{bool isAddEquipment = false}) async {
Future<void> _connect(String deviceName, ConnectStateCallBack connectStateCallBack, {bool isAddEquipment = false}) async {
connectDeviceName = deviceName;
//
final List<ScanResult> devicesList = scanDevices;
@ -525,8 +517,8 @@ class BlueManage {
//
bool isExistScanDevices(String connectDeviceName) {
final bool isExistDevice = scanDevices.any((ScanResult element) =>
element.device.platformName == connectDeviceName || element.advertisementData.advName == connectDeviceName);
final bool isExistDevice = scanDevices
.any((ScanResult element) => element.device.platformName == connectDeviceName || element.advertisementData.advName == connectDeviceName);
return isExistDevice;
}
@ -537,11 +529,8 @@ class BlueManage {
bool isAddEquipment = false, //
bool isReconnect = true, //
}) async {
//
// AppLog.log("devicesList:$devicesList");
final int knownDeviceIndex = devicesList.indexWhere(
(ScanResult d) => (d.device.platformName == deviceName) || (d.advertisementData.advName == deviceName));
final int knownDeviceIndex =
devicesList.indexWhere((ScanResult d) => (d.device.platformName == deviceName) || (d.advertisementData.advName == deviceName));
ScanResult? scanResult; //使
if (knownDeviceIndex >= 0) {
@ -552,7 +541,6 @@ class BlueManage {
bluetoothConnectDevice = devicesList[knownDeviceIndex].device;
scanResult = devicesList[knownDeviceIndex];
// AppLog.log('bluetoothConnectDevice: $bluetoothConnectDevice scanResult:$scanResult');
_initGetMtuSubscription();
_initListenConnectionState();
@ -564,87 +552,13 @@ class BlueManage {
upload: false);
return;
}
AppLog.log('调用了停止扫描的方法');
await stopScan();
if (scanResult.advertisementData.serviceUuids[0].toString().length >= 5 &&
(scanResult.advertisementData.serviceUuids[0].toString()[5] == '0') &&
isAddEquipment == false) {
//
if (isReconnect == true) {
AppLog.log('该锁已被重置, 重新发送扫描命令');
BuglyTool.uploadException(
message: '该锁已被重置, 重新发送扫描命令startScanSingle 上传记录当前方法是_connectDevice',
detail:
'添加这个判断是因为有些苹果设备或者安卓等性能比较好的设备时,添加完锁之后,锁板未改变为已添加状态之前,就进行了蓝牙连接,导致添加完锁就失败,这里进行了判断,如果第一次连接失败,就清除缓存重新扫描连接 该锁已被重置, 重新发送扫描命令 serviceUuids:${scanResult.advertisementData.serviceUuids[0].toString()}',
upload: false);
scanDevices.clear();
startScanSingle(deviceName, 15, (List<ScanResult> scanDevices) {
_connectDevice(scanDevices, deviceName, connectStateCallBack,
isAddEquipment: isAddEquipment, isReconnect: false);
});
} else {
connectStateCallBack(BluetoothConnectionState.disconnected);
if (!F.isSKY) {
EasyLoading.showToast('该锁已被重置'.tr, duration: 2000.milliseconds);
}
scanDevices.clear();
BuglyTool.uploadException(
message: '提示该锁已被重置, 回调断开连接, 清除缓存上传记录当前方法是_connectDevice',
detail: 'isReconnect:$isReconnect serviceUuids:${scanResult.advertisementData.serviceUuids[0].toString()}',
upload: false);
}
return;
}
if (scanResult.advertisementData.serviceUuids[0].toString().length >= 30 &&
(scanResult.advertisementData.serviceUuids[0].toString()[31] == '0') &&
isAddEquipment == false) {
//
if (isReconnect == true) {
AppLog.log('该锁已被重置, 重新发送扫描命令');
BuglyTool.uploadException(
message: '该锁已被重置, 重新发送扫描命令startScanSingle 上传记录当前方法是_connectDevice',
detail:
'添加这个判断是因为有些苹果设备或者安卓等性能比较好的设备时,添加完锁之后,锁板未改变为已添加状态之前,就进行了蓝牙连接,导致添加完锁就失败,这里进行了判断,如果第一次连接失败,就清除缓存重新扫描连接 该锁已被重置, 重新发送扫描命令 serviceUuids:${scanResult.advertisementData.serviceUuids[0].toString()}',
upload: false);
scanDevices.clear();
startScanSingle(deviceName, 15, (List<ScanResult> scanDevices) {
_connectDevice(scanDevices, deviceName, connectStateCallBack,
isAddEquipment: isAddEquipment, isReconnect: true);
});
} else {
connectStateCallBack(BluetoothConnectionState.disconnected);
if (!F.isSKY) {
EasyLoading.showToast('该锁已被重置'.tr, duration: 2000.milliseconds);
}
scanDevices.clear();
BuglyTool.uploadException(
message: '提示该锁已被重置, 回调断开连接, 清除缓存上传记录当前方法是_connectDevice',
detail: 'isReconnect:$isReconnect serviceUuids:${scanResult.advertisementData.serviceUuids[0].toString()}',
upload: false);
}
return;
}
BuglyTool.uploadException(
message: '从devicesList里面查到了设备 下一步连接设备 上传记录当前方法是_connectDevice',
detail:
'devicesList:$devicesList scanResult:${scanResult.toString()} bluetoothConnectDevice${bluetoothConnectDevice.toString()} connectDeviceMacAddress$connectDeviceMacAddress',
upload: false);
//
await bluetoothDeviceConnect(bluetoothConnectDevice!, connectStateCallBack);
}
//
Future<void> doNotSearchBLE(String masAdds, ConnectStateCallBack connectStateCallBack,
{bool isAddEquipment = false}) async {
Future<void> doNotSearchBLE(String masAdds, ConnectStateCallBack connectStateCallBack, {bool isAddEquipment = false}) async {
await FlutterBluePlus.stopScan();
if (bluetoothConnectDevice == null || bluetoothConnectDevice?.remoteId.str != masAdds) {
@ -652,9 +566,7 @@ class BlueManage {
_initGetMtuSubscription();
_initListenConnectionState();
BuglyTool.uploadException(
message: '直接给蓝牙设备写入 上传记录当前方法是doNotSearchBLE',
detail: '直接给蓝牙设备写入 通过fromId方法创建一个BluetoothDevice masAdds$masAdds',
upload: false);
message: '直接给蓝牙设备写入 上传记录当前方法是doNotSearchBLE', detail: '直接给蓝牙设备写入 通过fromId方法创建一个BluetoothDevice masAdds$masAdds', upload: false);
} else {
BuglyTool.uploadException(
message: '直接给蓝牙设备写入 上传记录当前方法是doNotSearchBLE',
@ -673,7 +585,10 @@ class BlueManage {
int attempt = 0;
while (attempt < maxAttempts) {
try {
await bluetoothConnectDevice.connect(timeout: 5.seconds);
await bluetoothConnectDevice.connect(
timeout: 5.seconds,
mtu: 512,
);
break; // If the connection is successful, break the loop
} catch (e) {
AppLog.log('连接失败 重连了: $e');
@ -706,6 +621,10 @@ class BlueManage {
_subScribeToCharacteristic(characteristic);
bluetoothConnectionState = BluetoothConnectionState.connected;
connectStateCallBack(bluetoothConnectionState!);
if (Platform.isAndroid) {
await bluetoothConnectDevice.requestMtu(512);
}
BuglyTool.uploadException(
message: '订阅成功 上传记录当前方法是bluetoothDeviceConnect',
detail: '发现服务,连接成功,订阅数据 bluetoothDeviceConnect:${bluetoothConnectDevice.toString()} ',
@ -732,9 +651,7 @@ class BlueManage {
connectStateCallBack(bluetoothConnectionState!);
AppLog.log('发现设备时失败 e:$e bluetoothConnectionState:$bluetoothConnectionState');
BuglyTool.uploadException(
message: '发现服务时失败',
detail: '发现服务时报错原因e$e bluetoothDeviceConnect:${bluetoothConnectDevice.toString()}',
upload: false);
message: '发现服务时失败', detail: '发现服务时报错原因e$e bluetoothDeviceConnect:${bluetoothConnectDevice.toString()}', upload: false);
rethrow;
}
}
@ -804,12 +721,24 @@ class BlueManage {
for (final BluetoothCharacteristic characteristic in service.characteristics) {
if (characteristic.characteristicUuid == _characteristicIdWrite) {
try {
_initGetMtuSubscription();
// MTU还是默认值
if ((_mtuSize == 23 || _mtuSize == 20) && bluetoothConnectDevice != null) {
try {
if (Platform.isAndroid) {
await bluetoothConnectDevice!.requestMtu(512);
}
} catch (e) {
AppLog.log('重新请求MTU失败: $e');
}
}
//
int retryCount = 0;
const int maxRetries = 3;
const int retryDelayMs = 500;
final List<int> valueList = value;
AppLog.log('发送数据时当前的mtuSize是:${_mtuSize}');
final List subData = splitList(valueList, _mtuSize!);
for (int i = 0; i < subData.length; i++) {
@ -885,14 +814,12 @@ class BlueManage {
_mtuSize = 20; // MTU为默认值
if (bluetoothConnectionState == BluetoothConnectionState.connected) {
AppLog.log('请求断开蓝牙连接');
//
await bluetoothConnectDevice!.disconnect(timeout: 3);
AppLog.log('断开连接成功');
await bluetoothConnectDevice!.disconnect(timeout: 1);
}
} on Exception catch (e, _) {
AppLog.log('断开连接失败: $e');
} finally {
bluetoothConnectionState = BluetoothConnectionState.disconnected;
}
}

View File

@ -0,0 +1,74 @@
import 'dart:convert';
import '../io_tool/io_tool.dart';
import '../sm4Encipher/sm4.dart';
import '../io_reply.dart';
import '../io_sender.dart';
import '../io_type.dart';
import 'package:crypto/crypto.dart' as crypto;
///
class SenderReadRegisterKeyCommand extends SenderProtocol {
SenderReadRegisterKeyCommand({
this.lockID,
this.token,
this.needAuthor,
this.publicKey,
this.privateKey,
}) : super(CommandType.readRegisterKey);
String? lockID;
List<int>? token;
int? needAuthor;
List<int>? publicKey;
List<int>? privateKey;
@override
String toString() {
return 'SenderReadRegisterKeyCommand{ lockID: $lockID, token: $token, '
'needAuthor: $needAuthor, publicKey: $publicKey, '
'privateKey: $privateKey}';
}
@override
List<int> messageDetail() {
List<int> data = <int>[];
List<int> ebcData = <int>[];
//
final int type = commandType!.typeValue;
final double typeDouble = type / 256;
final int type1 = typeDouble.toInt();
final int type2 = type % 256;
data.add(type1);
data.add(type2);
// id 40
final int lockIDLength = utf8.encode(lockID!).length;
data.addAll(utf8.encode(lockID!));
data = getFixedLengthList(data, 40 - lockIDLength);
if ((data.length % 16) != 0) {
final int add = 16 - data.length % 16;
for (int i = 0; i < add; i++) {
data.add(0);
}
}
printLog(data);
ebcData = SM4.encrypt(data, key: privateKey, mode: SM4CryptoMode.ECB);
return ebcData;
}
}
class SenderReadRegisterKeyCommandReply extends Reply {
SenderReadRegisterKeyCommandReply.parseData(CommandType commandType, List<int> dataDetail)
: super.parseData(commandType, dataDetail) {
data = dataDetail;
final int status = data[6];
errorWithStstus(status);
}
}

View File

@ -0,0 +1,106 @@
import 'dart:convert';
import '../io_tool/io_tool.dart';
import '../sm4Encipher/sm4.dart';
import '../io_reply.dart';
import '../io_sender.dart';
import '../io_type.dart';
import 'package:crypto/crypto.dart' as crypto;
///
class SenderAuthorizationCodeCommand extends SenderProtocol {
SenderAuthorizationCodeCommand({
this.lockID,
this.uuid,
this.key,
this.mac,
this.platform,
this.utcTimeStamp,
this.token,
this.needAuthor,
this.publicKey,
this.privateKey,
}) : super(CommandType.sendAuthorizationCode);
String? lockID;
String? uuid;
String? key;
String? mac;
int? platform; //01
int? utcTimeStamp;
List<int>? token;
int? needAuthor;
List<int>? publicKey;
List<int>? privateKey;
@override
String toString() {
return 'SenderAuthorizationCodeCommand{ lockID: $lockID, token: $token, '
'needAuthor: $needAuthor, publicKey: $publicKey, '
'privateKey: $privateKey}';
}
@override
List<int> messageDetail() {
List<int> data = <int>[];
List<int> ebcData = <int>[];
//
final int type = commandType!.typeValue;
final double typeDouble = type / 256;
final int type1 = typeDouble.toInt();
final int type2 = type % 256;
data.add(type1);
data.add(type2);
// id 40
final int lockIDLength = utf8.encode(lockID!).length;
data.addAll(utf8.encode(lockID!));
data = getFixedLengthList(data, 40 - lockIDLength);
// uuid 40
final int uuidLength = utf8.encode(uuid!).length;
data.addAll(utf8.encode(uuid!));
data = getFixedLengthList(data, 40 - uuidLength);
// key 40
final int keyLength = utf8.encode(key!).length;
data.addAll(utf8.encode(key!));
data = getFixedLengthList(data, 40 - keyLength);
// mac 40
final int macLength = utf8.encode(mac!).length;
data.addAll(utf8.encode(mac!));
data = getFixedLengthList(data, 40 - macLength);
data.add(platform!);
data.add((utcTimeStamp! & 0xff000000) >> 24);
data.add((utcTimeStamp! & 0xff0000) >> 16);
data.add((utcTimeStamp! & 0xff00) >> 8);
data.add(utcTimeStamp! & 0xff);
if ((data.length % 16) != 0) {
final int add = 16 - data.length % 16;
for (int i = 0; i < add; i++) {
data.add(0);
}
}
printLog(data);
ebcData = SM4.encrypt(data, key: privateKey, mode: SM4CryptoMode.ECB);
return ebcData;
}
}
class SenderAuthorizationCodeCommandReply extends Reply {
SenderAuthorizationCodeCommandReply.parseData(CommandType commandType, List<int> dataDetail)
: super.parseData(commandType, dataDetail) {
data = dataDetail;
final int status = data[6];
errorWithStstus(status);
}
}

View File

@ -27,6 +27,11 @@ class EventSendModel {
Uuid? serviceId;
Uuid? characteristicId;
bool? allowLongWrite = false;
@override
String toString() {
return 'EventSendModel{data: $data, topic: $topic, sendChannel: $sendChannel, deviceId: $deviceId, serviceId: $serviceId, characteristicId: $characteristicId, allowLongWrite: $allowLongWrite}';
}
}
///

View File

@ -9,10 +9,10 @@ List<String> getDeviceType(DeviceType deviceType) {
List<String> t = ['758824'];
switch (deviceType) {
case DeviceType.blue:
t = ['758824', '75', '768824', '76','24'];
t = ['758824', '75', '768824', '76', '24'];
break;
case DeviceType.gateway:
t = ['758825','25'];
t = ['758825', '25'];
break;
}
return t;
@ -53,6 +53,8 @@ enum CommandType {
gatewayGetWifiList, //wifi列表 0x30F6
gatewayGetWifiListResult, //wifi列表结果 0x30F7
gatewayGetStatus, // 0x30F8
readRegisterKey, // 0x30A7
sendAuthorizationCode, // 0x30A6
generalExtendedCommond, // = 0x3030
gecChangeAdministratorPassword, // - = 2
@ -245,6 +247,16 @@ extension ExtensionCommandType on CommandType {
type = CommandType.gatewayGetStatus;
}
break;
case 0x30A6:
{
type = CommandType.sendAuthorizationCode;
}
break;
case 0x30A7:
{
type = CommandType.readRegisterKey;
}
break;
default:
{
type = CommandType.readStarLockStatusInfo;
@ -353,6 +365,12 @@ extension ExtensionCommandType on CommandType {
case CommandType.setLockCurrentVoicePacket:
type = 0x30A5;
break;
case CommandType.sendAuthorizationCode:
type = 0x30A6;
break;
case CommandType.readRegisterKey:
type = 0x30A7;
break;
default:
type = 0x300A;
break;
@ -492,6 +510,12 @@ extension ExtensionCommandType on CommandType {
case 0x30A5:
t = '设置锁当前语音包';
break;
case 0x30A6:
t = '发送授权码';
break;
case 0x30A7:
t = '读取注册密钥';
break;
default:
t = '读星锁状态信息';
break;

View File

@ -16,10 +16,12 @@ import 'package:star_lock/blue/io_protocol/io_getDeviceModel.dart';
import 'package:star_lock/blue/io_protocol/io_otaUpgrade.dart';
import 'package:star_lock/blue/io_protocol/io_processOtaUpgrade.dart';
import 'package:star_lock/blue/io_protocol/io_readAdminPassword.dart';
import 'package:star_lock/blue/io_protocol/io_readRegisterKey.dart';
import 'package:star_lock/blue/io_protocol/io_readSupportFunctionsNoParameters.dart';
import 'package:star_lock/blue/io_protocol/io_readSupportFunctionsWithParameters.dart';
import 'package:star_lock/blue/io_protocol/io_readVoicePackageFinalResult.dart';
import 'package:star_lock/blue/io_protocol/io_referEventRecordTime.dart';
import 'package:star_lock/blue/io_protocol/io_sendAuthorizationCode.dart';
import 'package:star_lock/blue/io_protocol/io_setSupportFunctionsNoParameters.dart';
import 'package:star_lock/blue/io_protocol/io_setSupportFunctionsWithParameters.dart';
import 'package:star_lock/blue/io_protocol/io_setVoicePackageFinalResult.dart';
@ -331,6 +333,18 @@ class CommandReciverManager {
SetVoicePackageFinalResultReply.parseData(commandType, data);
}
break;
case CommandType.readRegisterKey:
{
reply =
SenderReadRegisterKeyCommandReply.parseData(commandType, data);
}
break;
case CommandType.sendAuthorizationCode:
{
reply =
SenderAuthorizationCodeCommandReply.parseData(commandType, data);
}
break;
case CommandType.generalExtendedCommond:
{
//

View File

@ -1,4 +1,3 @@
import 'dart:async';
import 'package:star_lock/blue/entity/lock_user_no_list_entity.dart';
@ -22,10 +21,10 @@ import 'io_tool/manager_event_bus.dart';
import 'sender_data.dart';
class SenderBeforeDataManage {
factory SenderBeforeDataManage() => shareManager()!;
SenderBeforeDataManage._init();
static SenderBeforeDataManage? _manager;
static SenderBeforeDataManage? shareManager() {
@ -42,6 +41,7 @@ class SenderBeforeDataManage {
//
StreamSubscription<Reply>? _replySubscription;
//
bool isBeforeAddUser = true;
@ -123,14 +123,14 @@ class SenderBeforeDataManage {
final int status = reply.data[6];
switch (status) {
case 0x00:
//
//
CommonDataManage().initUserNo = 0;
CommonDataManage().currentKeyInfo.initUserNo = 0;
_updateLockInitUserNo();
break;
case 0x06:
//
//
final List<int> token = reply.data.sublist(2, 6);
final List<String> strTokenList = changeIntListToStringList(token);
Storage.setStringList(saveBlueToken, strTokenList);
@ -139,17 +139,15 @@ class SenderBeforeDataManage {
CommandSenderManager().sendNormalData(transferSmartLockData);
break;
default:
//
//
break;
}
}
//
Future<List<int>> getCleanUpUsers({List<int>? tokenList}) async {
final LockUserNoListEntity entity = await ApiRepository.to
.getLockUserNoList(lockId: CommonDataManage().currentKeyInfo.lockId!);
if (!entity.errorCode!.codeIsSuccessful ||
(entity.data?.userNos ?? <int>[]).isEmpty) {
final LockUserNoListEntity entity = await ApiRepository.to.getLockUserNoList(lockId: CommonDataManage().currentKeyInfo.lockId!);
if (!entity.errorCode!.codeIsSuccessful || (entity.data?.userNos ?? <int>[]).isEmpty) {
throw Exception('ApiRepository.to.getLockUserNoList 访问失败');
}
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
@ -197,27 +195,28 @@ class SenderBeforeDataManage {
int endDateTime = 0;
bool isRound = false;
int useCountLimit = 0xffff;
if(currentKeyInfo.keyType == XSConstantMacro.keyTypeTime){
if (currentKeyInfo.keyType == XSConstantMacro.keyTypeTime) {
//
startDateTime = currentKeyInfo.startDate! ~/ 1000;
endDateTime = currentKeyInfo.endDate! ~/ 1000;
}else if(currentKeyInfo.keyType == XSConstantMacro.keyTypeLoop){
} else if (currentKeyInfo.keyType == XSConstantMacro.keyTypeLoop) {
//
isRound = true;
startTime = DateTime.fromMillisecondsSinceEpoch(currentKeyInfo.startDate!);
endTime = DateTime.fromMillisecondsSinceEpoch(currentKeyInfo.endDate!);
startDateTime = DateTool().dateToTimestamp(DateTool().dateToYMDString(currentKeyInfo.startDate!.toString()), 1) ~/ 1000;
endDateTime = (DateTool().dateToTimestamp(DateTool().dateToYMDString(currentKeyInfo.endDate!.toString()), 1) + CommonDataManage().dayLatestTime) ~/ 1000;
}else if(currentKeyInfo.keyType == XSConstantMacro.keyTypeOnce){
//
useCountLimit = 1;
}
endDateTime =
(DateTool().dateToTimestamp(DateTool().dateToYMDString(currentKeyInfo.endDate!.toString()), 1) + CommonDataManage().dayLatestTime) ~/ 1000;
} else if (currentKeyInfo.keyType == XSConstantMacro.keyTypeOnce) {
//
useCountLimit = 1;
}
// AppLog.log("startTime.hour:${startTime!.hour} startTime.minute:${startTime!.minute} endTime.hour:${endTime!.hour} endTime.minute:${endTime!.minute}}");
// AppLog.log("startTime.hour:${startTime!.hour} startTime.minute:${startTime!.minute} endTime.hour:${endTime!.hour} endTime.minute:${endTime!.minute}}");
final AddUserCommand addUserData = AddUserCommand(
lockID: BlueManage().connectDeviceName,
authUserID: currentKeyInfo.senderUserId!.toString(),
authUserID: currentKeyInfo.senderUserId?.toString() ?? '1',
keyID: currentKeyInfo.keyId.toString(),
userID: await Storage.getUid(),
openMode: 1,
@ -226,10 +225,7 @@ class SenderBeforeDataManage {
expireDate: endDateTime,
useCountLimit: useCountLimit,
isRound: isRound ? 1 : 0,
weekRound: isRound
? DateTool().accordingTheCycleIntoTheCorrespondingNumber(
currentKeyInfo.weekDays!)
: 0,
weekRound: isRound ? DateTool().accordingTheCycleIntoTheCorrespondingNumber(currentKeyInfo.weekDays!) : 0,
startHour: isRound ? startTime!.hour : 0,
startMin: isRound ? startTime!.minute : 0,
endHour: isRound ? endTime!.hour : 0,
@ -271,8 +267,7 @@ class SenderBeforeDataManage {
// NO
Future<void> _updateLockUserNo(List<int> dataList) async {
final LockNetTokenEntity entity = await ApiRepository.to.updateLockUserNo(
keyId: CommonDataManage().currentKeyInfo.keyId.toString(),
lockUserNo: CommonDataManage().currentKeyInfo.lockUserNo.toString());
keyId: CommonDataManage().currentKeyInfo.keyId.toString(), lockUserNo: CommonDataManage().currentKeyInfo.lockUserNo.toString());
if (entity.errorCode!.codeIsSuccessful) {
eventBus.fire(RefreshLockListInfoDataEvent());
eventBus.fire(LockAddUserSucceedEvent(<int>[0], 0));
@ -281,9 +276,8 @@ class SenderBeforeDataManage {
// InitUserNo
Future<void> _updateLockInitUserNo() async {
final LockNetTokenEntity entity = await ApiRepository.to.updateLockInitUserNo(
lockId: CommonDataManage().currentKeyInfo.lockId ?? 0,
initUserNo: CommonDataManage().currentKeyInfo.initUserNo ?? 0);
final LockNetTokenEntity entity = await ApiRepository.to
.updateLockInitUserNo(lockId: CommonDataManage().currentKeyInfo.lockId ?? 0, initUserNo: CommonDataManage().currentKeyInfo.initUserNo ?? 0);
if (entity.errorCode!.codeIsSuccessful) {
eventBus.fire(RefreshLockListInfoDataEvent());
eventBus.fire(LockInitUserNoEvent());

View File

@ -6,6 +6,8 @@ import 'package:star_lock/blue/io_protocol/io_deletUser.dart';
import 'package:star_lock/blue/io_protocol/io_otaUpgrade.dart';
import 'package:star_lock/blue/io_protocol/io_processOtaUpgrade.dart';
import 'package:star_lock/blue/io_protocol/io_readAdminPassword.dart';
import 'package:star_lock/blue/io_protocol/io_readRegisterKey.dart';
import 'package:star_lock/blue/io_protocol/io_sendAuthorizationCode.dart';
import 'io_gateway/io_gateway_configuringWifi.dart';
import 'io_gateway/io_gateway_getStatus.dart';
@ -1109,10 +1111,7 @@ class IoSenderManage {
//ota
static void senderProcessOtaUpgradeCommand(
{required int? index,
required int? size,
required List<int>? data,
CommandSendCallBack? callBack}) {
{required int? index, required int? size, required List<int>? data, CommandSendCallBack? callBack}) {
CommandSenderManager().managerSendData(
command: ProcessOtaUpgradeCommand(
index: index,
@ -1321,8 +1320,7 @@ class IoSenderManage {
}
// wifi列表
static void gatewayGetWifiCommand(
{required String? userID, CommandSendCallBack? callBack}) {
static void gatewayGetWifiCommand({required String? userID, CommandSendCallBack? callBack}) {
CommandSenderManager().managerSendData(
command: GatewayGetWifiCommand(
userID: userID,
@ -1339,21 +1337,51 @@ class IoSenderManage {
CommandSendCallBack? callBack}) {
CommandSenderManager().managerSendData(
command: GatewayConfiguringWifiCommand(
ssid: ssid,
password: password,
gatewayConfigurationStr: gatewayConfigurationStr),
ssid: ssid, password: password, gatewayConfigurationStr: gatewayConfigurationStr),
isBeforeAddUser: true,
callBack: callBack);
}
//
static void gatewayGetStatusCommand(
{required String? lockID,
required String? userID,
CommandSendCallBack? callBack}) {
{required String? lockID, required String? userID, CommandSendCallBack? callBack}) {
CommandSenderManager().managerSendData(
command: GatewayGetStatusCommand(lockID: lockID, userID: userID),
isBeforeAddUser: true,
callBack: callBack);
command: GatewayGetStatusCommand(lockID: lockID, userID: userID), isBeforeAddUser: true, callBack: callBack);
}
//
static void readRegisterKey({
required String? lockID,
CommandSendCallBack? callBack,
}) {
CommandSenderManager().managerSendData(
command: SenderReadRegisterKeyCommand(lockID: lockID),
isBeforeAddUser: true,
callBack: callBack,
);
}
//
static void sendAuthorizationCode({
required String? lockID,
required String? uuid,
required String? key,
required String? mac,
required int? platform,
required int? utcTimeStamp,
CommandSendCallBack? callBack,
}) {
CommandSenderManager().managerSendData(
command: SenderAuthorizationCodeCommand(
lockID: lockID,
uuid: uuid,
key: key,
mac: mac,
platform: platform,
utcTimeStamp: utcTimeStamp,
),
isBeforeAddUser: true,
callBack: callBack,
);
}
}

View File

@ -45,272 +45,229 @@ class _StarLockLoginPageState extends State<StarLockLoginPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: const Color(0xFFFFFFFF),
appBar: TitleAppBar(
barTitle: '登录'.tr,
haveBack: false,
backgroundColor: AppColors.mainColor,
actionsList: <Widget>[
if (state.isChina == true)
IconButton(
onPressed: () {
WechatManageTool.getAppInfo(
CustomerTool.openCustomerService);
resizeToAvoidBottomInset: false,
backgroundColor: const Color(0xFFFFFFFF),
appBar: TitleAppBar(
barTitle: '登录'.tr,
haveBack: false,
backgroundColor: AppColors.mainColor,
actionsList: <Widget>[
if (state.isChina == true)
IconButton(
onPressed: () {
WechatManageTool.getAppInfo(CustomerTool.openCustomerService);
},
icon: const Icon(
Icons.support_agent,
color: Colors.white,
)),
TextButton(
child: Text(
'注册'.tr,
style: TextStyle(color: Colors.white, fontSize: 24.sp),
),
onPressed: () async {
final data = await Get.toNamed(Routers.starLockRegisterPage);
if (data != null) {
state.emailOrPhoneController.text = data['phoneOrEmailStr'];
logic.checkNext(state.emailOrPhoneController);
state.pwdController.text = data['pwd'];
logic.checkNext(state.pwdController);
setState(() {});
}
},
),
],
),
body: GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child: ListView(
padding: EdgeInsets.only(top: 120.h, left: 40.w, right: 40.w),
children: <Widget>[
Container(
padding: EdgeInsets.all(10.w),
child: Center(child: Image.asset('images/icon_main_sky_1024.png', width: 110.w, height: 110.w))),
SizedBox(height: 50.w),
Obx(() => CommonItem(
leftTitel: '你所在的国家/地区'.tr,
rightTitle: '',
isHaveLine: true,
isPadding: false,
isHaveRightWidget: true,
isHaveDirection: true,
rightWidget: Text(
'${state.countryName} +${state.countryCode.value}',
textAlign: TextAlign.end,
style: TextStyle(fontSize: 22.sp, color: AppColors.darkGrayTextColor),
),
action: () async {
final result = await Get.toNamed(Routers.selectCountryRegionPage);
if (result != null) {
result as Map<String, dynamic>;
state.countryCode.value = result['code'];
state.countryKey.value = result['countryName'];
logic.checkIpAction();
}
},
icon: const Icon(
Icons.support_agent,
color: Colors.white,
)),
TextButton(
child: Text(
'注册'.tr,
style: TextStyle(color: Colors.white, fontSize: 24.sp),
),
onPressed: () async {
final data = await Get.toNamed(Routers.starLockRegisterPage);
if (data != null) {
state.emailOrPhoneController.text = data['phoneOrEmailStr'];
)),
LoginInput(
focusNode: logic.state.emailOrPhoneFocusNode,
controller: state.emailOrPhoneController,
onchangeAction: (v) {
logic.checkNext(state.emailOrPhoneController);
state.pwdController.text = data['pwd'];
},
leftWidget: Padding(
padding: EdgeInsets.only(top: 30.w, bottom: 20.w, right: 5.w, left: 5.w),
child: Image.asset(
'images/icon_login_account.png',
width: 36.w,
height: 36.w,
),
),
hintText: '请输入手机号或者邮箱'.tr,
// keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
// FilteringTextInputFormatter.allow(RegExp('[0-9]')),
LengthLimitingTextInputFormatter(30),
FilteringTextInputFormatter.singleLineFormatter
]),
SizedBox(height: 10.h),
LoginInput(
focusNode: logic.state.pwdFocusNode,
controller: state.pwdController,
onchangeAction: (v) {
logic.checkNext(state.pwdController);
setState(() {});
}
},
},
isPwd: true,
// isSuffixIcon: 2,
leftWidget: Padding(
padding: EdgeInsets.only(top: 30.w, bottom: 20.w, right: 5.w, left: 5.w),
child: Image.asset(
'images/icon_login_password.png',
width: 36.w,
height: 36.w,
),
),
hintText: '请输入密码'.tr,
inputFormatters: <TextInputFormatter>[
LengthLimitingTextInputFormatter(20),
]),
// SizedBox(height: 15.h),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Obx(() => GestureDetector(
onTap: () {
state.agree.value = !state.agree.value;
logic.changeAgreeState();
},
child: Container(
// color: Colors.red,
padding: EdgeInsets.only(left: 5.w, top: 20.w, right: 10.w, bottom: 20.h),
child: Image.asset(
state.agree.value ? 'images/icon_round_select.png' : 'images/icon_round_unSelect.png',
width: 35.w,
height: 35.w,
),
))),
// SizedBox(
// width: 5.w,
// ),
Flexible(
child: RichText(
text: TextSpan(
text: '我已阅读并同意'.tr,
style: TextStyle(color: const Color(0xff333333), fontSize: 20.sp),
children: <InlineSpan>[
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: GestureDetector(
child:
Text('${'用户协议'.tr}', style: TextStyle(color: AppColors.mainColor, fontSize: 20.sp)),
onTap: () {
Get.toNamed(Routers.webviewShowPage, arguments: <String, String>{
'url': XSConstantMacro.userAgreementURL,
'title': '用户协议'.tr
});
},
)),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: GestureDetector(
child:
Text('${'隐私政策'.tr}', style: TextStyle(color: AppColors.mainColor, fontSize: 20.sp)),
onTap: () {
Get.toNamed(Routers.webviewShowPage, arguments: <String, String>{
'url': XSConstantMacro.privacyPolicyURL,
'title': '隐私政策'.tr
});
},
)),
],
)),
)
],
),
SizedBox(height: 50.w),
Obx(() => SubmitBtn(
btnName: '登录'.tr,
fontSize: 28.sp,
borderRadius: 20.w,
padding: EdgeInsets.only(top: 25.w, bottom: 25.w),
isDisabled: state.canNext.value,
onClick: state.canNext.value
? () {
if (state.agree.value == false) {
logic.showToast('请先同意用户协议及隐私政策'.tr);
return;
} else {
logic.login();
}
}
: null)),
// SizedBox(height: 20.w),
// Obx(() => Visibility(
// visible: state.isCheckVerifyEnable.value,
// child: SubmitBtn(
// btnName: '一键登录',
// fontSize: 28.sp,
// borderRadius: 20.w,
// padding: EdgeInsets.only(top: 25.w, bottom: 25.w),
// // isDisabled: state.canNext.value,
// onClick: () {
// if (state.agree.value == false) {
// logic.showToast('请先同意用户协议及隐私政策'.tr);
// return;
// } else {
// logic.oneClickLoginAction();
// }
// }),
// )),
SizedBox(height: 50.w),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GestureDetector(
child: SizedBox(
// width: 150.w,
height: 50.h,
// color: Colors.red,
child: Center(
child: Text('${'忘记密码'.tr}', style: TextStyle(fontSize: 22.sp, color: AppColors.mainColor)),
),
),
onTap: () {
Navigator.pushNamed(context, Routers.starLockForgetPasswordPage);
},
),
],
),
],
),
body: GestureDetector(
onTap: (){
FocusScope.of(context).unfocus();
},
child: ListView(
padding: EdgeInsets.only(top: 120.h, left: 40.w, right: 40.w),
children: <Widget>[
Container(
padding: EdgeInsets.all(10.w),
child: Center(
child: Image.asset('images/icon_main_sky_1024.png',
width: 110.w, height: 110.w))),
SizedBox(height: 50.w),
Obx(() => CommonItem(
leftTitel: '你所在的国家/地区'.tr,
rightTitle: '',
isHaveLine: true,
isPadding: false,
isHaveRightWidget: true,
isHaveDirection: true,
rightWidget: Text(
'${state.countryName} +${state.countryCode.value}',
textAlign: TextAlign.end,
style: TextStyle(
fontSize: 22.sp, color: AppColors.darkGrayTextColor),
),
action: () async {
final result =
await Get.toNamed(Routers.selectCountryRegionPage);
if (result != null) {
result as Map<String, dynamic>;
state.countryCode.value = result['code'];
state.countryKey.value = result['countryName'];
logic.checkIpAction();
}
},
)),
LoginInput(
focusNode: logic.state.emailOrPhoneFocusNode,
controller: state.emailOrPhoneController,
onchangeAction: (v) {
logic.checkNext(state.emailOrPhoneController);
},
leftWidget: Padding(
padding: EdgeInsets.only(
top: 30.w, bottom: 20.w, right: 5.w, left: 5.w),
child: Image.asset(
'images/icon_login_account.png',
width: 36.w,
height: 36.w,
),
),
hintText: '请输入手机号或者邮箱'.tr,
// keyboardType: TextInputType.number,
inputFormatters: <TextInputFormatter>[
// FilteringTextInputFormatter.allow(RegExp('[0-9]')),
LengthLimitingTextInputFormatter(30),
FilteringTextInputFormatter.singleLineFormatter
]),
SizedBox(height: 10.h),
LoginInput(
focusNode: logic.state.pwdFocusNode,
controller: state.pwdController,
onchangeAction: (v) {
logic.checkNext(state.pwdController);
},
isPwd: true,
// isSuffixIcon: 2,
leftWidget: Padding(
padding: EdgeInsets.only(
top: 30.w, bottom: 20.w, right: 5.w, left: 5.w),
child: Image.asset(
'images/icon_login_password.png',
width: 36.w,
height: 36.w,
),
),
hintText: '请输入密码'.tr,
inputFormatters: <TextInputFormatter>[
LengthLimitingTextInputFormatter(20),
]),
// SizedBox(height: 15.h),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Obx(() => GestureDetector(
onTap: () {
state.agree.value = !state.agree.value;
logic.changeAgreeState();
},
child: Container(
// color: Colors.red,
padding: EdgeInsets.only(
left: 5.w, top: 20.w, right: 10.w, bottom: 20.h),
child: Image.asset(
state.agree.value
? 'images/icon_round_select.png'
: 'images/icon_round_unSelect.png',
width: 35.w,
height: 35.w,
),
))),
// SizedBox(
// width: 5.w,
// ),
Flexible(
child: RichText(
text: TextSpan(
text: '我已阅读并同意'.tr,
style: TextStyle(
color: const Color(0xff333333), fontSize: 20.sp),
children: <InlineSpan>[
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: GestureDetector(
child: Text('${'用户协议'.tr}',
style: TextStyle(
color: AppColors.mainColor,
fontSize: 20.sp)),
onTap: () {
Get.toNamed(Routers.webviewShowPage,
arguments: <String, String>{
'url': XSConstantMacro.userAgreementURL,
'title': '用户协议'.tr
});
},
)),
WidgetSpan(
alignment: PlaceholderAlignment.middle,
child: GestureDetector(
child: Text('${'隐私政策'.tr}',
style: TextStyle(
color: AppColors.mainColor,
fontSize: 20.sp)),
onTap: () {
Get.toNamed(Routers.webviewShowPage,
arguments: <String, String>{
'url': XSConstantMacro.privacyPolicyURL,
'title': '隐私政策'.tr
});
},
)),
],
)),
)
],
),
SizedBox(height: 50.w),
Obx(() => SubmitBtn(
btnName: '登录'.tr,
fontSize: 28.sp,
borderRadius: 20.w,
padding: EdgeInsets.only(top: 25.w, bottom: 25.w),
isDisabled: state.canNext.value,
onClick: state.canNext.value
? () {
if (state.agree.value == false) {
logic.showToast('请先同意用户协议及隐私政策'.tr);
return;
} else {
logic.login();
}
}
: null)),
// SizedBox(height: 20.w),
// Obx(() => Visibility(
// visible: state.isCheckVerifyEnable.value,
// child: SubmitBtn(
// btnName: '一键登录',
// fontSize: 28.sp,
// borderRadius: 20.w,
// padding: EdgeInsets.only(top: 25.w, bottom: 25.w),
// // isDisabled: state.canNext.value,
// onClick: () {
// if (state.agree.value == false) {
// logic.showToast('请先同意用户协议及隐私政策'.tr);
// return;
// } else {
// logic.oneClickLoginAction();
// }
// }),
// )),
SizedBox(height: 50.w),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
GestureDetector(
child: SizedBox(
// width: 150.w,
height: 50.h,
// color: Colors.red,
child: Center(
child: Text('${'忘记密码'.tr}',
style: TextStyle(
fontSize: 22.sp, color: AppColors.mainColor)),
),
),
onTap: () {
Navigator.pushNamed(
context, Routers.starLockForgetPasswordPage);
},
),
Expanded(
child: SizedBox(
width: 10.sp,
)),
Obx(() => Visibility(
visible: state.isCheckVerifyEnable.value &&
state.currentLanguage == 'zh_CN',
child: GestureDetector(
child: SizedBox(
// width: 150.w,
height: 50.h,
// color: Colors.red,
child: Center(
child: Text('一键登录'.tr,
style: TextStyle(
fontSize: 22.sp,
color: AppColors.mainColor)),
),
),
onTap: () {
logic.oneClickLoginAction(context);
},
),
))
],
),
],
),
));
),
);
}
Widget loginInput(
@ -351,8 +308,7 @@ class _StarLockLoginPageState extends State<StarLockLoginPage> {
inputFormatters: inputFormatters,
decoration: InputDecoration(
//
contentPadding: const EdgeInsets.only(
top: 8.0, left: -19.0, right: -15.0, bottom: 8.0),
contentPadding: const EdgeInsets.only(top: 8.0, left: -19.0, right: -15.0, bottom: 8.0),
labelText: label,
labelStyle: TextStyle(fontSize: 22.sp),
hintStyle: TextStyle(fontSize: 22.sp),

View File

@ -101,6 +101,7 @@ class StarLockRegisterLogic extends BaseGetXController {
if (entity.errorCode!.codeIsSuccessful) {
if (entity.data!.abbreviation != 'CN') {
showToast('当前国家不支持手机验证码注册,请使用邮箱进行注册'.tr);
state.isIphoneType.value = false;
return;
}
if (state.countryName.value != entity.data!.name) {
@ -149,7 +150,7 @@ class StarLockRegisterLogic extends BaseGetXController {
}
@override
void onReady() {
void onReady() async {
super.onReady();
XSConstantMacro().getDeviceInfoData().then((Map<String, dynamic> data) {
@ -158,5 +159,7 @@ class StarLockRegisterLogic extends BaseGetXController {
//
AppLog.log('获取设备信息时出错: $error');
});
await checkIpAction();
}
}

View File

@ -31,44 +31,45 @@ class _StarLockRegisterPageState extends State<StarLockRegisterPage> {
@override
Widget build(BuildContext context) {
return Scaffold(
resizeToAvoidBottomInset: false,
backgroundColor: const Color(0xFFFFFFFF),
appBar: TitleAppBar(barTitle: '注册'.tr, haveBack: true, backgroundColor: AppColors.mainColor),
body: GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child: ListView(
padding: EdgeInsets.only(top: 40.h, left: 40.w, right: 40.w),
children: <Widget>[
topSelectCountryAndRegionWidget(),
middleTFWidget(),
Obx(() {
return SubmitBtn(
btnName: '注册'.tr,
// backgroundColorList: state.canSub.value ? [AppColors.mainColor] :[Colors.grey],
fontSize: 30.sp,
borderRadius: 20.w,
padding: EdgeInsets.only(top: 25.w, bottom: 25.w),
isDisabled: state.canSub.value,
onClick: state.canSub.value
? () {
if (state.agree.value == false) {
logic.showToast('请先同意用户协议及隐私政策'.tr);
return;
} else {
logic.register();
}
resizeToAvoidBottomInset: false,
backgroundColor: const Color(0xFFFFFFFF),
appBar: TitleAppBar(barTitle: '注册'.tr, haveBack: true, backgroundColor: AppColors.mainColor),
body: GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child: ListView(
padding: EdgeInsets.only(top: 40.h, left: 40.w, right: 40.w),
children: <Widget>[
topSelectCountryAndRegionWidget(),
middleTFWidget(),
Obx(() {
return SubmitBtn(
btnName: '注册'.tr,
// backgroundColorList: state.canSub.value ? [AppColors.mainColor] :[Colors.grey],
fontSize: 30.sp,
borderRadius: 20.w,
padding: EdgeInsets.only(top: 25.w, bottom: 25.w),
isDisabled: state.canSub.value,
onClick: state.canSub.value
? () {
if (state.agree.value == false) {
logic.showToast('请先同意用户协议及隐私政策'.tr);
return;
} else {
logic.register();
}
: null);
}),
SizedBox(
height: 20.h,
),
_buildBottomAgreement()
],
),
));
}
: null);
}),
SizedBox(
height: 20.h,
),
_buildBottomAgreement()
],
),
),
);
}
Widget topSelectCountryAndRegionWidget() {

View File

@ -316,6 +316,7 @@ class FaceListLogic extends BaseGetXController {
{required bool isRefresh}) async {
if (isRefresh) {
state.faceItemListData.clear();
state.faceItemListData.refresh();
pageNo = 1;
}
final FingerprintListDataEntity entity =

View File

@ -25,11 +25,9 @@ class FingerprintListLogic extends BaseGetXController {
late StreamSubscription<Reply> _replySubscription;
void _initReplySubscription() {
_replySubscription =
EventBusManager().eventBus!.on<Reply>().listen((Reply reply) {
_replySubscription = EventBusManager().eventBus!.on<Reply>().listen((Reply reply) {
// ()
if ((reply is SenderAddFingerprintWithTimeCycleCoercionReply) &&
(state.ifCurrentScreen.value == true)) {
if ((reply is SenderAddFingerprintWithTimeCycleCoercionReply) && (state.ifCurrentScreen.value == true)) {
_replyAddFingerprintBegin(reply);
}
@ -57,13 +55,10 @@ class FingerprintListLogic extends BaseGetXController {
break;
case 0x06:
//
final List<String>? privateKey =
await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList =
changeStringListToIntList(privateKey!);
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
final List<String>? signKey =
await Storage.getStringList(saveBlueSignKey);
final List<String>? signKey = await Storage.getStringList(saveBlueSignKey);
final List<int> signKeyDataList = changeStringListToIntList(signKey!);
final List<int> token = reply.data.sublist(5, 9);
@ -257,20 +252,16 @@ class FingerprintListLogic extends BaseGetXController {
showBlueConnetctToastTimer(action: () {
dismissEasyLoading();
});
BlueManage().blueSendData(BlueManage().connectDeviceName,
(BluetoothConnectionState deviceConnectionState) async {
BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async {
if (deviceConnectionState == BluetoothConnectionState.connected) {
// var publicKey = await Storage.getStringList(saveBluePublicKey);
// List<int> publicKeyDataList = changeStringListToIntList(publicKey!);
final List<String>? signKey =
await Storage.getStringList(saveBlueSignKey);
final List<String>? signKey = await Storage.getStringList(saveBlueSignKey);
final List<int> signKeyDataList = changeStringListToIntList(signKey!);
final List<String>? privateKey =
await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList =
changeStringListToIntList(privateKey!);
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
final List<String>? token = await Storage.getStringList(saveBlueToken);
final List<int> getTokenList = changeStringListToIntList(token!);
@ -298,8 +289,7 @@ class FingerprintListLogic extends BaseGetXController {
privateKey: getPrivateKeyList,
token: getTokenList,
isBeforeAddUser: false);
} else if (deviceConnectionState ==
BluetoothConnectionState.disconnected) {
} else if (deviceConnectionState == BluetoothConnectionState.disconnected) {
dismissEasyLoading();
cancelBlueConnetctToastTimer();
if (state.ifCurrentScreen.value == true) {
@ -337,15 +327,15 @@ class FingerprintListLogic extends BaseGetXController {
*/
//
Future<FingerprintListDataEntity> getFingerprintsListData(
{required bool isRefresh}) async {
Future<FingerprintListDataEntity> getFingerprintsListData({required bool isRefresh}) async {
//
// if (isRefresh) {
state.fingerprintItemListData.clear();
if (isRefresh) {
pageNo = 1;
state.fingerprintItemListData.clear();
state.fingerprintItemListData.refresh();
}
final FingerprintListDataEntity entity =
await ApiRepository.to.getFingerprintsListData(
final FingerprintListDataEntity entity = await ApiRepository.to.getFingerprintsListData(
lockId: state.lockId.value.toString(),
pageNo: pageNo.toString(),
pageSize: pageSize.toString(),
@ -381,10 +371,7 @@ class FingerprintListLogic extends BaseGetXController {
type = '0';
}
final LoginEntity entity = await ApiRepository.to.deletFingerprintsData(
fingerprintId: fingerprintId,
lockId: state.lockId.value.toString(),
type: type,
deleteType: '1');
fingerprintId: fingerprintId, lockId: state.lockId.value.toString(), type: type, deleteType: '1');
if (entity.errorCode!.codeIsSuccessful) {
if (state.isDeletAll == false) {
showToast('删除成功'.tr, something: () {
@ -402,9 +389,7 @@ class FingerprintListLogic extends BaseGetXController {
late StreamSubscription _teamEvent;
void _initRefreshAction() {
_teamEvent = eventBus
.on<OtherTypeRefreshListEvent>()
.listen((OtherTypeRefreshListEvent event) async {
_teamEvent = eventBus.on<OtherTypeRefreshListEvent>().listen((OtherTypeRefreshListEvent event) async {
//
await getFingerprintsListData(isRefresh: true);
});
@ -415,8 +400,7 @@ class FingerprintListLogic extends BaseGetXController {
String keyTypeStr = ''; //
// (fingerprintItemData.fingerprintType! != 1) ? (fingerprintItemData.endDate! < DateTime.now().millisecondsSinceEpoch ? "已失效" : "") : ""
if (fingerprintItemData.fingerprintStatus == 1) {
if (fingerprintItemData.startDate! >
DateTime.now().millisecondsSinceEpoch) {
if (fingerprintItemData.startDate! > DateTime.now().millisecondsSinceEpoch) {
keyTypeStr = '未生效'.tr;
}
} else if (fingerprintItemData.fingerprintStatus == 2) {
@ -428,8 +412,7 @@ class FingerprintListLogic extends BaseGetXController {
String getKeyDateType(FingerprintItemData fingerprintItemData) {
String keyDateTypeStr = ''; // :1;23:4
if (fingerprintItemData.fingerprintType! == 1) {
keyDateTypeStr =
"${DateTool().dateToYMDHNString(fingerprintItemData.createDate.toString())} ${"永久".tr}";
keyDateTypeStr = "${DateTool().dateToYMDHNString(fingerprintItemData.createDate.toString())} ${"永久".tr}";
} else if (fingerprintItemData.fingerprintType! == 2) {
keyDateTypeStr =
'${DateTool().dateToYMDHNString(fingerprintItemData.startDate.toString())} - ${DateTool().dateToYMDHNString(fingerprintItemData.endDate.toString())} ${'限时'.tr}';
@ -442,11 +425,10 @@ class FingerprintListLogic extends BaseGetXController {
//
Future<void> refreshIndividualKeys({required int fingerprintId}) async {
final FingerprintEntity entity =
await ApiRepository.to.getFingerprintsData(fingerprintId);
final FingerprintEntity entity = await ApiRepository.to.getFingerprintsData(fingerprintId);
if (entity.errorCode!.codeIsSuccessful) {
final int index = state.fingerprintItemListData.indexWhere(
(FingerprintItemData item) => item.fingerprintId == fingerprintId);
final int index =
state.fingerprintItemListData.indexWhere((FingerprintItemData item) => item.fingerprintId == fingerprintId);
state.fingerprintItemListData.removeAt(index);
state.fingerprintItemListData.insert(index, entity.data!);
}

View File

@ -1,24 +1,109 @@
import 'dart:convert';
class ActivateInfoResponse {
ActivateInfoResponse({
this.description,
this.errorCode,
this.data, // List<ActivateInfo>
this.data, // List<ActivateInfo>
this.errorMsg,
});
ActivateInfoResponse.fromJson(dynamic json) {
description = json['description'];
errorCode = json['errorCode'];
// json['data'] List<ActivateInfo>
data = json['data'] != null
? (json['data'] as List).map((item) => ActivateInfo.fromJson(item)).toList()
: [];
// ActivateInfo
data = json['data'] != null ? ActivateInfo.fromJson(json['data']) : null;
errorMsg = json['errorMsg'];
}
String? description;
int? errorCode;
List<ActivateInfo>? data; // List<ActivateInfo>
ActivateInfo? data; // List<ActivateInfo>
String? errorMsg;
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['description'] = description;
map['errorCode'] = errorCode;
if (data != null) {
// ActivateInfo JSON
map['data'] = data!.toJson();
}
map['errorMsg'] = errorMsg;
return map;
}
@override
String toString() {
return 'ActivateInfoResponse{description: $description, errorCode: $errorCode, data: $data, errorMsg: $errorMsg}';
}
}
class ActivateInfo {
String? authCode;
String? activatedAt;
Map<String, dynamic>? extraParams; // Map
ActivateInfo({
this.authCode,
this.activatedAt,
this.extraParams,
});
ActivateInfo.fromJson(dynamic json) {
authCode = json['auth_code'] ?? '';
activatedAt = json['activated_at'] ?? '';
// extraParams Map
if (json['extra_params'] != null) {
if (json['extra_params'] is Map) {
extraParams = json['extra_params'];
} else if (json['extra_params'] is String) {
// JSON
try {
extraParams = jsonDecode(json['extra_params']);
} catch (e) {
// null map
extraParams = {};
}
}
} else {
extraParams = {};
}
}
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['authCode'] = authCode;
map['activatedAt'] = activatedAt;
map['extraParams'] = extraParams;
return map;
}
@override
String toString() {
return 'ActivateInfo{authCode: $authCode, activatedAt: $activatedAt, extraParams: $extraParams}';
}
}
class TppSupportResponse {
TppSupportResponse({
this.description,
this.errorCode,
this.data, // List<ActivateInfo>
this.errorMsg,
});
TppSupportResponse.fromJson(dynamic json) {
description = json['description'];
errorCode = json['errorCode'];
// json['data'] List<ActivateInfo>
data = json['data'] != null ? (json['data'] as List).map((item) => TppSupportInfo.fromJson(item)).toList() : [];
errorMsg = json['errorMsg'];
}
String? description;
int? errorCode;
List<TppSupportInfo>? data; // List<ActivateInfo>
String? errorMsg;
Map<String, dynamic> toJson() {
@ -35,33 +120,28 @@ class ActivateInfoResponse {
@override
String toString() {
return 'ActivateInfoResponse{description: $description, errorCode: $errorCode, data: $data, errorMsg: $errorMsg}';
return 'TppSupportResponse{description: $description, errorCode: $errorCode, data: $data, errorMsg: $errorMsg}';
}
}
class ActivateInfo {
String? platformName;
class TppSupportInfo {
int? platform;
String? platformName;
ActivateInfo({
this.platformName,
TppSupportInfo({
this.platform,
this.platformName,
});
ActivateInfo.fromJson(dynamic json) {
platformName = json['platformName'] ?? '';
platform = json['platform'] ?? '';
TppSupportInfo.fromJson(dynamic json) {
platform = json['platform'] as int? ?? -1;
platformName = json['platform_name'] as String? ?? '';
}
Map<String, dynamic> toJson() {
final map = <String, dynamic>{};
map['platformName'] = platformName;
map['platform'] = platform;
map['platform_name'] = platformName;
return map;
}
@override
String toString() {
return 'ActivateInfo{platformName: $platformName, platform: $platform}';
}
}

View File

@ -54,12 +54,26 @@ class LockDetailLogic extends BaseGetXController {
//
FunctionBlocker functionBlocker = FunctionBlocker(duration: const Duration(seconds: 2));
// Timer
Timer? _openDoorReplyDebounceTimer;
//
void _handleOpenDoorReplyWithDebounce(Reply reply) {
// Timer
_openDoorReplyDebounceTimer?.cancel();
// Timer200ms后执行
_openDoorReplyDebounceTimer = Timer(const Duration(milliseconds: 1500), () {
_replyOpenLock(reply);
});
}
//
void initReplySubscription() {
state.replySubscription = EventBusManager().eventBus!.on<Reply>().listen((Reply reply) async {
//
if (reply is OpenDoorReply) {
_replyOpenLock(reply);
_handleOpenDoorReplyWithDebounce(reply);
}
//
@ -143,7 +157,7 @@ class LockDetailLogic extends BaseGetXController {
await uploadElectricQuantityRequest();
resetOpenDoorState();
state.animationController!.stop();
state.animationController?.stop();
//
AppLog.log('开锁成功,开始同步所记录:getLockRecordLastUploadDataTime');
@ -165,6 +179,7 @@ class LockDetailLogic extends BaseGetXController {
}
_handleSynchronizeUploadLockData();
break;
case 0x06:
//
@ -339,7 +354,7 @@ class LockDetailLogic extends BaseGetXController {
//
void openDoorError() {
resetOpenDoorState();
state.animationController!.stop();
state.animationController?.stop();
blueManageDisconnect();
}
@ -348,7 +363,7 @@ class LockDetailLogic extends BaseGetXController {
state.openLockBtnState.value = 0;
// state.openDoorBtnisUneable.value = true;
if (state.animationController != null) {
state.animationController!.stop(canceled: true);
state.animationController?.stop(canceled: true);
}
cancelBlueConnetctToastTimer();
}
@ -643,6 +658,7 @@ class LockDetailLogic extends BaseGetXController {
if (list.isEmpty) {
return;
}
AppLog.log('list:${list}');
final KeyOperationRecordEntity entity =
await ApiRepository.to.lockRecordUploadData(lockId: state.keyInfos.value.lockId.toString(), records: list);
if (entity.errorCode!.codeIsSuccessful) {
@ -928,28 +944,31 @@ class LockDetailLogic extends BaseGetXController {
}
void _handleGetLockPasswordData() {
BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState connectionState) async {
if (connectionState == BluetoothConnectionState.connected) {
final List<String>? token = await Storage.getStringList(saveBlueToken);
final List<int> getTokenList = changeStringListToIntList(token!);
BlueManage().blueSendData(
BlueManage().connectDeviceName,
(BluetoothConnectionState connectionState) async {
if (connectionState == BluetoothConnectionState.connected) {
final List<String>? token = await Storage.getStringList(saveBlueToken);
final List<int> getTokenList = changeStringListToIntList(token!);
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
final List<String>? signKey = await Storage.getStringList(saveBlueSignKey);
final List<int> signKeyDataList = changeStringListToIntList(signKey!);
final List<String>? signKey = await Storage.getStringList(saveBlueSignKey);
final List<int> signKeyDataList = changeStringListToIntList(signKey!);
IoSenderManage.updataLockPasswordListCommand(
lockID: BlueManage().connectDeviceName,
userID: await Storage.getUid(),
page: state.uploadPasswordPage,
countReq: state.countReq,
token: getTokenList,
needAuthor: 1,
signKey: signKeyDataList,
privateKey: getPrivateKeyList);
}
});
IoSenderManage.updataLockPasswordListCommand(
lockID: BlueManage().connectDeviceName,
userID: await Storage.getUid(),
page: state.uploadPasswordPage,
countReq: state.countReq,
token: getTokenList,
needAuthor: 1,
signKey: signKeyDataList,
privateKey: getPrivateKeyList);
}
},
);
}
//
@ -963,7 +982,7 @@ class LockDetailLogic extends BaseGetXController {
// 10
state.uploadPasswordPage = state.uploadPasswordPage + 1;
final List<int> token = reply.data.sublist(3, 7);
showEasyLoading();
BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState connectionState) async {
if (connectionState == BluetoothConnectionState.connected) {
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
@ -1067,7 +1086,6 @@ class LockDetailLogic extends BaseGetXController {
final List<int> token = reply.data.sublist(3, 7);
showEasyLoading();
BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState connectionState) async {
if (connectionState == BluetoothConnectionState.connected) {
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
@ -1171,7 +1189,6 @@ class LockDetailLogic extends BaseGetXController {
final List<int> token = reply.data.sublist(3, 7);
showEasyLoading();
BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState connectionState) async {
if (connectionState == BluetoothConnectionState.connected) {
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
@ -1325,7 +1342,6 @@ class LockDetailLogic extends BaseGetXController {
final List<int> token = reply.data.sublist(3, 7);
showEasyLoading();
BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState connectionState) async {
if (connectionState == BluetoothConnectionState.connected) {
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
@ -1402,7 +1418,6 @@ class LockDetailLogic extends BaseGetXController {
final List<int> token = reply.data.sublist(3, 7);
showEasyLoading();
BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState connectionState) async {
if (connectionState == BluetoothConnectionState.connected) {
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
@ -1479,7 +1494,6 @@ class LockDetailLogic extends BaseGetXController {
final List<int> token = reply.data.sublist(3, 7);
showEasyLoading();
BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState connectionState) async {
if (connectionState == BluetoothConnectionState.connected) {
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
@ -1503,6 +1517,8 @@ class LockDetailLogic extends BaseGetXController {
} else {
state.indexCount.value = state.indexCount.value + 1;
_lockDataUpload(uploadType: 2, recordType: 7, records: state.uploadRemoteControlDataList);
AppLog.log('需要执行断开操作');
BlueManage().disconnect();
}
break;
case 0x06:

View File

@ -34,11 +34,7 @@ import '../../lockMian/entity/lockListInfo_entity.dart';
import 'lockDetail_logic.dart';
class LockDetailPage extends StatefulWidget {
const LockDetailPage(
{required this.isOnlyOneData,
required this.lockListInfoItemEntity,
Key? key})
: super(key: key);
const LockDetailPage({required this.isOnlyOneData, required this.lockListInfoItemEntity, Key? key}) : super(key: key);
final bool isOnlyOneData;
final LockListInfoItemEntity lockListInfoItemEntity;
@ -46,29 +42,26 @@ class LockDetailPage extends StatefulWidget {
State<LockDetailPage> createState() => _LockDetailPageState();
}
class _LockDetailPageState extends State<LockDetailPage>
with TickerProviderStateMixin, RouteAware {
class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStateMixin, RouteAware {
// with RouteAware
final LockDetailLogic logic = Get.put(LockDetailLogic());
final LockDetailState state = Get.find<LockDetailLogic>().state;
@override
void initState() {
super.initState();
state.animationController =
AnimationController(duration: const Duration(seconds: 1), vsync: this);
state.animationController!.repeat();
state.animationController = AnimationController(duration: const Duration(seconds: 1), vsync: this);
state.animationController?.repeat();
//StatusListener
state.animationController!.addStatusListener((AnimationStatus status) {
state.animationController?.addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed) {
state.animationController!.reset();
state.animationController!.forward();
state.animationController?.reset();
state.animationController?.forward();
} else if (status == AnimationStatus.dismissed) {
state.animationController!.reset();
state.animationController!.forward();
state.animationController?.reset();
state.animationController?.forward();
}
});
super.initState();
state.pageController.addListener(() {
state.currentPage.value = state.pageController.page!.round();
@ -77,9 +70,7 @@ class _LockDetailPageState extends State<LockDetailPage>
_initRefreshLockDetailInfoDataEventAction();
logic.initReplySubscription();
logic.initLockSetOpenOrCloseCheckInRefreshLockDetailWithAttendanceAction();
logic.loadData(
lockListInfoItemEntity: widget.lockListInfoItemEntity,
isOnlyOneData: widget.isOnlyOneData);
logic.loadData(lockListInfoItemEntity: widget.lockListInfoItemEntity, isOnlyOneData: widget.isOnlyOneData);
}
@override
@ -95,9 +86,8 @@ class _LockDetailPageState extends State<LockDetailPage>
void _initRefreshLockDetailInfoDataEventAction() {
// eventBus
_lockRefreshLockDetailInfoDataEvent = eventBus
.on<RefreshLockDetailInfoDataEvent>()
.listen((RefreshLockDetailInfoDataEvent event) {
_lockRefreshLockDetailInfoDataEvent =
eventBus.on<RefreshLockDetailInfoDataEvent>().listen((RefreshLockDetailInfoDataEvent event) {
setState(() {});
});
}
@ -109,17 +99,12 @@ class _LockDetailPageState extends State<LockDetailPage>
//
Widget xhjWidget() {
final bool isShowTip = (state.keyInfos.value.keyType ==
XSConstantMacro.keyTypeTime ||
final bool isShowTip = (state.keyInfos.value.keyType == XSConstantMacro.keyTypeTime ||
state.keyInfos.value.keyType == XSConstantMacro.keyTypeLoop) &&
(DateTool().compareTimeGetDaysFromNow(state.keyInfos.value.endDate!) <=
15 &&
DateTool()
.compareTimeGetDaysFromNow(state.keyInfos.value.endDate!) >=
0) &&
(DateTool().compareTimeGetDaysFromNow(state.keyInfos.value.endDate!) <= 15 &&
DateTool().compareTimeGetDaysFromNow(state.keyInfos.value.endDate!) >= 0) &&
(state.keyInfos.value.keyStatus == XSConstantMacro.keyStatusNormalUse ||
state.keyInfos.value.keyStatus ==
XSConstantMacro.keyStatusWaitReceive);
state.keyInfos.value.keyStatus == XSConstantMacro.keyStatusWaitReceive);
return Scaffold(
backgroundColor: Colors.white,
body: Obx(() {
@ -141,8 +126,7 @@ class _LockDetailPageState extends State<LockDetailPage>
maxLines: 2,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
style: TextStyle(
color: const Color(0xffCBA74B), fontSize: 24.sp)),
style: TextStyle(color: const Color(0xffCBA74B), fontSize: 24.sp)),
),
),
SizedBox(height: 10.h),
@ -161,10 +145,7 @@ class _LockDetailPageState extends State<LockDetailPage>
SizedBox(
height: 20.h,
),
labelText(
img: 'images/icon_slider_horizontal.png',
text: '功能'.tr,
child: bottomWidget()),
labelText(img: 'images/icon_slider_horizontal.png', text: '功能'.tr, child: bottomWidget()),
btnText(
img: 'images/main/icon_main_set.png',
text: '设置'.tr,
@ -172,17 +153,13 @@ class _LockDetailPageState extends State<LockDetailPage>
if (state.openDoorBtnisUneable.value == false) {
return;
}
Get.toNamed(Routers.lockSetPage,
arguments: <String, Object?>{
'lockId': state.keyInfos.value.lockId,
'isOnlyOneData': state.isOnlyOneData
});
Get.toNamed(Routers.lockSetPage, arguments: <String, Object?>{
'lockId': state.keyInfos.value.lockId,
'isOnlyOneData': state.isOnlyOneData
});
}),
if (!F.isProductionEnv)
labelText(
img: 'images/icon_puzzlepiece_extension.png',
text: '配件'.tr,
child: attachmentWidget()),
labelText(img: 'images/icon_puzzlepiece_extension.png', text: '配件'.tr, child: attachmentWidget()),
],
),
),
@ -201,24 +178,21 @@ class _LockDetailPageState extends State<LockDetailPage>
);
}
Widget btnText(
{required String img, required String text, required var onTap}) {
Widget btnText({required String img, required String text, required var onTap}) {
return GestureDetector(
onTap: onTap,
child: Container(
margin: EdgeInsets.symmetric(horizontal: 0.05.sw, vertical: 15.h),
padding: EdgeInsets.symmetric(horizontal: 0.05.sw, vertical: 30.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.r),
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.black.withOpacity(0.15),
offset: const Offset(0, 0),
blurRadius: 5.r,
spreadRadius: 0,
),
]),
decoration:
BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16.r), boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.black.withOpacity(0.15),
offset: const Offset(0, 0),
blurRadius: 5.r,
spreadRadius: 0,
),
]),
child: Row(
children: <Widget>[
FlavorsImg(
@ -247,22 +221,18 @@ class _LockDetailPageState extends State<LockDetailPage>
);
}
Widget labelText(
{required String img, required String text, required Widget child}) {
Widget labelText({required String img, required String text, required Widget child}) {
return Container(
margin: EdgeInsets.symmetric(horizontal: 0.05.sw, vertical: 15.h),
padding: EdgeInsets.symmetric(horizontal: 0.05.sw, vertical: 15.h),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16.r),
boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.black.withOpacity(0.15),
offset: const Offset(0, 0),
blurRadius: 5.r,
spreadRadius: 0,
),
]),
decoration: BoxDecoration(color: Colors.white, borderRadius: BorderRadius.circular(16.r), boxShadow: <BoxShadow>[
BoxShadow(
color: Colors.black.withOpacity(0.15),
offset: const Offset(0, 0),
blurRadius: 5.r,
spreadRadius: 0,
),
]),
child: Column(
children: <Widget>[
Row(
@ -328,13 +298,10 @@ class _LockDetailPageState extends State<LockDetailPage>
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Image.asset(showElectricIcon(state.electricQuantity.value),
width: 30.w, height: 24.w),
Image.asset(showElectricIcon(state.electricQuantity.value), width: 30.w, height: 24.w),
SizedBox(width: 2.w),
Text('${state.electricQuantity.value}%',
style: TextStyle(
fontSize: 18.sp,
color: AppColors.darkGrayTextColor)),
style: TextStyle(fontSize: 18.sp, color: AppColors.darkGrayTextColor)),
SizedBox(width: 2.w),
Icon(
Icons.info, // 使 warning
@ -345,24 +312,17 @@ class _LockDetailPageState extends State<LockDetailPage>
],
),
Visibility(
visible: state
.keyInfos.value.lockFeature!.isSupportBackupBattery ==
1,
visible: state.keyInfos.value.lockFeature!.isSupportBackupBattery == 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FlavorsImg(
child: Image.asset(
showElectricIcon(
state.electricQuantityStandby.value),
width: 30.w,
height: 24.w),
child: Image.asset(showElectricIcon(state.electricQuantityStandby.value),
width: 30.w, height: 24.w),
),
SizedBox(width: 2.w),
Text('${state.electricQuantityStandby.value}%',
style: TextStyle(
fontSize: 18.sp,
color: AppColors.darkGrayTextColor)),
style: TextStyle(fontSize: 18.sp, color: AppColors.darkGrayTextColor)),
SizedBox(width: 2.w),
FlavorsImg(
child: Icon(
@ -386,8 +346,7 @@ class _LockDetailPageState extends State<LockDetailPage>
child: GestureDetector(
onTap: () {
if (state.openDoorBtnisUneable.value == true) {
logic.functionBlocker
.block(isNeedRealNameAuthThenOpenLock);
logic.functionBlocker.block(isNeedRealNameAuthThenOpenLock);
}
},
onLongPressStart: (LongPressStartDetails details) {
@ -457,12 +416,10 @@ class _LockDetailPageState extends State<LockDetailPage>
right: 90.w,
bottom: 1,
child: Obx(() => Visibility(
visible:
state.keyInfos.value.lockSetting!.remoteUnlock == 1,
visible: state.keyInfos.value.lockSetting!.remoteUnlock == 1,
child: GestureDetector(
onTap: () {
ShowCupertinoAlertView().isToRemoteUnLockAlert(
remoteUnlockAction: () {
ShowCupertinoAlertView().isToRemoteUnLockAlert(remoteUnlockAction: () {
if (state.keyInfos.value.hasGateway != 1) {
logic.showToast('附近没有可用网关'.tr);
}
@ -508,34 +465,24 @@ class _LockDetailPageState extends State<LockDetailPage>
return ListView(
children: <Widget>[
Visibility(
visible:
(state.keyInfos.value.keyType == XSConstantMacro.keyTypeTime ||
state.keyInfos.value.keyType ==
XSConstantMacro.keyTypeLoop) && //
(DateTool().compareTimeGetDaysFromNow(
state.keyInfos.value.endDate!) <=
15 &&
DateTool().compareTimeGetDaysFromNow(
state.keyInfos.value.endDate!) >=
0) && // 030
(state.keyInfos.value.keyStatus ==
XSConstantMacro.keyStatusNormalUse ||
state.keyInfos.value.keyStatus ==
XSConstantMacro.keyStatusWaitReceive) // 使
visible: (state.keyInfos.value.keyType == XSConstantMacro.keyTypeTime ||
state.keyInfos.value.keyType == XSConstantMacro.keyTypeLoop) && //
(DateTool().compareTimeGetDaysFromNow(state.keyInfos.value.endDate!) <= 15 &&
DateTool().compareTimeGetDaysFromNow(state.keyInfos.value.endDate!) >= 0) && // 030
(state.keyInfos.value.keyStatus == XSConstantMacro.keyStatusNormalUse ||
state.keyInfos.value.keyStatus == XSConstantMacro.keyStatusWaitReceive) // 使
,
child: Container(
// height: 30.h,
width: 1.sw,
color: const Color(0xFFFBEFD4),
padding:
EdgeInsets.only(top: 8.h, bottom: 8.h, right: 10.w, left: 10.h),
padding: EdgeInsets.only(top: 8.h, bottom: 8.h, right: 10.w, left: 10.h),
child: Text(
"${"钥匙将在".tr}${DateTool().compareTimeGetDaysFromNow(state.keyInfos.value.endDate!)}${"天后失效".tr}",
maxLines: 2,
overflow: TextOverflow.ellipsis,
textAlign: TextAlign.center,
style:
TextStyle(color: const Color(0xffCBA74B), fontSize: 24.sp)),
style: TextStyle(color: const Color(0xffCBA74B), fontSize: 24.sp)),
),
),
Stack(children: <Widget>[
@ -618,16 +565,11 @@ class _LockDetailPageState extends State<LockDetailPage>
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FlavorsImg(
child: Image.asset(
showElectricIcon(state.electricQuantity.value),
width: 30.w,
height: 24.w),
child: Image.asset(showElectricIcon(state.electricQuantity.value), width: 30.w, height: 24.w),
),
SizedBox(width: 2.w),
Text('${state.electricQuantity.value}%',
style: TextStyle(
fontSize: 18.sp,
color: AppColors.darkGrayTextColor)),
style: TextStyle(fontSize: 18.sp, color: AppColors.darkGrayTextColor)),
SizedBox(width: 2.w),
state.keyInfos.value.network?.isOnline == 1
? Icon(
@ -649,24 +591,17 @@ class _LockDetailPageState extends State<LockDetailPage>
),
),
Visibility(
visible: state
.keyInfos.value.lockFeature!.isSupportBackupBattery ==
1,
visible: state.keyInfos.value.lockFeature!.isSupportBackupBattery == 1,
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FlavorsImg(
child: Image.asset(
showElectricIcon(
state.electricQuantityStandby.value),
width: 30.w,
height: 24.w),
child: Image.asset(showElectricIcon(state.electricQuantityStandby.value),
width: 30.w, height: 24.w),
),
SizedBox(width: 2.w),
Text('${state.electricQuantityStandby.value}%',
style: TextStyle(
fontSize: 18.sp,
color: AppColors.darkGrayTextColor)),
style: TextStyle(fontSize: 18.sp, color: AppColors.darkGrayTextColor)),
SizedBox(width: 2.w),
FlavorsImg(
child: Icon(
@ -756,12 +691,10 @@ class _LockDetailPageState extends State<LockDetailPage>
right: 90.w,
bottom: 1,
child: Obx(() => Visibility(
visible:
state.keyInfos.value.lockSetting!.remoteUnlock == 1,
visible: state.keyInfos.value.lockSetting!.remoteUnlock == 1,
child: GestureDetector(
onTap: () {
ShowCupertinoAlertView().isToRemoteUnLockAlert(
remoteUnlockAction: () {
ShowCupertinoAlertView().isToRemoteUnLockAlert(remoteUnlockAction: () {
if (state.keyInfos.value.hasGateway != 1) {
logic.showToast('附近没有可用网关'.tr);
}
@ -792,10 +725,7 @@ class _LockDetailPageState extends State<LockDetailPage>
logic.getKeyStatusTextAndShow(),
maxLines: 2,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 22.sp,
color: AppColors.btnDisableColor,
fontWeight: FontWeight.w500),
style: TextStyle(fontSize: 22.sp, color: AppColors.btnDisableColor, fontWeight: FontWeight.w500),
),
),
),
@ -818,8 +748,7 @@ class _LockDetailPageState extends State<LockDetailPage>
Widget adminInfoView({bool center = true, bool max = true}) {
return Row(
mainAxisAlignment:
center ? MainAxisAlignment.center : MainAxisAlignment.start,
mainAxisAlignment: center ? MainAxisAlignment.center : MainAxisAlignment.start,
children: <Widget>[
Row(
mainAxisSize: max ? MainAxisSize.max : MainAxisSize.min,
@ -834,11 +763,8 @@ class _LockDetailPageState extends State<LockDetailPage>
Text(
state.keyInfos.value.isLockOwner == 1
? '超级管理员英文'.tr
: (state.keyInfos.value.keyRight == 1
? '授权管理员英文'.tr
: '普通用户'.tr),
style: TextStyle(
fontSize: 20.sp, color: AppColors.darkGrayTextColor),
: (state.keyInfos.value.keyRight == 1 ? '授权管理员英文'.tr : '普通用户'.tr),
style: TextStyle(fontSize: 20.sp, color: AppColors.darkGrayTextColor),
),
SizedBox(width: 40.w),
],
@ -860,9 +786,7 @@ class _LockDetailPageState extends State<LockDetailPage>
'网关设备英文'.tr,
style: TextStyle(
fontSize: 20.sp,
color: state.keyInfos.value.hasGateway == 1
? AppColors.mainColor
: AppColors.btnDisableColor),
color: state.keyInfos.value.hasGateway == 1 ? AppColors.mainColor : AppColors.btnDisableColor),
),
SizedBox(width: 20.w),
],
@ -874,18 +798,14 @@ class _LockDetailPageState extends State<LockDetailPage>
child: Image.asset('images/main/icon_lockDetail_needNetwork.png',
width: 24.w,
height: 20.w,
color: state.isOpenLockNeedOnline.value == 1
? AppColors.mainColor
: AppColors.btnDisableColor),
color: state.isOpenLockNeedOnline.value == 1 ? AppColors.mainColor : AppColors.btnDisableColor),
),
SizedBox(width: 6.w),
Text(
'手机需联网英文'.tr,
style: TextStyle(
fontSize: 20.sp,
color: state.isOpenLockNeedOnline.value == 1
? AppColors.mainColor
: AppColors.btnDisableColor),
color: state.isOpenLockNeedOnline.value == 1 ? AppColors.mainColor : AppColors.btnDisableColor),
),
],
)
@ -893,9 +813,19 @@ class _LockDetailPageState extends State<LockDetailPage>
);
}
//
Widget xhjBuildRotationTransition(
{required double width, required double height}) {
Widget xhjBuildRotationTransition({required double width, required double height}) {
// animationController null
if (state.animationController == null) {
return Positioned(
child: Image.asset(
'images/icon_circle_dotted.png',
width: width,
height: height,
color: state.isOpenPassageMode.value == 1 ? Colors.red : AppColors.mainColor,
),
);
}
return Positioned(
child: RotationTransition(
//
@ -907,17 +837,29 @@ class _LockDetailPageState extends State<LockDetailPage>
'images/icon_circle_dotted.png',
width: width,
height: height,
color: state.isOpenPassageMode.value == 1
? Colors.red
: AppColors.mainColor,
color: state.isOpenPassageMode.value == 1 ? Colors.red : AppColors.mainColor,
),
),
);
}
//
Widget buildRotationTransition(
{required double width, required double height}) {
//
Widget buildRotationTransition({required double width, required double height}) {
//
if (state.animationController == null) {
return Positioned(
child: FlavorsImg(
child: Image.asset(
state.isOpenPassageMode.value == 1
? 'images/main/icon_main_normallyOpenMode_circle.png'
: 'images/main/icon_main_openLockBtn_circle.png',
width: width,
height: height,
),
),
);
}
return Positioned(
child: RotationTransition(
//
@ -949,9 +891,7 @@ class _LockDetailPageState extends State<LockDetailPage>
margin: EdgeInsets.symmetric(vertical: 10.0.w, horizontal: 6.0.w),
decoration: BoxDecoration(
shape: BoxShape.circle,
color: state.currentPage.value == index
? AppColors.mainColor
: Colors.grey,
color: state.currentPage.value == index ? AppColors.mainColor : Colors.grey,
),
);
}),
@ -997,8 +937,7 @@ class _LockDetailPageState extends State<LockDetailPage>
// '开门器', state.bottomBtnisUneable.value, () {}));
//
showWidgetArr.add(bottomItem('images/main/icon_main_addLock.png', '新增配件'.tr,
state.bottomBtnisEable.value, () {
showWidgetArr.add(bottomItem('images/main/icon_main_addLock.png', '新增配件'.tr, state.bottomBtnisEable.value, () {
Navigator.pushNamed(context, Routers.accessoriesListPage);
}));
@ -1015,8 +954,7 @@ class _LockDetailPageState extends State<LockDetailPage>
crossAxisSpacing: 0.h,
mainAxisExtent: 90.0, // item
),
itemCount: (state.keyInfos.value.isLockOwner == 1 ||
state.keyInfos.value.keyRight == 1)
itemCount: (state.keyInfos.value.isLockOwner == 1 || state.keyInfos.value.keyRight == 1)
? getAllWidget().length
: getNormalWidget().length,
itemBuilder: (context, index) {
@ -1041,8 +979,7 @@ class _LockDetailPageState extends State<LockDetailPage>
//
List<Widget> getBottomWidget() {
if (state.keyInfos.value.isLockOwner == 1 ||
state.keyInfos.value.keyRight == 1) {
if (state.keyInfos.value.isLockOwner == 1 || state.keyInfos.value.keyRight == 1) {
//
return getAllWidget();
} else {
@ -1062,22 +999,17 @@ class _LockDetailPageState extends State<LockDetailPage>
// }));
// }
//
showWidgetArr.add(bottomItem('images/main/icon_main_operatingRecord.png',
'操作记录'.tr, state.bottomBtnisEable.value, () {
showWidgetArr
.add(bottomItem('images/main/icon_main_operatingRecord.png', '操作记录'.tr, state.bottomBtnisEable.value, () {
Get.toNamed(Routers.doorLockLogPage,
arguments: <String, LockListInfoItemEntity>{
'keyInfo': state.keyInfos.value
});
arguments: <String, LockListInfoItemEntity>{'keyInfo': state.keyInfos.value});
}));
//
if (F.isSKY) {
showWidgetArr
.add(bottomItem('images/main/icon_main_set.png', '设置'.tr, true, () {
Get.toNamed(Routers.lockSetPage, arguments: <String, Object?>{
'lockId': state.keyInfos.value.lockId,
'isOnlyOneData': state.isOnlyOneData
});
showWidgetArr.add(bottomItem('images/main/icon_main_set.png', '设置'.tr, true, () {
Get.toNamed(Routers.lockSetPage,
arguments: <String, Object?>{'lockId': state.keyInfos.value.lockId, 'isOnlyOneData': state.isOnlyOneData});
}));
}
@ -1097,26 +1029,22 @@ class _LockDetailPageState extends State<LockDetailPage>
// }
//
showWidgetArr.add(bottomItem('images/main/icon_main_electronicKey.png',
'电子钥匙'.tr, state.bottomBtnisEable.value, () {
showWidgetArr
.add(bottomItem('images/main/icon_main_electronicKey.png', '电子钥匙'.tr, state.bottomBtnisEable.value, () {
Get.toNamed(Routers.electronicKeyListPage);
}));
//
if (state.keyInfos.value.lockFeature!.password == 1) {
showWidgetArr.add(bottomItem('images/main/icon_main_password.png',
'密码'.tr, state.bottomBtnisEable.value, () {
showWidgetArr.add(bottomItem('images/main/icon_main_password.png', '密码'.tr, state.bottomBtnisEable.value, () {
Get.toNamed(Routers.passwordKeyListPage,
arguments: <String, LockListInfoItemEntity>{
'keyInfo': state.keyInfos.value
});
arguments: <String, LockListInfoItemEntity>{'keyInfo': state.keyInfos.value});
}));
}
// ic卡
if (state.keyInfos.value.lockFeature!.icCard == 1) {
showWidgetArr.add(bottomItem('images/main/icon_main_icCard.png', ''.tr,
state.bottomBtnisEable.value, () {
showWidgetArr.add(bottomItem('images/main/icon_main_icCard.png', ''.tr, state.bottomBtnisEable.value, () {
Get.toNamed(Routers.cardListPage, arguments: <String, int?>{
'lockId': state.keyInfos.value.lockId,
});
@ -1125,8 +1053,7 @@ class _LockDetailPageState extends State<LockDetailPage>
//
if (state.keyInfos.value.lockFeature!.fingerprint == 1) {
showWidgetArr.add(bottomItem('images/main/icon_main_fingerprint.png',
'指纹'.tr, state.bottomBtnisEable.value, () {
showWidgetArr.add(bottomItem('images/main/icon_main_fingerprint.png', '指纹'.tr, state.bottomBtnisEable.value, () {
Get.toNamed(Routers.fingerprintListPage, arguments: <String, int?>{
'lockId': state.keyInfos.value.lockId,
});
@ -1135,8 +1062,8 @@ class _LockDetailPageState extends State<LockDetailPage>
//
if (state.keyInfos.value.lockFeature!.bluetoothRemoteControl == 1) {
showWidgetArr.add(bottomItem('images/main/icon_main_remoteControl.png',
'遥控'.tr, state.bottomBtnisEable.value, () {
showWidgetArr
.add(bottomItem('images/main/icon_main_remoteControl.png', '遥控'.tr, state.bottomBtnisEable.value, () {
Get.toNamed(Routers.remoteControlListPage, arguments: <String, int?>{
'lockId': state.keyInfos.value.lockId,
});
@ -1146,9 +1073,7 @@ class _LockDetailPageState extends State<LockDetailPage>
//->
if (state.keyInfos.value.lockFeature!.d3Face == 1) {
showWidgetArr.add(
bottomItem(
'images/main/icon_face.png', '人脸'.tr, state.bottomBtnisEable.value,
() {
bottomItem('images/main/icon_face.png', '人脸'.tr, state.bottomBtnisEable.value, () {
Get.toNamed(Routers.faceListPage, arguments: <String, int?>{
'lockId': state.keyInfos.value.lockId,
});
@ -1158,9 +1083,7 @@ class _LockDetailPageState extends State<LockDetailPage>
if (state.keyInfos.value.lockFeature!.isSupportIris == 1) {
showWidgetArr.add(
bottomItem(
'images/main/icon_iris.png', '虹膜'.tr, state.bottomBtnisEable.value,
() {
bottomItem('images/main/icon_iris.png', '虹膜'.tr, state.bottomBtnisEable.value, () {
Get.toNamed(Routers.irisListPage, arguments: <String, int?>{
'lockId': state.keyInfos.value.lockId,
});
@ -1170,9 +1093,7 @@ class _LockDetailPageState extends State<LockDetailPage>
if (state.keyInfos.value.lockFeature!.palmVein == 1) {
showWidgetArr.add(
bottomItem(
'images/main/icon_palm.png', '掌静脉'.tr, state.bottomBtnisEable.value,
() {
bottomItem('images/main/icon_palm.png', '掌静脉'.tr, state.bottomBtnisEable.value, () {
Get.toNamed(Routers.palmListPage, arguments: <String, int?>{
'lockId': state.keyInfos.value.lockId,
});
@ -1183,8 +1104,7 @@ class _LockDetailPageState extends State<LockDetailPage>
//->
if (state.keyInfos.value.lockFeature!.isSupportCatEye == 1) {
showWidgetArr.add(
bottomItem('images/main/icon_catEyes.png', '监控'.tr,
state.bottomBtnisEable.value, () async {
bottomItem('images/main/icon_catEyes.png', '监控'.tr, state.bottomBtnisEable.value, () async {
logic.sendMonitorMessage();
}),
);
@ -1192,33 +1112,28 @@ class _LockDetailPageState extends State<LockDetailPage>
//
if (state.keyInfos.value.isLockOwner == 1) {
showWidgetArr.add(bottomItem('images/main/icon_main_authorizedAdmin.png',
'授权管理员'.tr, state.bottomBtnisEable.value, () {
showWidgetArr
.add(bottomItem('images/main/icon_main_authorizedAdmin.png', '授权管理员'.tr, state.bottomBtnisEable.value, () {
Get.toNamed(Routers.authorizedAdminListPage,
arguments: <String, LockListInfoItemEntity>{
'keyInfo': state.keyInfos.value
});
arguments: <String, LockListInfoItemEntity>{'keyInfo': state.keyInfos.value});
}));
}
final List<Widget> endWiddget = <Widget>[];
endWiddget.add(
//
bottomItem('images/main/icon_main_operatingRecord.png', '操作记录'.tr,
state.bottomBtnisEable.value, () {
bottomItem('images/main/icon_main_operatingRecord.png', '操作记录'.tr, state.bottomBtnisEable.value, () {
// Get.toNamed(Routers.lockOperatingRecordPage,
// arguments: {"keyInfo": state.keyInfos.value});
Get.toNamed(Routers.doorLockLogPage,
arguments: <String, LockListInfoItemEntity>{
'keyInfo': state.keyInfos.value
});
arguments: <String, LockListInfoItemEntity>{'keyInfo': state.keyInfos.value});
}),
);
if (state.keyInfos.value.lockFeature!.isSupportCatEye == 1) {
//
endWiddget.add(bottomItem('images/main/icon_lockDetail_videoLog.png',
'视频日志'.tr, state.bottomBtnisEable.value, () {
endWiddget
.add(bottomItem('images/main/icon_lockDetail_videoLog.png', '视频日志'.tr, state.bottomBtnisEable.value, () {
Get.toNamed(Routers.videoLogPage, arguments: <String, int?>{
'lockId': state.keyInfos.value.lockId,
});
@ -1227,12 +1142,10 @@ class _LockDetailPageState extends State<LockDetailPage>
if (!F.isProductionEnv) {
endWiddget.add(
//
bottomItem('images/main/icon_lockDetail_messageReminding.png',
'消息提醒'.tr, state.bottomBtnisEable.value, () {
bottomItem('images/main/icon_lockDetail_messageReminding.png', '消息提醒'.tr, state.bottomBtnisEable.value, () {
Get.toNamed(Routers.msgNotificationPage, arguments: <String, int?>{
'lockId': state.keyInfos.value.lockId,
'isSupportCatEye':
state.keyInfos.value.lockFeature!.isSupportCatEye,
'isSupportCatEye': state.keyInfos.value.lockFeature!.isSupportCatEye,
});
}),
);
@ -1254,8 +1167,7 @@ class _LockDetailPageState extends State<LockDetailPage>
return showWidgetArr;
}
Widget bottomItem(
String iconUrl, String name, bool bottomBtnisEable, Function() onClick) {
Widget bottomItem(String iconUrl, String name, bool bottomBtnisEable, Function() onClick) {
final Widget child = F.sw(
skyCall: () => Container(
color: Colors.white,
@ -1268,9 +1180,7 @@ class _LockDetailPageState extends State<LockDetailPage>
child: Image.asset(iconUrl,
width: 42.w,
height: 42.w,
color: bottomBtnisEable
? AppColors.mainColor
: AppColors.lockDetailBottomBtnUneable,
color: bottomBtnisEable ? AppColors.mainColor : AppColors.lockDetailBottomBtnUneable,
fit: BoxFit.fitWidth),
),
SizedBox(height: 5.h),
@ -1278,9 +1188,7 @@ class _LockDetailPageState extends State<LockDetailPage>
child: Text(name,
style: TextStyle(
fontSize: 20.sp,
color: bottomBtnisEable
? AppColors.blackColor
: AppColors.lockDetailBottomBtnUneable),
color: bottomBtnisEable ? AppColors.blackColor : AppColors.lockDetailBottomBtnUneable),
textAlign: TextAlign.center))
],
),
@ -1298,9 +1206,7 @@ class _LockDetailPageState extends State<LockDetailPage>
child: Image.asset(iconUrl,
width: 42.w,
height: 42.w,
color: bottomBtnisEable
? AppColors.mainColor
: AppColors.lockDetailBottomBtnUneable,
color: bottomBtnisEable ? AppColors.mainColor : AppColors.lockDetailBottomBtnUneable,
fit: BoxFit.fitWidth),
),
SizedBox(height: 15.h),
@ -1313,9 +1219,7 @@ class _LockDetailPageState extends State<LockDetailPage>
style: TextStyle(
fontSize: 20.sp,
height: 1.0,
color: bottomBtnisEable
? AppColors.blackColor
: AppColors.lockDetailBottomBtnUneable),
color: bottomBtnisEable ? AppColors.blackColor : AppColors.lockDetailBottomBtnUneable),
),
],
),
@ -1347,18 +1251,14 @@ class _LockDetailPageState extends State<LockDetailPage>
Widget _unlockSuccessWidget() {
String lockAlias = state.keyInfos.value.lockAlias!;
final TextStyle lockAliasTextStyle =
TextStyle(color: AppColors.placeholderTextColor, fontSize: 24.sp);
final TextStyle lockAliasTextStyle = TextStyle(color: AppColors.placeholderTextColor, fontSize: 24.sp);
final TextPainter textPainter = TextPainter(
text: TextSpan(text: lockAlias, style: lockAliasTextStyle),
maxLines: 1,
textDirection: TextDirection.ltr)
text: TextSpan(text: lockAlias, style: lockAliasTextStyle), maxLines: 1, textDirection: TextDirection.ltr)
..layout(minWidth: 0, maxWidth: double.infinity);
final double textSizeWidth = textPainter.size.width; //
if (textSizeWidth > 358.w * 2 - 20) {
lockAlias =
'${lockAlias.substring(0, lockAlias.length > 25 ? 25 : lockAlias.length)}...';
lockAlias = '${lockAlias.substring(0, lockAlias.length > 25 ? 25 : lockAlias.length)}...';
}
return Center(
child: Stack(
@ -1378,17 +1278,13 @@ class _LockDetailPageState extends State<LockDetailPage>
Text(state.iSOpenLock.value == true ? '已开锁'.tr : '已闭锁'.tr,
maxLines: 2,
overflow: TextOverflow.ellipsis,
style: TextStyle(
color: AppColors.mainColor,
fontSize: 32.sp,
fontWeight: FontWeight.w600)),
style: TextStyle(color: AppColors.mainColor, fontSize: 32.sp, fontWeight: FontWeight.w600)),
SizedBox(
height: 10.h,
),
Text(
lockAlias,
style: TextStyle(
color: AppColors.placeholderTextColor, fontSize: 24.sp),
style: TextStyle(color: AppColors.placeholderTextColor, fontSize: 24.sp),
maxLines: 2,
),
SizedBox(
@ -1396,8 +1292,7 @@ class _LockDetailPageState extends State<LockDetailPage>
),
Text(
getCurrentFormattedTime(),
style: TextStyle(
color: AppColors.darkGrayTextColor, fontSize: 24.sp),
style: TextStyle(color: AppColors.darkGrayTextColor, fontSize: 24.sp),
)
],
)),
@ -1468,7 +1363,7 @@ class _LockDetailPageState extends State<LockDetailPage>
// }
state.iSOpenLock.value = true;
state.openLockBtnState.value = 1;
state.animationController!.forward();
state.animationController?.forward();
AppLog.log('点击开锁');
if (isOpenLockNeedOnline) {
//
@ -1476,10 +1371,7 @@ class _LockDetailPageState extends State<LockDetailPage>
// AppLog.log('点击开锁 state.openDoorModel = 0 不需要联网');
// FlutterBuglyPlugin.reportException(exceptionName: '点击了不需要联网开锁');
BuglyTool.uploadException(
message: '点击了不需要联网开锁',
detail: '点击了不需要联网开锁 openDoorModel:${state.openDoorModel}',
upload: false,
begin: true);
message: '点击了不需要联网开锁', detail: '点击了不需要联网开锁 openDoorModel:${state.openDoorModel}', upload: false, begin: true);
AppLog.log('点击开锁 state.openDoorModel = 0 不需要联网');
logic.openDoorAction();
} else {
@ -1488,10 +1380,7 @@ class _LockDetailPageState extends State<LockDetailPage>
// AppLog.log('点击开锁 state.openDoorModel = 2 需要联网');
// FlutterBuglyPlugin.reportException(exceptionName: '点击了需要联网开锁');
BuglyTool.uploadException(
message: '点击了需要联网开锁',
detail: '点击了需要联网开锁 openDoorModel:${state.openDoorModel}',
upload: false,
begin: true);
message: '点击了需要联网开锁', detail: '点击了需要联网开锁 openDoorModel:${state.openDoorModel}', upload: false, begin: true);
AppLog.log('点击开锁 state.openDoorModel = 2 需要联网');
logic.getLockNetToken();
}
@ -1503,7 +1392,7 @@ class _LockDetailPageState extends State<LockDetailPage>
}
state.iSOpenLock.value = false;
state.openLockBtnState.value = 1;
state.animationController!.forward();
state.animationController?.forward();
EasyLoading.showToast('正在尝试闭锁……'.tr, duration: 1000.milliseconds);
AppLog.log('长按闭锁');
if (state.isOpenLockNeedOnline.value == 0) {
@ -1526,11 +1415,10 @@ class _LockDetailPageState extends State<LockDetailPage>
state.closedUnlockSuccessfulTimer?.cancel();
_lockRefreshLockDetailInfoDataEvent?.cancel();
state.replySubscription.cancel();
state.lockSetOpenOrCloseCheckInRefreshLockDetailWithAttendanceEvent
?.cancel();
state.lockSetOpenOrCloseCheckInRefreshLockDetailWithAttendanceEvent?.cancel();
state.LockSetChangeSetRefreshLockDetailWithTypeSubscription?.cancel();
if (state.animationController != null) {
state.animationController!.dispose();
state.animationController?.dispose();
state.animationController = null;
}
super.dispose();

View File

@ -0,0 +1,7 @@
import 'package:star_lock/main/lockDetail/lockSet/aiAssistant/ai_assistant_state.dart';
import 'package:star_lock/tools/baseGetXController.dart';
class AiAssistantLogic extends BaseGetXController{
final AiAssistantState state = AiAssistantState();
}

View File

@ -0,0 +1,90 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:star_lock/app_settings/app_colors.dart';
import 'package:star_lock/main/lockDetail/lockSet/aiAssistant/ai_assistant_logic.dart';
import 'package:star_lock/main/lockDetail/lockSet/aiAssistant/ai_assistant_state.dart';
import 'package:star_lock/tools/commonItem.dart';
import 'package:star_lock/tools/titleAppBar.dart';
class AiAssistantPage extends StatefulWidget {
const AiAssistantPage({Key? key}) : super(key: key);
@override
State<AiAssistantPage> createState() => _AiAssistantPageState();
}
class _AiAssistantPageState extends State<AiAssistantPage> {
final AiAssistantLogic logic = Get.put(AiAssistantLogic());
final AiAssistantState state = Get.find<AiAssistantLogic>().state;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: TitleAppBar(barTitle: 'AI助理设置'.tr, haveBack: true, backgroundColor: AppColors.mainColor),
body: Container(
width: 1.sw,
padding: EdgeInsets.symmetric(horizontal: 20.w),
margin: EdgeInsets.only(top: 20.h),
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Image.asset(
'images/other/ai.png',
height: 250.h,
fit: BoxFit.cover,
),
SizedBox(
height: 50.h,
),
Text(
'AI助理的支持依赖网络授权下载打开功能开关时请保证手机数据网络的正常连接'.tr,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.sp,
color: Colors.grey,
fontWeight: FontWeight.w500,
),
),
SizedBox(
height: 50.h,
),
CommonItem(
leftTitel: 'Ai语音菜单'.tr,
rightTitle: '',
isHaveLine: true,
// 线
isHaveDirection: false,
isHaveRightWidget: true,
rightWidget: CupertinoSwitch(
value: false,
onChanged: (value) {},
),
action: () {
logic.showToast('功能待开放'.tr);
},
),
CommonItem(
leftTitel: 'Ai智能语音交互'.tr,
rightTitle: '',
isHaveLine: false,
// 线
isHaveDirection: false,
isHaveRightWidget: true,
rightWidget: CupertinoSwitch(
value: false,
onChanged: (value) {},
),
action: () {
logic.showToast('功能待开放'.tr);
},
)
],
),
),
);
}
}

View File

@ -0,0 +1,12 @@
import 'package:get/get.dart';
import 'package:star_lock/main/lockDetail/lockSet/lockSet/lockSetInfo_entity.dart';
class AiAssistantState {
AiAssistantState() {
final map = Get.arguments;
lockSetInfoData.value = map['lockSetInfoData'];
}
Rx<LockSetInfoData> lockSetInfoData = LockSetInfoData().obs;
}

View File

@ -35,10 +35,7 @@ class _BasicInformationPageState extends State<BasicInformationPage> {
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: AppColors.mainBackgroundColor,
appBar: TitleAppBar(
barTitle: '基本信息'.tr,
haveBack: true,
backgroundColor: AppColors.mainColor),
appBar: TitleAppBar(barTitle: '基本信息'.tr, haveBack: true, backgroundColor: AppColors.mainColor),
body: ListView(
children: <Widget>[
Obx(() => CommonItem(
@ -48,8 +45,7 @@ class _BasicInformationPageState extends State<BasicInformationPage> {
isHaveLine: true)),
Obx(() => CommonItem(
leftTitel: 'MAC/ID',
rightTitle:
"${state.lockBasicInfo.value.mac ?? ""}/${state.lockBasicInfo.value.lockId ?? ""}",
rightTitle: "${state.lockBasicInfo.value.mac ?? ""}/${state.lockBasicInfo.value.lockId ?? ""}",
allHeight: 70.h,
isHaveLine: true)),
// Obx(() => CommonItem(
@ -67,22 +63,17 @@ class _BasicInformationPageState extends State<BasicInformationPage> {
),
Obx(() => CommonItem(
leftTitel: '电量'.tr,
rightTitle:
'${state.lockBasicInfo.value.electricQuantity ?? 0}%',
rightTitle: '${state.lockBasicInfo.value.electricQuantity ?? 0}%',
isHaveLine: true,
isHaveDirection: true,
action: () {
Get.toNamed(Routers.uploadElectricQuantityPage,
arguments: <String, LockSetInfoData>{
'lockSetInfoData': state.lockSetInfoData.value
});
arguments: <String, LockSetInfoData>{'lockSetInfoData': state.lockSetInfoData.value});
})),
Obx(() => CommonItem(
leftTitel: '有效期'.tr,
rightTitle: logic.getUseKeyTypeStr(
state.lockBasicInfo.value.startDate,
state.lockBasicInfo.value.endDate,
state.lockBasicInfo.value.keyType),
rightTitle: logic.getUseKeyTypeStr(state.lockBasicInfo.value.startDate,
state.lockBasicInfo.value.endDate, state.lockBasicInfo.value.keyType),
allHeight: 70.h,
isHaveLine: true)),
Obx(() => Visibility(
@ -104,28 +95,19 @@ class _BasicInformationPageState extends State<BasicInformationPage> {
)),
SizedBox(height: 10.h),
Obx(() => Visibility(
visible: state.lockBasicInfo.value.isLockOwner == 1 ||
state.lockBasicInfo.value.keyRight == 1,
child: lockDataListItem(
'名称'.tr, state.lockBasicInfo.value.lockAlias ?? '',
() async {
visible: state.lockBasicInfo.value.isLockOwner == 1 || state.lockBasicInfo.value.keyRight == 1,
child: lockDataListItem('名称'.tr, state.lockBasicInfo.value.lockAlias ?? '', () async {
var data = await Get.toNamed(Routers.editLockNamePage,
arguments: <String, LockSetInfoData>{
'lockSetInfoData': state.lockSetInfoData.value
});
arguments: <String, LockSetInfoData>{'lockSetInfoData': state.lockSetInfoData.value});
if (data != null) {
setState(() {
state.lockBasicInfo.value = data['lockBasicInfo'];
});
}
}))),
Obx(() => lockDataListItem(
'锁分组'.tr, state.lockBasicInfo.value.groupName ?? '',
() async {
Obx(() => lockDataListItem('锁分组'.tr, state.lockBasicInfo.value.groupName ?? '', () async {
Get.toNamed(Routers.lockSelectGroupingPage,
arguments: <String, LockSetInfoData>{
'lockSetInfoData': state.lockSetInfoData.value
})!
arguments: <String, LockSetInfoData>{'lockSetInfoData': state.lockSetInfoData.value})!
.then((val) {
if (val != null) {
// mockNetworkDataRequest();
@ -142,22 +124,41 @@ class _BasicInformationPageState extends State<BasicInformationPage> {
isHaveDirection: true,
action: () {
Get.toNamed(Routers.adminOpenLockPasswordPage,
arguments: <String, LockSetInfoData>{
'lockSetInfoData': state.lockSetInfoData.value
});
arguments: <String, LockSetInfoData>{'lockSetInfoData': state.lockSetInfoData.value});
}),
)),
Obx(() => Visibility(
visible: (state.lockBasicInfo.value.lockName ?? '')
.contains('T9A'),
child: CommonItem(
leftTitel: '当前网络'.tr,
rightTitle:
state.lockBasicInfo.value.networkInfo?.wifiName ??
'-',
allHeight: 70.h,
isHaveLine: true),
)),
Obx(
() => Visibility(
visible: state.lockSetInfoData.value.lockFeature?.wifi == 1,
child: CommonItem(
leftTitel: '当前网络'.tr,
rightTitle: state.lockBasicInfo.value.networkInfo?.wifiName ?? '-',
allHeight: 70.h,
isHaveLine: true),
),
),
Obx(
() => Visibility(
visible: state.lockSetInfoData.value.lockFeature?.wifi == 1,
child: CommonItem(
leftTitel: '网络信号强度'.tr,
rightTitle: '-',
allHeight: 70.h,
isHaveLine: true,
),
),
),
Obx(
() => Visibility(
visible: state.lockSetInfoData.value.lockFeature?.wifi == 1,
child: CommonItem(
leftTitel: '设备时区'.tr,
rightTitle: '-',
allHeight: 70.h,
isHaveLine: true,
),
),
),
// Obx(() => CommonItem(
// leftTitel: '位置信息'.tr,
// // rightTitle: state.lockBasicInfo.value.address ?? "-",
@ -190,14 +191,12 @@ class _BasicInformationPageState extends State<BasicInformationPage> {
));
}
Widget lockDataListItem(
String leftTitle, String conentStr, Function()? action) {
Widget lockDataListItem(String leftTitle, String conentStr, Function()? action) {
return GestureDetector(
onTap: action,
child: Container(
// height: 70.h,
padding:
EdgeInsets.only(left: 20.w, right: 10.w, top: 15.h, bottom: 15.h),
padding: EdgeInsets.only(left: 20.w, right: 10.w, top: 15.h, bottom: 15.h),
decoration: BoxDecoration(
color: Colors.white,
border: Border(

View File

@ -553,6 +553,19 @@ class _LockSetPageState extends State<LockSetPage>
});
},
),
//
CommonItem(
leftTitel: 'AI助理'.tr,
rightTitle: '',
isHaveLine: true,
isHaveDirection: true,
action: () {
Get.toNamed(Routers.aiAssistant,
arguments: <String, LockSetInfoData>{
'lockSetInfoData': state.lockSetInfoData.value
});
},
),
// 广使
/* 2024-01-12 广 by DaisyWu
Obx(() => Visibility(

View File

@ -26,8 +26,7 @@ class LockTimeLogic extends BaseGetXController {
late StreamSubscription<Reply> _replySubscription;
void _initReplySubscription() {
_replySubscription =
EventBusManager().eventBus!.on<Reply>().listen((Reply reply) {
_replySubscription = EventBusManager().eventBus!.on<Reply>().listen((Reply reply) {
if (reply is TimingReply) {
_replyTiming(reply);
}
@ -50,10 +49,8 @@ class LockTimeLogic extends BaseGetXController {
//
final List<int> indate = reply.data.sublist(150, 154);
int indateValue = (0xff & indate[0]) << 24 |
(0xff & indate[1]) << 16 |
(0xff & indate[2]) << 8 |
(0xFF & indate[3]);
int indateValue =
(0xff & indate[0]) << 24 | (0xff & indate[1]) << 16 | (0xff & indate[2]) << 8 | (0xFF & indate[3]);
//
if (indateValue == 0xffffffff || indateValue <= 0) {
// 使
@ -62,23 +59,19 @@ class LockTimeLogic extends BaseGetXController {
}
// 20002100
final DateTime dateTime =
DateTime.fromMillisecondsSinceEpoch(indateValue * 1000);
final DateTime dateTime = DateTime.fromMillisecondsSinceEpoch(indateValue * 1000);
if (dateTime.year < 2000 || dateTime.year > 2100) {
AppLog.log('时间戳超出合理范围: $indateValue');
indateValue = DateTime.now().millisecondsSinceEpoch ~/ 1000;
}
AppLog.log(
'====================indate:$indate indateValue:$indateValue');
AppLog.log('====================indate:$indate indateValue:$indateValue');
state.dateTime.value = DateTool().dateToYMDHNString('$indateValue');
break;
case 0x06:
//
final List<String>? privateKey =
await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList =
changeStringListToIntList(privateKey!);
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
IoSenderManage.senderGetStarLockStatuInfo(
lockID: BlueManage().connectDeviceName,
@ -101,8 +94,7 @@ class LockTimeLogic extends BaseGetXController {
switch (status) {
case 0x00:
//
final String dataEime =
DateTool().dateToYMDHNString('${state.serverTime}');
final String dataEime = DateTool().dateToYMDHNString('${state.serverTime}');
state.dateTime.value = dataEime;
@ -149,19 +141,15 @@ class LockTimeLogic extends BaseGetXController {
dismissEasyLoading();
state.sureBtnState.value = 0;
});
BlueManage().blueSendData(BlueManage().connectDeviceName,
(BluetoothConnectionState connectionState) async {
BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState connectionState) async {
if (connectionState == BluetoothConnectionState.connected) {
final List<String>? privateKey =
await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList =
changeStringListToIntList(privateKey!);
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
final List<String>? token = await Storage.getStringList(saveBlueToken);
final List<int> getTokenList = changeStringListToIntList(token!);
final List<String>? signKey =
await Storage.getStringList(saveBlueSignKey);
final List<String>? signKey = await Storage.getStringList(saveBlueSignKey);
final List<int> getSignKeyList = changeStringListToIntList(signKey!);
IoSenderManage.senderTimingCommand(
@ -190,13 +178,10 @@ class LockTimeLogic extends BaseGetXController {
showBlueConnetctToastTimer(action: () {
dismissEasyLoading();
});
BlueManage().blueSendData(BlueManage().connectDeviceName,
(BluetoothConnectionState deviceConnectionState) async {
BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async {
if (deviceConnectionState == BluetoothConnectionState.connected) {
final List<String>? privateKey =
await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList =
changeStringListToIntList(privateKey!);
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
IoSenderManage.senderGetStarLockStatuInfo(
lockID: BlueManage().connectDeviceName,
@ -206,8 +191,7 @@ class LockTimeLogic extends BaseGetXController {
isBeforeAddUser: false,
privateKey: getPrivateKeyList,
);
} else if (deviceConnectionState ==
BluetoothConnectionState.disconnected) {
} else if (deviceConnectionState == BluetoothConnectionState.disconnected) {
dismissEasyLoading();
cancelBlueConnetctToastTimer();
// if (state.ifCurrentScreen.value == true) {
@ -219,8 +203,7 @@ class LockTimeLogic extends BaseGetXController {
//
Future<void> getLockTimeFromGateway() async {
final GetServerDatetimeEntity entity =
await ApiRepository.to.getLockTimeFromGateway(
final GetServerDatetimeEntity entity = await ApiRepository.to.getLockTimeFromGateway(
lockId: state.lockSetInfoData.value.lockId.toString(),
);
if (entity.errorCode!.codeIsSuccessful) {}
@ -228,10 +211,10 @@ class LockTimeLogic extends BaseGetXController {
//
Future<void> getServerDatetime(bool isSendTime) async {
final GetServerDatetimeEntity entity =
await ApiRepository.to.getServerDatetimeData(isUnShowLoading: false);
final GetServerDatetimeEntity entity = await ApiRepository.to.getServerDatetimeData(isUnShowLoading: false);
if (entity.errorCode!.codeIsSuccessful) {
state.serverTime = entity.data!.date! ~/ 1000;
AppLog.log('msg:${state.serverTime}');
// AppLog.log("entity.data!.date! ~/ 1000:${entity.data!.date! ~/ 1000} DateTime.now().millisecondsSinceEpoch ~/ 1000:${DateTime.now().millisecondsSinceEpoch ~/ 1000} 服务器时间差:${state.differentialTime}");
if (isSendTime == false) {
getStarLockStatus();

View File

@ -257,7 +257,7 @@ class SpeechLanguageSettingsLogic extends BaseGetXController {
}
break;
default:
showToast('获取设备型号失败'.tr);
// showToast('获取设备型号失败'.tr);
break;
}
}

View File

@ -1,40 +1,317 @@
import 'dart:async';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:get/get.dart';
import 'package:star_lock/app_settings/app_settings.dart';
import 'package:star_lock/blue/blue_manage.dart';
import 'package:star_lock/blue/io_protocol/io_getStarLockStatusInfo.dart';
import 'package:star_lock/blue/io_protocol/io_readRegisterKey.dart';
import 'package:star_lock/blue/io_protocol/io_sendAuthorizationCode.dart';
import 'package:star_lock/blue/io_reply.dart';
import 'package:star_lock/blue/io_tool/io_tool.dart';
import 'package:star_lock/blue/io_tool/manager_event_bus.dart';
import 'package:star_lock/blue/sender_manage.dart';
import 'package:star_lock/main/lockDetail/lockSet/lockTime/getServerDatetime_entity.dart';
import 'package:star_lock/main/lockDetail/lockSet/thirdPartyPlatform/third_party_platform_state.dart';
import 'package:star_lock/network/api_repository.dart';
import 'package:star_lock/network/start_company_api.dart';
import 'package:star_lock/tools/baseGetXController.dart';
import 'package:star_lock/tools/storage.dart';
class ThirdPartyPlatformLogic extends BaseGetXController {
ThirdPartyPlatformState state = ThirdPartyPlatformState();
void savePlatFormSetting() {
// showEasyLoading();
showToast('功能待开放'.tr);
// dismissEasyLoading();
}
/// TPP支持
void getActivateInfo() async {
final model = state.lockSetInfoData.value.lockBasicInfo?.model;
if (model != null && model != '') {
final response = await StartCompanyApi.to.getActivateInfo(model: model);
if (response.errorCode!.codeIsSuccessful) {
AppLog.log('${response.data}');
}
}
}
//
StreamSubscription<Reply>? _replySubscription;
@override
void onReady() async {
// TODO: implement onReady
super.onReady();
getActivateInfo();
await getActivateInfo();
_initReplySubscription();
await getServerDatetime();
showEasyLoading();
showBlueConnetctToastTimer(action: () {
dismissEasyLoading();
});
BlueManage().blueSendData(
BlueManage().connectDeviceName,
(BluetoothConnectionState connectionState) async {
if (connectionState == BluetoothConnectionState.connected) {
IoSenderManage.readRegisterKey(
lockID: BlueManage().connectDeviceName,
);
} else if (connectionState == BluetoothConnectionState.disconnected) {
dismissEasyLoading();
cancelBlueConnetctToastTimer();
}
},
);
}
@override
void dispose() {
dismissEasyLoading();
//
_replySubscription?.cancel();
_replySubscription = null;
//
AppLog.log('ThirdPartyPlatformLogic disposed, subscription cancelled');
super.dispose();
}
@override
void onClose() {
super.onClose();
//
_replySubscription?.cancel();
_replySubscription = null;
}
//
Future<void> getServerDatetime() async {
final GetServerDatetimeEntity entity = await ApiRepository.to.getServerDatetimeData(isUnShowLoading: true);
if (entity.errorCode!.codeIsSuccessful) {
state.serverTime = entity.data!.date! ~/ 1000;
state.differentialTime = entity.data!.date! ~/ 1000 - DateTime.now().millisecondsSinceEpoch ~/ 1000;
}
}
int getUTCNetTime() {
return DateTime.now().millisecondsSinceEpoch ~/ 1000 + state.differentialTime;
}
void _initReplySubscription() {
//
if (_replySubscription != null) {
AppLog.log('订阅已存在,避免重复初始化');
return;
}
_replySubscription = EventBusManager().eventBus!.on<Reply>().listen((Reply reply) async {
if (reply is SenderReadRegisterKeyCommandReply) {
_handleReadRegisterKeyReply(reply);
}
if (reply is GetStarLockStatuInfoReply) {
_replyGetStarLockStatusInfo(reply);
}
if (reply is SenderAuthorizationCodeCommandReply) {
_handleAuthorizationCodeReply(reply);
}
});
AppLog.log('创建新订阅:${_replySubscription.hashCode}');
}
//
Future<void> _replyGetStarLockStatusInfo(Reply reply) async {
final int status = reply.data[2];
switch (status) {
case 0x00:
//
dismissEasyLoading();
cancelBlueConnetctToastTimer();
//
// AppLog.log('获取锁状态成功');
//
int index = 3;
final List<int> vendor = reply.data.sublist(index, index + 20);
final String vendorStr = utf8String(vendor);
state.lockInfo['vendor'] = vendorStr;
// state.lockInfo["vendor"] = "XL";
index = index + 20;
// AppLog.log('厂商名称 vendorStr:$vendorStr');
//
final int product = reply.data[index];
state.lockInfo['product'] = product;
index = index + 1;
// AppLog.log('锁设备类型 product:$product');
//
final List<int> model = reply.data.sublist(index, index + 20);
final String modelStr = utf8String(model);
state.lockInfo['model'] = modelStr;
// state.lockInfo["model"] = "JL-BLE-01";
index = index + 20;
// AppLog.log('产品名称 mmodelStr:$modelStr');
//
final List<int> fwVersion = reply.data.sublist(index, index + 20);
final String fwVersionStr = utf8String(fwVersion);
state.lockInfo['fwVersion'] = fwVersionStr;
index = index + 20;
// AppLog.log('软件版本 fwVersionStr:$fwVersionStr');
//
final List<int> hwVersion = reply.data.sublist(index, index + 20);
final String hwVersionStr = utf8String(hwVersion);
state.lockInfo['hwVersion'] = hwVersionStr;
index = index + 20;
// AppLog.log('硬件版本 hwVersionStr:$hwVersionStr');
//
final List<int> serialNum0 = reply.data.sublist(index, index + 16);
final String serialNum0Str = utf8String(serialNum0);
state.lockInfo['serialNum0'] = serialNum0Str;
// state.lockInfo["serialNum0"] = "${DateTime.now().millisecondsSinceEpoch ~/ 10}";
index = index + 16;
// AppLog.log('厂商序列号 serialNum0Str:$serialNum0Str');
final response = await StartCompanyApi.to.getAuthorizationCode(
registerKey: state.registerKey.value,
model: modelStr,
serialNum0: serialNum0Str,
platform: 1,
);
if (response.errorCode!.codeIsSuccessful) {
BlueManage().blueSendData(
BlueManage().connectDeviceName,
(BluetoothConnectionState connectionState) async {
if (connectionState == BluetoothConnectionState.connected) {
IoSenderManage.sendAuthorizationCode(
lockID: BlueManage().connectDeviceName,
uuid: response.data?.extraParams?['uuid'],
key: response.data?.authCode,
mac: response.data?.extraParams?['mac'],
platform: state.selectPlatFormIndex.value,
utcTimeStamp: getUTCNetTime(),
);
} else if (connectionState == BluetoothConnectionState.disconnected) {
dismissEasyLoading();
cancelBlueConnetctToastTimer();
}
},
isAddEquipment: true,
);
}
break;
case 0x06:
//
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
IoSenderManage.senderGetStarLockStatuInfo(
lockID: BlueManage().connectDeviceName,
userID: await Storage.getUid(),
utcTimeStamp: 0,
unixTimeStamp: 0,
isBeforeAddUser: false,
privateKey: getPrivateKeyList,
);
break;
default:
//
break;
}
}
void savePlatFormSetting() {
if (state.selectPlatFormIndex.value == 1 || state.selectPlatFormIndex.value == 0) {
if (state.registerKey.isNotEmpty) {
_requestAuthorizationCode();
}
} else {
showToast('目前只支持切换至涂鸦智能、锁通通协议'.tr);
}
}
/// TPP支持
Future<void> getActivateInfo() async {
final model = state.lockSetInfoData.value.lockBasicInfo?.model;
if (model != null && model != '') {
final response = await StartCompanyApi.to.getTppSupport(model: model);
if (response.errorCode!.codeIsSuccessful) {
response.data?.forEach((element) {
state.tppSupportList.add(element);
});
state.tppSupportList.refresh();
}
}
}
void _handleReadRegisterKeyReply(SenderReadRegisterKeyCommandReply reply) {
final int status = reply.data[6];
switch (status) {
case 0x00:
final platform = reply.data[7];
// RegisterKey (740)
final List<int> registerKeyBytes = reply.data.sublist(8, 48);
final String registerKey = String.fromCharCodes(registerKeyBytes);
state.selectPlatFormIndex.value = platform;
print('platform: $platform');
print('Register Key: $registerKey');
state.registerKey.value = registerKey;
//
cancelBlueConnetctToastTimer();
dismissEasyLoading();
break;
default:
//
dismissEasyLoading();
cancelBlueConnetctToastTimer();
break;
}
}
void _requestAuthorizationCode() async {
showEasyLoading();
showBlueConnetctToastTimer(action: () {
dismissEasyLoading();
});
BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async {
if (deviceConnectionState == BluetoothConnectionState.connected) {
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
IoSenderManage.senderGetStarLockStatuInfo(
lockID: BlueManage().connectDeviceName,
userID: await Storage.getUid(),
utcTimeStamp: state.serverTime,
unixTimeStamp: getLocalTime2(),
isBeforeAddUser: false,
privateKey: getPrivateKeyList,
);
} else if (deviceConnectionState == BluetoothConnectionState.disconnected) {
//
dismissEasyLoading();
cancelBlueConnetctToastTimer();
}
});
}
int getLocalTime() {
final DateTime now = DateTime.now();
final Duration timeZoneOffset = now.timeZoneOffset;
return state.differentialTime + timeZoneOffset.inSeconds;
}
int getLocalTime2() {
final DateTime now = DateTime.now();
final Duration timeZoneOffset = now.timeZoneOffset;
return state.serverTime + timeZoneOffset.inSeconds;
}
void _handleAuthorizationCodeReply(SenderAuthorizationCodeCommandReply reply) {
final int status = reply.data[6];
switch (status) {
case 0x00:
//
cancelBlueConnetctToastTimer();
dismissEasyLoading();
if (state.selectPlatFormIndex.value == 1) {
showSuccess('操作成功,请尽快用"涂鸦”APP配置如不使用请关闭该设置支持'.tr);
} else if (state.selectPlatFormIndex.value == 0) {
showSuccess('操作成功'.tr);
}
break;
default:
//
dismissEasyLoading();
cancelBlueConnetctToastTimer();
break;
}
}
}

View File

@ -17,8 +17,7 @@ class ThirdPartyPlatformPage extends StatefulWidget {
class _ThirdPartyPlatformPageState extends State<ThirdPartyPlatformPage> {
final ThirdPartyPlatformLogic logic = Get.put(ThirdPartyPlatformLogic());
final ThirdPartyPlatformState state =
Get.find<ThirdPartyPlatformLogic>().state;
final ThirdPartyPlatformState state = Get.find<ThirdPartyPlatformLogic>().state;
@override
Widget build(BuildContext context) {
@ -46,52 +45,125 @@ class _ThirdPartyPlatformPageState extends State<ThirdPartyPlatformPage> {
}
Widget _buildBody() {
return ListView.builder(
itemCount: state.platFormSet.length,
itemBuilder: (BuildContext context, int index) {
// itemCount - 1
final isLastItem = index == state.platFormSet.length - 1;
return SingleChildScrollView(
child: Container(
decoration: BoxDecoration(
color: Colors.white,
),
constraints: BoxConstraints(
minHeight: MediaQuery.of(context).size.height - 100.h,
),
child: Stack(
children: [
// 1.
Container(
color: Colors.white,
padding: EdgeInsets.all(16.w),
),
Positioned(
top: 30.h,
left: 50.w,
child: Image.asset(
'images/other/tuya.png',
height: 80.h,
fit: BoxFit.cover,
),
),
Positioned(
top: 130.h,
left: 150.w,
right: 150.w,
child: Image.asset(
'images/other/2.png',
height: 220.h,
fit: BoxFit.contain,
),
),
// 2. 使 Row
Positioned(
top: 400.h,
right: 50.w,
child: Column(
children: [
Image.asset(
'images/other/matter.png',
width: 280.w,
fit: BoxFit.contain,
),
],
),
),
Positioned(
top: 530.h,
left: 20.w,
right: 20.w,
child: Text(
'第三方协议的支持依赖网络授权下载,打开功能开关时请保证手机数据网络的正常连接'.tr,
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 20.sp,
color: Colors.grey,
fontWeight: FontWeight.w500,
),
),
),
// platFormSet RxList<Platform>
final platform = state.platFormSet.value[index];
return CommonItem(
leftTitel: state.platFormSet.value[index],
rightTitle: '',
isHaveLine: !isLastItem,
// 线
isHaveDirection: false,
isHaveRightWidget: true,
rightWidget: Radio<String>(
// Radio 使 id
value: platform,
// selectPlatFormIndex id
groupValue:
state.platFormSet.value[state.selectPlatFormIndex.value],
//
activeColor: AppColors.mainColor,
// Radio
onChanged: (value) {
if (value != null) {
setState(() {
// id
final newIndex =
state.platFormSet.value.indexWhere((p) => p == value);
if (newIndex != -1) {
state.selectPlatFormIndex.value = newIndex;
}
});
}
},
),
action: () {
setState(() {
state.selectPlatFormIndex.value = index;
});
},
);
},
shrinkWrap: true,
physics: const AlwaysScrollableScrollPhysics(),
Positioned(
left: 0,
right: 0,
top: 620.h,
bottom: 0,
child: ListView.builder(
itemCount: state.platFormSet.length,
itemBuilder: (BuildContext context, int index) {
// itemCount - 1
final isLastItem = index == state.platFormSet.length - 1;
// platFormSet RxList<Platform>
final platform = state.platFormSet.value[index];
return CommonItem(
leftTitel: state.platFormSet.value[index],
rightTitle: '',
isHaveLine: !isLastItem,
// 线
isHaveDirection: false,
isHaveRightWidget: true,
rightWidget: Obx(
() => Radio<String>(
// Radio 使 id
value: platform,
// selectPlatFormIndex id
groupValue: state.platFormSet.value[state.selectPlatFormIndex.value],
//
activeColor: AppColors.mainColor,
// Radio
onChanged: (value) {
if (value != null) {
setState(() {
// id
final newIndex = state.platFormSet.value.indexWhere((p) => p == value);
if (newIndex != -1) {
state.selectPlatFormIndex.value = newIndex;
}
});
}
},
),
),
action: () {
setState(() {
state.selectPlatFormIndex.value = index;
});
},
);
},
shrinkWrap: true,
physics: const AlwaysScrollableScrollPhysics(),
),
),
],
),
),
);
}
}

View File

@ -1,6 +1,8 @@
import 'dart:ui';
import 'package:get/get.dart';
import 'package:get/get_rx/get_rx.dart';
import 'package:star_lock/main/lockDetail/lockDetail/ActivateInfoResponse.dart';
import 'package:star_lock/main/lockDetail/lockSet/lockSet/lockSetInfo_entity.dart';
import 'package:star_lock/translations/app_dept.dart';
@ -11,15 +13,23 @@ class ThirdPartyPlatformState {
}
Rx<LockSetInfoData> lockSetInfoData = LockSetInfoData().obs;
int differentialTime = 0; // UTC+0
// UI
final RxList<String> platFormSet = List.of({
'锁通通'.tr,
'涂鸦智能'.tr,
'Matter'.tr ,
'Matter'.tr,
}).obs;
// UI
final RxList<TppSupportInfo> tppSupportList = RxList<TppSupportInfo>([]);
RxInt selectPlatFormIndex = 0.obs;
RxBool openNumber = false.obs;
RxString registerKey = ''.obs;
Map lockInfo = {};
int serverTime = 0; // UTC+0
}

View File

@ -79,7 +79,7 @@ class UploadDataLogic extends BaseGetXController {
final int dataLength = reply.data[8];
state.uploadPasswordDataList.addAll(reply.data.sublist(9, reply.data.length));
if (dataLength == 10) {
if (dataLength == 10 || dataLength == 9) {
// 10
state.uploadPasswordPage = state.uploadPasswordPage + 1;
@ -128,7 +128,7 @@ class UploadDataLogic extends BaseGetXController {
final int dataLength = reply.data[8];
state.uploadCardDataList.addAll(reply.data.sublist(9, reply.data.length));
if (dataLength == 10) {
if (dataLength == 10 || dataLength == 9) {
// 10
state.uploadCardPage = state.uploadCardPage + 1;
@ -175,7 +175,7 @@ class UploadDataLogic extends BaseGetXController {
final int dataLength = reply.data[8];
state.uploadFingerprintDataList.addAll(reply.data.sublist(9, reply.data.length));
if (dataLength == 10) {
if (dataLength == 10 || dataLength == 9) {
// 10
state.uploadFingerprintPage = state.uploadFingerprintPage + 1;
@ -222,7 +222,7 @@ class UploadDataLogic extends BaseGetXController {
final int dataLength = reply.data[8];
state.uploadFaceDataList.addAll(reply.data.sublist(9, reply.data.length));
if (dataLength == 10) {
if (dataLength == 10 || dataLength == 9) {
// 10
state.uploadFacePage = state.uploadFacePage + 1;
@ -269,7 +269,7 @@ class UploadDataLogic extends BaseGetXController {
final int dataLength = reply.data[8];
state.uploadPalmVeinDataList.addAll(reply.data.sublist(9, reply.data.length));
if (dataLength == 10) {
if (dataLength == 10 || dataLength == 9) {
// 10
state.uploadPalmVeinPage = state.uploadPalmVeinPage + 1;
@ -316,7 +316,7 @@ class UploadDataLogic extends BaseGetXController {
final int dataLength = reply.data[8];
state.uploadRemoteControlDataList.addAll(reply.data.sublist(9, reply.data.length));
if (dataLength == 10) {
if (dataLength == 10 || dataLength == 9) {
// 10
state.uploadRemoteControlPage = state.uploadRemoteControlPage + 1;

View File

@ -32,12 +32,10 @@ class LockListLogic extends BaseGetXController {
final ShowTipView showTipView = ShowTipView();
List<GroupList> get groupDataListFiltered {
final List<GroupList> list =
groupDataList.map((GroupList e) => e.copy()).toList();
final List<GroupList> list = groupDataList.map((GroupList e) => e.copy()).toList();
if (state.searchStr.value != '' && state.showSearch.value) {
list.forEach((GroupList element) {
element.lockList?.removeWhere((LockListInfoItemEntity element) =>
!(element.lockAlias?.contains(state.searchStr.value) ?? true));
element.lockList?.removeWhere((LockListInfoItemEntity element) => !(element.lockAlias?.contains(state.searchStr.value) ?? true));
});
}
if (list.length > 0) {
@ -46,8 +44,7 @@ class LockListLogic extends BaseGetXController {
final lockList = element.lockList;
if (lockList != null && lockList.length > 0) {
lockList.forEach((element) {
if (element.network?.peerId != null &&
element.network?.peerId != '') {
if (element.network?.peerId != null && element.network?.peerId != '') {
StartChartManage().lockListPeerId.add(element);
}
});
@ -68,11 +65,9 @@ class LockListLogic extends BaseGetXController {
late StreamSubscription _setLockListInfoGroupEntity;
void _initReplySubscription() {
_replySubscription =
EventBusManager().eventBus!.on<Reply>().listen((Reply reply) async {
_replySubscription = EventBusManager().eventBus!.on<Reply>().listen((Reply reply) async {
//
if ((reply is FactoryDataResetReply) &&
(state.ifCurrentScreen.value == true)) {
if ((reply is FactoryDataResetReply)) {
_replyFactoryDataResetKey(reply);
}
});
@ -93,19 +88,18 @@ class LockListLogic extends BaseGetXController {
break;
case 0x06:
//
final List<String>? token = await Storage.getStringList(saveBlueToken);
final List<int> getTokenList = changeStringListToIntList(token!);
final List<int> tokenData = reply.data.sublist(2, 6);
final List<String> saveStrList = changeIntListToStringList(tokenData);
Storage.setStringList(saveBlueToken, saveStrList);
IoSenderManage.senderFactoryDataReset(
lockID: BlueManage().connectDeviceName,
userID: await Storage.getUid(),
keyID: '1',
needAuthor: 1,
publicKey:
state.lockListInfoItemEntity.bluetooth!.publicKey!.cast<int>(),
privateKey:
state.lockListInfoItemEntity.bluetooth!.privateKey!.cast<int>(),
token: getTokenList);
lockID: BlueManage().connectDeviceName,
userID: await Storage.getUid(),
keyID: '1',
needAuthor: 1,
publicKey: state.publicKey,
privateKey: state.privateKey,
token: tokenData,
);
break;
case 0x07:
//
@ -146,8 +140,7 @@ class LockListLogic extends BaseGetXController {
keyInfo.keyType == XSConstantMacro.keyTypeLong ||
keyInfo.keyType == XSConstantMacro.keyTypeLoop) {
// 使
if (keyInfo.keyStatus == XSConstantMacro.keyStatusNormalUse ||
keyInfo.keyStatus == XSConstantMacro.keyStatusWaitReceive) {
if (keyInfo.keyStatus == XSConstantMacro.keyStatusNormalUse || keyInfo.keyStatus == XSConstantMacro.keyStatusWaitReceive) {
return "${"".tr}${DateTool().compareTimeGetDaysFromNow(keyInfo.endDate!)}${"".tr}";
} else {
return XSConstantMacro.getKeyStatusStr(keyInfo.keyStatus!);
@ -160,11 +153,7 @@ class LockListLogic extends BaseGetXController {
//
bool getShowType(LockListInfoItemEntity keyInfo) {
final List<int> keyTypes = <int>[
XSConstantMacro.keyTypeTime,
XSConstantMacro.keyTypeOnce,
XSConstantMacro.keyTypeLoop
];
final List<int> keyTypes = <int>[XSConstantMacro.keyTypeTime, XSConstantMacro.keyTypeOnce, XSConstantMacro.keyTypeLoop];
final List<int> keyStatus = <int>[
XSConstantMacro.keyStatusWaitIneffective,
XSConstantMacro.keyStatusFrozen,
@ -172,36 +161,30 @@ class LockListLogic extends BaseGetXController {
];
//--&&
final bool isLongFrozenStatus =
keyInfo.keyType == XSConstantMacro.keyTypeLong &&
keyInfo.keyStatus == XSConstantMacro.keyStatusFrozen;
final DateTime endDate =
DateTime.fromMillisecondsSinceEpoch(keyInfo.endDate ?? 0);
final bool isLongFrozenStatus = keyInfo.keyType == XSConstantMacro.keyTypeLong && keyInfo.keyStatus == XSConstantMacro.keyStatusFrozen;
final DateTime endDate = DateTime.fromMillisecondsSinceEpoch(keyInfo.endDate ?? 0);
final DateTime now = DateTime.now();
final bool isKeyType = keyTypes.contains(keyInfo.keyType);
final bool isKeyStatus = keyStatus.contains(keyInfo.keyStatus);
final Duration difference = endDate.difference(now);
final bool isExpirationSoon = isKeyType && difference.inDays <= 15;
final bool isShow =
isKeyType && isKeyStatus || isExpirationSoon || isLongFrozenStatus;
final bool isShow = isKeyType && isKeyStatus || isExpirationSoon || isLongFrozenStatus;
return isShow;
}
///
void deleyLockLogicOfRoles() {
if (state.lockListInfoItemEntity.isLockOwner == 1) {
void deleyLockLogicOfRoles(LockListInfoItemEntity keyInfo) {
if (state.lockListInfoItemEntity.value.isLockOwner == 1) {
//
showTipView.showIosTipWithContentDialog('删除锁后,所有信息都会一起删除,确定删除锁吗?'.tr, () {
//
AppLog.log('调用了删除锁');
showTipView.resetGetController();
showTipView.showTFViewAlertDialog(
state.passwordTF, '请输入登录密码'.tr, '请输入登录密码'.tr, checkLoginPassword);
showTipView.showTFViewAlertDialog(state.passwordTF, '请输入登录密码'.tr, '请输入登录密码'.tr, () => checkLoginPassword(keyInfo));
});
} else if (state.lockListInfoItemEntity.keyRight == 1) {
} else if (state.lockListInfoItemEntity.value.keyRight == 1) {
//
showTipView.showDeleteAdministratorIsHaveAllDataDialog(
'同时删除其发送的所有钥匙,钥匙删除后不能恢复'.tr, (bool a) {
showTipView.showDeleteAdministratorIsHaveAllDataDialog('同时删除其发送的所有钥匙,钥匙删除后不能恢复'.tr, (bool a) {
//
state.deleteAdministratorIsHaveAllData.value = a;
deletKeyData();
@ -213,20 +196,20 @@ class LockListLogic extends BaseGetXController {
}
//
Future<void> checkLoginPassword() async {
Future<void> checkLoginPassword(LockListInfoItemEntity keyInfo) async {
final LockListInfoEntity entity = await ApiRepository.to.checkLoginPassword(
password: state.passwordTF.text,
);
if (entity.errorCode!.codeIsSuccessful) {
Get.back();
factoryDataResetAction();
factoryDataResetAction(keyInfo);
}
}
//
Future<void> deletLockInfoData() async {
final LockListInfoEntity entity = await ApiRepository.to.deletOwnerLockData(
lockId: state.lockListInfoItemEntity.lockId!,
lockId: state.lockListInfoItemEntity.value.lockId!,
);
if (entity.errorCode!.codeIsSuccessful) {
BlueManage().connectDeviceMacAddress = '';
@ -239,10 +222,9 @@ class LockListLogic extends BaseGetXController {
//
Future<void> deletKeyData() async {
final LockListInfoEntity entity = await ApiRepository.to.deletOwnerKeyData(
lockId: state.lockListInfoItemEntity.lockId.toString(),
keyId: state.lockListInfoItemEntity.keyId.toString(),
includeUnderlings:
state.deleteAdministratorIsHaveAllData.value == true ? 1 : 0);
lockId: state.lockListInfoItemEntity.value.lockId.toString(),
keyId: state.lockListInfoItemEntity.value.keyId.toString(),
includeUnderlings: state.deleteAdministratorIsHaveAllData.value == true ? 1 : 0);
if (entity.errorCode!.codeIsSuccessful) {
BlueManage().connectDeviceMacAddress = '';
SchedulerBinding.instance.addPostFrameCallback((_) {
@ -252,7 +234,7 @@ class LockListLogic extends BaseGetXController {
}
//
Future<void> factoryDataResetAction() async {
Future<void> factoryDataResetAction(LockListInfoItemEntity keyInfo) async {
showEasyLoading();
showBlueConnetctToastTimer(
isShowBlueConnetctToast: false,
@ -260,31 +242,23 @@ class LockListLogic extends BaseGetXController {
dismissEasyLoading();
showDeletAlertTipDialog();
});
BlueManage().blueSendData(state.lockListInfoItemEntity.lockName!,
(BluetoothConnectionState connectionState) async {
BlueManage().blueSendData(state.lockListInfoItemEntity.value.lockName!, (BluetoothConnectionState connectionState) async {
if (connectionState == BluetoothConnectionState.connected) {
final List<int> publicKeyData =
state.lockListInfoItemEntity.bluetooth!.publicKey!.cast<int>();
final List<String> saveStrList =
changeIntListToStringList(publicKeyData);
final List<int> publicKeyData = state.lockListInfoItemEntity.value.bluetooth!.publicKey!.cast<int>();
final List<String> saveStrList = changeIntListToStringList(publicKeyData);
await Storage.setStringList(saveBluePublicKey, saveStrList);
//
final List<int> privateKeyData =
state.lockListInfoItemEntity.bluetooth!.privateKey!.cast<int>();
final List<String> savePrivateKeyList =
changeIntListToStringList(privateKeyData);
final List<int> privateKeyData = state.lockListInfoItemEntity.value.bluetooth!.privateKey!.cast<int>();
final List<String> savePrivateKeyList = changeIntListToStringList(privateKeyData);
await Storage.setStringList(saveBluePrivateKey, savePrivateKeyList);
// signKey
final List<int> signKeyData =
state.lockListInfoItemEntity.bluetooth!.signKey!.cast<int>();
final List<String> saveSignKeyList =
changeIntListToStringList(signKeyData);
final List<int> signKeyData = state.lockListInfoItemEntity.value.bluetooth!.signKey!.cast<int>();
final List<String> saveSignKeyList = changeIntListToStringList(signKeyData);
await Storage.setStringList(saveBlueSignKey, saveSignKeyList);
final List<String> saveTokenList =
changeIntListToStringList(<int>[0, 0, 0, 0]);
final List<String> saveTokenList = changeIntListToStringList(<int>[0, 0, 0, 0]);
await Storage.setStringList(saveBlueToken, saveTokenList);
IoSenderManage.senderFactoryDataReset(
@ -292,11 +266,13 @@ class LockListLogic extends BaseGetXController {
userID: await Storage.getUid(),
keyID: '1',
needAuthor: 1,
publicKey:
state.lockListInfoItemEntity.bluetooth!.publicKey!.cast<int>(),
privateKey:
state.lockListInfoItemEntity.bluetooth!.privateKey!.cast<int>(),
publicKey: keyInfo.bluetooth?.publicKey ?? [],
privateKey: keyInfo.bluetooth?.privateKey ?? [],
token: <int>[0, 0, 0, 0]);
state.publicKey.value = keyInfo.bluetooth?.publicKey ?? [];
state.privateKey.value = keyInfo.bluetooth?.privateKey ?? [];
state.publicKey.refresh();
state.privateKey.refresh();
} else if (connectionState == BluetoothConnectionState.disconnected) {
dismissEasyLoading();
cancelBlueConnetctToastTimer();
@ -353,9 +329,7 @@ class LockListLogic extends BaseGetXController {
}
void _initEventHandler() {
_setLockListInfoGroupEntity = eventBus
.on<SetLockListInfoGroupEntity>()
.listen((SetLockListInfoGroupEntity event) async {
_setLockListInfoGroupEntity = eventBus.on<SetLockListInfoGroupEntity>().listen((SetLockListInfoGroupEntity event) async {
setLockListInfoGroupEntity(event.lockListInfoGroupEntity);
});
}

View File

@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:flutter_slidable/flutter_slidable.dart';
import 'package:get/get.dart';
import 'package:star_lock/app_settings/app_settings.dart';
import 'package:star_lock/main/lockMian/lockList/lockList_state.dart';
import '../../../appRouters.dart';
@ -14,8 +15,7 @@ import 'lockListGroup_view.dart';
import 'lockList_logic.dart';
class LockListPage extends StatefulWidget {
const LockListPage({required this.lockListInfoGroupEntity, Key? key})
: super(key: key);
const LockListPage({required this.lockListInfoGroupEntity, Key? key}) : super(key: key);
final LockListInfoGroupEntity lockListInfoGroupEntity;
@override
@ -30,41 +30,41 @@ class _LockListPageState extends State<LockListPage> with RouteAware {
void initState() {
super.initState();
logic = Get.put(LockListLogic(widget.lockListInfoGroupEntity));
state = Get
.find<LockListLogic>()
.state;
state = Get.find<LockListLogic>().state;
}
@override
Widget build(BuildContext context) {
return Obx(() => Scaffold(
body: ListView.separated(
itemCount: logic.groupDataListFiltered.length,
itemBuilder: (BuildContext context, int index) {
final GroupList itemData = logic.groupDataListFiltered[index];
return _buildLockExpandedList(context, index, itemData, key: ValueKey(itemData.groupId));
},
shrinkWrap: true,
physics: const AlwaysScrollableScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return const Divider(
height: 1,
color: AppColors.greyLineColor,
);
}),
));
return Obx(
() => Scaffold(
body: ListView.separated(
itemCount: logic.groupDataList.length,
itemBuilder: (BuildContext context, int index) {
final GroupList itemData = logic.groupDataListFiltered[index];
return _buildLockExpandedList(context, index, itemData, key: ValueKey(itemData.groupId));
},
shrinkWrap: true,
physics: const AlwaysScrollableScrollPhysics(),
separatorBuilder: (BuildContext context, int index) {
return const Divider(
height: 1,
color: AppColors.greyLineColor,
);
},
),
),
);
}
//
Widget _buildLockExpandedList(BuildContext context, int index,
GroupList itemData, {Key? key}) {
final List<LockListInfoItemEntity> lockItemList =
itemData.lockList ?? <LockListInfoItemEntity>[];
Widget _buildLockExpandedList(BuildContext context, int index, GroupList itemData, {Key? key}) {
final List<LockListInfoItemEntity> lockItemList = itemData.lockList ?? <LockListInfoItemEntity>[];
return LockListGroupView(
key: key,
onTap: () {
//
if (itemData.isChecked) {} else {}
if (itemData.isChecked) {
} else {}
setState(() {});
},
typeImgList: const <dynamic>[],
@ -91,8 +91,12 @@ class _LockListPageState extends State<LockListPage> with RouteAware {
children: <Widget>[
SlidableAction(
onPressed: (BuildContext context) {
state.lockListInfoItemEntity = keyInfo;
logic.deleyLockLogicOfRoles();
state.publicKey.value = keyInfo.bluetooth?.publicKey ?? [];
state.privateKey.value = keyInfo.bluetooth?.privateKey ?? [];
state.lockListInfoItemEntity.value = keyInfo;
state.lockListInfoItemEntity.refresh();
AppLog.log('msg=================:${state.lockListInfoItemEntity.value}');
logic.deleyLockLogicOfRoles(keyInfo);
},
backgroundColor: Colors.red,
foregroundColor: Colors.white,
@ -102,58 +106,46 @@ class _LockListPageState extends State<LockListPage> with RouteAware {
],
),
child: lockInfoListItem(keyInfo, isLast, () {
if ((keyInfo.keyType == XSConstantMacro.keyTypeTime ||
keyInfo.keyType == XSConstantMacro.keyTypeLoop) &&
(keyInfo.keyStatus ==
XSConstantMacro.keyStatusWaitIneffective)) {
if ((keyInfo.keyType == XSConstantMacro.keyTypeTime || keyInfo.keyType == XSConstantMacro.keyTypeLoop) &&
(keyInfo.keyStatus == XSConstantMacro.keyStatusWaitIneffective)) {
logic.showToast('您的钥匙未生效'.tr);
return;
}
if ((keyInfo.keyType == XSConstantMacro.keyTypeTime ||
keyInfo.keyType == XSConstantMacro.keyTypeLong ||
keyInfo.keyType == XSConstantMacro.keyTypeLoop) &&
keyInfo.keyType == XSConstantMacro.keyTypeLong ||
keyInfo.keyType == XSConstantMacro.keyTypeLoop) &&
(keyInfo.keyStatus == XSConstantMacro.keyStatusFrozen)) {
logic.showToast('您的钥匙已冻结'.tr);
return;
}
if ((keyInfo.keyType == XSConstantMacro.keyTypeTime ||
keyInfo.keyType == XSConstantMacro.keyTypeLoop) &&
if ((keyInfo.keyType == XSConstantMacro.keyTypeTime || keyInfo.keyType == XSConstantMacro.keyTypeLoop) &&
(keyInfo.keyStatus == XSConstantMacro.keyStatusExpired)) {
logic.showToast('您的钥匙已过期'.tr);
return;
}
Get.toNamed(Routers.lockDetailMainPage,
arguments: <String, Object>{
// "lockMainEntity": widget.lockMainEntity,
'keyInfo': keyInfo,
'isOnlyOneData': false,
});
Get.toNamed(Routers.lockDetailMainPage, arguments: <String, Object>{
// "lockMainEntity": widget.lockMainEntity,
'keyInfo': keyInfo,
'isOnlyOneData': false,
});
}),
);
}),
);
}
Widget lockInfoListItem(LockListInfoItemEntity keyInfo, bool isLast,
Function() action) {
Widget lockInfoListItem(LockListInfoItemEntity keyInfo, bool isLast, Function() action) {
return GestureDetector(
onTap: action,
child: Container(
// height: 122.h,
margin: isLast
? EdgeInsets.only(left: 20.w, right: 20.w, top: 20.w, bottom: 20.w)
: EdgeInsets.only(left: 20.w, right: 20.w, top: 20.w),
margin: isLast ? EdgeInsets.only(left: 20.w, right: 20.w, top: 20.w, bottom: 20.w) : EdgeInsets.only(left: 20.w, right: 20.w, top: 20.w),
decoration: BoxDecoration(
color: (((keyInfo.keyType == XSConstantMacro.keyTypeTime ||
keyInfo.keyType == XSConstantMacro.keyTypeLoop) &&
(keyInfo.keyStatus ==
XSConstantMacro.keyStatusWaitIneffective ||
keyInfo.keyStatus ==
XSConstantMacro.keyStatusFrozen ||
keyInfo.keyStatus ==
XSConstantMacro.keyStatusExpired)) ||
(keyInfo.keyType == XSConstantMacro.keyTypeLong &&
keyInfo.keyStatus == XSConstantMacro.keyStatusFrozen))
color: (((keyInfo.keyType == XSConstantMacro.keyTypeTime || keyInfo.keyType == XSConstantMacro.keyTypeLoop) &&
(keyInfo.keyStatus == XSConstantMacro.keyStatusWaitIneffective ||
keyInfo.keyStatus == XSConstantMacro.keyStatusFrozen ||
keyInfo.keyStatus == XSConstantMacro.keyStatusExpired)) ||
(keyInfo.keyType == XSConstantMacro.keyTypeLong && keyInfo.keyStatus == XSConstantMacro.keyStatusFrozen))
? AppColors.greyBackgroundColor
: Colors.white,
borderRadius: BorderRadius.circular(20.w),
@ -177,9 +169,7 @@ class _LockListPageState extends State<LockListPage> with RouteAware {
style: TextStyle(
fontSize: 24.sp,
fontWeight: FontWeight.w500,
color: keyInfo.passageMode == 1
? AppColors.openPassageModeColor
: AppColors.darkGrayTextColor),
color: keyInfo.passageMode == 1 ? AppColors.openPassageModeColor : AppColors.darkGrayTextColor),
),
],
),
@ -193,8 +183,7 @@ class _LockListPageState extends State<LockListPage> with RouteAware {
SizedBox(width: 2.w),
Text(
'${keyInfo.electricQuantity!}%',
style: TextStyle(
fontSize: 18.sp, color: AppColors.darkGrayTextColor),
style: TextStyle(fontSize: 18.sp, color: AppColors.darkGrayTextColor),
),
SizedBox(width: 30.w),
],
@ -211,10 +200,7 @@ class _LockListPageState extends State<LockListPage> with RouteAware {
borderRadius: BorderRadius.circular(5.w),
color: AppColors.openPassageModeColor,
),
child: Text('常开模式开启'.tr,
style: TextStyle(
fontSize: 18.sp,
color: AppColors.appBarIconColor)),
child: Text('常开模式开启'.tr, style: TextStyle(fontSize: 18.sp, color: AppColors.appBarIconColor)),
),
],
)),
@ -226,8 +212,7 @@ class _LockListPageState extends State<LockListPage> with RouteAware {
SizedBox(width: 30.w),
Text(
'远程开锁'.tr,
style: TextStyle(
fontSize: 18.sp, color: AppColors.darkGrayTextColor),
style: TextStyle(fontSize: 18.sp, color: AppColors.darkGrayTextColor),
),
],
)),
@ -241,15 +226,11 @@ class _LockListPageState extends State<LockListPage> with RouteAware {
padding: EdgeInsets.only(right: 5.w, left: 5.w),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(5.w),
color:
DateTool().compareTimeIsOvertime(keyInfo.endDate!)
? AppColors.listTimeYellowColor
: AppColors.mainColor,
),
child: Text(logic.getKeyEffective(keyInfo),
style: TextStyle(fontSize: 18.sp, color: Colors.white)
// child: Text(logic.compareTimeIsOvertime(keyInfo.endDate!) ? "已过期" : "${logic.compareTimeGetDaysFromNow(keyInfo.endDate!)}", style: TextStyle(fontSize: 18.sp, color: Colors.white)
color: DateTool().compareTimeIsOvertime(keyInfo.endDate!) ? AppColors.listTimeYellowColor : AppColors.mainColor,
),
child: Text(logic.getKeyEffective(keyInfo), style: TextStyle(fontSize: 18.sp, color: Colors.white)
// child: Text(logic.compareTimeIsOvertime(keyInfo.endDate!) ? "已过期" : "${logic.compareTimeGetDaysFromNow(keyInfo.endDate!)}", style: TextStyle(fontSize: 18.sp, color: Colors.white)
),
),
],
)),
@ -258,13 +239,8 @@ class _LockListPageState extends State<LockListPage> with RouteAware {
children: <Widget>[
SizedBox(width: 30.w),
Text(
"${logic.getUseKeyTypeStr(keyInfo.startDate, keyInfo.endDate,
keyInfo.keyType)}/${keyInfo.isLockOwner == 1
? '超级管理员'.tr
: (keyInfo.keyRight == 1 ? "授权管理员".tr : "普通用户"
.tr)}",
style: TextStyle(
fontSize: 18.sp, color: AppColors.darkGrayTextColor),
"${logic.getUseKeyTypeStr(keyInfo.startDate, keyInfo.endDate, keyInfo.keyType)}/${keyInfo.isLockOwner == 1 ? '超级管理员'.tr : (keyInfo.keyRight == 1 ? "授权管理员".tr : "普通用户".tr)}",
style: TextStyle(fontSize: 18.sp, color: AppColors.darkGrayTextColor),
),
],
),
@ -286,13 +262,10 @@ class _LockListPageState extends State<LockListPage> with RouteAware {
@override
void dispose() {
Get.delete<LockListLogic>();
///
AppRouteObserver().routeObserver.unsubscribe(this);
super
.
dispose
(
);
super.dispose();
}
///

View File

@ -7,11 +7,14 @@ import '../entity/lockListInfo_entity.dart';
class LockListState{
RxBool itemStatusIsEable = false.obs; // item是否能点击
LockListInfoItemEntity lockListInfoItemEntity = LockListInfoItemEntity(); // item
//
Rx<LockListInfoItemEntity> lockListInfoItemEntity = LockListInfoItemEntity().obs; // item
TextEditingController passwordTF = TextEditingController();
RxBool deleteAdministratorIsHaveAllData = false.obs; //
RxBool ifCurrentScreen = true.obs; // ,
RxBool showSearch = false.obs; // ,
RxString searchStr = ''.obs; // ,
RxList<int> publicKey=<int>[].obs;
RxList<int> privateKey=<int>[].obs;
}

View File

@ -192,8 +192,9 @@ class _LockListXHJPageState extends State<LockListXHJPage> with RouteAware {
children: <Widget>[
SlidableAction(
onPressed: (BuildContext context) {
state.lockListInfoItemEntity = keyInfo;
logic.deleyLockLogicOfRoles();
state.lockListInfoItemEntity.value = keyInfo;
state.lockListInfoItemEntity.refresh();
logic.deleyLockLogicOfRoles(keyInfo);
},
backgroundColor: Colors.red,
foregroundColor: Colors.white,

View File

@ -31,8 +31,7 @@ class SaveLockLogic extends BaseGetXController {
late StreamSubscription<Reply> _replySubscription;
void _initReplySubscription() {
_replySubscription =
EventBusManager().eventBus!.on<Reply>().listen((Reply reply) {
_replySubscription = EventBusManager().eventBus!.on<Reply>().listen((Reply reply) {
if (reply is AddUserReply && state.ifCurrentScreen.value == true) {
_replyAddUserKey(reply);
}
@ -66,15 +65,11 @@ class SaveLockLogic extends BaseGetXController {
break;
case 0x06:
//
final List<String>? privateKey =
await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList =
changeStringListToIntList(privateKey!);
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
final List<String>? publicKey =
await Storage.getStringList(saveBluePublicKey);
final List<int> publicKeyDataList =
changeStringListToIntList(publicKey!);
final List<String>? publicKey = await Storage.getStringList(saveBluePublicKey);
final List<int> publicKeyDataList = changeStringListToIntList(publicKey!);
IoSenderManage.senderAddUser(
lockID: BlueManage().connectDeviceName,
@ -215,19 +210,14 @@ class SaveLockLogic extends BaseGetXController {
showBlueConnetctToast();
}
});
BlueManage().blueSendData(BlueManage().connectDeviceName,
(BluetoothConnectionState deviceConnectionState) async {
BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState deviceConnectionState) async {
if (deviceConnectionState == BluetoothConnectionState.connected) {
//
final List<String>? privateKey =
await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList =
changeStringListToIntList(privateKey!);
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
final List<String>? publicKey =
await Storage.getStringList(saveBluePublicKey);
final List<int> publicKeyDataList =
changeStringListToIntList(publicKey!);
final List<String>? publicKey = await Storage.getStringList(saveBluePublicKey);
final List<int> publicKeyDataList = changeStringListToIntList(publicKey!);
final List<String>? token = await Storage.getStringList(saveBlueToken);
List<int> getTokenList = <int>[0, 0, 0, 0];
@ -257,8 +247,7 @@ class SaveLockLogic extends BaseGetXController {
privateKey: getPrivateKeyList,
token: getTokenList,
isBeforeAddUser: true);
} else if (deviceConnectionState ==
BluetoothConnectionState.disconnected) {
} else if (deviceConnectionState == BluetoothConnectionState.disconnected) {
dismissEasyLoading();
cancelBlueConnetctToastTimer();
state.sureBtnState.value = 0;
@ -376,16 +365,14 @@ class SaveLockLogic extends BaseGetXController {
// positionMap['address'] = state.addressInfo['address'];
final Map<String, dynamic> bluetooth = <String, dynamic>{};
bluetooth['bluetoothDeviceId'] = BlueManage().connectDeviceMacAddress;
bluetooth['bluetoothDeviceId'] = state.lockInfo['mac'];
bluetooth['bluetoothDeviceName'] = BlueManage().connectDeviceName;
final List<String>? publicKey =
await Storage.getStringList(saveBluePublicKey);
final List<String>? publicKey = await Storage.getStringList(saveBluePublicKey);
final List<int> publicKeyDataList = changeStringListToIntList(publicKey!);
bluetooth['publicKey'] = publicKeyDataList;
final List<String>? privateKey =
await Storage.getStringList(saveBluePrivateKey);
final List<String>? privateKey = await Storage.getStringList(saveBluePrivateKey);
final List<int> getPrivateKeyList = changeStringListToIntList(privateKey!);
bluetooth['privateKey'] = getPrivateKeyList;
@ -410,8 +397,7 @@ class SaveLockLogic extends BaseGetXController {
final String getMobile = (await Storage.getMobile())!;
ApmHelper.instance.trackEvent('save_lock_result', {
'lock_name': BlueManage().connectDeviceName,
'account':
getMobile.isNotEmpty ? getMobile : (await Storage.getEmail())!,
'account': getMobile.isNotEmpty ? getMobile : (await Storage.getEmail())!,
'date': DateTool().getNowDateWithType(1),
'save_lock_result': '成功',
});
@ -427,8 +413,7 @@ class SaveLockLogic extends BaseGetXController {
final String getMobile = (await Storage.getMobile())!;
ApmHelper.instance.trackEvent('save_lock_result', {
'lock_name': BlueManage().connectDeviceName,
'account':
getMobile.isNotEmpty ? getMobile : (await Storage.getEmail())!,
'account': getMobile.isNotEmpty ? getMobile : (await Storage.getEmail())!,
'date': DateTool().getNowDateWithType(1),
'save_lock_result': '${entity.errorCode}--${entity.errorMsg}',
});
@ -489,26 +474,18 @@ class SaveLockLogic extends BaseGetXController {
// BlueManage().disconnect();
//
final LockSetInfoEntity entity =
await ApiRepository.to.getLockSettingInfoData(
final LockSetInfoEntity entity = await ApiRepository.to.getLockSettingInfoData(
lockId: state.lockId.toString(),
);
if (entity.errorCode!.codeIsSuccessful) {
state.lockSetInfoData.value = entity.data!;
if (state.lockSetInfoData.value.lockFeature?.wifi == 1) {
// wifi锁WIFI
Get.toNamed(Routers.wifiListPage, arguments: {
'lockSetInfoData': state.lockSetInfoData.value,
'pageName': 'saveLock'
});
Get.toNamed(Routers.wifiListPage, arguments: {'lockSetInfoData': state.lockSetInfoData.value, 'pageName': 'saveLock'});
} else if (state.lockSetInfoData.value.lockFeature?.languageSpeech == 1) {
Get.toNamed(Routers.lockVoiceSettingPage, arguments: {
'lockSetInfoData': state.lockSetInfoData.value,
'pageName': 'saveLock'
});
Get.toNamed(Routers.lockVoiceSettingPage, arguments: {'lockSetInfoData': state.lockSetInfoData.value, 'pageName': 'saveLock'});
} else {
eventBus.fire(RefreshLockListInfoDataEvent(
clearScanDevices: true, isUnShowLoading: true));
eventBus.fire(RefreshLockListInfoDataEvent(clearScanDevices: true, isUnShowLoading: true));
Future<void>.delayed(const Duration(seconds: 1), () {
// Get.close(state.isFromMap == 1
// ? (CommonDataManage().seletLockType == 0 ? 4 : 5)
@ -518,15 +495,12 @@ class SaveLockLogic extends BaseGetXController {
// 2
Future<void>.delayed(const Duration(milliseconds: 200), () {
if (Get.isRegistered<LockDetailLogic>()) {
Get.find<LockDetailLogic>()
.functionBlocker
.countdownProhibited(duration: const Duration(seconds: 2));
Get.find<LockDetailLogic>().functionBlocker.countdownProhibited(duration: const Duration(seconds: 2));
}
});
}
} else {
eventBus.fire(RefreshLockListInfoDataEvent(
clearScanDevices: true, isUnShowLoading: true));
eventBus.fire(RefreshLockListInfoDataEvent(clearScanDevices: true, isUnShowLoading: true));
Future<void>.delayed(const Duration(seconds: 1), () {
// Get.close(state.isFromMap == 1
// ? (CommonDataManage().seletLockType == 0 ? 4 : 5)
@ -536,9 +510,7 @@ class SaveLockLogic extends BaseGetXController {
// 2
Future<void>.delayed(const Duration(milliseconds: 200), () {
if (Get.isRegistered<LockDetailLogic>()) {
Get.find<LockDetailLogic>()
.functionBlocker
.countdownProhibited(duration: const Duration(seconds: 2));
Get.find<LockDetailLogic>().functionBlocker.countdownProhibited(duration: const Duration(seconds: 2));
}
});
}

View File

@ -22,17 +22,16 @@ class GatewayConfigurationWifiLogic extends BaseGetXController {
final GatewayConfigurationWifiState state = GatewayConfigurationWifiState();
Future<void> gatewayDistributionNetwork() async {
final LoginEntity entity = await ApiRepository.to
.gatewayDistributionNetwork(
gatewayName: state.gatewayNameTF.text,
gatewayMac: state.gatewayModel.mac,
serialNumber: state.gatewayModel.serialNum,
gatewayType: 2,
networkName: state.wifiNameTF.text,
networkMac: state.gatewayModel.wifiMac,
version: state.gatewayModel.gatewayVersion,
gatewayJson: jsonDecode(state.gatewayJson),
timeout: 60);
final LoginEntity entity = await ApiRepository.to.gatewayDistributionNetwork(
gatewayName: state.gatewayNameTF.text,
gatewayMac: state.gatewayModel.mac,
serialNumber: state.gatewayModel.serialNum,
gatewayType: 2,
networkName: state.wifiNameTF.text,
networkMac: state.gatewayModel.wifiMac,
version: state.gatewayModel.gatewayVersion,
gatewayJson: jsonDecode(state.gatewayJson),
timeout: 60);
if (entity.errorCode!.codeIsSuccessful) {
showToast('网关添加成功'.tr, something: () {
// eventBus.fire(PassCurrentLockInformationEvent(state.lockSetInfoData.value));
@ -42,19 +41,35 @@ class GatewayConfigurationWifiLogic extends BaseGetXController {
}
Future<void> getGatewayConfiguration() async {
final GetGatewayConfigurationEntity entity =
await ApiRepository.to.getGatewayConfiguration(timeout: 60);
final GetGatewayConfigurationEntity entity = await ApiRepository.to.getGatewayConfiguration(timeout: 60);
if (entity.errorCode!.codeIsSuccessful) {
state.getGatewayConfigurationStr = entity.data ?? '';
// AppLog.log('state.getGatewayConfigurationStr:${state.getGatewayConfigurationStr}');
String configStr = entity.data ?? '';
//
if (configStr.isNotEmpty) {
try {
Map<String, dynamic> config = jsonDecode(configStr);
config['timeZoneOffset'] = DateTime.now().timeZoneOffset.inSeconds;
AppLog.log('state.config:$config');
state.getGatewayConfigurationStr = jsonEncode(config);
} catch (e) {
AppLog.log('处理网关配置时区信息失败: $e');
// 使
state.getGatewayConfigurationStr = configStr;
}
} else {
state.getGatewayConfigurationStr = configStr;
}
AppLog.log('state.getGatewayConfigurationStr:${state.getGatewayConfigurationStr}');
}
}
//
late StreamSubscription<Reply> _replySubscription;
void _initReplySubscription() {
_replySubscription =
EventBusManager().eventBus!.on<Reply>().listen((Reply reply) async {
_replySubscription = EventBusManager().eventBus!.on<Reply>().listen((Reply reply) async {
// WIFI配网
// if(reply is GatewayConfiguringWifiReply) {
// _replySenderConfiguringWifi(reply);
@ -96,8 +111,7 @@ class GatewayConfigurationWifiLogic extends BaseGetXController {
cancelBlueConnetctToastTimer();
dismissEasyLoading();
final int secretKeyJsonLength = (reply.data[3] << 8) + reply.data[4];
final List<int> secretKeyList =
reply.data.sublist(5, 5 + secretKeyJsonLength);
final List<int> secretKeyList = reply.data.sublist(5, 5 + secretKeyJsonLength);
state.gatewayJson = utf8String(secretKeyList);
gatewayDistributionNetwork();
@ -113,8 +127,7 @@ class GatewayConfigurationWifiLogic extends BaseGetXController {
// wifi
Future<void> senderConfiguringWifiAction() async {
AppLog.log(
'state.getGatewayConfigurationStr:${state.getGatewayConfigurationStr}');
AppLog.log('state.getGatewayConfigurationStr:${state.getGatewayConfigurationStr}');
if (state.wifiNameTF.text.isEmpty) {
showToast('请输入wifi名称'.tr);
return;
@ -142,8 +155,7 @@ class GatewayConfigurationWifiLogic extends BaseGetXController {
dismissEasyLoading();
state.sureBtnState.value = 0;
});
BlueManage().blueSendData(BlueManage().connectDeviceName,
(BluetoothConnectionState connectionState) async {
BlueManage().blueSendData(BlueManage().connectDeviceName, (BluetoothConnectionState connectionState) async {
if (connectionState == BluetoothConnectionState.connected) {
IoSenderManage.gatewayConfiguringWifiCommand(
ssid: state.wifiNameTF.text,
@ -162,6 +174,7 @@ class GatewayConfigurationWifiLogic extends BaseGetXController {
}
final NetworkInfo _networkInfo = NetworkInfo();
Future<String> getWifiName() async {
String ssid = '';
ssid = (await _networkInfo.getWifiName())!;

View File

@ -305,6 +305,8 @@ abstract class Api {
'/lockSetting/updateLockSetting'; //
final String reportBuyRequestURL =
'/service/reportBuyRequest'; //
final String getActivateInfoURL =
final String getTppSupportURL =
'/api/authCode/getTppSupport'; // ttp
final String getActivateInfoURL =
'/api/authCode/getActivateInfo'; // ttp
}

View File

@ -10,28 +10,48 @@ import 'package:star_lock/talk/starChart/entity/star_chart_register_node_entity.
import 'package:star_lock/tools/storage.dart';
class StartCompanyApi extends BaseProvider {
// url
// url
String _startChartHost = 'https://company.skychip.top';
static StartCompanyApi get to => Get.put(StartCompanyApi());
// host
// host
set startChartHost(String value) {
_startChartHost = value;
}
// host
// host
String get startChartHost => _startChartHost;
// ttp查询
Future<ActivateInfoResponse> getActivateInfo({
Future<TppSupportResponse> getTppSupport({
required String model,
}) async {
final response = await post(
_startChartHost + getTppSupportURL.toUrl,
jsonEncode(<String, dynamic>{
'model': model,
}),
isUnShowLoading: true,
isUserBaseUrl: false,
);
return TppSupportResponse.fromJson(response.body);
}
//
Future<ActivateInfoResponse> getAuthorizationCode({
required String registerKey,
required String model,
required String serialNum0,
required int platform,
}) async {
final response = await post(
_startChartHost + getActivateInfoURL.toUrl,
jsonEncode(<String, dynamic>{
'register_key': registerKey,
'platform': platform,
'model': model,
'serial_num0': serialNum0,
}),
isUnShowLoading: true,
isUserBaseUrl: false,

View File

@ -14,8 +14,7 @@ import 'package:star_lock/talk/starChart/proto/talk_data_h264_frame.pb.dart';
// class UdpTalkDataHandler extends ScpMessageBaseHandle
// implements ScpMessageHandler {
class UdpTalkDataHandler extends ScpMessageBaseHandle
implements ScpMessageHandler {
class UdpTalkDataHandler extends ScpMessageBaseHandle implements ScpMessageHandler {
//
static final UdpTalkDataHandler instance = UdpTalkDataHandler();
@ -73,10 +72,7 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle
// _asyncLog(
// '分包数据:messageId:$messageId [$spIndex/$spTotal] PayloadLength:$PayloadLength');
if (messageType == MessageTypeConstant.RealTimeData) {
if (spTotal != null &&
spTotal > 1 &&
messageId != null &&
spIndex != null) {
if (spTotal != null && spTotal > 1 && messageId != null && spIndex != null) {
//
return handleFragmentedPayload(
messageId: messageId,
@ -129,13 +125,11 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle
talkDataH264Frame.mergeFromBuffer(talkData.content);
// AppLog.log('处理H264帧: frameType=${talkDataH264Frame.frameType}, frameSeq=${talkDataH264Frame.frameSeq},MessageId:${scpMessage.MessageId}');
frameHandler.handleFrame(talkDataH264Frame, talkData, scpMessage);
}
///
void _handleVideoImage(TalkData talkData) async {
final List<Uint8List> processCompletePayload =
await _processCompletePayload(Uint8List.fromList(talkData.content));
final List<Uint8List> processCompletePayload = await _processCompletePayload(Uint8List.fromList(talkData.content));
processCompletePayload.forEach((element) {
talkData.content = element;
talkDataRepository.addTalkData(
@ -177,8 +171,7 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle
startIdx = i;
i++; // Skip the next byte
} else if (nextByte == 0xD9 && startIdx != -1) {
frames
.add(Uint8List.view(payload.buffer, startIdx, i + 2 - startIdx));
frames.add(Uint8List.view(payload.buffer, startIdx, i + 2 - startIdx));
startIdx = -1;
i++; // Skip the next byte
}

View File

@ -18,20 +18,18 @@ import 'package:star_lock/tools/push/xs_jPhush.dart';
import 'package:star_lock/tools/storage.dart';
import 'package:star_lock/translations/current_locale_tool.dart';
class UdpTalkRequestHandler extends ScpMessageBaseHandle
implements ScpMessageHandler {
RxString currentLanguage =
CurrentLocaleTool.getCurrentLocaleString().obs; //
class UdpTalkRequestHandler extends ScpMessageBaseHandle implements ScpMessageHandler {
RxString currentLanguage = CurrentLocaleTool.getCurrentLocaleString().obs; //
@override
void handleReq(ScpMessage scpMessage) async {
//
final loginData = await Storage.getLoginData();
//
if (loginData != null &&
(talkStatus.status != TalkStatus.passiveCallWaitingAnswer ||
talkStatus.status != TalkStatus.answeredSuccessfully)) {
if (loginData != null && (talkStatus.status != TalkStatus.passiveCallWaitingAnswer || talkStatus.status != TalkStatus.answeredSuccessfully)) {
//
AppLog.log('收到对讲请求ToPeerId${scpMessage.ToPeerId}');
AppLog.log('收到对讲请求FromPeerId${scpMessage.FromPeerId}');
final TalkReq talkReq = scpMessage.Payload;
startChartManage.FromPeerId = scpMessage.ToPeerId!;
startChartManage.ToPeerId = scpMessage.FromPeerId!;
@ -64,8 +62,6 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle
_handleResponseSendExpect(
lockPeerID: scpMessage.FromPeerId!,
);
//
startChartManage.startTalkExpectTimer();
//
startChartManage.stopCallRequestMessageTimer();
//
@ -97,8 +93,7 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle
}
//
Future<void> _showTalkRequestNotification(
{required String talkObjectName}) async {
Future<void> _showTalkRequestNotification({required String talkObjectName}) async {
if (Platform.isAndroid) {
final Map<String, dynamic> message = {
'platform': 'all',
@ -167,14 +162,12 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle
void _handleRequestSendExpect({
required String lockPeerID,
}) async {
final LockListInfoItemEntity currentKeyInfo =
CommonDataManage().currentKeyInfo;
final LockListInfoItemEntity currentKeyInfo = CommonDataManage().currentKeyInfo;
var isH264 = currentKeyInfo.lockFeature?.isH264 == 1;
var isMJpeg = currentKeyInfo.lockFeature?.isMJpeg == 1;
final LockListInfoGroupEntity? lockListInfoGroupEntity =
await Storage.getLockMainListData();
final LockListInfoGroupEntity? lockListInfoGroupEntity = await Storage.getLockMainListData();
if (lockListInfoGroupEntity != null) {
lockListInfoGroupEntity!.groupList?.forEach((element) {
final lockList = element.lockList;
@ -195,18 +188,15 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle
if (isH264) {
// H264H264视频和G711音频期望
startChartManage.sendOnlyH264VideoTalkExpectData();
print(
'app收到的对讲请求后发送的预期数据=========锁支持H264发送H264视频格式期望数据,peerID=${lockPeerID}');
print('app收到的对讲请求后发送的预期数据=========锁支持H264发送H264视频格式期望数据,peerID=${lockPeerID}');
} else if (isMJpeg) {
// MJPEGG711音频期望
startChartManage.sendOnlyImageVideoTalkExpectData();
print(
'app收到的对讲请求后发送的预期数据=========锁不支持H264支持MJPEG发送MJPEG视频格式期望数据,peerID=${lockPeerID}');
print('app收到的对讲请求后发送的预期数据=========锁不支持H264支持MJPEG发送MJPEG视频格式期望数据,peerID=${lockPeerID}');
} else {
// 使
startChartManage.sendOnlyImageVideoTalkExpectData();
print(
'app收到的对讲请求后发送的预期数据=========锁不支持H264和MJPEG默认发送MJPEG视频格式期望数据,peerID=${lockPeerID}');
print('app收到的对讲请求后发送的预期数据=========锁不支持H264和MJPEG默认发送MJPEG视频格式期望数据,peerID=${lockPeerID}');
}
}
@ -214,14 +204,12 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle
void _handleResponseSendExpect({
required String lockPeerID,
}) async {
final LockListInfoItemEntity currentKeyInfo =
CommonDataManage().currentKeyInfo;
final LockListInfoItemEntity currentKeyInfo = CommonDataManage().currentKeyInfo;
var isH264 = currentKeyInfo.lockFeature?.isH264 == 1;
var isMJpeg = currentKeyInfo.lockFeature?.isMJpeg == 1;
final LockListInfoGroupEntity? lockListInfoGroupEntity =
await Storage.getLockMainListData();
final LockListInfoGroupEntity? lockListInfoGroupEntity = await Storage.getLockMainListData();
if (lockListInfoGroupEntity != null) {
lockListInfoGroupEntity!.groupList?.forEach((element) {
final lockList = element.lockList;
@ -242,18 +230,15 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle
if (isH264) {
// H264H264视频和G711音频期望
startChartManage.sendH264VideoAndG711AudioTalkExpectData();
AppLog.log(
'app主动发对讲请求收到回复后发送的预期数据=======锁支持H264发送H264视频格式期望数据,peerID=${lockPeerID}');
AppLog.log('app主动发对讲请求收到回复后发送的预期数据=======锁支持H264发送H264视频格式期望数据,peerID=${lockPeerID}');
} else if (isMJpeg) {
// MJPEGG711音频期望
startChartManage.sendImageVideoAndG711AudioTalkExpectData();
AppLog.log(
'app主动发对讲请求收到回复后发送的预期数据=======锁不支持H264支持MJPEG发送MJPEG视频格式期望数据,peerID=${lockPeerID}');
AppLog.log('app主动发对讲请求收到回复后发送的预期数据=======锁不支持H264支持MJPEG发送MJPEG视频格式期望数据,peerID=${lockPeerID}');
} else {
// 使
startChartManage.sendImageVideoAndG711AudioTalkExpectData();
AppLog.log(
'app主动发对讲请求收到回复后发送的预期数据=======锁不支持H264和MJPEG默认发送MJPEG视频格式期望数据,peerID=${lockPeerID}');
AppLog.log('app主动发对讲请求收到回复后发送的预期数据=======锁不支持H264和MJPEG默认发送MJPEG视频格式期望数据,peerID=${lockPeerID}');
}
}
}

View File

@ -61,12 +61,9 @@ class StartChartManage {
//
static final StartChartManage _instance = StartChartManage._internal();
final TalkeRequestOverTimeTimerManager talkeRequestOverTimeTimerManager =
TalkeRequestOverTimeTimerManager();
final TalkePingOverTimeTimerManager talkePingOverTimeTimerManager =
TalkePingOverTimeTimerManager();
final TalkDataOverTimeTimerManager talkDataOverTimeTimerManager =
TalkDataOverTimeTimerManager();
final TalkeRequestOverTimeTimerManager talkeRequestOverTimeTimerManager = TalkeRequestOverTimeTimerManager();
final TalkePingOverTimeTimerManager talkePingOverTimeTimerManager = TalkePingOverTimeTimerManager();
final TalkDataOverTimeTimerManager talkDataOverTimeTimerManager = TalkDataOverTimeTimerManager();
//
factory StartChartManage() {
@ -171,8 +168,7 @@ class StartChartManage {
FromPeerId = loginData?.starchart?.starchartId ?? '';
} else {
_log(text: '开始注册客户端');
final StarChartRegisterNodeEntity requestStarChartRegisterNode =
await _requestStarChartRegisterNode();
final StarChartRegisterNodeEntity requestStarChartRegisterNode = await _requestStarChartRegisterNode();
await _saveStarChartRegisterNodeToStorage(requestStarChartRegisterNode);
FromPeerId = requestStarChartRegisterNode.peer!.id ?? '';
bindUserStarchart(requestStarChartRegisterNode);
@ -180,18 +176,14 @@ class StartChartManage {
}
//
Future<void> bindUserStarchart(
StarChartRegisterNodeEntity requestStarChartRegisterNode) async {
Future<void> bindUserStarchart(StarChartRegisterNodeEntity requestStarChartRegisterNode) async {
try {
final LoginEntity entity = await ApiRepository.to.bindUserStarchart(
starchartId: requestStarChartRegisterNode.peer?.id ?? '',
starchartPeerPublicKey:
requestStarChartRegisterNode.peer?.publicKey ?? '',
starchartPeerPrivateKey:
requestStarChartRegisterNode.peer?.privateKey ?? '',
starchartPeerPublicKey: requestStarChartRegisterNode.peer?.publicKey ?? '',
starchartPeerPrivateKey: requestStarChartRegisterNode.peer?.privateKey ?? '',
);
requestStarChartRegisterNode.peer?.id =
entity.data?.starchart?.starchartId;
requestStarChartRegisterNode.peer?.id = entity.data?.starchart?.starchartId;
if (entity.errorCode!.codeIsSuccessful) {
AppLog.log('绑定成功');
} else {
@ -204,14 +196,12 @@ class StartChartManage {
//
Future<void> _relayQuery() async {
final RelayInfoEntity relayInfoEntity =
await StartChartApi.to.relayQueryInfo();
final RelayInfoEntity relayInfoEntity = await StartChartApi.to.relayQueryInfo();
_saveRelayInfoEntityToStorage(relayInfoEntity);
if (relayInfoEntity.client_addr != null) {
localPublicHost = relayInfoEntity.client_addr!;
}
if (relayInfoEntity.relay_list != null &&
relayInfoEntity.relay_list!.length > 0) {
if (relayInfoEntity.relay_list != null && relayInfoEntity.relay_list!.length > 0) {
for (int i = 0; i <= relayInfoEntity.relay_list!.length; i++) {
final data = relayInfoEntity.relay_list?[i];
if (data?.peerID != FromPeerId) {
@ -239,8 +229,7 @@ class StartChartManage {
// udp
Future<void> _onlineRelayService() async {
var addressIListenFrom = InternetAddress.anyIPv4;
await RawDatagramSocket.bind(addressIListenFrom, localPort)
.then((RawDatagramSocket socket) {
await RawDatagramSocket.bind(addressIListenFrom, localPort).then((RawDatagramSocket socket) {
// (SO_RCVBUF = 8)
if (AppPlatform.isAndroid) {
socket.setRawOption(
@ -291,15 +280,15 @@ class StartChartManage {
}
// RbcuInfo
void _sendRbcuInfoMessage(
{required String ToPeerId, bool isResp = false}) async {
void _sendRbcuInfoMessage({required String ToPeerId, bool isResp = false}) async {
final uuid = _uuid.v1();
final int timestamp = DateTime.now().millisecondsSinceEpoch;
final int timestamp = DateTime
.now()
.millisecondsSinceEpoch;
final Int64 int64Timestamp = Int64(timestamp); // 使
// ip地址和中继返回的外网地址
final List<ListenAddrData> listenAddrDataList =
await _makeListenAddrDataList();
final List<ListenAddrData> listenAddrDataList = await _makeListenAddrDataList();
listenAddrDataList.insert(
0, //
ListenAddrData(
@ -309,15 +298,13 @@ class StartChartManage {
);
final address = listenAddrDataList
.where((element) =>
element.type == ListenAddrTypeConstant.local) //
.where((element) => element.type == ListenAddrTypeConstant.local) //
.map((e) => e.address) // List<String?>
.where((addr) => addr != null) // null
.map(
(addr) => addr!.replaceAll(IpConstant.udpUrl, ''),
) // "udp://"
.cast<
String>(); // Iterable<String>// Iterable<String?> Iterable<String>
) // "udp://"
.cast<String>(); // Iterable<String>// Iterable<String?> Iterable<String>
_rbcuSessionId = uuid;
final RbcuInfo rbcuInfo = RbcuInfo(
sessionId: uuid,
@ -340,28 +327,21 @@ class StartChartManage {
void _sendRbcuProbeMessage() async {
//
String generateRandomString(int length) {
const chars =
'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
const chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
final random = Random();
return String.fromCharCodes(
List.generate(
length, (index) => chars.codeUnitAt(random.nextInt(chars.length))),
List.generate(length, (index) => chars.codeUnitAt(random.nextInt(chars.length))),
);
}
if (rbcuInfo != null &&
rbcuInfo!.address != null &&
rbcuInfo!.address.length > 0) {
if (rbcuInfo != null && rbcuInfo!.address != null && rbcuInfo!.address.length > 0) {
rbcuInfo!.address.forEach((element) {
// element
final parts = element.split(':');
final host = parts[0]; // IP
final port = int.tryParse(parts[1]) ?? 0; // 0
final RbcuProbe rbcuProbe = RbcuProbe(
sessionId: _rbcuSessionId,
data: generateRandomString(100),
targetAddress: element);
final RbcuProbe rbcuProbe = RbcuProbe(sessionId: _rbcuSessionId, data: generateRandomString(100), targetAddress: element);
final rbcuProBeBuffer = rbcuProbe.writeToBuffer();
_sendNatMessage(message: rbcuProBeBuffer, host: host, port: port);
});
@ -378,8 +358,7 @@ class StartChartManage {
//
void startSendingRbcuInfoMessages({required String ToPeerId}) {
// 1 _sendRbcuInfoMessage
rbcuInfoTimer ??=
Timer.periodic(Duration(seconds: _defaultIntervalTime), (timer) {
rbcuInfoTimer ??= Timer.periodic(Duration(seconds: _defaultIntervalTime), (timer) {
// RbcuInfo
_log(text: '发送RbcuInfo 地址交换消息');
_sendRbcuInfoMessage(ToPeerId: ToPeerId);
@ -389,8 +368,7 @@ class StartChartManage {
//
void startSendingRbcuProbeTMessages() {
// 1 _sendRbcuInfoMessage
rbcuProbeTimer ??=
Timer.periodic(Duration(seconds: _defaultIntervalTime), (timer) {
rbcuProbeTimer ??= Timer.periodic(Duration(seconds: _defaultIntervalTime), (timer) {
// RbcuProbe
_sendRbcuProbeMessage();
});
@ -398,8 +376,7 @@ class StartChartManage {
//
void startSendingRbcuConfirmTMessages() {
rbcuConfirmTimer ??=
Timer.periodic(Duration(seconds: _defaultIntervalTime), (timer) {
rbcuConfirmTimer ??= Timer.periodic(Duration(seconds: _defaultIntervalTime), (timer) {
// RbcuProbe
_sendRbcuConfirmMessage();
});
@ -458,11 +435,11 @@ class StartChartManage {
// );
// }
// }
final LockListInfoItemEntity currentKeyInfo =
CommonDataManage().currentKeyInfo;
final LockListInfoItemEntity currentKeyInfo = CommonDataManage().currentKeyInfo;
final isH264 = currentKeyInfo.lockFeature?.isH264 == 1;
final isMJpeg = currentKeyInfo.lockFeature?.isMJpeg == 1;
AppLog.log('isH264:${isH264}');
AppLog.log('isMJpeg:${isMJpeg}');
// 使H264MJPEG
if (isH264) {
Get.toNamed(
@ -482,7 +459,8 @@ class StartChartManage {
Duration(
seconds: _defaultIntervalTime,
),
(Timer timer) async {
(Timer timer) async {
AppLog.log('发送对讲请求:${ToPeerId}');
await sendCallRequestMessage(ToPeerId: ToPeerId);
},
);
@ -526,8 +504,7 @@ class StartChartManage {
List<int> packetTalkData = payload.sublist(start, end);
// messageID
final messageId =
MessageCommand.getNextMessageId(toPeerId, increment: false);
final messageId = MessageCommand.getNextMessageId(toPeerId, increment: false);
//
final message = MessageCommand.talkDataMessage(
ToPeerId: toPeerId,
@ -558,12 +535,11 @@ class StartChartManage {
Duration(
seconds: heartbeatIntervalTime,
),
(Timer timer) async {
(Timer timer) async {
final List<int> message = MessageCommand.heartbeatMessage(
FromPeerId: FromPeerId,
ToPeerId: relayPeerId,
MessageId:
MessageCommand.getNextMessageId(relayPeerId, increment: true),
MessageId: MessageCommand.getNextMessageId(relayPeerId, increment: true),
);
await _sendMessage(message: message);
},
@ -572,8 +548,7 @@ class StartChartManage {
}
//
void sendEchoMessage(
{required List<int> payload, required String toPeerId}) async {
void sendEchoMessage({required List<int> payload, required String toPeerId}) async {
//
final int totalPackets = (payload.length / _maxPayloadSize).ceil();
//
@ -587,8 +562,7 @@ class StartChartManage {
List<int> packet = payload.sublist(start, end);
// messageID
final messageId =
MessageCommand.getNextMessageId(toPeerId, increment: false);
final messageId = MessageCommand.getNextMessageId(toPeerId, increment: false);
//
final message = MessageCommand.echoMessage(
ToPeerId: toPeerId,
@ -606,13 +580,14 @@ class StartChartManage {
}
//
void sendGatewayResetMessage(
{required String ToPeerId, required int gatewayId}) async {
void sendGatewayResetMessage({required String ToPeerId, required int gatewayId}) async {
final message = MessageCommand.gatewayResetMessage(
ToPeerId: ToPeerId,
FromPeerId: FromPeerId,
gatewayId: gatewayId,
time: DateTime.now().millisecondsSinceEpoch ~/ 1000,
time: DateTime
.now()
.millisecondsSinceEpoch ~/ 1000,
MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: true),
);
await _sendMessage(message: message);
@ -646,7 +621,7 @@ class StartChartManage {
talkRejectTimer ??= Timer.periodic(
Duration(seconds: _defaultIntervalTime),
(Timer timer) async {
(Timer timer) async {
_sendTalkRejectMessage();
count++;
if (count >= maxCount) {
@ -728,8 +703,7 @@ class StartChartManage {
}
//
Future<void> sendTalkPingMessage(
{required String ToPeerId, required String FromPeerId}) async {
Future<void> sendTalkPingMessage({required String ToPeerId, required String FromPeerId}) async {
final message = MessageCommand.talkPingMessage(
ToPeerId: ToPeerId,
FromPeerId: FromPeerId,
@ -753,7 +727,7 @@ class StartChartManage {
void startTalkHangupMessageTimer() {
talkHangupTimer ??= Timer.periodic(
Duration(seconds: _defaultIntervalTime),
(Timer timer) async {
(Timer timer) async {
_sendTalkHangupMessage();
},
);
@ -797,7 +771,7 @@ class StartChartManage {
Duration(
seconds: _defaultIntervalTime,
),
(Timer timer) async {
(Timer timer) async {
// 线
await _sendOnlineMessage();
},
@ -825,11 +799,9 @@ class StartChartManage {
//
Future<void> _sendMessage({required List<int> message}) async {
var result = await _udpSocket?.send(
message, InternetAddress(remoteHost), remotePort);
var result = await _udpSocket?.send(message, InternetAddress(remoteHost), remotePort);
if (result != message.length) {
throw StartChartMessageException(
'❌Udp send data error----> $result ${message.length}');
throw StartChartMessageException('❌Udp send data error----> $result ${message.length}');
// _udpSocket = null;
}
@ -846,15 +818,11 @@ class StartChartManage {
}
//
Future<void> _sendNatMessage(
{required List<int> message,
required String host,
required int port}) async {
Future<void> _sendNatMessage({required List<int> message, required String host, required int port}) async {
_log(text: '发送nat消息');
var result = await _udpSocket?.send(message, InternetAddress(host), port);
if (result != message.length) {
throw StartChartMessageException(
'❌Udp send data error----> $result ${message.length}');
throw StartChartMessageException('❌Udp send data error----> $result ${message.length}');
// _udpSocket = null;
}
}
@ -864,8 +832,7 @@ class StartChartManage {
//
final Map<String, String> deviceInfo = await _getDeviceInfo();
//
final StarChartRegisterNodeEntity response =
await StartChartApi.to.starChartRegisterNode(
final StarChartRegisterNodeEntity response = await StartChartApi.to.starChartRegisterNode(
product: _productName,
model: '${deviceInfo['brand']}_${deviceInfo['model']}',
name: '${deviceInfo['id']}',
@ -875,8 +842,7 @@ class StartChartManage {
}
//
Future<void> _saveStarChartRegisterNodeToStorage(
StarChartRegisterNodeEntity starChartRegisterNodeEntity) async {
Future<void> _saveStarChartRegisterNodeToStorage(StarChartRegisterNodeEntity starChartRegisterNodeEntity) async {
if (starChartRegisterNodeEntity != null) {
await Storage.saveStarChartRegisterNodeInfo(starChartRegisterNodeEntity);
final LoginData? loginData = await Storage.getLoginData();
@ -886,8 +852,7 @@ class StartChartManage {
}
//
Future<void> _saveRelayInfoEntityToStorage(
RelayInfoEntity relayInfoEntity) async {
Future<void> _saveRelayInfoEntityToStorage(RelayInfoEntity relayInfoEntity) async {
if (relayInfoEntity != null) {
await Storage.saveRelayInfo(relayInfoEntity);
}
@ -909,8 +874,7 @@ class StartChartManage {
);
// ip地址和中继返回的外网地址
final List<ListenAddrData> listenAddrDataList =
await _makeListenAddrDataList();
final List<ListenAddrData> listenAddrDataList = await _makeListenAddrDataList();
//
final RelayServiceData relayServiceData = RelayServiceData(
@ -977,11 +941,12 @@ class StartChartManage {
String ipAddress = address.address;
// IPv6
if (ipAddress.contains('%')) {
ipAddress = ipAddress.split('%').first;
ipAddress = ipAddress
.split('%')
.first;
}
// IP
if (ipAddress.isNotEmpty &&
!IpConstant.reportExcludeIp.contains(ipAddress)) {
if (ipAddress.isNotEmpty && !IpConstant.reportExcludeIp.contains(ipAddress)) {
ipAddresses.add(ipAddress);
}
}
@ -998,8 +963,7 @@ class StartChartManage {
///
Future<Map<String, String>> _getDeviceInfo() async {
final Map<String, String> deviceInfo =
await DeviceInfoUtils.getDeviceInfo();
final Map<String, String> deviceInfo = await DeviceInfoUtils.getDeviceInfo();
return deviceInfo;
}
@ -1016,8 +980,7 @@ class StartChartManage {
if (host != null && port != null) {
try {
// DNS
final List<InternetAddress> addresses =
await InternetAddress.lookup(host);
final List<InternetAddress> addresses = await InternetAddress.lookup(host);
if (addresses.isEmpty) {
throw FormatException('DNS resolution failed for $host');
}
@ -1082,8 +1045,7 @@ class StartChartManage {
final int payloadType = scpMessage.PayloadType ?? 0;
final int messageType = scpMessage.MessageType ?? 0;
try {
final ScpMessageHandler handler =
ScpMessageHandlerFactory.createHandler(payloadType);
final ScpMessageHandler handler = ScpMessageHandlerFactory.createHandler(payloadType);
if (messageType == MessageTypeConstant.Req) {
handler.handleReq(scpMessage);
} else if (messageType == MessageTypeConstant.Resp) {
@ -1104,7 +1066,7 @@ class StartChartManage {
Duration(
seconds: _defaultIntervalTime,
),
(Timer timer) async {
(Timer timer) async {
await sendTalkPingMessage(
ToPeerId: ToPeerId,
FromPeerId: FromPeerId,
@ -1125,7 +1087,7 @@ class StartChartManage {
Duration(
seconds: _defaultIntervalTime,
),
(Timer timer) {
(Timer timer) {
//
sendTalkExpectMessage(
talkExpect: _defaultTalkExpect,
@ -1140,7 +1102,7 @@ class StartChartManage {
Duration(
seconds: _defaultIntervalTime,
),
(Timer timer) {
(Timer timer) {
sendTalkAcceptMessage();
},
);
@ -1170,10 +1132,18 @@ class StartChartManage {
}
///
void changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(
{required TalkExpectReq talkExpect}) {
void changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer({required TalkExpectReq talkExpect}) {
_defaultTalkExpect = talkExpect;
reStartTalkExpectMessageTimer();
sendTalkExpectMessage(
talkExpect: _defaultTalkExpect,
);
sendTalkExpectMessage(
talkExpect: _defaultTalkExpect,
);
sendTalkExpectMessage(
talkExpect: _defaultTalkExpect,
);
// reStartTalkExpectMessageTimer();
}
void reSetDefaultTalkExpect() {
@ -1190,8 +1160,7 @@ class StartChartManage {
videoType: [VideoTypeE.IMAGE],
audioType: [],
);
changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(
talkExpect: talkExpectReq);
changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(talkExpect: talkExpectReq);
}
///
@ -1200,20 +1169,17 @@ class StartChartManage {
videoType: [VideoTypeE.H264],
audioType: [],
);
changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(
talkExpect: talkExpectReq);
changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(talkExpect: talkExpectReq);
}
///
void sendImageVideoAndG711AudioTalkExpectData() {
changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(
talkExpect: TalkConstant.ImageExpect);
changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(talkExpect: TalkConstant.ImageExpect);
}
///
void sendH264VideoAndG711AudioTalkExpectData() {
changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(
talkExpect: TalkConstant.H264Expect);
changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(talkExpect: TalkConstant.H264Expect);
}
///

View File

@ -104,10 +104,6 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
codecType: 'h264',
);
// textureId
AppLog.log(
'StartChartManage().videoWidth:${StartChartManage().videoWidth}');
AppLog.log(
'StartChartManage().videoHeight:${StartChartManage().videoHeight}');
final textureId = await VideoDecodePlugin.initDecoder(config);
if (textureId != null) {
Future.microtask(() => state.textureId.value = textureId);
@ -172,15 +168,11 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
ScpMessage scpMessage,
) {
// frameSeq较小时阈值也小
if (!_pendingStreamReset &&
_lastFrameSeq != null &&
frameType == TalkDataH264Frame_FrameTypeE.I &&
frameSeq < _lastFrameSeq!) {
if (!_pendingStreamReset && _lastFrameSeq != null && frameType == TalkDataH264Frame_FrameTypeE.I && frameSeq < _lastFrameSeq!) {
int dynamicThreshold = _getFrameSeqRolloverThreshold(_lastFrameSeq!);
if ((_lastFrameSeq! - frameSeq) > dynamicThreshold) {
// I帧frameSeq大幅回绕loading并重置所有本地状态
AppLog.log(
'检测到新流I帧frameSeq大幅回绕进入loading并重置: frameSeq=$frameSeq, lastFrameSeq=$_lastFrameSeq, 阈值=$dynamicThreshold');
AppLog.log('检测到新流I帧frameSeq大幅回绕进入loading并重置: frameSeq=$frameSeq, lastFrameSeq=$_lastFrameSeq, 阈值=$dynamicThreshold');
Future.microtask(() => state.isLoading.value = true);
_pendingStreamReset = true;
//
@ -197,8 +189,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
//
} else {
//
AppLog.log(
'检测到I帧乱序未超过回绕阈值$dynamicThreshold),丢弃: frameSeq=$frameSeq, lastFrameSeq=$_lastFrameSeq');
AppLog.log('检测到I帧乱序未超过回绕阈值$dynamicThreshold),丢弃: frameSeq=$frameSeq, lastFrameSeq=$_lastFrameSeq');
return;
}
}
@ -238,8 +229,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
// P/B帧
while (state.h264FrameBuffer.length >= state.maxFrameBufferSize) {
int pbIndex = state.h264FrameBuffer
.indexWhere((f) => f['frameType'] == TalkDataH264Frame_FrameTypeE.P);
int pbIndex = state.h264FrameBuffer.indexWhere((f) => f['frameType'] == TalkDataH264Frame_FrameTypeE.P);
if (pbIndex != -1) {
state.h264FrameBuffer.removeAt(pbIndex);
} else {
@ -260,15 +250,17 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
final int intervalMs = (1000 / state.targetFps).round();
//
state.frameProcessTimer =
Timer.periodic(Duration(milliseconds: intervalMs), (timer) {
state.frameProcessTimer = Timer.periodic(Duration(milliseconds: intervalMs), (timer) {
_processNextFrameFromBuffer();
});
AppLog.log('启动帧处理定时器,目标帧率: ${state.targetFps}fps间隔: ${intervalMs}ms');
}
///
///
void _processNextFrameFromBuffer() async {
final startTime = DateTime.now().microsecondsSinceEpoch;
//
if (state.isProcessingFrame) {
return;
@ -279,85 +271,19 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
return;
}
// I帧frameSeq最小的I帧消费
final iFrames = state.h264FrameBuffer
.where((f) => f['frameType'] == TalkDataH264Frame_FrameTypeE.I)
.toList();
iFrames
.sort((a, b) => (a['frameSeq'] as int).compareTo(b['frameSeq'] as int));
try {
// I帧frameSeq最小的I帧消费
final iFrames = state.h264FrameBuffer.where((f) => f['frameType'] == TalkDataH264Frame_FrameTypeE.I).toList();
iFrames.sort((a, b) => (a['frameSeq'] as int).compareTo(b['frameSeq'] as int));
if (iFrames.isNotEmpty) {
// I帧I帧frameSeq
final minIFrame = iFrames.first;
final minIFrameSeq = minIFrame['frameSeq'];
final targetIndex = state.h264FrameBuffer.indexWhere(
(f) =>
f['frameType'] == TalkDataH264Frame_FrameTypeE.I &&
f['frameSeq'] == minIFrameSeq,
);
state.isProcessingFrame = true;
final Map<String, dynamic>? frameMap =
state.h264FrameBuffer.removeAt(targetIndex);
if (frameMap == null) {
state.isProcessingFrame = false;
return;
}
final List<int>? frameData = frameMap['frameData'];
final TalkDataH264Frame_FrameTypeE? frameType = frameMap['frameType'];
final int? frameSeq = frameMap['frameSeq'];
final int? frameSeqI = frameMap['frameSeqI'];
final int? pts = frameMap['pts'];
final ScpMessage? scpMessage = frameMap['scpMessage'];
if (frameData == null ||
frameType == null ||
frameSeq == null ||
frameSeqI == null ||
pts == null) {
state.isProcessingFrame = false;
return;
}
if (state.textureId.value == null) {
state.isProcessingFrame = false;
return;
}
lastDecodedIFrameSeq = minIFrameSeq;
// AppLog.log('送入解码器的P帧数据frameSeq:${frameSeq},frameSeqI:${frameSeqI},'
// 'frameType:${frameType},messageId:${scpMessage!.MessageId}');
// final spsData = NaluUtils.filterNalusByType(frameData, 7);
// final ppsData = NaluUtils.filterNalusByType(frameData, 8);
// AppLog.log('SPSDATA:${spsData}ppsData:${ppsData}');
await VideoDecodePlugin.sendFrame(
frameData: frameData,
frameType: 0,
frameSeq: frameSeq,
timestamp: pts,
splitNalFromIFrame: true,
refIFrameSeq: frameSeqI,
);
state.isProcessingFrame = false;
return;
}
// I帧时refIFrameSeq等于lastDecodedIFrameSeq的P帧
if (lastDecodedIFrameSeq != null) {
final validPFrames = state.h264FrameBuffer
.where((f) =>
f['frameType'] == TalkDataH264Frame_FrameTypeE.P &&
f['frameSeqI'] == lastDecodedIFrameSeq)
.toList();
if (validPFrames.isNotEmpty) {
validPFrames.sort(
(a, b) => (a['frameSeq'] as int).compareTo(b['frameSeq'] as int));
final minPFrame = validPFrames.first;
if (iFrames.isNotEmpty) {
final minIFrame = iFrames.first;
final minIFrameSeq = minIFrame['frameSeq'];
final targetIndex = state.h264FrameBuffer.indexWhere(
(f) =>
f['frameType'] == TalkDataH264Frame_FrameTypeE.P &&
f['frameSeq'] == minPFrame['frameSeq'] &&
f['frameSeqI'] == lastDecodedIFrameSeq,
(f) => f['frameType'] == TalkDataH264Frame_FrameTypeE.I && f['frameSeq'] == minIFrameSeq,
);
state.isProcessingFrame = true;
final Map<String, dynamic>? frameMap =
state.h264FrameBuffer.removeAt(targetIndex);
final Map<String, dynamic>? frameMap = state.h264FrameBuffer.removeAt(targetIndex);
if (frameMap == null) {
state.isProcessingFrame = false;
return;
@ -367,12 +293,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
final int? frameSeq = frameMap['frameSeq'];
final int? frameSeqI = frameMap['frameSeqI'];
final int? pts = frameMap['pts'];
final ScpMessage? scpMessage = frameMap['scpMessage'];
if (frameData == null ||
frameType == null ||
frameSeq == null ||
frameSeqI == null ||
pts == null) {
if (frameData == null || frameType == null || frameSeq == null || frameSeqI == null || pts == null) {
state.isProcessingFrame = false;
return;
}
@ -380,12 +301,11 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
state.isProcessingFrame = false;
return;
}
// AppLog.log('送入解码器的I帧数据frameSeq:${frameSeq},frameSeqI:${frameSeqI},'
// 'frameType:${frameType},messageId:${scpMessage!.MessageId}');
lastDecodedIFrameSeq = minIFrameSeq;
await VideoDecodePlugin.sendFrame(
frameData: frameData,
frameType: 1,
frameType: 0,
frameSeq: frameSeq,
timestamp: pts,
splitNalFromIFrame: true,
@ -394,8 +314,61 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
state.isProcessingFrame = false;
return;
}
// I帧时refIFrameSeq等于lastDecodedIFrameSeq的P帧
if (lastDecodedIFrameSeq != null) {
final validPFrames =
state.h264FrameBuffer.where((f) => f['frameType'] == TalkDataH264Frame_FrameTypeE.P && f['frameSeqI'] == lastDecodedIFrameSeq).toList();
if (validPFrames.isNotEmpty) {
validPFrames.sort((a, b) => (a['frameSeq'] as int).compareTo(b['frameSeq'] as int));
final minPFrame = validPFrames.first;
final targetIndex = state.h264FrameBuffer.indexWhere(
(f) =>
f['frameType'] == TalkDataH264Frame_FrameTypeE.P && f['frameSeq'] == minPFrame['frameSeq'] && f['frameSeqI'] == lastDecodedIFrameSeq,
);
state.isProcessingFrame = true;
final Map<String, dynamic>? frameMap = state.h264FrameBuffer.removeAt(targetIndex);
if (frameMap == null) {
state.isProcessingFrame = false;
return;
}
final List<int>? frameData = frameMap['frameData'];
final TalkDataH264Frame_FrameTypeE? frameType = frameMap['frameType'];
final int? frameSeq = frameMap['frameSeq'];
final int? frameSeqI = frameMap['frameSeqI'];
final int? pts = frameMap['pts'];
if (frameData == null || frameType == null || frameSeq == null || frameSeqI == null || pts == null) {
state.isProcessingFrame = false;
return;
}
if (state.textureId.value == null) {
state.isProcessingFrame = false;
return;
}
await VideoDecodePlugin.sendFrame(
frameData: frameData,
frameType: 1,
frameSeq: frameSeq,
timestamp: pts,
splitNalFromIFrame: true,
refIFrameSeq: frameSeqI,
);
state.isProcessingFrame = false;
return;
}
}
// I帧到来
} finally {
final endTime = DateTime.now().microsecondsSinceEpoch;
final durationMs = (endTime - startTime) / 1000.0;
// > 5ms
if (durationMs > 5) {
debugPrint('[_processNextFrameFromBuffer] 耗时: ${durationMs.toStringAsFixed(2)} ms');
// 使
// AppLog.log('Frame processing took ${durationMs.toStringAsFixed(2)} ms');
}
}
// I帧到来
}
///
@ -427,8 +400,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
AppLog.log("==== 启动新的数据流监听 ====");
_isListening = true;
_streamSubscription = state.talkDataRepository.talkDataStream
.listen((TalkDataModel talkDataModel) async {
_streamSubscription = state.talkDataRepository.talkDataStream.listen((TalkDataModel talkDataModel) async {
_processFrame(talkDataModel);
});
}
@ -437,8 +409,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
void _playAudioFrames() {
//
//
if (state.audioBuffer.isEmpty ||
state.audioBuffer.length < audioBufferSize) {
if (state.audioBuffer.isEmpty || state.audioBuffer.length < audioBufferSize) {
return;
}
@ -446,8 +417,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
TalkData? oldestFrame;
int oldestIndex = -1;
for (int i = 0; i < state.audioBuffer.length; i++) {
if (oldestFrame == null ||
state.audioBuffer[i].durationMs < oldestFrame.durationMs) {
if (oldestFrame == null || state.audioBuffer[i].durationMs < oldestFrame.durationMs) {
oldestFrame = state.audioBuffer[i];
oldestIndex = i;
}
@ -477,8 +447,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
break;
case TalkStatus.answeredSuccessfully:
state.oneMinuteTimeTimer?.cancel(); //
state.oneMinuteTimeTimer ??=
Timer.periodic(const Duration(seconds: 1), (Timer t) {
state.oneMinuteTimeTimer ??= Timer.periodic(const Duration(seconds: 1), (Timer t) {
if (state.isLoading.isFalse) {
state.oneMinuteTime.value++;
}
@ -493,9 +462,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
///
void _playAudioData(TalkData talkData) async {
if (state.isOpenVoice.value &&
state.isLoading.isFalse &&
state.isRecordingAudio.value == false) {
if (state.isOpenVoice.value && state.isLoading.isFalse && state.isRecordingAudio.value == false) {
List<int> encodedData = G711Tool.decode(talkData.content, 0); // 0A-law
// PCM PcmArrayInt16
final PcmArrayInt16 fromList = PcmArrayInt16.fromList(encodedData);
@ -670,11 +637,9 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
AppLog.log('截图失败: 未找到当前上下文');
return;
}
final RenderRepaintBoundary boundary = state.globalKey.currentContext!
.findRenderObject()! as RenderRepaintBoundary;
final RenderRepaintBoundary boundary = state.globalKey.currentContext!.findRenderObject()! as RenderRepaintBoundary;
final ui.Image image = await boundary.toImage();
final ByteData? byteData =
await image.toByteData(format: ui.ImageByteFormat.png);
final ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png);
if (byteData == null) {
AppLog.log('截图失败: 图像数据为空');
@ -702,15 +667,13 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
//
Future<void> remoteOpenLock() async {
final LockListInfoItemEntity currentKeyInfo =
CommonDataManage().currentKeyInfo;
final LockListInfoItemEntity currentKeyInfo = CommonDataManage().currentKeyInfo;
var lockId = currentKeyInfo.lockId ?? 0;
var remoteUnlock = currentKeyInfo.lockSetting?.remoteUnlock ?? 0;
final lockPeerId = StartChartManage().lockPeerId;
final LockListInfoGroupEntity? lockListInfoGroupEntity =
await Storage.getLockMainListData();
final LockListInfoGroupEntity? lockListInfoGroupEntity = await Storage.getLockMainListData();
if (lockListInfoGroupEntity != null) {
lockListInfoGroupEntity!.groupList?.forEach((element) {
final lockList = element.lockList;
@ -728,8 +691,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
});
}
if (remoteUnlock == 1) {
final LoginEntity entity = await ApiRepository.to
.remoteOpenLock(lockId: lockId.toString(), timeOut: 60);
final LoginEntity entity = await ApiRepository.to.remoteOpenLock(lockId: lockId.toString(), timeOut: 60);
if (entity.errorCode!.codeIsSuccessful) {
showToast('已开锁'.tr);
StartChartManage().lockListPeerId = [];
@ -756,8 +718,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
state.startRecordingAudioTime.value = DateTime.now();
//
state.voiceProcessor
?.addFrameListeners(<VoiceProcessorFrameListener>[_onFrame]);
state.voiceProcessor?.addFrameListeners(<VoiceProcessorFrameListener>[_onFrame]);
state.voiceProcessor?.addErrorListener(_onError);
} else {
// state.errorMessage.value = 'Recording permission not granted';
@ -777,8 +738,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
state.endRecordingAudioTime.value = DateTime.now();
//
final Duration duration = state.endRecordingAudioTime.value
.difference(state.startRecordingAudioTime.value);
final Duration duration = state.endRecordingAudioTime.value.difference(state.startRecordingAudioTime.value);
state.recordingAudioTime.value = duration.inSeconds;
} on PlatformException catch (ex) {
@ -848,10 +808,8 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
_bufferedAudioFrames.addAll(encodedData);
//
if (_startProcessingAudioTimer == null &&
_bufferedAudioFrames.length > chunkSize) {
_startProcessingAudioTimer =
Timer.periodic(Duration(milliseconds: intervalMs), _sendAudioChunk);
if (_startProcessingAudioTimer == null && _bufferedAudioFrames.length > chunkSize) {
_startProcessingAudioTimer = Timer.periodic(Duration(milliseconds: intervalMs), _sendAudioChunk);
}
}
@ -887,8 +845,7 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
}
///
StartChartManage().changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(
talkExpect: talkExpectReq);
StartChartManage().changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(talkExpect: talkExpectReq);
// loadingframeSeq回绕检测
// frameSeq回绕检测标志

View File

@ -29,15 +29,12 @@ class TalkViewNativeDecodePage extends StatefulWidget {
const TalkViewNativeDecodePage({Key? key}) : super(key: key);
@override
State<TalkViewNativeDecodePage> createState() =>
_TalkViewNativeDecodePageState();
State<TalkViewNativeDecodePage> createState() => _TalkViewNativeDecodePageState();
}
class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage>
with TickerProviderStateMixin {
class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage> with TickerProviderStateMixin {
final TalkViewNativeDecodeLogic logic = Get.put(TalkViewNativeDecodeLogic());
final TalkViewNativeDecodeState state =
Get.find<TalkViewNativeDecodeLogic>().state;
final TalkViewNativeDecodeState state = Get.find<TalkViewNativeDecodeLogic>().state;
final startChartManage = StartChartManage();
@override
@ -69,66 +66,48 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage>
// false 退
return false;
},
child: SizedBox(
child: Container(
width: 1.sw,
height: 1.sh,
color: Colors.black.withOpacity(0.7),
child: Stack(
alignment: Alignment.center,
children: <Widget>[
//
Obx(
() {
final double screenWidth = MediaQuery.of(context).size.width;
final double screenHeight = MediaQuery.of(context).size.height;
final double logicalWidth = MediaQuery.of(context).size.width;
final double logicalHeight = MediaQuery.of(context).size.height;
final double devicePixelRatio =
MediaQuery.of(context).devicePixelRatio;
//
final double physicalWidth = logicalWidth * devicePixelRatio;
final double physicalHeight = logicalHeight * devicePixelRatio;
//
const int rotatedImageWidth = 480; //
const int rotatedImageHeight = 864; //
//
final double scaleWidth = physicalWidth / rotatedImageWidth;
final double scaleHeight = physicalHeight / rotatedImageHeight;
max(scaleWidth, scaleHeight); //
// loading中或textureId为nullloading/
if (state.isLoading.isTrue || state.textureId.value == null) {
return Image.asset(
'images/main/monitorBg.png',
width: screenWidth,
height: screenHeight,
width: 1.sw,
height: 1.sh,
fit: BoxFit.cover,
);
} else {
return Positioned.fill(
return Positioned(
top: 0,
left: 0,
right: 0,
child: PopScope(
canPop: false,
child: RepaintBoundary(
key: state.globalKey,
child: SizedBox.expand(
child: RotatedBox(
// 使RotatedBox
quarterTurns: startChartManage.rotateAngle ~/ 90,
child: Platform.isIOS
? Transform.scale(
scale: 1.008, // iOS白边
child: Texture(
textureId: state.textureId.value!,
filterQuality: FilterQuality.medium,
),
)
: Texture(
child: RotatedBox(
// 使RotatedBox
quarterTurns: startChartManage.rotateAngle ~/ 90,
child: state.isFullScreen.isFalse
? AspectRatio(
aspectRatio: StartChartManage().videoWidth / StartChartManage().videoHeight,
child: Texture(
textureId: state.textureId.value!,
filterQuality: FilterQuality.medium,
),
),
)
: Texture(
textureId: state.textureId.value!,
filterQuality: FilterQuality.medium,
),
),
),
),
@ -151,19 +130,14 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage>
width: 1.sw,
child: Obx(
() {
final String sec = (state.oneMinuteTime.value % 60)
.toString()
.padLeft(2, '0');
final String min = (state.oneMinuteTime.value ~/ 60)
.toString()
.padLeft(2, '0');
final String sec = (state.oneMinuteTime.value % 60).toString().padLeft(2, '0');
final String min = (state.oneMinuteTime.value ~/ 60).toString().padLeft(2, '0');
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'$min:$sec',
style: TextStyle(
fontSize: 26.sp, color: Colors.white),
style: TextStyle(fontSize: 26.sp, color: Colors.white),
),
],
);
@ -177,9 +151,7 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage>
width: 1.sw - 30.w * 2,
// height: 300.h,
margin: EdgeInsets.all(30.w),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.2),
borderRadius: BorderRadius.circular(20.h)),
decoration: BoxDecoration(color: Colors.black.withOpacity(0.2), borderRadius: BorderRadius.circular(20.h)),
child: Column(
children: <Widget>[
SizedBox(height: 20.h),
@ -191,9 +163,7 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage>
),
),
),
Obx(() => state.isLoading.isTrue
? buildRotationTransition()
: Container()),
Obx(() => state.isLoading.isTrue ? buildRotationTransition() : Container()),
Obx(() => state.isLongPressing.value
? Positioned(
top: 80.h,
@ -213,8 +183,7 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage>
SizedBox(width: 10.w),
Text(
'正在说话...'.tr,
style: TextStyle(
fontSize: 20.sp, color: Colors.white),
style: TextStyle(fontSize: 20.sp, color: Colors.white),
),
],
),
@ -246,10 +215,8 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage>
width: 40.w,
height: 40.w,
image: state.isOpenVoice.value
? const AssetImage(
'images/main/icon_lockDetail_monitoringOpenVoice.png')
: const AssetImage(
'images/main/icon_lockDetail_monitoringCloseVoice.png'))),
? const AssetImage('images/main/icon_lockDetail_monitoringOpenVoice.png')
: const AssetImage('images/main/icon_lockDetail_monitoringCloseVoice.png'))),
),
),
SizedBox(width: 50.w),
@ -264,11 +231,7 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage>
width: 50.w,
height: 50.w,
padding: EdgeInsets.all(5.w),
child: Image(
width: 40.w,
height: 40.w,
image: const AssetImage(
'images/main/icon_lockDetail_monitoringScreenshot.png')),
child: Image(width: 40.w, height: 40.w, image: const AssetImage('images/main/icon_lockDetail_monitoringScreenshot.png')),
),
),
SizedBox(width: 50.w),
@ -293,8 +256,7 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage>
width: 40.w,
height: 40.w,
fit: BoxFit.fill,
image: const AssetImage(
'images/main/icon_lockDetail_monitoringScreenRecording.png'),
image: const AssetImage('images/main/icon_lockDetail_monitoringScreenRecording.png'),
),
),
),
@ -330,13 +292,8 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage>
Text(
q,
style: TextStyle(
color: state.currentQuality.value == q
? AppColors.mainColor
: Colors.black,
fontWeight:
state.currentQuality.value == q
? FontWeight.bold
: FontWeight.normal,
color: state.currentQuality.value == q ? AppColors.mainColor : Colors.black,
fontWeight: state.currentQuality.value == q ? FontWeight.bold : FontWeight.normal,
fontSize: 28.sp,
),
),
@ -352,8 +309,7 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage>
);
},
child: Container(
child: Icon(Icons.high_quality_outlined,
color: Colors.white, size: 38.w),
child: Icon(Icons.high_quality_outlined, color: Colors.white, size: 38.w),
),
),
Visibility(
@ -377,61 +333,56 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage>
}
Widget bottomBottomBtnWidget() {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: <Widget>[
//
Obx(
() => bottomBtnItemWidget(
getAnswerBtnImg(),
getAnswerBtnName(),
Colors.white,
longPress: () async {
if (state.talkStatus.value == TalkStatus.answeredSuccessfully) {
//
logic.startProcessingAudio();
state.isLongPressing.value = true;
}
},
longPressUp: () async {
//
logic.stopProcessingAudio();
state.isLongPressing.value = false;
},
onClick: () async {
if (state.talkStatus.value ==
TalkStatus.passiveCallWaitingAnswer) {
//
logic.initiateAnswerCommand();
}
},
),
),
bottomBtnItemWidget(
'images/main/icon_lockDetail_hangUp.png', '挂断'.tr, Colors.red,
onClick: () {
//
logic.udpHangUpAction();
}),
bottomBtnItemWidget(
'images/main/icon_lockDetail_monitoringUnlock.png',
'开锁'.tr,
AppColors.mainColor,
onClick: () {
// if (state.talkStatus.value == TalkStatus.answeredSuccessfully &&
// state.listData.value.length > 0) {
// logic.udpOpenDoorAction();
// }
// if (UDPManage().remoteUnlock == 1) {
// logic.udpOpenDoorAction();
// showDeletPasswordAlertDialog(context);
// } else {
// logic.showToast('请在锁设置中开启远程开锁'.tr);
// }
logic.remoteOpenLock();
},
)
]);
return Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[
//
Obx(
() => bottomBtnItemWidget(
getAnswerBtnImg(),
getAnswerBtnName(),
Colors.white,
longPress: () async {
if (state.talkStatus.value == TalkStatus.answeredSuccessfully) {
//
logic.startProcessingAudio();
state.isLongPressing.value = true;
}
},
longPressUp: () async {
//
logic.stopProcessingAudio();
state.isLongPressing.value = false;
},
onClick: () async {
if (state.talkStatus.value == TalkStatus.passiveCallWaitingAnswer) {
//
logic.initiateAnswerCommand();
}
},
),
),
bottomBtnItemWidget('images/main/icon_lockDetail_hangUp.png', '挂断'.tr, Colors.red, onClick: () {
//
logic.udpHangUpAction();
}),
bottomBtnItemWidget(
'images/main/icon_lockDetail_monitoringUnlock.png',
'开锁'.tr,
AppColors.mainColor,
onClick: () {
// if (state.talkStatus.value == TalkStatus.answeredSuccessfully &&
// state.listData.value.length > 0) {
// logic.udpOpenDoorAction();
// }
// if (UDPManage().remoteUnlock == 1) {
// logic.udpOpenDoorAction();
// showDeletPasswordAlertDialog(context);
// } else {
// logic.showToast('请在锁设置中开启远程开锁'.tr);
// }
logic.remoteOpenLock();
},
)
]);
}
String getAnswerBtnImg() {

View File

@ -0,0 +1,518 @@
import 'dart:async';
import 'dart:math';
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:http/http.dart' as http;
import 'package:provider/provider.dart';
import 'package:star_lock/appRouters.dart';
import 'package:star_lock/flavors.dart';
import 'package:star_lock/talk/call/callTalk.dart';
import 'package:star_lock/talk/starChart/constant/talk_status.dart';
import 'package:star_lock/talk/starChart/handle/impl/debug_Info_model.dart';
import 'package:star_lock/talk/starChart/handle/impl/udp_talk_data_handler.dart';
import 'package:star_lock/talk/starChart/star_chart_manage.dart';
import 'package:star_lock/talk/starChart/views/native/talk_view_native_decode_logic.dart';
import 'package:star_lock/talk/starChart/views/native/talk_view_native_decode_state.dart';
import 'package:star_lock/talk/starChart/views/talkView/talk_view_logic.dart';
import 'package:star_lock/talk/starChart/views/talkView/talk_view_state.dart';
import 'package:video_decode_plugin/video_decode_plugin.dart';
import '../../../../app_settings/app_colors.dart';
import '../../../../tools/showTFView.dart';
class TalkViewNativeDecodePageDebug extends StatefulWidget {
const TalkViewNativeDecodePageDebug({Key? key}) : super(key: key);
@override
State<TalkViewNativeDecodePageDebug> createState() => _TalkViewNativeDecodePageDebugState();
}
class _TalkViewNativeDecodePageDebugState extends State<TalkViewNativeDecodePageDebug> with TickerProviderStateMixin {
final TalkViewNativeDecodeLogic logic = Get.put(TalkViewNativeDecodeLogic());
final TalkViewNativeDecodeState state = Get.find<TalkViewNativeDecodeLogic>().state;
final startChartManage = StartChartManage();
@override
void initState() {
super.initState();
state.animationController = AnimationController(
vsync: this, // 使TickerProvider是当前Widget
duration: const Duration(seconds: 1),
);
state.animationController.repeat();
//StatusListener
state.animationController.addStatusListener((AnimationStatus status) {
if (status == AnimationStatus.completed) {
state.animationController.reset();
state.animationController.forward();
} else if (status == AnimationStatus.dismissed) {
state.animationController.reset();
state.animationController.forward();
}
});
}
@override
Widget build(BuildContext context) {
return WillPopScope(
onWillPop: () async {
// false 退
return false;
},
child: Container(
width: 1.sw,
height: 1.sh,
color: Colors.black.withOpacity(0.7),
child: Stack(
alignment: Alignment.center,
children: <Widget>[
//
Obx(
() {
final double screenWidth = MediaQuery.of(context).size.width;
final double screenHeight = MediaQuery.of(context).size.height;
// loading中或textureId为nullloading/
if (state.isLoading.isTrue || state.textureId.value == null) {
return Image.asset(
'images/main/monitorBg.png',
width: screenWidth,
height: screenHeight,
fit: BoxFit.cover,
);
} else {
return PopScope(
canPop: false,
child: RepaintBoundary(
key: state.globalKey,
child: RotatedBox(
// 使RotatedBox
quarterTurns: startChartManage.rotateAngle ~/ 90,
child: Platform.isIOS
? Transform.scale(
scale: 1.008, // iOS白边
child: Texture(
textureId: state.textureId.value!,
filterQuality: FilterQuality.medium,
),
)
: state.isFullScreen.isFalse
? AspectRatio(
aspectRatio: StartChartManage().videoWidth / StartChartManage().videoHeight,
child: Texture(
textureId: state.textureId.value!,
filterQuality: FilterQuality.medium,
),
)
: Texture(
textureId: state.textureId.value!,
filterQuality: FilterQuality.medium,
),
),
),
);
}
},
),
ElevatedButton(
onPressed: () {
state.isFullScreen.value = !state.isFullScreen.value;
},
child: Obx(
() => Text(state.isFullScreen.isTrue ? '退出全屏' : '全屏'),
),
),
Obx(() => state.isLoading.isTrue
? Positioned(
bottom: 310.h,
child: Text(
'正在创建安全连接...'.tr,
style: TextStyle(color: Colors.black, fontSize: 26.sp),
))
: Container()),
Obx(() => state.isLoading.isFalse && state.oneMinuteTime.value > 0
? Positioned(
top: ScreenUtil().statusBarHeight + 75.h,
width: 1.sw,
child: Obx(
() {
final String sec = (state.oneMinuteTime.value % 60).toString().padLeft(2, '0');
final String min = (state.oneMinuteTime.value ~/ 60).toString().padLeft(2, '0');
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'$min:$sec',
style: TextStyle(fontSize: 26.sp, color: Colors.white),
),
],
);
},
),
)
: Container()),
Positioned(
bottom: 10.w,
child: Container(
width: 1.sw - 30.w * 2,
// height: 300.h,
margin: EdgeInsets.all(30.w),
decoration: BoxDecoration(color: Colors.black.withOpacity(0.2), borderRadius: BorderRadius.circular(20.h)),
child: Column(
children: <Widget>[
SizedBox(height: 20.h),
bottomTopBtnWidget(),
SizedBox(height: 20.h),
bottomBottomBtnWidget(),
SizedBox(height: 20.h),
],
),
),
),
Obx(() => state.isLoading.isTrue ? buildRotationTransition() : Container()),
Obx(() => state.isLongPressing.value
? Positioned(
top: 80.h,
left: 0,
right: 0,
child: Center(
child: Container(
padding: EdgeInsets.all(10.w),
decoration: BoxDecoration(
color: Colors.black.withOpacity(0.7),
borderRadius: BorderRadius.circular(10.w),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
Icon(Icons.mic, color: Colors.white, size: 24.w),
SizedBox(width: 10.w),
Text(
'正在说话...'.tr,
style: TextStyle(fontSize: 20.sp, color: Colors.white),
),
],
),
),
),
)
: Container()),
],
),
),
);
}
Widget bottomTopBtnWidget() {
return Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
//
GestureDetector(
onTap: () {
if (state.talkStatus.value == TalkStatus.answeredSuccessfully) {
//
logic.updateTalkExpect();
}
},
child: Container(
width: 50.w,
height: 50.w,
padding: EdgeInsets.all(5.w),
child: Obx(() => Image(
width: 40.w,
height: 40.w,
image: state.isOpenVoice.value
? const AssetImage('images/main/icon_lockDetail_monitoringOpenVoice.png')
: const AssetImage('images/main/icon_lockDetail_monitoringCloseVoice.png'))),
),
),
SizedBox(width: 50.w),
//
GestureDetector(
onTap: () async {
if (state.talkStatus.value == TalkStatus.answeredSuccessfully) {
await logic.captureAndSavePng();
}
},
child: Container(
width: 50.w,
height: 50.w,
padding: EdgeInsets.all(5.w),
child: Image(width: 40.w, height: 40.w, image: const AssetImage('images/main/icon_lockDetail_monitoringScreenshot.png')),
),
),
SizedBox(width: 50.w),
//
GestureDetector(
onTap: () async {
logic.showToast('功能暂未开放'.tr);
// if (
// state.talkStatus.value == TalkStatus.answeredSuccessfully) {
// if (state.isRecordingScreen.value) {
// await logic.stopRecording();
// } else {
// await logic.startRecording();
// }
// }
},
child: Container(
width: 50.w,
height: 50.w,
padding: EdgeInsets.all(5.w),
child: Image(
width: 40.w,
height: 40.w,
fit: BoxFit.fill,
image: const AssetImage('images/main/icon_lockDetail_monitoringScreenRecording.png'),
),
),
),
SizedBox(width: 50.w),
//
GestureDetector(
onTap: () async {
//
showModalBottomSheet(
context: context,
backgroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.vertical(top: Radius.circular(20.w)),
),
builder: (BuildContext context) {
final List<String> qualities = ['高清', '标清'];
return SafeArea(
child: SingleChildScrollView(
child: Column(
mainAxisSize: MainAxisSize.min,
children: qualities.map((q) {
return Obx(() => InkWell(
onTap: () {
Navigator.of(context).pop();
logic.onQualityChanged(q);
},
child: Container(
padding: EdgeInsets.symmetric(vertical: 18.w),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
children: [
Text(
q,
style: TextStyle(
color: state.currentQuality.value == q ? AppColors.mainColor : Colors.black,
fontWeight: state.currentQuality.value == q ? FontWeight.bold : FontWeight.normal,
fontSize: 28.sp,
),
),
],
),
),
));
}).toList(),
),
),
);
},
);
},
child: Container(
child: Icon(Icons.high_quality_outlined, color: Colors.white, size: 38.w),
),
),
Visibility(
visible: state.currentLanguage == 'zh_CN' && Platform.isAndroid,
child: SizedBox(width: 38.w),
),
Visibility(
visible: state.currentLanguage == 'zh_CN' && Platform.isAndroid,
child: IconButton(
icon: Icon(
Icons.notification_add_sharp,
size: 32.w,
color: Colors.white,
),
onPressed: () {
Get.toNamed(Routers.permissionGuidancePage);
},
),
)
]);
}
Widget bottomBottomBtnWidget() {
return Row(mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: <Widget>[
//
Obx(
() => bottomBtnItemWidget(
getAnswerBtnImg(),
getAnswerBtnName(),
Colors.white,
longPress: () async {
if (state.talkStatus.value == TalkStatus.answeredSuccessfully) {
//
logic.startProcessingAudio();
state.isLongPressing.value = true;
}
},
longPressUp: () async {
//
logic.stopProcessingAudio();
state.isLongPressing.value = false;
},
onClick: () async {
if (state.talkStatus.value == TalkStatus.passiveCallWaitingAnswer) {
//
logic.initiateAnswerCommand();
}
},
),
),
bottomBtnItemWidget('images/main/icon_lockDetail_hangUp.png', '挂断'.tr, Colors.red, onClick: () {
//
logic.udpHangUpAction();
}),
bottomBtnItemWidget(
'images/main/icon_lockDetail_monitoringUnlock.png',
'开锁'.tr,
AppColors.mainColor,
onClick: () {
// if (state.talkStatus.value == TalkStatus.answeredSuccessfully &&
// state.listData.value.length > 0) {
// logic.udpOpenDoorAction();
// }
// if (UDPManage().remoteUnlock == 1) {
// logic.udpOpenDoorAction();
// showDeletPasswordAlertDialog(context);
// } else {
// logic.showToast('请在锁设置中开启远程开锁'.tr);
// }
logic.remoteOpenLock();
},
)
]);
}
String getAnswerBtnImg() {
switch (state.talkStatus.value) {
case TalkStatus.passiveCallWaitingAnswer:
return 'images/main/icon_lockDetail_monitoringAnswerCalls.png';
case TalkStatus.answeredSuccessfully:
case TalkStatus.proactivelyCallWaitingAnswer:
return 'images/main/icon_lockDetail_monitoringUnTalkback.png';
default:
return 'images/main/icon_lockDetail_monitoringAnswerCalls.png';
}
}
String getAnswerBtnName() {
switch (state.talkStatus.value) {
case TalkStatus.passiveCallWaitingAnswer:
return '接听'.tr;
case TalkStatus.proactivelyCallWaitingAnswer:
case TalkStatus.answeredSuccessfully:
return '长按说话'.tr;
default:
return '接听'.tr;
}
}
Widget bottomBtnItemWidget(
String iconUrl,
String name,
Color backgroundColor, {
required Function() onClick,
Function()? longPress,
Function()? longPressUp,
}) {
double wh = 80.w;
return GestureDetector(
onTap: onClick,
onLongPress: longPress,
onLongPressUp: longPressUp,
child: SizedBox(
height: 160.w,
width: 140.w,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
Container(
width: wh,
height: wh,
constraints: BoxConstraints(
minWidth: wh,
),
decoration: BoxDecoration(
color: backgroundColor,
borderRadius: BorderRadius.circular((wh + 10.w * 2) / 2),
),
padding: EdgeInsets.all(20.w),
child: Image.asset(iconUrl, fit: BoxFit.fitWidth),
),
SizedBox(height: 20.w),
Text(
name,
style: TextStyle(fontSize: 20.sp, color: Colors.white),
textAlign: TextAlign.center, // 使
maxLines: 2, // 1
)
],
),
),
);
}
//
Color _getPacketLossColor(double lossRate) {
if (lossRate < 1.0) {
return Colors.green; // 1%绿
} else if (lossRate < 5.0) {
return Colors.yellow; // 1%-5%
} else if (lossRate < 10.0) {
return Colors.orange; // 5%-10%
} else {
return Colors.red; // 10%
}
}
//
Widget buildRotationTransition() {
return Positioned(
left: ScreenUtil().screenWidth / 2 - 220.w / 2,
top: ScreenUtil().screenHeight / 2 - 220.w / 2 - 150.h,
child: GestureDetector(
child: RotationTransition(
//
alignment: Alignment.center,
//
turns: state.animationController,
//view
child: AnimatedOpacity(
opacity: 0.5,
duration: const Duration(seconds: 2),
child: Image.asset(
'images/main/realTime_connecting.png',
width: 220.w,
height: 220.w,
),
),
),
onTap: () {
state.animationController.forward();
},
),
);
}
@override
void dispose() {
state.animationController.dispose();
CallTalk().finishAVData();
super.dispose();
}
}

View File

@ -37,8 +37,7 @@ class TalkViewNativeDecodeState {
Future<String?> userMobileIP = NetworkInfo().getWifiIP();
Future<String?> userUid = Storage.getUid();
RxInt udpStatus =
0.obs; //0 1 2 3 4 5 6 8 9
RxInt udpStatus = 0.obs; //0 1 2 3 4 5 6 8 9
TextEditingController passwordTF = TextEditingController();
RxList<int> listAudioData = <int>[].obs; //
@ -63,13 +62,12 @@ class TalkViewNativeDecodeState {
RxBool isPlaying = false.obs; //
Rx<TalkStatus> talkStatus = TalkStatus.none.obs; //
// startChartTalkStatus
final StartChartTalkStatus startChartTalkStatus =
StartChartTalkStatus.instance;
final StartChartTalkStatus startChartTalkStatus = StartChartTalkStatus.instance;
//
final TalkDataRepository talkDataRepository = TalkDataRepository.instance;
RxBool isOpenVoice = true.obs; //
RxBool isOpenVoice = false.obs; //
RxBool isRecordingScreen = false.obs; //
RxBool isRecordingAudio = false.obs; //
Rx<DateTime> startRecordingAudioTime = DateTime.now().obs; //
@ -81,6 +79,7 @@ class TalkViewNativeDecodeState {
RxBool isLongPressing = false.obs; //
// ID
Rx<int?> textureId = Rx<int?>(null);
// FPS监测相关变量
RxInt lastFpsUpdateTime = 0.obs; // FPS更新时间
@ -107,11 +106,11 @@ class TalkViewNativeDecodeState {
// Mapkey为textureId_frameSeq
Map<String, Map<String, dynamic>> frameTracker = {};
// H264帧缓冲区相关
final List<Map<String, dynamic>> h264FrameBuffer = <Map<String, dynamic>>[]; // H264帧缓冲区
final int maxFrameBufferSize = 50; //
final int targetFps = 60; // ,native的缓冲区
final int maxFrameBufferSize = 25; //
final int targetFps = 25; // ,native的缓冲区
Timer? frameProcessTimer; //
bool isProcessingFrame = false; //
int lastProcessedTimestamp = 0; //
@ -122,6 +121,8 @@ class TalkViewNativeDecodeState {
// '高清'
RxString currentQuality = '高清'.obs; //
RxString currentLanguage =
CurrentLocaleTool.getCurrentLocaleString().obs; //
RxString currentLanguage = CurrentLocaleTool.getCurrentLocaleString().obs; //
//
RxBool isFullScreen = false.obs;
}

View File

@ -176,7 +176,7 @@ dependencies:
url_launcher: ^6.1.10
#蓝牙
# flutter_reactive_ble: ^5.1.1
flutter_blue_plus: 1.32.7
flutter_blue_plus: 1.33.0
#
event_bus: ^2.0.0
#菊花
@ -235,6 +235,7 @@ dependencies:
# ffmpeg_kit_flutter: 5.1.0-LTS
fast_gbk: ^1.0.0
flutter_pcm_sound: ^1.1.0
intl: ^0.18.0
# flutter_audio_capture: <1.1.5
@ -246,7 +247,7 @@ dependencies:
#侧滑删除
flutter_slidable: ^3.0.1
# audio_service: ^0.18.12
app_settings: ^5.1.1
app_settings: ^6.1.1
flutter_local_notifications: ^17.0.0
fluwx: 4.5.5
system_settings: ^2.0.0
@ -321,6 +322,7 @@ flutter:
assets:
- images/
- images/tabbar/
- images/other/
- images/guide/
- images/main/
- images/main/addFingerprint/