Compare commits
82 Commits
develop_sk
...
canary_rel
| Author | SHA1 | Date | |
|---|---|---|---|
| 217705fc8d | |||
| 8b1f3a8e65 | |||
| 2954afefb6 | |||
| 73618a7fb0 | |||
| 472a6f20d3 | |||
| c9683b4cba | |||
| eb3d53c0af | |||
| b67e23ebaf | |||
| bcf7f04a70 | |||
| f77009d6d9 | |||
| 7704083571 | |||
| 61fa6c7e77 | |||
| bd04e2dc88 | |||
| cebc1c09f3 | |||
| 272b02f2c9 | |||
| 392cb83156 | |||
| a5b5d6ad0e | |||
| 19e300b4db | |||
| bc8f9c364a | |||
| 73988c9bab | |||
| cfa9796a2a | |||
| d3ced373d8 | |||
| 572a41a23c | |||
| 197f21e26c | |||
| 1a9db3bc28 | |||
| 9bb7fcf7a5 | |||
| 16bdb49173 | |||
| 7a71bea629 | |||
| 0ba2364d5d | |||
| 4f522919af | |||
| 5e29e2c780 | |||
| 22956365f0 | |||
| 9cf543d638 | |||
| dd0e7fd1f1 | |||
| dc5bfdb756 | |||
| 1bdfc43b53 | |||
| 6f1f1e44cd | |||
| c98772c806 | |||
| 72d451268e | |||
| f55e69c67f | |||
| e983540b79 | |||
| 20c7b5ba9b | |||
| 507ba73fa9 | |||
| b506e679da | |||
| e04fb1adbc | |||
| 91c597b9a5 | |||
| d5a608cb50 | |||
| ed10354105 | |||
| 376a8a1bd8 | |||
| 5767351b6e | |||
| ba2fb28018 | |||
| e105a0f9cc | |||
| b77f29a0bf | |||
| bb13b9fb87 | |||
| 700e713279 | |||
| 5a8f4afc26 | |||
| 2a2e6d31f5 | |||
| fad838a6c6 | |||
| b41bf00f8a | |||
| b8d34cda29 | |||
| 311fddf1ed | |||
| 7105f16daa | |||
| 0fffc2c4b2 | |||
| c6940e56cf | |||
| f4fd0668b0 | |||
| ec80c224f8 | |||
| 1f0172f61f | |||
| b907028e26 | |||
| c26e2068b6 | |||
| d32529518e | |||
| fe955dd9bb | |||
| f98370a6ef | |||
| bd12720b33 | |||
| eed55cb920 | |||
| 336915144a | |||
| 72f1bfbe61 | |||
| 8f75c8805d | |||
| 8c17f506b7 | |||
| 453150a570 | |||
| 067b5f9022 | |||
| 204bbc9483 | |||
| 8d0222240a |
@ -1,251 +0,0 @@
|
||||
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"
|
||||
@ -61,7 +61,7 @@ keytool -list -v -keystore android/app/sky.jks
|
||||
```
|
||||
|
||||
输入密码(在android/app/build.gradle:38可以看到)
|
||||
测试ci
|
||||
|
||||
一般需要的是:证书指纹-SHA1 看起来像 95:6B:***********(共59个字符)
|
||||
|
||||
## 编译
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 70 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 7.0 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 19 KiB |
@ -1167,11 +1167,5 @@
|
||||
"服务,图像视频信息随心存!": "معلومات الخدمة والصور والفيديو في قلبك!",
|
||||
"图像": "صورة",
|
||||
"视频": "فيديو",
|
||||
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "حاليا ، لا تدعم الدولة تسجيل رمز التحقق من الهاتف المحمول ، يرجى استخدام عنوان بريدك الإلكتروني للتسجيل",
|
||||
"注:": "ملاحظة:",
|
||||
"必需在开始时间24小时内使用一次,否则将失效": "يجب استخدامه مرة واحدة خلال 24 ساعة من وقت البدء، وإلا سيصبح غير صالح",
|
||||
"这是单次密码,只能使用一次": "هذا رمز مرور لمرة واحدة فقط، ولا يمكن استخدامه سوى مرة واحدة",
|
||||
"您好": "مرحبًا",
|
||||
"您的开门密码是": "رمز فتح الباب الخاص بك هو",
|
||||
"开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标": "عند الفتح، قم أولاً بتنشيط لوحة مفاتيح القفل، ثم أدخل الرمز وكلّمه بـ #، ويقع مفتاح # في الزاوية السفلى اليمنى من لوحة المفاتيح وقد يكون مُميّزًا برمز آخر"
|
||||
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "حاليا ، لا تدعم الدولة تسجيل رمز التحقق من الهاتف المحمول ، يرجى استخدام عنوان بريدك الإلكتروني للتسجيل"
|
||||
}
|
||||
2346
lan/lan_bg.json
2346
lan/lan_bg.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_bn.json
2346
lan/lan_bn.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_cs.json
2346
lan/lan_cs.json
File diff suppressed because one or more lines are too long
2346
lan/lan_da.json
2346
lan/lan_da.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_de.json
2346
lan/lan_de.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_el.json
2346
lan/lan_el.json
File diff suppressed because it is too large
Load Diff
@ -1174,11 +1174,5 @@
|
||||
"服务,图像视频信息随心存!": "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",
|
||||
"注:": "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"
|
||||
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "Currently, the country does not support mobile phone verification code registration, please use your email address to register"
|
||||
}
|
||||
2346
lan/lan_es.json
2346
lan/lan_es.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_et.json
2346
lan/lan_et.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_fi.json
2346
lan/lan_fi.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_fr.json
2346
lan/lan_fr.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_he.json
2346
lan/lan_he.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_hi.json
2346
lan/lan_hi.json
File diff suppressed because it is too large
Load Diff
@ -1043,6 +1043,7 @@
|
||||
"一键开锁": "一鍵解鎖",
|
||||
"已开通": "打開",
|
||||
"英文": "English",
|
||||
"英文": "English",
|
||||
"简体中文": "简体中文",
|
||||
"繁体中文": "繁體中文",
|
||||
"法语": "Français",
|
||||
@ -1168,11 +1169,5 @@
|
||||
"服务,图像视频信息随心存!": "服務,圖像視頻資訊隨心存!",
|
||||
"图像": "圖像",
|
||||
"视频": "視頻",
|
||||
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "當前國家不支援手機驗證碼註冊,請使用郵箱進行註冊",
|
||||
"注:": "註:",
|
||||
"必需在开始时间24小时内使用一次,否则将失效": "必須在開始時間24小時內使用一次,否則將失效",
|
||||
"这是单次密码,只能使用一次": "這是單次密碼,只能使用一次",
|
||||
"您好": "您好",
|
||||
"您的开门密码是": "您的開門密碼是",
|
||||
"开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标": "開鎖時,先啟動鎖鍵盤,再輸入密碼,以#號結束,#號鍵在鍵盤右下角,有可能是其他圖標"
|
||||
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "當前國家不支援手機驗證碼註冊,請使用郵箱進行註冊"
|
||||
}
|
||||
2346
lan/lan_hr.json
2346
lan/lan_hr.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_hu.json
2346
lan/lan_hu.json
File diff suppressed because it is too large
Load Diff
2358
lan/lan_hy.json
2358
lan/lan_hy.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_id.json
2346
lan/lan_id.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_it.json
2346
lan/lan_it.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_ja.json
2346
lan/lan_ja.json
File diff suppressed because it is too large
Load Diff
2358
lan/lan_ka.json
2358
lan/lan_ka.json
File diff suppressed because it is too large
Load Diff
@ -1179,11 +1179,5 @@
|
||||
"服务,图像视频信息随心存!": "服务,图像视频信息随心存!",
|
||||
"图像": "图像",
|
||||
"视频": "视频",
|
||||
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "当前国家不支持手机验证码注册,请使用邮箱进行注册",
|
||||
"注:":"注:",
|
||||
"必需在开始时间24小时内使用一次,否则将失效": "必需在开始时间24小时内使用一次,否则将失效",
|
||||
"这是单次密码,只能使用一次": "这是单次密码,只能使用一次",
|
||||
"您好": "您好",
|
||||
"您的开门密码是": "您的开门密码是",
|
||||
"开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标": "开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标"
|
||||
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "当前国家不支持手机验证码注册,请使用邮箱进行注册"
|
||||
}
|
||||
|
||||
2346
lan/lan_kk.json
2346
lan/lan_kk.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_ko.json
2346
lan/lan_ko.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_lt.json
2346
lan/lan_lt.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_ms.json
2346
lan/lan_ms.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_nl.json
2346
lan/lan_nl.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_pl.json
2346
lan/lan_pl.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_pt.json
2346
lan/lan_pt.json
File diff suppressed because it is too large
Load Diff
@ -1173,11 +1173,5 @@
|
||||
"服务,图像视频信息随心存!": "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",
|
||||
"注:": "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"
|
||||
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "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"
|
||||
}
|
||||
2346
lan/lan_ro.json
2346
lan/lan_ro.json
File diff suppressed because it is too large
Load Diff
2354
lan/lan_ru.json
2354
lan/lan_ru.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_sk.json
2346
lan/lan_sk.json
File diff suppressed because it is too large
Load Diff
@ -1168,11 +1168,5 @@
|
||||
"服务,图像视频信息随心存!": "Сервис , слике и видео информације су у вашем срцу!",
|
||||
"图像": "Слика",
|
||||
"视频": "Пријава",
|
||||
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "Тренутно , земља не подржава регистрацију кода за верификацију мобилног телефона, молимо вас да користите своју адресу е-поште за регистрацију",
|
||||
"注:": "Напомена:",
|
||||
"必需在开始时间24小时内使用一次,否则将失效": "Мора се користити једном у року од 24 сата од времена почетка, иначе истиче",
|
||||
"这是单次密码,只能使用一次": "Ово је једнократна лозинка која се може користити само једном",
|
||||
"您好": "Здраво",
|
||||
"您的开门密码是": "Ваш код за отварање врата је",
|
||||
"开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标": "Да бисте откључали, прво активирајте тастатуру браве, затим унесите лозинку и завршите са #. Тастер # се налази у доњем десном углу тастатуре и може имати другу икону"
|
||||
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "Тренутно , земља не подржава регистрацију кода за верификацију мобилног телефона, молимо вас да користите своју адресу е-поште за регистрацију"
|
||||
}
|
||||
2346
lan/lan_sv.json
2346
lan/lan_sv.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_th.json
2346
lan/lan_th.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_tr.json
2346
lan/lan_tr.json
File diff suppressed because it is too large
Load Diff
@ -1168,11 +1168,5 @@
|
||||
"服务,图像视频信息随心存!": "服務,圖像視頻資訊隨心存!",
|
||||
"图像": "圖像",
|
||||
"视频": "視頻",
|
||||
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "當前國家不支援手機驗證碼註冊,請使用郵箱進行註冊",
|
||||
"注:": "註:",
|
||||
"必需在开始时间24小时内使用一次,否则将失效": "必須在開始時間24小時內使用一次,否則將失效",
|
||||
"这是单次密码,只能使用一次": "這是單次密碼,只能使用一次",
|
||||
"您好": "您好",
|
||||
"您的开门密码是": "您的開門密碼是",
|
||||
"开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标": "開鎖時,先啟動鎖鍵盤,再輸入密碼,以#號結束,#號鍵在鍵盤右下角,有可能是其他圖標"
|
||||
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "當前國家不支援手機驗證碼註冊,請使用郵箱進行註冊"
|
||||
}
|
||||
2346
lan/lan_uk.json
2346
lan/lan_uk.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_ur.json
2346
lan/lan_ur.json
File diff suppressed because it is too large
Load Diff
2346
lan/lan_vi.json
2346
lan/lan_vi.json
File diff suppressed because it is too large
Load Diff
@ -1180,11 +1180,5 @@
|
||||
"服务,图像视频信息随心存!": "服务,图像视频信息随心存!",
|
||||
"图像": "图像",
|
||||
"视频": "视频",
|
||||
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "当前国家不支持手机验证码注册,请使用邮箱进行注册",
|
||||
"注:":"注:",
|
||||
"必需在开始时间24小时内使用一次,否则将失效": "必需在开始时间24小时内使用一次,否则将失效",
|
||||
"这是单次密码,只能使用一次": "这是单次密码,只能使用一次",
|
||||
"您好": "您好",
|
||||
"您的开门密码是": "您的开门密码是",
|
||||
"开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标": "开锁时,先激活锁键盘,再输入密码,以#号结束,#号键在键盘右下角,有可能是其他图标"
|
||||
"当前国家不支持手机验证码注册,请使用邮箱进行注册": "当前国家不支持手机验证码注册,请使用邮箱进行注册"
|
||||
}
|
||||
@ -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,24 +15,27 @@ 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>
|
||||
|
||||
@ -18,7 +18,6 @@ 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';
|
||||
@ -533,7 +532,6 @@ abstract class Routers {
|
||||
static const String permissionGuidancePage =
|
||||
'/permissionGuidancePage'; // 锁屏权限通知引导页面
|
||||
static const String lockVoiceSettingPage = '/lockVoiceSetting'; // 锁屏权限通知引导页面
|
||||
static const String aiAssistant = '/aiAssistant';
|
||||
}
|
||||
|
||||
abstract class AppRouters {
|
||||
@ -1226,9 +1224,6 @@ 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播放页面
|
||||
];
|
||||
|
||||
@ -46,7 +46,7 @@ class BlueManage {
|
||||
StreamSubscription<BluetoothConnectionState>? _connectionStateSubscription;
|
||||
|
||||
StreamSubscription<int>? _mtuSubscription;
|
||||
int? _mtuSize = 30;
|
||||
int? _mtuSize = 20;
|
||||
|
||||
// 当前连接设备的名字
|
||||
String connectDeviceName = '';
|
||||
@ -119,7 +119,8 @@ 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');
|
||||
});
|
||||
@ -158,20 +159,26 @@ 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) {
|
||||
@ -208,7 +215,8 @@ 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);
|
||||
@ -216,7 +224,8 @@ 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 {
|
||||
@ -233,14 +242,16 @@ 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) {
|
||||
@ -383,11 +394,8 @@ 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');
|
||||
@ -398,24 +406,22 @@ 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();
|
||||
@ -428,7 +434,8 @@ 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('开始扫描 蓝牙不可用,不能进行蓝牙操作');
|
||||
}
|
||||
@ -436,7 +443,8 @@ 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;
|
||||
@ -517,8 +525,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;
|
||||
}
|
||||
|
||||
@ -529,8 +537,11 @@ class BlueManage {
|
||||
bool isAddEquipment = false, // 是否是添加设备之前
|
||||
bool isReconnect = true, // 是否是重连
|
||||
}) async {
|
||||
final int knownDeviceIndex =
|
||||
devicesList.indexWhere((ScanResult d) => (d.device.platformName == deviceName) || (d.advertisementData.advName == deviceName));
|
||||
// 判断数组列表里面是否有这个设备
|
||||
// AppLog.log("devicesList:$devicesList");
|
||||
|
||||
final int knownDeviceIndex = devicesList.indexWhere(
|
||||
(ScanResult d) => (d.device.platformName == deviceName) || (d.advertisementData.advName == deviceName));
|
||||
|
||||
ScanResult? scanResult; //使用局部变量防止出现缓存
|
||||
if (knownDeviceIndex >= 0) {
|
||||
@ -541,6 +552,7 @@ class BlueManage {
|
||||
|
||||
bluetoothConnectDevice = devicesList[knownDeviceIndex].device;
|
||||
scanResult = devicesList[knownDeviceIndex];
|
||||
// AppLog.log('bluetoothConnectDevice: $bluetoothConnectDevice scanResult:$scanResult');
|
||||
|
||||
_initGetMtuSubscription();
|
||||
_initListenConnectionState();
|
||||
@ -552,13 +564,87 @@ 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) {
|
||||
@ -566,7 +652,9 @@ 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',
|
||||
@ -585,10 +673,7 @@ class BlueManage {
|
||||
int attempt = 0;
|
||||
while (attempt < maxAttempts) {
|
||||
try {
|
||||
await bluetoothConnectDevice.connect(
|
||||
timeout: 5.seconds,
|
||||
mtu: 512,
|
||||
);
|
||||
await bluetoothConnectDevice.connect(timeout: 5.seconds);
|
||||
break; // If the connection is successful, break the loop
|
||||
} catch (e) {
|
||||
AppLog.log('连接失败 重连了: $e');
|
||||
@ -621,10 +706,6 @@ class BlueManage {
|
||||
_subScribeToCharacteristic(characteristic);
|
||||
bluetoothConnectionState = BluetoothConnectionState.connected;
|
||||
connectStateCallBack(bluetoothConnectionState!);
|
||||
|
||||
if (Platform.isAndroid) {
|
||||
await bluetoothConnectDevice.requestMtu(512);
|
||||
}
|
||||
BuglyTool.uploadException(
|
||||
message: '订阅成功 上传记录当前方法是:bluetoothDeviceConnect',
|
||||
detail: '发现服务,连接成功,订阅数据 bluetoothDeviceConnect:${bluetoothConnectDevice.toString()} ',
|
||||
@ -651,7 +732,9 @@ 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;
|
||||
}
|
||||
}
|
||||
@ -721,24 +804,12 @@ 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++) {
|
||||
@ -814,12 +885,14 @@ class BlueManage {
|
||||
_mtuSize = 20; // 重置MTU为默认值
|
||||
|
||||
if (bluetoothConnectionState == BluetoothConnectionState.connected) {
|
||||
AppLog.log('请求断开蓝牙连接');
|
||||
//加快蓝牙断连
|
||||
await bluetoothConnectDevice!.disconnect(timeout: 1);
|
||||
await bluetoothConnectDevice!.disconnect(timeout: 3);
|
||||
AppLog.log('断开连接成功');
|
||||
}
|
||||
} on Exception catch (e, _) {
|
||||
AppLog.log('断开连接失败: $e');
|
||||
} finally {
|
||||
bluetoothConnectionState = BluetoothConnectionState.disconnected;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,74 +0,0 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -1,106 +0,0 @@
|
||||
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; //0:锁通通;1:涂鸦智能
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -27,11 +27,6 @@ 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}';
|
||||
}
|
||||
}
|
||||
|
||||
///接收数据类
|
||||
|
||||
@ -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,8 +53,6 @@ enum CommandType {
|
||||
gatewayGetWifiList, //网关获取附近的wifi列表 0x30F6
|
||||
gatewayGetWifiListResult, //网关获取附近的wifi列表结果 0x30F7
|
||||
gatewayGetStatus, //获取网关状态 0x30F8
|
||||
readRegisterKey, //读取注册密钥 0x30A7
|
||||
sendAuthorizationCode, //发送授权码 0x30A6
|
||||
|
||||
generalExtendedCommond, // 通用扩展指令 = 0x3030
|
||||
gecChangeAdministratorPassword, // 通用扩展指令子命令-修改管理员密码 = 2
|
||||
@ -247,16 +245,6 @@ extension ExtensionCommandType on CommandType {
|
||||
type = CommandType.gatewayGetStatus;
|
||||
}
|
||||
break;
|
||||
case 0x30A6:
|
||||
{
|
||||
type = CommandType.sendAuthorizationCode;
|
||||
}
|
||||
break;
|
||||
case 0x30A7:
|
||||
{
|
||||
type = CommandType.readRegisterKey;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
{
|
||||
type = CommandType.readStarLockStatusInfo;
|
||||
@ -365,12 +353,6 @@ 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;
|
||||
@ -510,12 +492,6 @@ extension ExtensionCommandType on CommandType {
|
||||
case 0x30A5:
|
||||
t = '设置锁当前语音包';
|
||||
break;
|
||||
case 0x30A6:
|
||||
t = '发送授权码';
|
||||
break;
|
||||
case 0x30A7:
|
||||
t = '读取注册密钥';
|
||||
break;
|
||||
default:
|
||||
t = '读星锁状态信息';
|
||||
break;
|
||||
|
||||
@ -16,12 +16,10 @@ 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';
|
||||
@ -333,18 +331,6 @@ 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:
|
||||
{
|
||||
// 子命令类型
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:star_lock/blue/entity/lock_user_no_list_entity.dart';
|
||||
@ -21,10 +22,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() {
|
||||
@ -41,7 +42,6 @@ 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,15 +139,17 @@ 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);
|
||||
@ -195,28 +197,27 @@ 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() ?? '1',
|
||||
authUserID: currentKeyInfo.senderUserId!.toString(),
|
||||
keyID: currentKeyInfo.keyId.toString(),
|
||||
userID: await Storage.getUid(),
|
||||
openMode: 1,
|
||||
@ -225,7 +226,10 @@ 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,
|
||||
@ -267,7 +271,8 @@ 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));
|
||||
@ -276,8 +281,9 @@ 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());
|
||||
|
||||
@ -6,8 +6,6 @@ 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';
|
||||
@ -1111,7 +1109,10 @@ 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,
|
||||
@ -1320,7 +1321,8 @@ 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,
|
||||
@ -1337,51 +1339,21 @@ 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);
|
||||
}
|
||||
|
||||
// 读取注册密钥
|
||||
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,
|
||||
);
|
||||
command: GatewayGetStatusCommand(lockID: lockID, userID: userID),
|
||||
isBeforeAddUser: true,
|
||||
callBack: callBack);
|
||||
}
|
||||
}
|
||||
|
||||
@ -45,229 +45,272 @@ 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);
|
||||
},
|
||||
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();
|
||||
}
|
||||
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);
|
||||
},
|
||||
)),
|
||||
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
|
||||
});
|
||||
},
|
||||
)),
|
||||
],
|
||||
icon: const Icon(
|
||||
Icons.support_agent,
|
||||
color: Colors.white,
|
||||
)),
|
||||
)
|
||||
],
|
||||
),
|
||||
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);
|
||||
},
|
||||
),
|
||||
],
|
||||
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();
|
||||
}
|
||||
},
|
||||
)),
|
||||
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(
|
||||
@ -308,7 +351,8 @@ 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),
|
||||
|
||||
@ -101,7 +101,6 @@ 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) {
|
||||
@ -150,7 +149,7 @@ class StarLockRegisterLogic extends BaseGetXController {
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() async {
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
|
||||
XSConstantMacro().getDeviceInfoData().then((Map<String, dynamic> data) {
|
||||
@ -159,7 +158,5 @@ class StarLockRegisterLogic extends BaseGetXController {
|
||||
// 适当处理错误
|
||||
AppLog.log('获取设备信息时出错: $error');
|
||||
});
|
||||
|
||||
await checkIpAction();
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,45 +31,44 @@ 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() {
|
||||
|
||||
@ -316,7 +316,6 @@ class FaceListLogic extends BaseGetXController {
|
||||
{required bool isRefresh}) async {
|
||||
if (isRefresh) {
|
||||
state.faceItemListData.clear();
|
||||
state.faceItemListData.refresh();
|
||||
pageNo = 1;
|
||||
}
|
||||
final FingerprintListDataEntity entity =
|
||||
|
||||
@ -25,9 +25,11 @@ 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);
|
||||
}
|
||||
|
||||
@ -55,10 +57,13 @@ 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);
|
||||
@ -252,16 +257,20 @@ 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!);
|
||||
@ -289,7 +298,8 @@ 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) {
|
||||
@ -327,15 +337,15 @@ class FingerprintListLogic extends BaseGetXController {
|
||||
*/
|
||||
|
||||
// 获取指纹列表
|
||||
Future<FingerprintListDataEntity> getFingerprintsListData({required bool isRefresh}) async {
|
||||
Future<FingerprintListDataEntity> getFingerprintsListData(
|
||||
{required bool isRefresh}) async {
|
||||
// 如果是下拉刷新,清空已有数据
|
||||
if (isRefresh) {
|
||||
pageNo = 1;
|
||||
// if (isRefresh) {
|
||||
state.fingerprintItemListData.clear();
|
||||
state.fingerprintItemListData.refresh();
|
||||
}
|
||||
pageNo = 1;
|
||||
|
||||
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(),
|
||||
@ -371,7 +381,10 @@ 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: () {
|
||||
@ -389,7 +402,9 @@ 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);
|
||||
});
|
||||
@ -400,7 +415,8 @@ 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) {
|
||||
@ -412,7 +428,8 @@ class FingerprintListLogic extends BaseGetXController {
|
||||
String getKeyDateType(FingerprintItemData fingerprintItemData) {
|
||||
String keyDateTypeStr = ''; // 永久:1;限时2,单次3,循环: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}';
|
||||
@ -425,10 +442,11 @@ 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!);
|
||||
}
|
||||
|
||||
@ -1,109 +1,24 @@
|
||||
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'];
|
||||
// 修改这里:直接解析为单个 ActivateInfo 对象
|
||||
data = json['data'] != null ? ActivateInfo.fromJson(json['data']) : null;
|
||||
errorMsg = json['errorMsg'];
|
||||
}
|
||||
|
||||
String? description;
|
||||
int? errorCode;
|
||||
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() : [];
|
||||
data = json['data'] != null
|
||||
? (json['data'] as List).map((item) => ActivateInfo.fromJson(item)).toList()
|
||||
: [];
|
||||
errorMsg = json['errorMsg'];
|
||||
}
|
||||
|
||||
String? description;
|
||||
int? errorCode;
|
||||
List<TppSupportInfo>? data; // 改为 List<ActivateInfo>
|
||||
List<ActivateInfo>? data; // 改为 List<ActivateInfo>
|
||||
String? errorMsg;
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
@ -120,28 +35,33 @@ class TppSupportResponse {
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'TppSupportResponse{description: $description, errorCode: $errorCode, data: $data, errorMsg: $errorMsg}';
|
||||
return 'ActivateInfoResponse{description: $description, errorCode: $errorCode, data: $data, errorMsg: $errorMsg}';
|
||||
}
|
||||
}
|
||||
|
||||
class TppSupportInfo {
|
||||
int? platform;
|
||||
class ActivateInfo {
|
||||
String? platformName;
|
||||
int? platform;
|
||||
|
||||
TppSupportInfo({
|
||||
this.platform,
|
||||
ActivateInfo({
|
||||
this.platformName,
|
||||
this.platform,
|
||||
});
|
||||
|
||||
TppSupportInfo.fromJson(dynamic json) {
|
||||
platform = json['platform'] as int? ?? -1;
|
||||
platformName = json['platform_name'] as String? ?? '';
|
||||
ActivateInfo.fromJson(dynamic json) {
|
||||
platformName = json['platformName'] ?? '';
|
||||
platform = json['platform'] ?? '';
|
||||
}
|
||||
|
||||
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}';
|
||||
}
|
||||
}
|
||||
|
||||
@ -54,26 +54,12 @@ class LockDetailLogic extends BaseGetXController {
|
||||
//节流器,用来限制开锁按钮的触发频率
|
||||
FunctionBlocker functionBlocker = FunctionBlocker(duration: const Duration(seconds: 2));
|
||||
|
||||
// 防抖Timer
|
||||
Timer? _openDoorReplyDebounceTimer;
|
||||
|
||||
// 带防抖控制的开门回复处理
|
||||
void _handleOpenDoorReplyWithDebounce(Reply reply) {
|
||||
// 取消之前的Timer
|
||||
_openDoorReplyDebounceTimer?.cancel();
|
||||
|
||||
// 设置新的Timer,200ms后执行
|
||||
_openDoorReplyDebounceTimer = Timer(const Duration(milliseconds: 1500), () {
|
||||
_replyOpenLock(reply);
|
||||
});
|
||||
}
|
||||
|
||||
// 监听设备返回的数据
|
||||
void initReplySubscription() {
|
||||
state.replySubscription = EventBusManager().eventBus!.on<Reply>().listen((Reply reply) async {
|
||||
// 开门
|
||||
if (reply is OpenDoorReply) {
|
||||
_handleOpenDoorReplyWithDebounce(reply);
|
||||
_replyOpenLock(reply);
|
||||
}
|
||||
|
||||
// 开完锁之后上传记录
|
||||
@ -157,7 +143,7 @@ class LockDetailLogic extends BaseGetXController {
|
||||
await uploadElectricQuantityRequest();
|
||||
|
||||
resetOpenDoorState();
|
||||
state.animationController?.stop();
|
||||
state.animationController!.stop();
|
||||
|
||||
//锁数据更新
|
||||
AppLog.log('开锁成功,开始同步所记录:getLockRecordLastUploadDataTime');
|
||||
@ -179,7 +165,6 @@ class LockDetailLogic extends BaseGetXController {
|
||||
}
|
||||
|
||||
_handleSynchronizeUploadLockData();
|
||||
|
||||
break;
|
||||
case 0x06:
|
||||
//无权限
|
||||
@ -354,7 +339,7 @@ class LockDetailLogic extends BaseGetXController {
|
||||
//开门指令失败
|
||||
void openDoorError() {
|
||||
resetOpenDoorState();
|
||||
state.animationController?.stop();
|
||||
state.animationController!.stop();
|
||||
blueManageDisconnect();
|
||||
}
|
||||
|
||||
@ -363,7 +348,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();
|
||||
}
|
||||
@ -658,7 +643,6 @@ 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) {
|
||||
@ -944,31 +928,28 @@ 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 上传数据获取锁密码列表
|
||||
@ -982,7 +963,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);
|
||||
@ -1086,6 +1067,7 @@ 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);
|
||||
@ -1189,6 +1171,7 @@ 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);
|
||||
@ -1342,6 +1325,7 @@ 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);
|
||||
@ -1418,6 +1402,7 @@ 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);
|
||||
@ -1494,6 +1479,7 @@ 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);
|
||||
@ -1517,8 +1503,6 @@ 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:
|
||||
|
||||
@ -34,7 +34,11 @@ 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;
|
||||
|
||||
@ -42,26 +46,29 @@ 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() {
|
||||
state.animationController = AnimationController(duration: const Duration(seconds: 1), vsync: this);
|
||||
state.animationController?.repeat();
|
||||
super.initState();
|
||||
|
||||
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();
|
||||
@ -70,7 +77,9 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
_initRefreshLockDetailInfoDataEventAction();
|
||||
logic.initReplySubscription();
|
||||
logic.initLockSetOpenOrCloseCheckInRefreshLockDetailWithAttendanceAction();
|
||||
logic.loadData(lockListInfoItemEntity: widget.lockListInfoItemEntity, isOnlyOneData: widget.isOnlyOneData);
|
||||
logic.loadData(
|
||||
lockListInfoItemEntity: widget.lockListInfoItemEntity,
|
||||
isOnlyOneData: widget.isOnlyOneData);
|
||||
}
|
||||
|
||||
@override
|
||||
@ -86,8 +95,9 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
|
||||
void _initRefreshLockDetailInfoDataEventAction() {
|
||||
// 蓝牙协议通知传输跟蓝牙之外的数据传输类不一样 eventBus
|
||||
_lockRefreshLockDetailInfoDataEvent =
|
||||
eventBus.on<RefreshLockDetailInfoDataEvent>().listen((RefreshLockDetailInfoDataEvent event) {
|
||||
_lockRefreshLockDetailInfoDataEvent = eventBus
|
||||
.on<RefreshLockDetailInfoDataEvent>()
|
||||
.listen((RefreshLockDetailInfoDataEvent event) {
|
||||
setState(() {});
|
||||
});
|
||||
}
|
||||
@ -99,12 +109,17 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
|
||||
//鑫泓佳布局
|
||||
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(() {
|
||||
@ -126,7 +141,8 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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),
|
||||
@ -145,7 +161,10 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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,
|
||||
@ -153,13 +172,17 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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()),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -178,21 +201,24 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
);
|
||||
}
|
||||
|
||||
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(
|
||||
@ -221,18 +247,22 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
);
|
||||
}
|
||||
|
||||
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(
|
||||
@ -298,10 +328,13 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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 图标,它是一个叹号
|
||||
@ -312,17 +345,24 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
],
|
||||
),
|
||||
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(
|
||||
@ -346,7 +386,8 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
if (state.openDoorBtnisUneable.value == true) {
|
||||
logic.functionBlocker.block(isNeedRealNameAuthThenOpenLock);
|
||||
logic.functionBlocker
|
||||
.block(isNeedRealNameAuthThenOpenLock);
|
||||
}
|
||||
},
|
||||
onLongPressStart: (LongPressStartDetails details) {
|
||||
@ -416,10 +457,12 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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);
|
||||
}
|
||||
@ -465,24 +508,34 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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) && // 0到30天
|
||||
(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) && // 0到30天
|
||||
(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>[
|
||||
@ -565,11 +618,16 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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(
|
||||
@ -591,17 +649,24 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
),
|
||||
),
|
||||
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(
|
||||
@ -691,10 +756,12 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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);
|
||||
}
|
||||
@ -725,7 +792,10 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -748,7 +818,8 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
|
||||
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,
|
||||
@ -763,8 +834,11 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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),
|
||||
],
|
||||
@ -786,7 +860,9 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
'网关设备英文'.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),
|
||||
],
|
||||
@ -798,14 +874,18 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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),
|
||||
),
|
||||
],
|
||||
)
|
||||
@ -813,19 +893,9 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
);
|
||||
}
|
||||
|
||||
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,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
//旋转动画
|
||||
Widget xhjBuildRotationTransition(
|
||||
{required double width, required double height}) {
|
||||
return Positioned(
|
||||
child: RotationTransition(
|
||||
//设置动画的旋转中心
|
||||
@ -837,29 +907,17 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
'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}) {
|
||||
// 如果动画控制器未初始化,则不显示动画
|
||||
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,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
//旋转动画
|
||||
Widget buildRotationTransition(
|
||||
{required double width, required double height}) {
|
||||
return Positioned(
|
||||
child: RotationTransition(
|
||||
//设置动画的旋转中心
|
||||
@ -891,7 +949,9 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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,
|
||||
),
|
||||
);
|
||||
}),
|
||||
@ -937,7 +997,8 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
// '开门器', 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);
|
||||
}));
|
||||
|
||||
@ -954,7 +1015,8 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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) {
|
||||
@ -979,7 +1041,8 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
|
||||
// 根据权限显示不同的底部按钮
|
||||
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 {
|
||||
@ -999,17 +1062,22 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
// }));
|
||||
// }
|
||||
// 操作记录
|
||||
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
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
@ -1029,22 +1097,26 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
// }
|
||||
|
||||
// 电子钥匙
|
||||
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,
|
||||
});
|
||||
@ -1053,7 +1125,8 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
|
||||
// 指纹
|
||||
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,
|
||||
});
|
||||
@ -1062,8 +1135,8 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
|
||||
// 遥控
|
||||
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,
|
||||
});
|
||||
@ -1073,7 +1146,9 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
//可视对讲门锁新增->人脸
|
||||
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,
|
||||
});
|
||||
@ -1083,7 +1158,9 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
|
||||
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,
|
||||
});
|
||||
@ -1093,7 +1170,9 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
|
||||
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,
|
||||
});
|
||||
@ -1104,7 +1183,8 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
//可视对讲门锁新增->监控
|
||||
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();
|
||||
}),
|
||||
);
|
||||
@ -1112,28 +1192,33 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
|
||||
// 授权管理员
|
||||
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,
|
||||
});
|
||||
@ -1142,10 +1227,12 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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,
|
||||
});
|
||||
}),
|
||||
);
|
||||
@ -1167,7 +1254,8 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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,
|
||||
@ -1180,7 +1268,9 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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),
|
||||
@ -1188,7 +1278,9 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
child: Text(name,
|
||||
style: TextStyle(
|
||||
fontSize: 20.sp,
|
||||
color: bottomBtnisEable ? AppColors.blackColor : AppColors.lockDetailBottomBtnUneable),
|
||||
color: bottomBtnisEable
|
||||
? AppColors.blackColor
|
||||
: AppColors.lockDetailBottomBtnUneable),
|
||||
textAlign: TextAlign.center))
|
||||
],
|
||||
),
|
||||
@ -1206,7 +1298,9 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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),
|
||||
@ -1219,7 +1313,9 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
style: TextStyle(
|
||||
fontSize: 20.sp,
|
||||
height: 1.0,
|
||||
color: bottomBtnisEable ? AppColors.blackColor : AppColors.lockDetailBottomBtnUneable),
|
||||
color: bottomBtnisEable
|
||||
? AppColors.blackColor
|
||||
: AppColors.lockDetailBottomBtnUneable),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -1251,14 +1347,18 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
|
||||
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(
|
||||
@ -1278,13 +1378,17 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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(
|
||||
@ -1292,7 +1396,8 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
),
|
||||
Text(
|
||||
getCurrentFormattedTime(),
|
||||
style: TextStyle(color: AppColors.darkGrayTextColor, fontSize: 24.sp),
|
||||
style: TextStyle(
|
||||
color: AppColors.darkGrayTextColor, fontSize: 24.sp),
|
||||
)
|
||||
],
|
||||
)),
|
||||
@ -1363,7 +1468,7 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
// }
|
||||
state.iSOpenLock.value = true;
|
||||
state.openLockBtnState.value = 1;
|
||||
state.animationController?.forward();
|
||||
state.animationController!.forward();
|
||||
AppLog.log('点击开锁');
|
||||
if (isOpenLockNeedOnline) {
|
||||
// 不需要联网
|
||||
@ -1371,7 +1476,10 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
// 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 {
|
||||
@ -1380,7 +1488,10 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
// 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();
|
||||
}
|
||||
@ -1392,7 +1503,7 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
}
|
||||
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) {
|
||||
@ -1415,10 +1526,11 @@ class _LockDetailPageState extends State<LockDetailPage> with TickerProviderStat
|
||||
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();
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
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();
|
||||
|
||||
}
|
||||
@ -1,90 +0,0 @@
|
||||
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);
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
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;
|
||||
|
||||
}
|
||||
@ -35,7 +35,10 @@ 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(
|
||||
@ -45,7 +48,8 @@ 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(
|
||||
@ -63,17 +67,22 @@ 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(
|
||||
@ -95,19 +104,28 @@ 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();
|
||||
@ -124,41 +142,22 @@ 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.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(() => Visibility(
|
||||
visible: (state.lockBasicInfo.value.lockName ?? '')
|
||||
.contains('T9A'),
|
||||
child: CommonItem(
|
||||
leftTitel: '当前网络'.tr,
|
||||
rightTitle:
|
||||
state.lockBasicInfo.value.networkInfo?.wifiName ??
|
||||
'-',
|
||||
allHeight: 70.h,
|
||||
isHaveLine: true),
|
||||
)),
|
||||
// Obx(() => CommonItem(
|
||||
// leftTitel: '位置信息'.tr,
|
||||
// // rightTitle: state.lockBasicInfo.value.address ?? "-",
|
||||
@ -191,12 +190,14 @@ 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(
|
||||
|
||||
@ -553,19 +553,6 @@ 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(
|
||||
|
||||
@ -26,7 +26,8 @@ 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);
|
||||
}
|
||||
@ -49,8 +50,10 @@ 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) {
|
||||
// 使用当前系统时间戳(秒)
|
||||
@ -59,19 +62,23 @@ class LockTimeLogic extends BaseGetXController {
|
||||
}
|
||||
|
||||
// 验证时间戳是否在合理范围内(例如:2000年到2100年之间)
|
||||
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,
|
||||
@ -94,7 +101,8 @@ 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;
|
||||
|
||||
@ -141,15 +149,19 @@ 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(
|
||||
@ -178,10 +190,13 @@ 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,
|
||||
@ -191,7 +206,8 @@ 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) {
|
||||
@ -203,7 +219,8 @@ 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) {}
|
||||
@ -211,10 +228,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();
|
||||
|
||||
@ -257,7 +257,7 @@ class SpeechLanguageSettingsLogic extends BaseGetXController {
|
||||
}
|
||||
break;
|
||||
default:
|
||||
// showToast('获取设备型号失败'.tr);
|
||||
showToast('获取设备型号失败'.tr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,317 +1,40 @@
|
||||
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();
|
||||
|
||||
// 监听设备返回的数据
|
||||
StreamSubscription<Reply>? _replySubscription;
|
||||
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}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onReady() async {
|
||||
// TODO: implement onReady
|
||||
super.onReady();
|
||||
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();
|
||||
}
|
||||
},
|
||||
);
|
||||
getActivateInfo();
|
||||
}
|
||||
|
||||
@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 (从第7个字节开始,长度为40)
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,8 @@ 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) {
|
||||
@ -45,125 +46,52 @@ class _ThirdPartyPlatformPageState extends State<ThirdPartyPlatformPage> {
|
||||
}
|
||||
|
||||
Widget _buildBody() {
|
||||
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,
|
||||
),
|
||||
),
|
||||
),
|
||||
return ListView.builder(
|
||||
itemCount: state.platFormSet.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
// 判断是否是最后一个元素(索引等于 itemCount - 1)
|
||||
final isLastItem = index == state.platFormSet.length - 1;
|
||||
|
||||
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(),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// 获取当前平台数据(假设 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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,6 @@
|
||||
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';
|
||||
|
||||
@ -13,23 +11,15 @@ 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时间
|
||||
}
|
||||
|
||||
@ -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 || dataLength == 9) {
|
||||
if (dataLength == 10) {
|
||||
// 当数据是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 || dataLength == 9) {
|
||||
if (dataLength == 10) {
|
||||
// 当数据是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 || dataLength == 9) {
|
||||
if (dataLength == 10) {
|
||||
// 当数据是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 || dataLength == 9) {
|
||||
if (dataLength == 10) {
|
||||
// 当数据是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 || dataLength == 9) {
|
||||
if (dataLength == 10) {
|
||||
// 当数据是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 || dataLength == 9) {
|
||||
if (dataLength == 10) {
|
||||
// 当数据是10的时候继续请求
|
||||
state.uploadRemoteControlPage = state.uploadRemoteControlPage + 1;
|
||||
|
||||
|
||||
@ -32,10 +32,12 @@ 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) {
|
||||
@ -44,7 +46,8 @@ 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);
|
||||
}
|
||||
});
|
||||
@ -65,9 +68,11 @@ 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)) {
|
||||
if ((reply is FactoryDataResetReply) &&
|
||||
(state.ifCurrentScreen.value == true)) {
|
||||
_replyFactoryDataResetKey(reply);
|
||||
}
|
||||
});
|
||||
@ -88,18 +93,19 @@ class LockListLogic extends BaseGetXController {
|
||||
break;
|
||||
case 0x06:
|
||||
//无权限
|
||||
final List<int> tokenData = reply.data.sublist(2, 6);
|
||||
final List<String> saveStrList = changeIntListToStringList(tokenData);
|
||||
Storage.setStringList(saveBlueToken, saveStrList);
|
||||
final List<String>? token = await Storage.getStringList(saveBlueToken);
|
||||
final List<int> getTokenList = changeStringListToIntList(token!);
|
||||
|
||||
IoSenderManage.senderFactoryDataReset(
|
||||
lockID: BlueManage().connectDeviceName,
|
||||
userID: await Storage.getUid(),
|
||||
keyID: '1',
|
||||
needAuthor: 1,
|
||||
publicKey: state.publicKey,
|
||||
privateKey: state.privateKey,
|
||||
token: tokenData,
|
||||
);
|
||||
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);
|
||||
break;
|
||||
case 0x07:
|
||||
//无权限
|
||||
@ -140,7 +146,8 @@ 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!);
|
||||
@ -153,7 +160,11 @@ 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,
|
||||
@ -161,30 +172,36 @@ 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(LockListInfoItemEntity keyInfo) {
|
||||
if (state.lockListInfoItemEntity.value.isLockOwner == 1) {
|
||||
void deleyLockLogicOfRoles() {
|
||||
if (state.lockListInfoItemEntity.isLockOwner == 1) {
|
||||
// 超级管理员必须通过连接蓝牙删除
|
||||
showTipView.showIosTipWithContentDialog('删除锁后,所有信息都会一起删除,确定删除锁吗?'.tr, () {
|
||||
// 删除锁
|
||||
AppLog.log('调用了删除锁');
|
||||
showTipView.resetGetController();
|
||||
showTipView.showTFViewAlertDialog(state.passwordTF, '请输入登录密码'.tr, '请输入登录密码'.tr, () => checkLoginPassword(keyInfo));
|
||||
showTipView.showTFViewAlertDialog(
|
||||
state.passwordTF, '请输入登录密码'.tr, '请输入登录密码'.tr, checkLoginPassword);
|
||||
});
|
||||
} else if (state.lockListInfoItemEntity.value.keyRight == 1) {
|
||||
} else if (state.lockListInfoItemEntity.keyRight == 1) {
|
||||
// 授权管理员弹框提示
|
||||
showTipView.showDeleteAdministratorIsHaveAllDataDialog('同时删除其发送的所有钥匙,钥匙删除后不能恢复'.tr, (bool a) {
|
||||
showTipView.showDeleteAdministratorIsHaveAllDataDialog(
|
||||
'同时删除其发送的所有钥匙,钥匙删除后不能恢复'.tr, (bool a) {
|
||||
// 授权管理员删除
|
||||
state.deleteAdministratorIsHaveAllData.value = a;
|
||||
deletKeyData();
|
||||
@ -196,20 +213,20 @@ class LockListLogic extends BaseGetXController {
|
||||
}
|
||||
|
||||
// 查询账户密码
|
||||
Future<void> checkLoginPassword(LockListInfoItemEntity keyInfo) async {
|
||||
Future<void> checkLoginPassword() async {
|
||||
final LockListInfoEntity entity = await ApiRepository.to.checkLoginPassword(
|
||||
password: state.passwordTF.text,
|
||||
);
|
||||
if (entity.errorCode!.codeIsSuccessful) {
|
||||
Get.back();
|
||||
factoryDataResetAction(keyInfo);
|
||||
factoryDataResetAction();
|
||||
}
|
||||
}
|
||||
|
||||
// 当是锁拥有者的时候,删除锁
|
||||
Future<void> deletLockInfoData() async {
|
||||
final LockListInfoEntity entity = await ApiRepository.to.deletOwnerLockData(
|
||||
lockId: state.lockListInfoItemEntity.value.lockId!,
|
||||
lockId: state.lockListInfoItemEntity.lockId!,
|
||||
);
|
||||
if (entity.errorCode!.codeIsSuccessful) {
|
||||
BlueManage().connectDeviceMacAddress = '';
|
||||
@ -222,9 +239,10 @@ class LockListLogic extends BaseGetXController {
|
||||
// 普通用户或者授权管理员删除钥匙
|
||||
Future<void> deletKeyData() async {
|
||||
final LockListInfoEntity entity = await ApiRepository.to.deletOwnerKeyData(
|
||||
lockId: state.lockListInfoItemEntity.value.lockId.toString(),
|
||||
keyId: state.lockListInfoItemEntity.value.keyId.toString(),
|
||||
includeUnderlings: state.deleteAdministratorIsHaveAllData.value == true ? 1 : 0);
|
||||
lockId: state.lockListInfoItemEntity.lockId.toString(),
|
||||
keyId: state.lockListInfoItemEntity.keyId.toString(),
|
||||
includeUnderlings:
|
||||
state.deleteAdministratorIsHaveAllData.value == true ? 1 : 0);
|
||||
if (entity.errorCode!.codeIsSuccessful) {
|
||||
BlueManage().connectDeviceMacAddress = '';
|
||||
SchedulerBinding.instance.addPostFrameCallback((_) {
|
||||
@ -234,7 +252,7 @@ class LockListLogic extends BaseGetXController {
|
||||
}
|
||||
|
||||
// 恢复出厂设置
|
||||
Future<void> factoryDataResetAction(LockListInfoItemEntity keyInfo) async {
|
||||
Future<void> factoryDataResetAction() async {
|
||||
showEasyLoading();
|
||||
showBlueConnetctToastTimer(
|
||||
isShowBlueConnetctToast: false,
|
||||
@ -242,23 +260,31 @@ class LockListLogic extends BaseGetXController {
|
||||
dismissEasyLoading();
|
||||
showDeletAlertTipDialog();
|
||||
});
|
||||
BlueManage().blueSendData(state.lockListInfoItemEntity.value.lockName!, (BluetoothConnectionState connectionState) async {
|
||||
BlueManage().blueSendData(state.lockListInfoItemEntity.lockName!,
|
||||
(BluetoothConnectionState connectionState) async {
|
||||
if (connectionState == BluetoothConnectionState.connected) {
|
||||
final List<int> publicKeyData = state.lockListInfoItemEntity.value.bluetooth!.publicKey!.cast<int>();
|
||||
final List<String> saveStrList = changeIntListToStringList(publicKeyData);
|
||||
final List<int> publicKeyData =
|
||||
state.lockListInfoItemEntity.bluetooth!.publicKey!.cast<int>();
|
||||
final List<String> saveStrList =
|
||||
changeIntListToStringList(publicKeyData);
|
||||
await Storage.setStringList(saveBluePublicKey, saveStrList);
|
||||
|
||||
// 私钥
|
||||
final List<int> privateKeyData = state.lockListInfoItemEntity.value.bluetooth!.privateKey!.cast<int>();
|
||||
final List<String> savePrivateKeyList = changeIntListToStringList(privateKeyData);
|
||||
final List<int> privateKeyData =
|
||||
state.lockListInfoItemEntity.bluetooth!.privateKey!.cast<int>();
|
||||
final List<String> savePrivateKeyList =
|
||||
changeIntListToStringList(privateKeyData);
|
||||
await Storage.setStringList(saveBluePrivateKey, savePrivateKeyList);
|
||||
|
||||
// signKey
|
||||
final List<int> signKeyData = state.lockListInfoItemEntity.value.bluetooth!.signKey!.cast<int>();
|
||||
final List<String> saveSignKeyList = changeIntListToStringList(signKeyData);
|
||||
final List<int> signKeyData =
|
||||
state.lockListInfoItemEntity.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(
|
||||
@ -266,13 +292,11 @@ class LockListLogic extends BaseGetXController {
|
||||
userID: await Storage.getUid(),
|
||||
keyID: '1',
|
||||
needAuthor: 1,
|
||||
publicKey: keyInfo.bluetooth?.publicKey ?? [],
|
||||
privateKey: keyInfo.bluetooth?.privateKey ?? [],
|
||||
publicKey:
|
||||
state.lockListInfoItemEntity.bluetooth!.publicKey!.cast<int>(),
|
||||
privateKey:
|
||||
state.lockListInfoItemEntity.bluetooth!.privateKey!.cast<int>(),
|
||||
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();
|
||||
@ -329,7 +353,9 @@ 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);
|
||||
});
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ 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';
|
||||
@ -15,7 +14,8 @@ 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.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,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
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,
|
||||
);
|
||||
}),
|
||||
));
|
||||
}
|
||||
|
||||
//设备多层级列表
|
||||
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,12 +91,8 @@ class _LockListPageState extends State<LockListPage> with RouteAware {
|
||||
children: <Widget>[
|
||||
SlidableAction(
|
||||
onPressed: (BuildContext context) {
|
||||
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);
|
||||
state.lockListInfoItemEntity = keyInfo;
|
||||
logic.deleyLockLogicOfRoles();
|
||||
},
|
||||
backgroundColor: Colors.red,
|
||||
foregroundColor: Colors.white,
|
||||
@ -106,46 +102,58 @@ 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),
|
||||
@ -169,7 +177,9 @@ 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),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -183,7 +193,8 @@ 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),
|
||||
],
|
||||
@ -200,7 +211,10 @@ 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)),
|
||||
),
|
||||
],
|
||||
)),
|
||||
@ -212,7 +226,8 @@ 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),
|
||||
),
|
||||
],
|
||||
)),
|
||||
@ -226,11 +241,15 @@ 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,
|
||||
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)
|
||||
),
|
||||
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)
|
||||
),
|
||||
),
|
||||
],
|
||||
)),
|
||||
@ -239,8 +258,13 @@ 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),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -262,10 +286,13 @@ class _LockListPageState extends State<LockListPage> with RouteAware {
|
||||
@override
|
||||
void dispose() {
|
||||
Get.delete<LockListLogic>();
|
||||
|
||||
/// 取消路由订阅
|
||||
AppRouteObserver().routeObserver.unsubscribe(this);
|
||||
super.dispose();
|
||||
super
|
||||
.
|
||||
dispose
|
||||
(
|
||||
);
|
||||
}
|
||||
|
||||
/// 从上级界面进入 当前界面即将出现
|
||||
|
||||
@ -7,14 +7,11 @@ import '../entity/lockListInfo_entity.dart';
|
||||
class LockListState{
|
||||
|
||||
RxBool itemStatusIsEable = false.obs; // 列表里面item是否能点击
|
||||
// 修改后
|
||||
Rx<LockListInfoItemEntity> lockListInfoItemEntity = LockListInfoItemEntity().obs; // 当前选中要删除的item
|
||||
LockListInfoItemEntity lockListInfoItemEntity = LockListInfoItemEntity(); // 当前选中要删除的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;
|
||||
|
||||
}
|
||||
@ -192,9 +192,8 @@ class _LockListXHJPageState extends State<LockListXHJPage> with RouteAware {
|
||||
children: <Widget>[
|
||||
SlidableAction(
|
||||
onPressed: (BuildContext context) {
|
||||
state.lockListInfoItemEntity.value = keyInfo;
|
||||
state.lockListInfoItemEntity.refresh();
|
||||
logic.deleyLockLogicOfRoles(keyInfo);
|
||||
state.lockListInfoItemEntity = keyInfo;
|
||||
logic.deleyLockLogicOfRoles();
|
||||
},
|
||||
backgroundColor: Colors.red,
|
||||
foregroundColor: Colors.white,
|
||||
|
||||
@ -31,7 +31,8 @@ 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);
|
||||
}
|
||||
@ -65,11 +66,15 @@ 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,
|
||||
@ -210,14 +215,19 @@ 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];
|
||||
@ -247,7 +257,8 @@ 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;
|
||||
@ -365,14 +376,16 @@ class SaveLockLogic extends BaseGetXController {
|
||||
// positionMap['address'] = state.addressInfo['address'];
|
||||
|
||||
final Map<String, dynamic> bluetooth = <String, dynamic>{};
|
||||
bluetooth['bluetoothDeviceId'] = state.lockInfo['mac'];
|
||||
bluetooth['bluetoothDeviceId'] = BlueManage().connectDeviceMacAddress;
|
||||
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;
|
||||
|
||||
@ -397,7 +410,8 @@ 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': '成功',
|
||||
});
|
||||
@ -413,7 +427,8 @@ 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}',
|
||||
});
|
||||
@ -474,18 +489,26 @@ 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)
|
||||
@ -495,12 +518,15 @@ 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)
|
||||
@ -510,7 +536,9 @@ 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));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@ -22,16 +22,17 @@ 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));
|
||||
@ -41,35 +42,19 @@ 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) {
|
||||
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}');
|
||||
state.getGatewayConfigurationStr = entity.data ?? '';
|
||||
// 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);
|
||||
@ -111,7 +96,8 @@ 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();
|
||||
@ -127,7 +113,8 @@ 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;
|
||||
@ -155,7 +142,8 @@ 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,
|
||||
@ -174,7 +162,6 @@ class GatewayConfigurationWifiLogic extends BaseGetXController {
|
||||
}
|
||||
|
||||
final NetworkInfo _networkInfo = NetworkInfo();
|
||||
|
||||
Future<String> getWifiName() async {
|
||||
String ssid = '';
|
||||
ssid = (await _networkInfo.getWifiName())!;
|
||||
|
||||
@ -305,8 +305,6 @@ abstract class Api {
|
||||
'/lockSetting/updateLockSetting'; // 设置语音包
|
||||
final String reportBuyRequestURL =
|
||||
'/service/reportBuyRequest'; // 上报增值服务购买请求
|
||||
final String getTppSupportURL =
|
||||
'/api/authCode/getTppSupport'; // 查询ttp
|
||||
final String getActivateInfoURL =
|
||||
'/api/authCode/getActivateInfo'; // 查询ttp
|
||||
'/api/authCode/getTppSupport'; // 查询ttp
|
||||
}
|
||||
|
||||
@ -10,48 +10,28 @@ 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<TppSupportResponse> getTppSupport({
|
||||
Future<ActivateInfoResponse> getActivateInfo({
|
||||
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,
|
||||
|
||||
@ -14,7 +14,8 @@ 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();
|
||||
|
||||
@ -72,7 +73,10 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle implements ScpMessageHandl
|
||||
// _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,
|
||||
@ -125,11 +129,13 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle implements ScpMessageHandl
|
||||
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(
|
||||
@ -171,7 +177,8 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle implements ScpMessageHandl
|
||||
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
|
||||
}
|
||||
|
||||
@ -18,18 +18,20 @@ 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!;
|
||||
@ -62,6 +64,8 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle implements ScpMessageHa
|
||||
_handleResponseSendExpect(
|
||||
lockPeerID: scpMessage.FromPeerId!,
|
||||
);
|
||||
// 发送预期数据
|
||||
startChartManage.startTalkExpectTimer();
|
||||
// 停止发送对讲请求
|
||||
startChartManage.stopCallRequestMessageTimer();
|
||||
// 收到应答后取消超时判断
|
||||
@ -93,7 +97,8 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle implements ScpMessageHa
|
||||
}
|
||||
|
||||
// 收到来电请求时进行本地通知
|
||||
Future<void> _showTalkRequestNotification({required String talkObjectName}) async {
|
||||
Future<void> _showTalkRequestNotification(
|
||||
{required String talkObjectName}) async {
|
||||
if (Platform.isAndroid) {
|
||||
final Map<String, dynamic> message = {
|
||||
'platform': 'all',
|
||||
@ -162,12 +167,14 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle implements ScpMessageHa
|
||||
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;
|
||||
@ -188,15 +195,18 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle implements ScpMessageHa
|
||||
if (isH264) {
|
||||
// 锁支持H264,发送H264视频和G711音频期望
|
||||
startChartManage.sendOnlyH264VideoTalkExpectData();
|
||||
print('app收到的对讲请求后,发送的预期数据=========锁支持H264,发送H264视频格式期望数据,peerID=${lockPeerID}');
|
||||
print(
|
||||
'app收到的对讲请求后,发送的预期数据=========锁支持H264,发送H264视频格式期望数据,peerID=${lockPeerID}');
|
||||
} else if (isMJpeg) {
|
||||
// 锁只支持MJPEG,发送图像视频和G711音频期望
|
||||
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}');
|
||||
}
|
||||
}
|
||||
|
||||
@ -204,12 +214,14 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle implements ScpMessageHa
|
||||
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;
|
||||
@ -230,15 +242,18 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle implements ScpMessageHa
|
||||
if (isH264) {
|
||||
// 锁支持H264,发送H264视频和G711音频期望
|
||||
startChartManage.sendH264VideoAndG711AudioTalkExpectData();
|
||||
AppLog.log('app主动发对讲请求,收到回复后发送的预期数据=======锁支持H264,发送H264视频格式期望数据,peerID=${lockPeerID}');
|
||||
AppLog.log(
|
||||
'app主动发对讲请求,收到回复后发送的预期数据=======锁支持H264,发送H264视频格式期望数据,peerID=${lockPeerID}');
|
||||
} else if (isMJpeg) {
|
||||
// 锁只支持MJPEG,发送图像视频和G711音频期望
|
||||
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}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,9 +61,12 @@ 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() {
|
||||
@ -168,7 +171,8 @@ 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);
|
||||
@ -176,14 +180,18 @@ 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 {
|
||||
@ -196,12 +204,14 @@ 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) {
|
||||
@ -229,7 +239,8 @@ 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(
|
||||
@ -280,15 +291,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(
|
||||
@ -298,13 +309,15 @@ 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,
|
||||
@ -327,21 +340,28 @@ 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);
|
||||
});
|
||||
@ -358,7 +378,8 @@ 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);
|
||||
@ -368,7 +389,8 @@ class StartChartManage {
|
||||
// 发送打洞包
|
||||
void startSendingRbcuProbeTMessages() {
|
||||
// 每隔 1 秒执行一次 _sendRbcuInfoMessage
|
||||
rbcuProbeTimer ??= Timer.periodic(Duration(seconds: _defaultIntervalTime), (timer) {
|
||||
rbcuProbeTimer ??=
|
||||
Timer.periodic(Duration(seconds: _defaultIntervalTime), (timer) {
|
||||
// 发送RbcuProbe
|
||||
_sendRbcuProbeMessage();
|
||||
});
|
||||
@ -376,7 +398,8 @@ class StartChartManage {
|
||||
|
||||
// 发送打洞确认包
|
||||
void startSendingRbcuConfirmTMessages() {
|
||||
rbcuConfirmTimer ??= Timer.periodic(Duration(seconds: _defaultIntervalTime), (timer) {
|
||||
rbcuConfirmTimer ??=
|
||||
Timer.periodic(Duration(seconds: _defaultIntervalTime), (timer) {
|
||||
// 发送RbcuProbe
|
||||
_sendRbcuConfirmMessage();
|
||||
});
|
||||
@ -435,11 +458,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}');
|
||||
|
||||
// 优先使用H264,其次是MJPEG
|
||||
if (isH264) {
|
||||
Get.toNamed(
|
||||
@ -459,8 +482,7 @@ class StartChartManage {
|
||||
Duration(
|
||||
seconds: _defaultIntervalTime,
|
||||
),
|
||||
(Timer timer) async {
|
||||
AppLog.log('发送对讲请求:${ToPeerId}');
|
||||
(Timer timer) async {
|
||||
await sendCallRequestMessage(ToPeerId: ToPeerId);
|
||||
},
|
||||
);
|
||||
@ -504,7 +526,8 @@ 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,
|
||||
@ -535,11 +558,12 @@ 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);
|
||||
},
|
||||
@ -548,7 +572,8 @@ 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();
|
||||
// 循环遍历
|
||||
@ -562,7 +587,8 @@ 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,
|
||||
@ -580,14 +606,13 @@ 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);
|
||||
@ -621,7 +646,7 @@ class StartChartManage {
|
||||
|
||||
talkRejectTimer ??= Timer.periodic(
|
||||
Duration(seconds: _defaultIntervalTime),
|
||||
(Timer timer) async {
|
||||
(Timer timer) async {
|
||||
_sendTalkRejectMessage();
|
||||
count++;
|
||||
if (count >= maxCount) {
|
||||
@ -703,7 +728,8 @@ 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,
|
||||
@ -727,7 +753,7 @@ class StartChartManage {
|
||||
void startTalkHangupMessageTimer() {
|
||||
talkHangupTimer ??= Timer.periodic(
|
||||
Duration(seconds: _defaultIntervalTime),
|
||||
(Timer timer) async {
|
||||
(Timer timer) async {
|
||||
_sendTalkHangupMessage();
|
||||
},
|
||||
);
|
||||
@ -771,7 +797,7 @@ class StartChartManage {
|
||||
Duration(
|
||||
seconds: _defaultIntervalTime,
|
||||
),
|
||||
(Timer timer) async {
|
||||
(Timer timer) async {
|
||||
// 重新发送上线消息
|
||||
await _sendOnlineMessage();
|
||||
},
|
||||
@ -799,9 +825,11 @@ 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;
|
||||
}
|
||||
|
||||
@ -818,11 +846,15 @@ 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;
|
||||
}
|
||||
}
|
||||
@ -832,7 +864,8 @@ 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']}',
|
||||
@ -842,7 +875,8 @@ 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();
|
||||
@ -852,7 +886,8 @@ class StartChartManage {
|
||||
}
|
||||
|
||||
// 保存星图中继服务器信息至缓存
|
||||
Future<void> _saveRelayInfoEntityToStorage(RelayInfoEntity relayInfoEntity) async {
|
||||
Future<void> _saveRelayInfoEntityToStorage(
|
||||
RelayInfoEntity relayInfoEntity) async {
|
||||
if (relayInfoEntity != null) {
|
||||
await Storage.saveRelayInfo(relayInfoEntity);
|
||||
}
|
||||
@ -874,7 +909,8 @@ class StartChartManage {
|
||||
);
|
||||
|
||||
// 获取本机所有ip地址和中继返回的外网地址
|
||||
final List<ListenAddrData> listenAddrDataList = await _makeListenAddrDataList();
|
||||
final List<ListenAddrData> listenAddrDataList =
|
||||
await _makeListenAddrDataList();
|
||||
|
||||
//
|
||||
final RelayServiceData relayServiceData = RelayServiceData(
|
||||
@ -941,12 +977,11 @@ 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);
|
||||
}
|
||||
}
|
||||
@ -963,7 +998,8 @@ 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;
|
||||
}
|
||||
|
||||
@ -980,7 +1016,8 @@ 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');
|
||||
}
|
||||
@ -1045,7 +1082,8 @@ 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) {
|
||||
@ -1066,7 +1104,7 @@ class StartChartManage {
|
||||
Duration(
|
||||
seconds: _defaultIntervalTime,
|
||||
),
|
||||
(Timer timer) async {
|
||||
(Timer timer) async {
|
||||
await sendTalkPingMessage(
|
||||
ToPeerId: ToPeerId,
|
||||
FromPeerId: FromPeerId,
|
||||
@ -1087,7 +1125,7 @@ class StartChartManage {
|
||||
Duration(
|
||||
seconds: _defaultIntervalTime,
|
||||
),
|
||||
(Timer timer) {
|
||||
(Timer timer) {
|
||||
// 发送期望接受消息
|
||||
sendTalkExpectMessage(
|
||||
talkExpect: _defaultTalkExpect,
|
||||
@ -1102,7 +1140,7 @@ class StartChartManage {
|
||||
Duration(
|
||||
seconds: _defaultIntervalTime,
|
||||
),
|
||||
(Timer timer) {
|
||||
(Timer timer) {
|
||||
sendTalkAcceptMessage();
|
||||
},
|
||||
);
|
||||
@ -1132,18 +1170,10 @@ class StartChartManage {
|
||||
}
|
||||
|
||||
/// 修改预期接收到的数据
|
||||
void changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer({required TalkExpectReq talkExpect}) {
|
||||
void changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(
|
||||
{required TalkExpectReq talkExpect}) {
|
||||
_defaultTalkExpect = talkExpect;
|
||||
sendTalkExpectMessage(
|
||||
talkExpect: _defaultTalkExpect,
|
||||
);
|
||||
sendTalkExpectMessage(
|
||||
talkExpect: _defaultTalkExpect,
|
||||
);
|
||||
sendTalkExpectMessage(
|
||||
talkExpect: _defaultTalkExpect,
|
||||
);
|
||||
// reStartTalkExpectMessageTimer();
|
||||
reStartTalkExpectMessageTimer();
|
||||
}
|
||||
|
||||
void reSetDefaultTalkExpect() {
|
||||
@ -1160,7 +1190,8 @@ class StartChartManage {
|
||||
videoType: [VideoTypeE.IMAGE],
|
||||
audioType: [],
|
||||
);
|
||||
changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(talkExpect: talkExpectReq);
|
||||
changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(
|
||||
talkExpect: talkExpectReq);
|
||||
}
|
||||
|
||||
/// 修改预期接收到的数据
|
||||
@ -1169,17 +1200,20 @@ 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);
|
||||
}
|
||||
|
||||
/// 发送远程开锁
|
||||
|
||||
@ -104,6 +104,10 @@ 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);
|
||||
@ -168,11 +172,15 @@ 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;
|
||||
// 先暂停帧处理定时器,防止竞态
|
||||
@ -189,7 +197,8 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
||||
// 继续往下执行
|
||||
} else {
|
||||
// 小幅度乱序,直接丢弃
|
||||
AppLog.log('检测到I帧乱序(未超过回绕阈值$dynamicThreshold),丢弃: frameSeq=$frameSeq, lastFrameSeq=$_lastFrameSeq');
|
||||
AppLog.log(
|
||||
'检测到I帧乱序(未超过回绕阈值$dynamicThreshold),丢弃: frameSeq=$frameSeq, lastFrameSeq=$_lastFrameSeq');
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -229,7 +238,8 @@ 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 {
|
||||
@ -250,17 +260,15 @@ 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;
|
||||
@ -271,19 +279,85 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
||||
return;
|
||||
}
|
||||
|
||||
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));
|
||||
// 优先查找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) {
|
||||
final minIFrame = iFrames.first;
|
||||
final minIFrameSeq = minIFrame['frameSeq'];
|
||||
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;
|
||||
final targetIndex = state.h264FrameBuffer.indexWhere(
|
||||
(f) => f['frameType'] == TalkDataH264Frame_FrameTypeE.I && f['frameSeq'] == minIFrameSeq,
|
||||
(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);
|
||||
final Map<String, dynamic>? frameMap =
|
||||
state.h264FrameBuffer.removeAt(targetIndex);
|
||||
if (frameMap == null) {
|
||||
state.isProcessingFrame = false;
|
||||
return;
|
||||
@ -293,7 +367,12 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
||||
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) {
|
||||
final ScpMessage? scpMessage = frameMap['scpMessage'];
|
||||
if (frameData == null ||
|
||||
frameType == null ||
|
||||
frameSeq == null ||
|
||||
frameSeqI == null ||
|
||||
pts == null) {
|
||||
state.isProcessingFrame = false;
|
||||
return;
|
||||
}
|
||||
@ -301,11 +380,12 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
||||
state.isProcessingFrame = false;
|
||||
return;
|
||||
}
|
||||
lastDecodedIFrameSeq = minIFrameSeq;
|
||||
// AppLog.log('送入解码器的I帧数据frameSeq:${frameSeq},frameSeqI:${frameSeqI},'
|
||||
// 'frameType:${frameType},messageId:${scpMessage!.MessageId}');
|
||||
|
||||
await VideoDecodePlugin.sendFrame(
|
||||
frameData: frameData,
|
||||
frameType: 0,
|
||||
frameType: 1,
|
||||
frameSeq: frameSeq,
|
||||
timestamp: pts,
|
||||
splitNalFromIFrame: true,
|
||||
@ -314,61 +394,8 @@ 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帧到来
|
||||
}
|
||||
|
||||
/// 停止帧处理定时器
|
||||
@ -400,7 +427,8 @@ 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);
|
||||
});
|
||||
}
|
||||
@ -409,7 +437,8 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
||||
void _playAudioFrames() {
|
||||
// 如果缓冲区为空或未达到目标大小,不进行播放
|
||||
// 音频缓冲区要求更小,以减少延迟
|
||||
if (state.audioBuffer.isEmpty || state.audioBuffer.length < audioBufferSize) {
|
||||
if (state.audioBuffer.isEmpty ||
|
||||
state.audioBuffer.length < audioBufferSize) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -417,7 +446,8 @@ 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;
|
||||
}
|
||||
@ -447,7 +477,8 @@ 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++;
|
||||
}
|
||||
@ -462,7 +493,9 @@ 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); // 0表示A-law
|
||||
// 将 PCM 数据转换为 PcmArrayInt16
|
||||
final PcmArrayInt16 fromList = PcmArrayInt16.fromList(encodedData);
|
||||
@ -637,9 +670,11 @@ 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('截图失败: 图像数据为空');
|
||||
@ -667,13 +702,15 @@ 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;
|
||||
@ -691,7 +728,8 @@ 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 = [];
|
||||
@ -718,7 +756,8 @@ 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';
|
||||
@ -738,7 +777,8 @@ 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) {
|
||||
@ -808,8 +848,10 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -845,7 +887,8 @@ class TalkViewNativeDecodeLogic extends BaseGetXController {
|
||||
}
|
||||
|
||||
/// 修改发送预期数据
|
||||
StartChartManage().changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(talkExpect: talkExpectReq);
|
||||
StartChartManage().changeTalkExpectDataTypeAndReStartTalkExpectMessageTimer(
|
||||
talkExpect: talkExpectReq);
|
||||
|
||||
// 不立即loading,继续解码旧流帧,等待frameSeq回绕检测
|
||||
// 仅重置frameSeq回绕检测标志
|
||||
|
||||
@ -29,12 +29,15 @@ 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
|
||||
@ -66,48 +69,66 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage> wit
|
||||
// 返回 false 表示禁止退出
|
||||
return false;
|
||||
},
|
||||
child: Container(
|
||||
child: SizedBox(
|
||||
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为null,优先渲染loading/占位
|
||||
if (state.isLoading.isTrue || state.textureId.value == null) {
|
||||
return Image.asset(
|
||||
'images/main/monitorBg.png',
|
||||
width: 1.sw,
|
||||
height: 1.sh,
|
||||
width: screenWidth,
|
||||
height: screenHeight,
|
||||
fit: BoxFit.cover,
|
||||
);
|
||||
} else {
|
||||
return Positioned(
|
||||
top: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
return Positioned.fill(
|
||||
child: PopScope(
|
||||
canPop: false,
|
||||
child: RepaintBoundary(
|
||||
key: state.globalKey,
|
||||
child: RotatedBox(
|
||||
// 解码器不支持硬件旋转,使用RotatedBox
|
||||
quarterTurns: startChartManage.rotateAngle ~/ 90,
|
||||
child: state.isFullScreen.isFalse
|
||||
? AspectRatio(
|
||||
aspectRatio: StartChartManage().videoWidth / StartChartManage().videoHeight,
|
||||
child: Texture(
|
||||
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(
|
||||
textureId: state.textureId.value!,
|
||||
filterQuality: FilterQuality.medium,
|
||||
),
|
||||
)
|
||||
: Texture(
|
||||
textureId: state.textureId.value!,
|
||||
filterQuality: FilterQuality.medium,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -130,14 +151,19 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage> wit
|
||||
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),
|
||||
),
|
||||
],
|
||||
);
|
||||
@ -151,7 +177,9 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage> wit
|
||||
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),
|
||||
@ -163,7 +191,9 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage> wit
|
||||
),
|
||||
),
|
||||
),
|
||||
Obx(() => state.isLoading.isTrue ? buildRotationTransition() : Container()),
|
||||
Obx(() => state.isLoading.isTrue
|
||||
? buildRotationTransition()
|
||||
: Container()),
|
||||
Obx(() => state.isLongPressing.value
|
||||
? Positioned(
|
||||
top: 80.h,
|
||||
@ -183,7 +213,8 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage> wit
|
||||
SizedBox(width: 10.w),
|
||||
Text(
|
||||
'正在说话...'.tr,
|
||||
style: TextStyle(fontSize: 20.sp, color: Colors.white),
|
||||
style: TextStyle(
|
||||
fontSize: 20.sp, color: Colors.white),
|
||||
),
|
||||
],
|
||||
),
|
||||
@ -215,8 +246,10 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage> wit
|
||||
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),
|
||||
@ -231,7 +264,11 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage> wit
|
||||
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),
|
||||
@ -256,7 +293,8 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage> wit
|
||||
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'),
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -292,8 +330,13 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage> wit
|
||||
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,
|
||||
),
|
||||
),
|
||||
@ -309,7 +352,8 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage> wit
|
||||
);
|
||||
},
|
||||
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(
|
||||
@ -333,56 +377,61 @@ class _TalkViewNativeDecodePageState extends State<TalkViewNativeDecodePage> wit
|
||||
}
|
||||
|
||||
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() {
|
||||
|
||||
@ -1,518 +0,0 @@
|
||||
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为null,优先渲染loading/占位
|
||||
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();
|
||||
}
|
||||
}
|
||||
@ -37,7 +37,8 @@ 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; //得到的音频流字节数据
|
||||
@ -62,12 +63,13 @@ 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 = false.obs; // 是否打开声音
|
||||
RxBool isOpenVoice = true.obs; // 是否打开声音
|
||||
RxBool isRecordingScreen = false.obs; // 是否录屏中
|
||||
RxBool isRecordingAudio = false.obs; // 是否录音中
|
||||
Rx<DateTime> startRecordingAudioTime = DateTime.now().obs; // 开始录音时间
|
||||
@ -79,7 +81,6 @@ class TalkViewNativeDecodeState {
|
||||
RxBool isLongPressing = false.obs; // 旋转角度(以弧度为单位)
|
||||
// 视频解码器纹理ID
|
||||
Rx<int?> textureId = Rx<int?>(null);
|
||||
|
||||
// FPS监测相关变量
|
||||
|
||||
RxInt lastFpsUpdateTime = 0.obs; // 上次FPS更新时间
|
||||
@ -106,11 +107,11 @@ class TalkViewNativeDecodeState {
|
||||
|
||||
// 帧跟踪Map,记录每个提交的帧,key为textureId_frameSeq
|
||||
Map<String, Map<String, dynamic>> frameTracker = {};
|
||||
|
||||
|
||||
// H264帧缓冲区相关
|
||||
final List<Map<String, dynamic>> h264FrameBuffer = <Map<String, dynamic>>[]; // H264帧缓冲区,存储帧数据和类型
|
||||
final int maxFrameBufferSize = 25; // 最大缓冲区大小
|
||||
final int targetFps = 25; // 目标解码帧率,只是为了快速填充native的缓冲区
|
||||
final int maxFrameBufferSize = 50; // 最大缓冲区大小
|
||||
final int targetFps = 60; // 目标解码帧率,只是为了快速填充native的缓冲区
|
||||
Timer? frameProcessTimer; // 帧处理定时器
|
||||
bool isProcessingFrame = false; // 是否正在处理帧
|
||||
int lastProcessedTimestamp = 0; // 上次处理帧的时间戳
|
||||
@ -121,8 +122,6 @@ class TalkViewNativeDecodeState {
|
||||
// 当前清晰度选项,初始为'高清'
|
||||
RxString currentQuality = '高清'.obs; // 可选:高清、标清、流畅
|
||||
|
||||
RxString currentLanguage = CurrentLocaleTool.getCurrentLocaleString().obs; // 当前选择语言
|
||||
|
||||
// 是否拉伸至全屏
|
||||
RxBool isFullScreen = false.obs;
|
||||
RxString currentLanguage =
|
||||
CurrentLocaleTool.getCurrentLocaleString().obs; // 当前选择语言
|
||||
}
|
||||
|
||||
@ -176,7 +176,7 @@ dependencies:
|
||||
url_launcher: ^6.1.10
|
||||
#蓝牙
|
||||
# flutter_reactive_ble: ^5.1.1
|
||||
flutter_blue_plus: 1.33.0
|
||||
flutter_blue_plus: 1.32.7
|
||||
#
|
||||
event_bus: ^2.0.0
|
||||
#菊花
|
||||
@ -235,7 +235,6 @@ 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
|
||||
|
||||
@ -247,7 +246,7 @@ dependencies:
|
||||
#侧滑删除
|
||||
flutter_slidable: ^3.0.1
|
||||
# audio_service: ^0.18.12
|
||||
app_settings: ^6.1.1
|
||||
app_settings: ^5.1.1
|
||||
flutter_local_notifications: ^17.0.0
|
||||
fluwx: 4.5.5
|
||||
system_settings: ^2.0.0
|
||||
@ -322,7 +321,6 @@ flutter:
|
||||
assets:
|
||||
- images/
|
||||
- images/tabbar/
|
||||
- images/other/
|
||||
- images/guide/
|
||||
- images/main/
|
||||
- images/main/addFingerprint/
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user