Merge branch 'develop_liyi' of code-internal.star-lock.cn:StarlockTeam/app-starlock into develop_liyi

# Conflicts:
#	pubspec.yaml
This commit is contained in:
“DaisyWu” 2025-01-21 15:14:22 +08:00
commit 2ee6782714
78 changed files with 1193 additions and 452 deletions

View File

@ -20,6 +20,7 @@ variables:
- if: $CI_COMMIT_BRANCH == "release"
- if: $CI_COMMIT_BRANCH =~ /feat_[a-zA-Z]+/
- if: $CI_COMMIT_BRANCH == "canary_release"
- if: $CI_COMMIT_BRANCH == "develop_liyi"
- if: $CI_COMMIT_TAG =~ /^v[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z]+\.[0-9]+)?$/
.notify_rule:

View File

@ -78,6 +78,13 @@ android {
keyAlias = 'upload'
keyPassword 'xhj8872'
}
xhj_bundle {
storeFile file("xhj_bundle.jks")
storePassword 'xhj8872'
keyAlias = 'xhj'
keyPassword 'xhj8872'
}
}
// ----- BEGIN flavorDimensions (autogenerated by flutter_flavorizr) -----
@ -135,6 +142,16 @@ android {
manifestPlaceholders.JPUSH_PKGNAME = "com.xhjcn.lock"
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules-xhj.pro'
}
xhj_bundle {
dimension "flavor-type"
applicationId "ltd.xhjcn.lock"
signingConfig signingConfigs.xhj_bundle
resValue "string", "app_name", "Star Lock"
manifestPlaceholders.JPUSH_PKGNAME = "ltd.xhjcn.lock"
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules-xhj.pro'
}
xhj_pre {
dimension "flavor-type"
applicationId "com.xhjcn.lock.pre"

View File

@ -0,0 +1,48 @@
{
"project_info": {
"project_number": "281500445726",
"project_id": "skychip2023-ecdff",
"storage_bucket": "skychip2023-ecdff.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:281500445726:android:ddf52ac7b7f83cf5c4d20f",
"android_client_info": {
"package_name": "com.skychip.lock.dev"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyC-3-ABWuy9LrYyAw_KxDRto4DanQ0sq9g"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:281500445726:android:468195b9cc68dd6cc4d20f",
"android_client_info": {
"package_name": "com.starlock.lock.local"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyC-3-ABWuy9LrYyAw_KxDRto4DanQ0sq9g"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

View File

@ -0,0 +1,48 @@
{
"project_info": {
"project_number": "281500445726",
"project_id": "skychip2023-ecdff",
"storage_bucket": "skychip2023-ecdff.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:281500445726:android:ddf52ac7b7f83cf5c4d",
"android_client_info": {
"package_name": "com.skychip.lock.pre"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyC-3-ABWuy9LrYyAw_KxDRto4DanQ"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:281500445726:android:468195b9cc68dd6cc",
"android_client_info": {
"package_name": "com.starlock.lock.local"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyC-3-ABWuy9LrYyAw_KxDRto4DanQ0sq9g"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

View File

@ -0,0 +1,48 @@
{
"project_info": {
"project_number": "28150044todo",
"project_id": "skychip2023-etodo",
"storage_bucket": "skychip2023-etodo.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:281500445726:android:ddf52ac7b7f83cf5c4todo",
"android_client_info": {
"package_name": "com.xhjcn.lock"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyC-3-ABWuy9LrYyAw_KxDRto4DanQ0todo"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:281500445726:android:468195b9cc68dd6cc4todo",
"android_client_info": {
"package_name": "com.xhjcn.lock.local"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyC-3-ABWuy9LrYyAw_KxDRto4Dank9todo"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

View File

@ -0,0 +1,48 @@
{
"project_info": {
"project_number": "28150044todo",
"project_id": "skychip2023-etodo",
"storage_bucket": "skychip2023-etodo.firebasestorage.app"
},
"client": [
{
"client_info": {
"mobilesdk_app_id": "1:281500445726:android:ddf52ac7b7f83cf5c4todo",
"android_client_info": {
"package_name": "com.xhjcn.lock"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyC-3-ABWuy9LrYyAw_KxDRto4DanQ0todo"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
},
{
"client_info": {
"mobilesdk_app_id": "1:281500445726:android:468195b9cc68dd6cc4todo",
"android_client_info": {
"package_name": "com.xhjcn.lock.local"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "AIzaSyC-3-ABWuy9LrYyAw_KxDRto4Dank9todo"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],
"configuration_version": "1"
}

BIN
android/app/xhj_bundle.jks Normal file

Binary file not shown.

View File

@ -18,7 +18,7 @@ elif [[ $ENV_BUILD_TAG =~ $regex ]]; then
echo "===build release===$ENV_BUILD_TAG"
bundle exec fastlane release_apk flavor:xhj --verbose
bundle exec fastlane release_apk flavor:sky --verbose
bundle exec fastlane release_bundle flavor:xhj --verbose
bundle exec fastlane release_bundle flavor:xhj_bundle --verbose
bundle exec fastlane release_bundle flavor:sky --verbose
elif [[ "${ENV_BUILD_BRANCH}" == "develop" ]]; then
echo "===build dev===${NEXT_VERSION}"

View File

@ -2,4 +2,5 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
#distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
distributionUrl=https://mirrors.cloud.tencent.com/gradle/gradle-7.4-all.zip

View File

@ -23,7 +23,7 @@ elif [[ "${ENV_BUILD_BRANCH}" == "develop" ]]; then
echo "===build dev===${NEXT_VERSION}"
bundle exec fastlane beta flavor:xhj env:Dev --verbose
bundle exec fastlane beta flavor:sky env:Dev --verbose
elif [[ "${ENV_BUILD_BRANCH}" == "release" ]] || [[ "${ENV_BUILD_BRANCH}" == "feat_devops" ]] ; then
elif [[ "${ENV_BUILD_BRANCH}" == "release" ]] || [[ "${ENV_BUILD_BRANCH}" == "feat_devops" ]] || [[ "${ENV_BUILD_BRANCH}" == "develop_liyi" ]] ; then
echo "===build pre===${NEXT_VERSION}"
bundle exec fastlane beta flavor:xhj env:Pre --verbose
bundle exec fastlane beta flavor:sky env:Pre --verbose

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "اتبع النظام",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "بعد إعادة الضبط ، سيتم حذف بصمات القفل. هل أنت متأكد أنك تريد إعادة ضبطه ؟",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "بعد إعادة الضبط ، سيتم حذف جهاز التحكم عن بعد للقفل. هل تريد إعادة ضبطه ؟"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "بعد إعادة الضبط ، سيتم حذف جهاز التحكم عن بعد للقفل. هل تريد إعادة ضبطه ؟",
"版本说明": "تعليمات الإصدار"
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Следете система",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "След нулиране, пръстовите отпечатъци на заключването ще бъдат изтрити. Сигурен ли сте, че искате да го нулирате?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "След нулиране, дистанционното управление на ключалката ще бъде изтрито. Искате ли да го нулирате?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "След нулиране, дистанционното управление на ключалката ще бъде изтрито. Искате ли да го нулирате?",
"版本说明": "Обяснение на версията"
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "এক্ফক্লোসিস্টেম",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "ব্যান্ডোটারট্রাসেট, thelock'sferprinতা?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "ব্যান্ডোটারপ্রাসেট, নিয়ন্ত্রণের নিয়ন্ত্রণ। ডোডো ডাইভান্টটুরে?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "ব্যান্ডোটারপ্রাসেট, নিয়ন্ত্রণের নিয়ন্ত্রণ। ডোডো ডাইভান্টটুরে?",
"版本说明": "ভার্সনপরিচিতি",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Sledovat systém",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Po resetování budou otisky prstů zámku odstraněny. Opravdu ho chcete obnovit?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Po resetu bude dálkové ovládání zámku odstraněno. Chcete ho obnovit?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Po resetu bude dálkové ovládání zámku odstraněno. Chcete ho obnovit?",
"版本说明": "Verze"
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Følg systemet",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Efter nulstilling vil låsens fingeraftryk blive slettet. Er du sikker på at du vil nulstille den?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Efter nulstilling, fjernbetjeningen af låsen vil blive slettet. Vil du nulstille den?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Efter nulstilling, fjernbetjeningen af låsen vil blive slettet. Vil du nulstille den?",
"版本说明": "Versionsbeskrivelse",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "System folgen",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Nach dem Zurücksetzen werden die Finger abdrücke des Schlosses gelöscht. Sind Sie sicher, dass Sie es zurücksetzen wollen?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Nach dem Zurücksetzen wird die Fernbedienung des Schlosses gelöscht. Willst du es zurücksetzen?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Nach dem Zurücksetzen wird die Fernbedienung des Schlosses gelöscht. Willst du es zurücksetzen?",
"版本说明": "Versionsbeschreibung"
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Ακολουθήστε το σύστημα",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Μετά την επαναφορά, τα αποτυπώματα της κλειδαριάς θα διαγραφούν. Είστε σίγουροι ότι θέλετε να το επαναφέρετε;",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Μετά την επαναφορά, το τηλεχειριστήριο της κλειδαριάς θα διαγραφεί. Θέλεις να το επαναφέρεις;"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Μετά την επαναφορά, το τηλεχειριστήριο της κλειδαριάς θα διαγραφεί. Θέλεις να το επαναφέρεις;",
"版本说明": "Περιγραφή έκδοσης",
}

View File

@ -61,7 +61,7 @@
"请输入员工账号": "Enter Employee's Account",
"批量授权锁": "Grant multiple locks",
"授权管理员拥有操作这把锁的重要权限,请确保只发给我你信任的人": "The authorized administrator will have majority permission to operate this lock.",
"功能开启后,你将可以通过网关远程开锁。此功能的开启和关闭只能在锁附近通过手机蓝牙进行。":"This feature allows you to Unlock the Smart Lock Remotely via a Gateway.This Feature can ONLY be Turned ON or OFF via Bluetooth.",
"功能开启后,你将可以通过网关远程开锁。此功能的开启和关闭只能在锁附近通过手机蓝牙进行。": "This feature allows you to Unlock the Smart Lock Remotely via a Gateway.This Feature can ONLY be Turned ON or OFF via Bluetooth.",
"排列方式": "List Type",
"早到榜": "Early List",
"迟到榜": "Late List",
@ -732,18 +732,18 @@
"请选择有效日": "Please select the effective day",
"公司名字长度不能小于 6 ": "The length of the company name cannot be less than 6",
"已是最新版本": "No updates",
"一":"One",
"二":"Two",
"三":"Three",
"四":"Four",
"五":"Five",
"六":"Six",
"日":"Sun",
"新建短信模版":"Creat SMS template",
"新建邮件模版":"Creat email template",
"自定义短信模版":"SMS template",
"自定义邮件模版":"Email template",
"名称":"Name",
"一": "One",
"二": "Two",
"三": "Three",
"四": "Four",
"五": "Five",
"六": "Six",
"日": "Sun",
"新建短信模版": "Creat SMS template",
"新建邮件模版": "Creat email template",
"自定义短信模版": "SMS template",
"自定义邮件模版": "Email template",
"名称": "Name",
"星星锁": "Star lock",
"无考勤记录": "No Records",
"大家干劲十足": "Everyone comes in time",
@ -780,20 +780,20 @@
"该已锁被删除": "The locked is deleted",
"授权管理员只能查看和管理自己下发的钥匙、密码等权限": "The authorized admin can only manage passcodes,ekeys and etccreated by himself.",
"添加授权管理员": "Create Admin",
"导出记录":"Export records",
"选择时间段":"Select time period",
"导出":"Export",
"批量导出":"Batch export",
"读取记录":"Refresh Records",
"设备":"Device",
"消息":"Messages",
"智能分析":"Intelligent analytics",
"精准识别设备事件,过滤无效信息":"Accurately identify device events and filter out invalid information",
"系统设置":"System settings",
"系统的全局配置在此项内进行设置":"The global configuration of the system is set in this item",
"导出操作记录":"Export records",
"立即查看":"View",
"导出成功":"Exported successfully",
"导出记录": "Export records",
"选择时间段": "Select time period",
"导出": "Export",
"批量导出": "Batch export",
"读取记录": "Refresh Records",
"设备": "Device",
"消息": "Messages",
"智能分析": "Intelligent analytics",
"精准识别设备事件,过滤无效信息": "Accurately identify device events and filter out invalid information",
"系统设置": "System settings",
"系统的全局配置在此项内进行设置": "The global configuration of the system is set in this item",
"导出操作记录": "Export records",
"立即查看": "View",
"导出成功": "Exported successfully",
"发送钥匙": "Send ekey",
"进度": "Rate",
"失败": "Failed",
@ -926,7 +926,7 @@
"或发生异常事件时": "or an abnormal event occurs",
"逗留达到10秒": "Stay for 10 seconds",
"约1.5米": "About 1.5 meters",
"随时":"Anytime",
"随时": "Anytime",
"立即录像": "Record immediately",
"录像时机": "Video timing",
"有人出现时录像": "Record when someone appears",
@ -1094,7 +1094,7 @@
"支持的国家": "Supported countries",
"支持的国家值": "USA, Canada, UK, Australia, India, Germany, France, Italy, Spain, Japan",
"操作流程": "Operation process",
"操作流程值":"1 Add a lock and gateway with the Smart lock APP \n\n2 Enable the remote unlocking function of the lock in the APP (this function is turned off by default). If you do not have this option, the lock does not support Alexa \n\n3 Add skills to Alexa and authorize them with the Smart lock APP's account and password. After the authorization is successful, you can discover devices under the account \n\n4 Locate the lock in the Alexa app, turn on the voice unlock function, and set the language password \n\n5 The lock can be operated through Alexa",
"操作流程值": "1 Add a lock and gateway with the Smart lock APP \n\n2 Enable the remote unlocking function of the lock in the APP (this function is turned off by default). If you do not have this option, the lock does not support Alexa \n\n3 Add skills to Alexa and authorize them with the Smart lock APP's account and password. After the authorization is successful, you can discover devices under the account \n\n4 Locate the lock in the Alexa app, turn on the voice unlock function, and set the language password \n\n5 The lock can be operated through Alexa",
"Google Home": "Google Home",
"Action name": "Action name",
"ScienerSmart": "ScienerSmart",
@ -1121,5 +1121,12 @@
"分简称": "M",
"跟随系统": "Follow system",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "After reset, the lock's fingerprints will be deleted. Are you sure you want to reset it?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "After reset, the remote control of the lock will be deleted. Do you want to reset it?"
"通话未接通,已挂断": "Call not connected, hung up",
"通话异常中断": "Abnormal call interruption",
"通话连接失败": "Call connection failed",
"已挂断": "Hanging up",
"正在说话...": "Talking now...",
"下载完成,请到相册查看": "Download completed, please go to the album to view",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "After reset, the remote control of the lock will be deleted. Do you want to reset it?",
"版本说明": "Version description"
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Seguir sistema",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Después de restablecer, se eliminarán las huellas dactilares del bloqueo. ¿Está seguro de que desea restablecerlo?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Después del reinicio, se eliminará el control remoto de la cerradura. ¿Quieres resetearlo?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Después del reinicio, se eliminará el control remoto de la cerradura. ¿Quieres resetearlo?",
"版本说明": "Instrucciones de versión"
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Süsteemi jälgimine",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Pärast lähtestamist kustutatakse luku sõrmejäljed. Kas tõesti soovid see lähtestada?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Pärast lähtestamist kustutatakse luku kaugjuhtimine. Kas sa tahad seda lähtestada?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Pärast lähtestamist kustutatakse luku kaugjuhtimine. Kas sa tahad seda lähtestada?",
"版本说明": "Versiooniteave",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Seuraa järjestelmää",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Lukon sormenjäljet poistetaan. Haluatko varmasti nollataa sen?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Nollauksen jälkeen lukon kaukosäädin poistetaan. Haluatko palauttaa sen?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Nollauksen jälkeen lukon kaukosäädin poistetaan. Haluatko palauttaa sen?",
"版本说明": "Versio",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Suivre le système",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Après réinitialisation, les empreintes digitales de la serrure seront supprimées. Êtes-vous sûr de vouloir le réinitialiser?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Après réinitialisation, la télécommande du verrou sera supprimée. Voulez-vous le réinitialiser?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Après réinitialisation, la télécommande du verrou sera supprimée. Voulez-vous le réinitialiser?",
"版本说明": "Explication de la version",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "מערכת מעקב",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "לאחר איפוס, טביעות האצבעות של המנעול יימחקו. אתה בטוח שברצונך לאפס את זה?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "לאחר איפוס, השליטה מרחוק של המנעול יימחק. אתה רוצה לאפס את זה?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "לאחר איפוס, השליטה מרחוק של המנעול יימחק. אתה רוצה לאפס את זה?",
"版本说明": "המידע על גרסה",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "跟隨系統",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "重置之後,鎖嘅指紋將被刪除。 你確定要重置它啊?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "重置之後,鎖嘅遙控器將被刪除。 是否要重置它?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "重置之後,鎖嘅遙控器將被刪除。 是否要重置它?",
"版本说明": "版本說明"
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Prati sistem:",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Nakon resetovanja, otisci brave će biti izbrisani. Jeste li sigurni da ga želite resetirati?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Nakon resetovanja, daljinski upravljač brave će biti izbrisan. Hoæeš da ga resetuješ?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Nakon resetovanja, daljinski upravljač brave će biti izbrisan. Hoæeš da ga resetuješ?",
"版本说明": "Informacije o verziji",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Follow system",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "A reset után a zár ujjlenyomatai törlődnek. Biztos vagy benne, hogy vissza szeretné állítani?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "A reset után a zár távirányítója törlődik. Szeretné visszaállítani?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "A reset után a zár távirányítója törlődik. Szeretné visszaállítani?",
"版本说明": "Versió leírás",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Sistem mengikuti",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Setelah mengulang, sidik jari kunci akan dihapus. Yakin ingin meresetnya?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Setelah reset, remote control kunci akan dihapus. Ingin mengatur ulang?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Setelah reset, remote control kunci akan dihapus. Ingin mengatur ulang?",
"版本说明": "Catatan versi",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Seguire il sistema",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Dopo il reset, le impronte digitali del lucchetto verranno cancellate. Sei sicuro di volerlo resettare?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Dopo il reset, il telecomando del lucchetto verrà eliminato. Vuoi resettarlo?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Dopo il reset, il telecomando del lucchetto verrà eliminato. Vuoi resettarlo?",
"版本说明": "Versione"
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "システムに従う",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "リセット後、ロックの指紋は削除されます。リセットしてもよろしいですか。",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "リセット後、ロックのリモコンが削除されます。リセットしますか?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "リセット後、ロックのリモコンが削除されます。リセットしますか?",
"版本说明": "バージョン説明",
}

View File

@ -1121,5 +1121,12 @@
"分简称": "分简称",
"跟随系统": "跟随系统",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "重置后,该锁的指纹都将被删除哦,确认要重置吗?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "重置后,该锁的遥控都将被删除哦,确认要重置吗?"
"通话未接通,已挂断": "通话未接通,已挂断",
"通话异常中断": "通话异常中断",
"通话连接失败": "通话连接失败",
"已挂断": "已挂断",
"正在说话...": "正在说话...",
"下载完成,请到相册查看": "下载完成,请到相册查看",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "重置后,该锁的遥控都将被删除哦,确认要重置吗?",
"版本说明": "版本说明"
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Жүйені қолдану",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Қайта ысырып тастау Шынымен ысырып тастауды қалайсыз ба?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Қайта ысырып ысырып тасымалдауын өшіріледі. Оны ысырып тастауды қалайсыз ба?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Қайта ысырып ысырып тасымалдауын өшіріледі. Оны ысырып тастауды қалайсыз ба?",
"版本说明": "Версиятын көрсету",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "시스템을 따르십시오",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "재설정 후 잠금 장치의 지문이 삭제됩니다. 당신은 그것을 재설정 하시겠습니까?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "재설정 후 잠금 장치의 리모컨이 삭제됩니다. 당신은 그것을 재설정 하시겠습니까?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "재설정 후 잠금 장치의 리모컨이 삭제됩니다. 당신은 그것을 재설정 하시겠습니까?",
"版本说明": "버전 설명",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Sekti sistema",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Po reset užrakto pirštų atspaudai bus ištrinti. Ar tikrai norite jį atkurti?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Po reset, nuotolinis valdymo pultas užraktas bus ištrintas. Ar norite jį atkurti?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Po reset, nuotolinis valdymo pultas užraktas bus ištrintas. Ar norite jį atkurti?",
"版本说明": "Versijos aprašymas",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Ikut system",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Selepas menetapkan semula, cap jari kunci akan dipadamkan. Adakah anda pasti anda mahu menetapkan semula?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Selepas menetapkan semula, kawalan jauh kunci akan dipadamkan. Adakah anda mahu menetapkan semula?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Selepas menetapkan semula, kawalan jauh kunci akan dipadamkan. Adakah anda mahu menetapkan semula?",
"版本说明": "Penerangan versi",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Systeem volgen",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Na het resetten worden de vingerafdrukken van het slot verwijderd. Weet je zeker dat je het wilt resetten?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Na het resetten wordt de afstandsbediening van het slot verwijderd. Wilt u het resetten?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Na het resetten wordt de afstandsbediening van het slot verwijderd. Wilt u het resetten?",
"版本说明": "Versieomschrijving",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Śledź system",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Po zresetowaniu odciski palców zamka zostaną usunięte. Czy na pewno chcesz go zresetować?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Po zresetowaniu zdalne sterowanie zamkiem zostanie usunięte. Czy chcesz go zresetować?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Po zresetowaniu zdalne sterowanie zamkiem zostanie usunięte. Czy chcesz go zresetować?",
"版本说明": "Wersja",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Siga o sistema",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Após a redefinição, as impressões digitais do bloqueio serão apagadas. Tens a certeza que queres redefini-lo?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Após a reinicialização, o controle remoto do bloqueio será excluído. Você quer redefini-lo?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Após a reinicialização, o controle remoto do bloqueio será excluído. Você quer redefini-lo?",
"版本说明": "Descrição da versão",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Urmează sistemul:",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "După resetare, amprentele încuietorii vor fi şterse. Sigur doriți să-l resetați?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "După resetare, telecomanda de blocare va fi ştersă. Vrei să-l resetezi?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "După resetare, telecomanda de blocare va fi ştersă. Vrei să-l resetezi?",
"版本说明": "Descrierea versiunii",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Следуйте системе",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "После сброса отпечатки пальцев замка будут удалены. Вы уверены, что хотите его перезагрузить?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "После сброса пульт дистанционного управления замком будет удален. А вы хотите его сбросить?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "После сброса пульт дистанционного управления замком будет удален. А вы хотите его сбросить?",
"版本说明": "Объяснение версии",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Sledovať systém",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Po resetovaní budú prstové odtlačky zámku vymazané. Ste si istí, že chcete obnoviť?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Po resetovaní bude diaľkové ovládanie zámku vymazané. Chcete ho obnoviť?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Po resetovaní bude diaľkové ovládanie zámku vymazané. Chcete ho obnoviť?",
"版本说明": "Popis verzie"
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Пратите систем",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Након ресетовања, отисци прстију браве ће бити избрисани. Да ли сте сигурни да желите да га ресетујете?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Након ресетовања, даљински управљач браве ће бити избрисан. Да ли желите да га ресетујете?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Након ресетовања, даљински управљач браве ће бити избрисан. Да ли желите да га ресетујете?",
"版本说明": "Опис верзије"
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Följ system",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Efter återställd, kommer låsets fingeravtryck tas bort. Är du säker på att du vill återställa den?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Efter återställd, fjärrkontrollen på låset tas bort. Vill du återställa den?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Efter återställd, fjärrkontrollen på låset tas bort. Vill du återställa den?",
"版本说明": "Versionsbeskrivning"
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "ระบบติดตามผล",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "หลังจากรีเซ็ตแล้วลายนิ้วมือของล็อคจะถูกลบออกคุณแน่ใจว่าอยากจะรีเซ็ต?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "หลังจากรีเซ็ตแล้วรีโมทคอนโทรลของล็อคจะถูกลบออกคุณต้องการรีเซ็ตไหม"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "หลังจากรีเซ็ตแล้วรีโมทคอนโทรลของล็อคจะถูกลบออกคุณต้องการรีเซ็ตไหม",
"版本说明": "คำอธิบายรุ่น"
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Sistemi takip et",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Sıfırlamadan sonra, kilidin parmak izleri silinecektir. Sıfırlamak istediğine emin misin?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Sıfırladıktan sonra, kilidin uzaktan kumandası silinecektir. Sıfırlamak ister misin?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Sıfırladıktan sonra, kilidin uzaktan kumandası silinecektir. Sıfırlamak ister misin?",
"版本说明": "Sürüm açıklaması",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "跟蹤系統",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "重置後,鎖的指紋將被刪除。 是否確實要重置它?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "重置後,鎖的遙控器將被刪除。 是否要重置它?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "重置後,鎖的遙控器將被刪除。 是否要重置它?",
"版本说明": "版本說明",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Система стеження за",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Після скидання відбитки пальців замка будуть видалені. Ви впевнені, що хочете скинути налаштування?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Після скидання пульт дистанційного керування замком буде видалено. Хочете його скинути?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Після скидання пульт дистанційного керування замком буде видалено. Хочете його скинути?",
"版本说明": "Опис версії",
}

View File

@ -1120,5 +1120,6 @@
"分简称": "M",
"跟随系统": "Hệ thống theo dõi",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "Sau khi đặt lại, dấu vân tay của khóa sẽ bị xóa. Bạn có chắc chắn muốn thiết lập lại nó?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Sau khi đặt lại, điều khiển từ xa của khóa sẽ bị xóa. Bạn có muốn đặt lại không?"
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "Sau khi đặt lại, điều khiển từ xa của khóa sẽ bị xóa. Bạn có muốn đặt lại không?",
"版本说明": "Thông tin phiên bản"
}

View File

@ -1121,5 +1121,12 @@
"分简称": "分",
"跟随系统": "跟随系统",
"重置后,该锁的指纹都将被删除哦,确认要重置吗?": "重置后,该锁的指纹都将被删除哦,确认要重置吗?",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "重置后,该锁的遥控都将被删除哦,确认要重置吗?"
"通话未接通,已挂断": "通话未接通,已挂断",
"通话异常中断": "通话异常中断",
"通话连接失败": "通话连接失败",
"已挂断": "已挂断",
"正在说话...": "正在说话...",
"下载完成,请到相册查看": "下载完成,请到相册查看",
"重置后,该锁的遥控都将被删除哦,确认要重置吗?": "重置后,该锁的遥控都将被删除哦,确认要重置吗?",
"版本说明": "版本说明"
}

View File

@ -199,28 +199,28 @@ class F {
switch (appFlavor) {
case Flavor.local:
return const UmengKey(
androidKey: '671244cf80464b33f6df9648',
iosKey: '671244ae80464b33f6df9646',
androidKey: 'local-671244cf80464b33f6df9648',
iosKey: 'local-671244ae80464b33f6df9646',
channel: 'Product');
case Flavor.dev:
return const UmengKey(
androidKey: '671244cf80464b33f6df9648',
iosKey: '671244ae80464b33f6df9646',
androidKey: 'dev-671244cf80464b33f6df9648',
iosKey: 'dev-671244ae80464b33f6df9646',
channel: 'Product');
case Flavor.pre:
return const UmengKey(
androidKey: '671244cf80464b33f6df9648',
iosKey: '671244ae80464b33f6df9646',
androidKey: 'pre-671244cf80464b33f6df9648',
iosKey: 'pre-671244ae80464b33f6df9646',
channel: 'Product');
case Flavor.sky_dev:
return const UmengKey(
androidKey: '671244cf80464b33f6df9648',
iosKey: '671244ae80464b33f6df9646',
androidKey: 'sky_dev-671244cf80464b33f6df9648',
iosKey: 'sky_dev-671244ae80464b33f6df9646',
channel: 'Product');
case Flavor.sky_pre:
return const UmengKey(
androidKey: '671244cf80464b33f6df9648',
iosKey: '671244ae80464b33f6df9646',
androidKey: 'sky_pre-671244cf80464b33f6df9648',
iosKey: 'sky_pre-671244ae80464b33f6df9646',
channel: 'Product');
case Flavor.sky:
return const UmengKey(
@ -229,13 +229,13 @@ class F {
channel: 'Product');
case Flavor.xhj_dev:
return const UmengKey(
androidKey: '671244cf80464b33f6df9648',
iosKey: '671244ae80464b33f6df9646',
androidKey: 'xhj_dev-671244cf80464b33f6df9648',
iosKey: 'xhj_dev-671244ae80464b33f6df9646',
channel: 'Product');
case Flavor.xhj_pre:
return const UmengKey(
androidKey: '671244cf80464b33f6df9648',
iosKey: '671244ae80464b33f6df9646',
androidKey: 'xhj_pre-671244cf80464b33f6df9648',
iosKey: 'xhj_pre-671244ae80464b33f6df9646',
channel: 'Product');
case Flavor.xhj:
return const UmengKey(
@ -244,8 +244,8 @@ class F {
channel: 'Product');
default:
return const UmengKey(
androidKey: '671244cf80464b33f6df9648',
iosKey: '671244ae80464b33f6df9646',
androidKey: 'default-671244cf80464b33f6df9648',
iosKey: 'default-671244ae80464b33f6df9646',
channel: 'Product');
}
}

View File

@ -29,10 +29,12 @@ class Data {
Data.fromJson(Map<String, dynamic> json) {
wechatServiceUrl = json['wechat_service_url'];
appSiteUrl = json['app_site_url'];
appVersionHistoryUrl = json['appVersionHistoryUrl'];
}
String? wechatServiceUrl;
String? appSiteUrl;
String? appVersionHistoryUrl;
Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};

View File

@ -9,7 +9,7 @@ import 'package:star_lock/main/lockDetail/doorLockLog/doorLockLog_state.dart';
import 'package:star_lock/main/lockDetail/doorLockLog/exportRecordDialog/exportRecordDialog_page.dart';
import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart';
import 'package:star_lock/main/lockDetail/videoLog/widget/full_screenImage_page.dart';
import 'package:star_lock/main/lockDetail/videoLog/widget/video_thumbnail.dart';
import 'package:star_lock/main/lockDetail/videoLog/widget/video_thumbnail_image.dart';
import 'package:star_lock/tools/EasyRefreshTool.dart';
import 'package:star_lock/tools/advancedCalendar/src/widget.dart';
import 'package:star_lock/tools/commonDataManage.dart';
@ -399,13 +399,27 @@ class _DoorLockLogPageState extends State<DoorLockLogPage> with RouteAware {
}
_buildVideoItem(RecordListData recordData) {
return VideoThumbnail(videoUrl: recordData.videoUrl!);
return VideoThumbnailImage(videoUrl: recordData.videoUrl!);
}
_buildImageItem(RecordListData recordData) {
return Image.network(
recordData.imagesUrl!,
fit: BoxFit.cover,
return RotatedBox(
quarterTurns: -1,
child: Image.network(
recordData.imagesUrl!,
fit: BoxFit.cover,
errorBuilder:
(BuildContext context, Object error, StackTrace? stackTrace) {
//
return RotatedBox(
quarterTurns: -1,
child: Image.asset(
'images/icon_unHaveData.png', //
fit: BoxFit.cover,
),
);
},
),
);
}

View File

@ -4,6 +4,7 @@ import 'dart:io';
import 'dart:typed_data';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart';
import 'package:network_info_plus/network_info_plus.dart';
import 'package:permission_handler/permission_handler.dart';
@ -151,11 +152,6 @@ class ConfiguringWifiLogic extends BaseGetXController {
}
state.sureBtnState.value = 1;
showBlueConnetctToastTimer(action: () {
dismissEasyLoading();
state.sureBtnState.value = 0;
});
final GetGatewayConfigurationEntity entity =
await ApiRepository.to.getGatewayConfiguration(timeout: 60);
if (entity.errorCode!.codeIsSuccessful) {
@ -191,6 +187,7 @@ class ConfiguringWifiLogic extends BaseGetXController {
//
state.getGatewayConfigurationStr = "{\"userPeerld\": \"$appPeerId\"}";
}
BlueManage().blueSendData(BlueManage().connectDeviceName,
(BluetoothConnectionState connectionState) async {
if (connectionState == BluetoothConnectionState.connected) {
@ -199,11 +196,7 @@ class ConfiguringWifiLogic extends BaseGetXController {
password: state.wifiPWDController.text,
gatewayConfigurationStr: state.getGatewayConfigurationStr,
);
} else if (connectionState == BluetoothConnectionState.disconnected) {
state.sureBtnState.value = 0;
if (state.ifCurrentScreen.value == true) {
showBlueConnetctToast();
}
EasyLoading.show();
}
}, isAddEquipment: true);
}

View File

@ -1,4 +1,10 @@
import 'dart:io';
import 'package:dio/dio.dart';
import 'package:get/get.dart';
import 'package:image_gallery_saver/image_gallery_saver.dart';
import 'package:path_provider/path_provider.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart';
import 'package:star_lock/network/api_repository.dart';
import 'package:star_lock/tools/baseGetXController.dart';
@ -9,18 +15,20 @@ class EditVideoLogLogic extends BaseGetXController {
EditVideoLogState state = EditVideoLogState();
Future<void> deleteLockCloudStorageList() async {
final VersionUndateEntity entity = await ApiRepository.to.deleteLockCloudStorageList(
final VersionUndateEntity entity =
await ApiRepository.to.deleteLockCloudStorageList(
recordIds: state.selectVideoLogList.value.map((e) => e.recordId).toList(),
);
if (entity.errorCode!.codeIsSuccessful) {
state.selectVideoLogList.value.clear();
showToast('删除成功'.tr);
getLockCloudStorageList();
await getLockCloudStorageList();
state.selectVideoLogList.clear();
}
}
Future<void> getLockCloudStorageList() async {
final VideoLogEntity entity = await ApiRepository.to.getLockCloudStorageList(
final VideoLogEntity entity =
await ApiRepository.to.getLockCloudStorageList(
lockId: state.getLockId.value,
);
if (entity.errorCode!.codeIsSuccessful) {
@ -28,4 +36,59 @@ class EditVideoLogLogic extends BaseGetXController {
state.videoLogList.refresh();
}
}
Future<void> downloadAndSaveToGallery(String url, String fileName) async {
try {
//
if (await _requestPermission()) {
final dio = Dio();
final directory = await getTemporaryDirectory();
final filePath = '${directory.path}/$fileName';
//
await dio.download(
url,
filePath,
onReceiveProgress: (received, total) {
if (total != -1) {
final progress = (received / total) * 100;
print('下载进度: $progress%');
}
},
);
//
if (fileName.endsWith('.jpg') || fileName.endsWith('.png')) {
//
final result = await ImageGallerySaver.saveFile(filePath);
if (result['isSuccess']) {
print('图片已保存到图库');
} else {
print('图片保存失败');
}
} else if (fileName.endsWith('.mp4')) {
//
final result = await ImageGallerySaver.saveFile(filePath);
if (result['isSuccess']) {
print('视频已保存到图库');
} else {
print('视频保存失败');
}
}
//
await File(filePath).delete();
} else {
print('存储权限被拒绝');
}
} catch (e) {
print('下载或保存文件失败: $e');
}
}
//
Future<bool> _requestPermission() async {
final status = await Permission.storage.request();
return status.isGranted;
}
}

View File

@ -1,10 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
import 'package:star_lock/appRouters.dart';
import 'package:star_lock/main/lockDetail/videoLog/editVideoLog/editVideoLog_state.dart';
import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart';
import 'package:star_lock/main/lockDetail/videoLog/widget/full_screenImage_page.dart';
import 'package:star_lock/main/lockDetail/videoLog/widget/video_thumbnail_image.dart';
import 'package:star_lock/tools/dateTool.dart';
import '../../../../app_settings/app_colors.dart';
@ -97,6 +99,7 @@ class _EditVideoLogPageState extends State<EditVideoLogPage> {
double itemW = (1.sw - 15.w * 4) / 3;
double itemH = (1.sw - 15.w * 4) / 3 + 40.h;
Widget mainListView(int index, CloudStorageData itemData) {
return GridView.builder(
padding: EdgeInsets.only(left: 15.w, right: 15.w),
@ -114,116 +117,269 @@ class _EditVideoLogPageState extends State<EditVideoLogPage> {
childAspectRatio: itemW / itemH),
itemBuilder: (BuildContext context, int index) {
final RecordListData recordData = itemData.recordList![index];
return videoItem(recordData, index);
return videoItem(recordData);
},
);
}
Widget videoItem(RecordListData recordData, int index) {
return Container(
width: itemW,
height: itemH,
color: Colors.white,
child: GestureDetector(
onTap: () {
recordData.isSelect = !recordData.isSelect!;
if (recordData.isSelect! == true) {
state.selectVideoLogList.add(recordData);
} else {
state.selectVideoLogList.remove(recordData);
}
setState(() {});
},
child: Stack(
children: <Widget>[
Column(
children: <Widget>[
Container(
width: itemW,
height: itemW,
margin: const EdgeInsets.all(0),
color: Colors.white,
child: ClipRRect(
borderRadius: BorderRadius.circular(10.w),
child: Image(
fit: BoxFit.cover,
image: Image.network(recordData.imagesUrl ??
'images/icon_video_placeholder.jpg')
.image),
),
),
SizedBox(height: 5.h),
Text(
DateTool()
.dateToYMDHNString(recordData.operateDate.toString()),
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18.sp))
],
),
Positioned(
top: 0.w,
right: 0.w,
child: Image(
width: 36.w,
height: 36.w,
image: state.selectVideoLogList.value.contains(recordData)
? const AssetImage('images/icon_round_select.png')
: const AssetImage('images/icon_round_unSelect.png')))
],
)),
);
}
// Widget videoItem(RecordListData recordData, int index) {
// return Container(
// width: itemW,
// height: itemH,
// color: Colors.white,
// child: GestureDetector(
// onTap: () {
// recordData.isSelect = !recordData.isSelect!;
// if (recordData.isSelect! == true) {
// state.selectVideoLogList.add(recordData);
// } else {
// state.selectVideoLogList.remove(recordData);
// }
// setState(() {});
// },
// child: Stack(
// children: <Widget>[
// Column(
// children: <Widget>[
// Container(
// width: itemW,
// height: itemW,
// margin: const EdgeInsets.all(0),
// color: Colors.white,
// child: ClipRRect(
// borderRadius: BorderRadius.circular(10.w),
// child: Image(
// fit: BoxFit.cover,
// image: Image.network(recordData.imagesUrl ??
// 'images/icon_video_placeholder.jpg')
// .image),
// ),
// ),
// SizedBox(height: 5.h),
// Text(
// DateTool()
// .dateToYMDHNString(recordData.operateDate.toString()),
// textAlign: TextAlign.center,
// style: TextStyle(fontSize: 18.sp))
// ],
// ),
// Positioned(
// top: 0.w,
// right: 0.w,
// child: Image(
// width: 36.w,
// height: 36.w,
// image: state.selectVideoLogList.value.contains(recordData)
// ? const AssetImage('images/icon_round_select.png')
// : const AssetImage('images/icon_round_unSelect.png')))
// ],
// )),
// );
// }
Widget bottomBottomBtnWidget() {
return SizedBox(
width: 1.sw,
child: Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
child:
Row(mainAxisAlignment: MainAxisAlignment.center, children: <Widget>[
bottomBtnItemWidget(
'images/main/icon_lockDetail_monitoringDownloadVideo.png',
'下载'.tr,
Colors.white, () {
if (state.selectVideoLogList.value.isNotEmpty) {
Get.toNamed(Routers.videoLogDownLoadPage, arguments: <String, List<RecordListData>>{
'downloadVideoLogList': state.selectVideoLogList.value
});
} else {
logic.showToast('请选择要下载的视频');
}
}),
'images/main/icon_lockDetail_monitoringDownloadVideo.png',
'下载'.tr,
Colors.white,
_onDownLoadClick,
),
SizedBox(width: 100.w),
bottomBtnItemWidget(
'images/main/icon_lockDetail_monitoringDeletVideo.png',
'删除'.tr,
AppColors.mainColor, () {
if (state.selectVideoLogList.value.isNotEmpty) {
logic.deleteLockCloudStorageList();
} else {
logic.showToast('请选择要删除的视频'.tr);
}
})
'images/main/icon_lockDetail_monitoringDeletVideo.png',
'删除'.tr,
AppColors.mainColor,
_onDelClick,
)
]),
);
}
Future<void> _onDownLoadClick() async {
if (state.selectVideoLogList.value.isNotEmpty) {
double _progress = 0.0;
//
//
EasyLoading.showProgress(_progress, status: '加载数据中'.tr);
//
for (int i = 0; i <= state.selectVideoLogList.length - 1; i++) {
final item = state.selectVideoLogList.value[i];
// imagesUrl
if (item.imagesUrl != null && item.imagesUrl!.isNotEmpty) {
await logic.downloadAndSaveToGallery(item.imagesUrl!, 'image_$i.jpg');
}
// videoUrl
if (item.videoUrl != null && item.videoUrl!.isNotEmpty) {
await logic.downloadAndSaveToGallery(item.videoUrl!, 'video_$i.mp4');
}
//
_progress = (i + 1) / state.selectVideoLogList.length;
EasyLoading.showProgress(_progress, status: '加载数据中'.tr);
}
//
EasyLoading.dismiss();
EasyLoading.showSuccess('下载完成,请到相册查看'.tr);
state.selectVideoLogList.clear();
setState(() {});
// Get.toNamed(Routers.videoLogDownLoadPage,
// arguments: <String, List<RecordListData>>{
// 'downloadVideoLogList': state.selectVideoLogList.value
// });
} else {
logic.showToast('请选择要下载的视频');
}
}
Future<void> _onDelClick() async {
if (state.selectVideoLogList.value.isNotEmpty) {
await logic.deleteLockCloudStorageList();
setState(() {});
} else {
logic.showToast('请选择要删除的视频'.tr);
}
}
Widget bottomBtnItemWidget(
String iconUrl, String name, Color backgroundColor, Function() onClick) {
String iconUrl,
String name,
Color backgroundColor,
Future<void> Function() onClick,
) {
final double wh = 40.w;
return GestureDetector(
onTap: onClick,
child: SizedBox(
height: 140.h,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(height: 30.w),
Image.asset(iconUrl, width: wh, height: wh, fit: BoxFit.fitWidth),
SizedBox(height: 10.w),
Expanded(
child: Text(name,
style: TextStyle(fontSize: 22.sp),
textAlign: TextAlign.center))
],
)),
height: 140.h,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: <Widget>[
SizedBox(height: 30.w),
Image.asset(iconUrl, width: wh, height: wh, fit: BoxFit.fitWidth),
SizedBox(height: 10.w),
Expanded(
child: Text(name,
style: TextStyle(fontSize: 22.sp),
textAlign: TextAlign.center),
)
],
),
),
);
}
Widget videoItem(RecordListData recordData) {
return GestureDetector(
onTap: () {
if (recordData.videoUrl != null && recordData.videoUrl!.isNotEmpty) {
Get.toNamed(Routers.videoLogDetailPage, arguments: <String, Object>{
'recordData': recordData,
'videoDataList': state.videoLogList.value
});
} else if (recordData.imagesUrl != null &&
recordData.imagesUrl!.isNotEmpty) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => FullScreenImagePage(
imageUrl: recordData.imagesUrl!,
),
),
);
}
},
child: Stack(
children: [
SizedBox(
width: itemW,
height: itemH,
child: Column(
children: <Widget>[
Container(
width: itemW,
height: itemW,
margin: const EdgeInsets.all(0),
color: Colors.white,
child: ClipRRect(
borderRadius: BorderRadius.circular(10.w),
child: _buildImageOrVideoItem(recordData),
),
),
SizedBox(height: 5.h),
Text(
DateTool()
.dateToYMDHNString(recordData.operateDate.toString()),
textAlign: TextAlign.center,
style: TextStyle(fontSize: 18.sp),
)
],
),
),
Positioned(
top: 0.w,
right: 0.w,
child: GestureDetector(
onTap: () {
recordData.isSelect = !recordData.isSelect!;
if (recordData.isSelect! == true) {
state.selectVideoLogList.add(recordData);
} else {
state.selectVideoLogList.remove(recordData);
}
setState(() {});
},
child: Image(
width: 36.w,
height: 36.w,
image: state.selectVideoLogList.value.contains(recordData)
? const AssetImage('images/icon_round_select.png')
: const AssetImage('images/icon_round_unSelect.png'),
),
),
)
],
),
);
}
_buildImageOrVideoItem(RecordListData recordData) {
if (recordData.videoUrl != null && recordData.videoUrl!.isNotEmpty) {
return _buildVideoItem(recordData);
} else {
return _buildImageItem(recordData);
}
}
_buildVideoItem(RecordListData recordData) {
return VideoThumbnailImage(videoUrl: recordData.videoUrl!);
}
_buildImageItem(RecordListData recordData) {
return RotatedBox(
quarterTurns: -1,
child: Image.network(
recordData.imagesUrl!,
fit: BoxFit.cover,
errorBuilder:
(BuildContext context, Object error, StackTrace? stackTrace) {
//
return RotatedBox(
quarterTurns: -1,
child: Image.asset(
'images/icon_unHaveData.png', //
fit: BoxFit.cover,
),
);
},
),
);
}
}

View File

@ -1,3 +1,6 @@
import 'dart:convert';
import 'dart:typed_data';
class VideoLogEntity {
int? errorCode;
String? description;
@ -61,21 +64,28 @@ class RecordListData {
int? operateDate;
String? imagesUrl;
String? videoUrl;
Uint8List? thumbnailData; // Uint8List
int? recordType;
bool? isSelect = false;
RecordListData(
{this.recordId,
this.operateDate,
this.imagesUrl,
this.videoUrl,
this.recordType});
RecordListData({
this.recordId,
this.operateDate,
this.imagesUrl,
this.videoUrl,
this.thumbnailData, //
this.recordType,
});
RecordListData.fromJson(Map<String, dynamic> json) {
recordId = json['recordId'];
operateDate = json['operateDate'];
imagesUrl = json['imagesUrl'];
videoUrl = json['videoUrl'];
// JSON base64 Uint8List
if (json['thumbnailData'] != null) {
thumbnailData = base64Decode(json['thumbnailData']);
}
recordType = json['recordType'];
}
@ -85,6 +95,10 @@ class RecordListData {
data['operateDate'] = operateDate;
data['imagesUrl'] = imagesUrl;
data['videoUrl'] = videoUrl;
// Uint8List base64
if (thumbnailData != null) {
data['thumbnailData'] = base64Encode(thumbnailData!);
}
data['recordType'] = recordType;
return data;
}

View File

@ -1,5 +1,6 @@
import 'dart:typed_data';
import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart';
import 'package:star_lock/network/api_repository.dart';
import 'package:star_lock/tools/baseGetXController.dart';
@ -14,6 +15,15 @@ class VideoLogLogic extends BaseGetXController {
);
if (entity.errorCode!.codeIsSuccessful) {
state.videoLogList.value = entity.data!;
state.videoLogList.value.forEach((element) {
// imagesUrl videoUrl null
element.recordList = element.recordList!
.where((record) =>
(record.imagesUrl != null && record.imagesUrl!.isNotEmpty) ||
(record.videoUrl != null && record.videoUrl!.isNotEmpty))
.toList();
});
state.videoLogList.refresh();
}
}

View File

@ -6,7 +6,7 @@ import 'package:star_lock/flavors.dart';
import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart';
import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_state.dart';
import 'package:star_lock/main/lockDetail/videoLog/widget/full_screenImage_page.dart';
import 'package:star_lock/main/lockDetail/videoLog/widget/video_thumbnail.dart';
import 'package:star_lock/main/lockDetail/videoLog/widget/video_thumbnail_image.dart';
import 'package:star_lock/tools/dateTool.dart';
import 'package:star_lock/tools/noData.dart';
import 'package:video_player/video_player.dart';
@ -228,15 +228,18 @@ class _VideoLogPageState extends State<VideoLogPage> {
child: Row(
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Text(state.isNavLocal.value == true ? '已下载'.tr : '全部视频'.tr,
style: TextStyle(fontSize: 26.sp, fontWeight: FontWeight.w500)),
Text(
state.isNavLocal.value == true ? '已下载'.tr : '全部视频'.tr,
style: TextStyle(fontSize: 26.sp, fontWeight: FontWeight.w500),
),
Expanded(child: SizedBox(width: 10.w)),
IconButton(
icon: Image(
width: 40.w,
height: 40.w,
image: const AssetImage(
'images/main/icon_lockDetail_monitoringEditVoice.png')),
width: 40.w,
height: 40.w,
image: const AssetImage(
'images/main/icon_lockDetail_monitoringEditVoice.png'),
),
iconSize: 30,
color: Colors.black54,
onPressed: () {
@ -338,13 +341,27 @@ class _VideoLogPageState extends State<VideoLogPage> {
}
_buildVideoItem(RecordListData recordData) {
return VideoThumbnail(videoUrl: recordData.videoUrl!);
return VideoThumbnailImage(videoUrl: recordData.videoUrl!);
}
_buildImageItem(RecordListData recordData) {
return Image.network(
recordData.imagesUrl!,
fit: BoxFit.cover,
return RotatedBox(
quarterTurns: -1,
child: Image.network(
recordData.imagesUrl!,
fit: BoxFit.cover,
errorBuilder:
(BuildContext context, Object error, StackTrace? stackTrace) {
//
return RotatedBox(
quarterTurns: -1,
child: Image.asset(
'images/icon_unHaveData.png', //
fit: BoxFit.cover,
),
);
},
),
);
}
}

View File

@ -148,9 +148,10 @@ class _ControlsOverlayState extends State<ControlsOverlay> {
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
DateTool().dateToYMDHNString(
widget.recordData.operateDate.toString()),
style: TextStyle(color: Colors.white, fontSize: 20.sp)),
DateTool().dateToYMDHNString(
widget.recordData.operateDate.toString()),
style: TextStyle(color: Colors.white, fontSize: 20.sp),
),
// Expanded(child: SizedBox(width: 10.w)),
// Container(
// width: 50.w,

View File

@ -1,5 +1,6 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
@ -7,7 +8,7 @@ import 'package:star_lock/main/lockDetail/videoLog/videoLog/videoLog_entity.dart
import 'package:star_lock/main/lockDetail/videoLog/videoLogDetail/controlsOverlay_page.dart';
import 'package:star_lock/main/lockDetail/videoLog/videoLogDetail/videoLogDetail_state.dart';
import 'package:star_lock/main/lockDetail/videoLog/widget/full_screenImage_page.dart';
import 'package:star_lock/main/lockDetail/videoLog/widget/video_thumbnail.dart';
import 'package:star_lock/main/lockDetail/videoLog/widget/video_thumbnail_image.dart';
import 'package:star_lock/tools/dateTool.dart';
import 'package:video_player/video_player.dart';
@ -72,37 +73,35 @@ class _VideoLogDetailPageState extends State<VideoLogDetailPage> {
? Column(
children: <Widget>[
Container(
color: Colors.black,
height: 500.h,
decoration: BoxDecoration(color: Colors.black),
child: AspectRatio(
aspectRatio: 16 / 9,
child: Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[
FittedBox(
child: SizedBox(
width: state.videoController.value.size.width,
height: state.videoController.value.size.height,
child: VideoPlayer(state.videoController),
),
fit: BoxFit.cover,
width: 1.sw,
child: Stack(
alignment: Alignment.bottomCenter,
children: <Widget>[
RotatedBox(
quarterTurns: -1,
child: AspectRatio(
aspectRatio: state.videoController.value.size.width /
state.videoController.value.size.height,
child: VideoPlayer(state.videoController),
),
ControlsOverlay(
controller: state.videoController,
recordData: state.recordData.value,
),
ControlsOverlay(
controller: state.videoController,
recordData: state.recordData.value,
),
if (state.videoController.value.isPlaying ||
state.videoController.value.isBuffering)
Container()
else
VideoProgressIndicator(
state.videoController,
colors: VideoProgressColors(
playedColor: AppColors.mainColor),
allowScrubbing: true,
),
if (state.videoController.value.isPlaying ||
state.videoController.value.isBuffering)
Container()
else
VideoProgressIndicator(
state.videoController,
colors: VideoProgressColors(
playedColor: AppColors.mainColor),
allowScrubbing: true,
),
],
),
],
),
),
_buildOther(),
@ -166,13 +165,27 @@ class _VideoLogDetailPageState extends State<VideoLogDetailPage> {
}
_buildVideoItem(RecordListData recordData) {
return VideoThumbnail(videoUrl: recordData.videoUrl!);
return VideoThumbnailImage(videoUrl: recordData.videoUrl!);
}
_buildImageItem(RecordListData recordData) {
return Image.network(
recordData.imagesUrl!,
fit: BoxFit.cover,
return RotatedBox(
quarterTurns: 1,
child: Image.network(
recordData.imagesUrl!,
fit: BoxFit.cover,
errorBuilder:
(BuildContext context, Object error, StackTrace? stackTrace) {
//
return RotatedBox(
quarterTurns: -1,
child: Image.asset(
'images/icon_unHaveData.png', //
fit: BoxFit.cover,
),
);
},
),
);
}

View File

@ -10,9 +10,17 @@ class FullScreenImagePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
child: PhotoView(
imageProvider: NetworkImage(imageUrl),
body: GestureDetector(
onTap: (){
Navigator.pop(context); //
},
child: Container(
child: RotatedBox(
quarterTurns: -1,
child: PhotoView(
imageProvider: NetworkImage(imageUrl),
),
),
),
),
);

View File

@ -1,73 +0,0 @@
import 'package:flutter/material.dart';
import 'package:video_player/video_player.dart';
class VideoThumbnail extends StatefulWidget {
final String videoUrl;
VideoThumbnail({required this.videoUrl});
@override
_VideoThumbnailState createState() => _VideoThumbnailState();
}
class _VideoThumbnailState extends State<VideoThumbnail> {
late VideoPlayerController _controller;
late Future<void> _initializeVideoPlayerFuture;
@override
void initState() {
super.initState();
// VideoPlayerController
_controller = VideoPlayerController.network(widget.videoUrl)
..initialize().then((_) {
// UI以便显示第一帧
setState(() {});
});
}
@override
void dispose() {
//
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return GestureDetector(
// onTap: () {
// setState(() {
// if (_controller.value.isPlaying) {
// _controller.pause();
// } else {
// _controller.play();
// }
// });
// },
child: Stack(
alignment: Alignment.center,
children: <Widget>[
//
_controller.value.isInitialized
? AspectRatio(
aspectRatio: 1 / 1, //
child: FittedBox(
fit: BoxFit.cover, //
child: SizedBox(
width: _controller.value.size.width,
height: _controller.value.size.height,
child: VideoPlayer(_controller),
),
),
)
: Center(
child: CircularProgressIndicator(), //
),
if (!_controller.value.isPlaying && _controller.value.isInitialized)
Icon(Icons.play_arrow_rounded,
size: 80, color: Colors.white.withOpacity(0.8)),
],
),
);
}
}

View File

@ -0,0 +1,97 @@
import 'dart:io'; // dart:io 使 File
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:path_provider/path_provider.dart'; // path_provider
import 'package:video_thumbnail/video_thumbnail.dart'; // video_thumbnail
class VideoThumbnailImage extends StatefulWidget {
final String videoUrl;
VideoThumbnailImage({required this.videoUrl});
@override
_VideoThumbnailState createState() => _VideoThumbnailState();
}
class _VideoThumbnailState extends State<VideoThumbnailImage> {
final Map<String, String> _thumbnailCache = {}; //
late Future<String?> _thumbnailFuture; // Future
@override
void initState() {
super.initState();
_thumbnailFuture = _generateThumbnail(); // initState Future
}
//
Future<String?> _generateThumbnail() async {
try {
//
if (_thumbnailCache.containsKey(widget.videoUrl)) {
return _thumbnailCache[widget.videoUrl];
}
//
final tempDir = await getTemporaryDirectory();
final thumbnailPath = await VideoThumbnail.thumbnailFile(
video: widget.videoUrl,
// URL
thumbnailPath: tempDir.path,
//
imageFormat: ImageFormat.JPEG,
//
maxHeight: 200,
//
quality: 100, // (0-100)
);
//
_thumbnailCache[widget.videoUrl] = thumbnailPath!;
return thumbnailPath;
} catch (e) {
print('Failed to generate thumbnail: $e');
return null;
}
}
@override
Widget build(BuildContext context) {
return FutureBuilder<String?>(
future: _thumbnailFuture, // Future
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
//
return Center(child: CircularProgressIndicator());
} else if (snapshot.hasError || !snapshot.hasData) {
//
return Image.asset(
'images/icon_unHaveData.png', //
fit: BoxFit.cover,
);
} else {
//
final thumbnailPath = snapshot.data!;
return Stack(
alignment: Alignment.center,
children: <Widget>[
RotatedBox(
quarterTurns: -1,
child: Image.file(
File(thumbnailPath), //
width: 200,
height: 200,
fit: BoxFit.cover,
),
),
Icon(
Icons.play_arrow_rounded,
size: 80,
color: Colors.white.withOpacity(0.8),
),
],
);
}
},
);
}
}

View File

@ -1,3 +1,6 @@
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'package:get/get.dart';
@ -9,7 +12,9 @@ import 'package:star_lock/mine/about/about_console.dart';
import '../../app_settings/app_colors.dart';
import '../../flavors.dart';
import '../../tools/commonItem.dart';
import '../../tools/storage.dart';
import '../../tools/titleAppBar.dart';
import '../../tools/wechat/wechatManageTool.dart';
class AboutPage extends StatefulWidget {
const AboutPage({Key? key}) : super(key: key);
@ -80,6 +85,44 @@ class _AboutPageState extends State<AboutPage> {
Widget listView() {
Widget view = Column(
children: [
CommonItem(
leftTitel: '版本说明'.tr,
rightTitle: '',
isHaveLine: false,
isHaveDirection: true,
action: () async {
WechatManageTool.getAppInfo(() async {
final String? appVersionHistoryBaseUrl =
await Storage.getString(appVersionHistoryUrl);
if (appVersionHistoryBaseUrl == null) {
return;
}
String brandName = '';
if (Platform.isAndroid) {
final AndroidDeviceInfo androidDeviceInfo =
await DeviceInfoPlugin().androidInfo;
brandName = androidDeviceInfo.manufacturer;
} else {
final IosDeviceInfo iosDeviceInfo =
await DeviceInfoPlugin().iosInfo;
brandName = iosDeviceInfo.systemName ?? 'ios';
}
final PackageInfo packageInfo =
await PackageInfo.fromPlatform();
Navigator.pushNamed(context, Routers.webviewShowPage,
arguments: {
'url': appVersionHistoryBaseUrl +
'?version=${packageInfo.version}&brandName=${brandName}',
'title': '版本说明'.tr
});
});
}),
Divider(
height: 1,
color: AppColors.greyLineColor,
indent: 20.w,
endIndent: 20.w,
),
CommonItem(
leftTitel: '介绍'.tr,
rightTitle: '',

View File

@ -11,7 +11,7 @@ class StartChartApi extends BaseProvider {
// url
final String _startChartHost = 'http://sls1-scd.star-lock.cn:8080';
static StartChartApi get to => Get.find<StartChartApi>();
static StartChartApi get to => Get.put(StartChartApi());
// --
Future<StarChartRegisterNodeEntity> starChartRegisterNode({
@ -68,6 +68,4 @@ class StartChartApi extends BaseProvider {
);
return response;
}
}

View File

@ -149,122 +149,200 @@ class ScpMessage {
static ScpMessage deserialize(Uint8List bytes) {
final message = ScpMessage();
final length = bytes.length;
int offset = 0;
// String hexString =
// bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
// // _log(text: 'result bytes hex: ${hexString}');
// _log(
// text:
// '\n result bytes hex: ${hexString} \n payload hex: ${hexString.substring(210)}');
//
if (length < 4 + 1 + 2 + 1 + 1 + 44 + 44 + 2 + 2 + 4) {
throw FormatException("Invalid message length");
}
// 使 ByteData
final byteData = ByteData.sublistView(bytes);
// ProtocolFlag (4 bytes)
if (bytes.length - offset >= 4) {
message.ProtocolFlag = utf8.decode(bytes.sublist(offset, offset + 4));
offset += 4;
} else {
throw FormatException("Invalid ProtocolFlag length");
}
message.ProtocolFlag = utf8.decode(bytes.sublist(offset, offset + 4));
offset += 4;
// MessageType (1 byte)
if (bytes.length - offset >= 1) {
message.MessageType = bytes[offset];
offset += 1;
} else {
throw FormatException("Invalid MessageType length");
}
message.MessageType = bytes[offset];
offset += 1;
// MessageId (2 bytes, little-endian)
if (bytes.length - offset >= 2) {
message.MessageId = (bytes[offset + 1] << 8) | bytes[offset];
offset += 2;
} else {
throw FormatException("Invalid MessageId length");
}
message.MessageId = byteData.getUint16(offset, Endian.little);
offset += 2;
// SpTotal (1 byte)
if (bytes.length - offset >= 1) {
message.SpTotal = bytes[offset];
offset += 1;
} else {
throw FormatException("Invalid SpTotal length");
}
message.SpTotal = bytes[offset];
offset += 1;
// SpIndex (1 byte)
if (bytes.length - offset >= 1) {
message.SpIndex = bytes[offset];
offset += 1;
} else {
throw FormatException("Invalid SpIndex length");
}
message.SpIndex = bytes[offset];
offset += 1;
// FromPeerId (44)
if (bytes.length - offset >= 44) {
message.FromPeerId =
utf8.decode(bytes.sublist(offset, offset + 44)).trimRight();
offset += 44;
} else {
throw FormatException("Invalid FromPeerId length");
}
// FromPeerId (44 bytes)
message.FromPeerId =
utf8.decode(bytes.sublist(offset, offset + 44)).trimRight();
offset += 44;
// ToPeerId (44)
if (bytes.length - offset >= 44) {
message.ToPeerId =
utf8.decode(bytes.sublist(offset, offset + 44)).trimRight();
offset += 44;
} else {
throw FormatException("Invalid ToPeerId length");
}
// ToPeerId (44 bytes)
message.ToPeerId =
utf8.decode(bytes.sublist(offset, offset + 44)).trimRight();
offset += 44;
// PayloadType (2 bytes, little-endian)
if (bytes.length - offset >= 2) {
message.PayloadType = (bytes[offset + 1] << 8) | bytes[offset];
offset += 2;
} else {
throw FormatException("Invalid PayloadType length");
}
message.PayloadType = byteData.getUint16(offset, Endian.little);
offset += 2;
// PayloadCRC (2 bytes, little-endian)
if (bytes.length - offset >= 2) {
message.PayloadCRC = (bytes[offset + 1] << 8) | bytes[offset];
offset += 2;
} else {
throw FormatException("Invalid PayloadCRC length");
}
message.PayloadCRC = byteData.getUint16(offset, Endian.little);
offset += 2;
// PayloadLength (4 bytes, little-endian)
if (bytes.length - offset >= 4) {
message.PayloadLength = (bytes[offset] |
(bytes[offset + 1] << 8) |
(bytes[offset + 2] << 16) |
(bytes[offset + 3] << 24)); // little-endian
offset += 4;
} else {
throw FormatException("Invalid PayloadLength length");
}
message.PayloadLength = byteData.getUint32(offset, Endian.little);
offset += 4;
// Payload
if (message.PayloadLength != null &&
bytes.length - offset >= message.PayloadLength!) {
final sublist = bytes.sublist(offset, offset + message.PayloadLength!);
offset += message.PayloadLength!;
message.Payload = _handlePayLoad(
payloadType: message.PayloadType ?? 0,
messageType: message.MessageType ?? 0,
byte: sublist,
offset: offset,
PayloadLength: message.PayloadLength,
spIndex: message.SpIndex,
spTotal: message.SpTotal,
messageId: message.MessageId,
);
} else {
// Payload
if (message.PayloadLength == null ||
length - offset < message.PayloadLength!) {
throw FormatException("Invalid Payload or PayloadLength");
}
// Payload
final payloadBytes = bytes.sublist(offset, offset + message.PayloadLength!);
offset += message.PayloadLength!;
message.Payload = _handlePayLoad(
payloadType: message.PayloadType ?? 0,
messageType: message.MessageType ?? 0,
byte: payloadBytes,
offset: offset,
PayloadLength: message.PayloadLength,
spIndex: message.SpIndex,
spTotal: message.SpTotal,
messageId: message.MessageId,
);
return message;
}
// static ScpMessage deserialize(Uint8List bytes) {
// final message = ScpMessage();
// int offset = 0;
//
// // String hexString =
// // bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();
// // // _log(text: 'result bytes hex: ${hexString}');
// // _log(
// // text:
// // '\n result bytes hex: ${hexString} \n payload hex: ${hexString.substring(210)}');
//
// // ProtocolFlag (4 bytes)
// if (bytes.length - offset >= 4) {
// message.ProtocolFlag = utf8.decode(bytes.sublist(offset, offset + 4));
// offset += 4;
// } else {
// throw FormatException("Invalid ProtocolFlag length");
// }
//
// // MessageType (1 byte)
// if (bytes.length - offset >= 1) {
// message.MessageType = bytes[offset];
// offset += 1;
// } else {
// throw FormatException("Invalid MessageType length");
// }
//
// // MessageId (2 bytes, little-endian)
// if (bytes.length - offset >= 2) {
// message.MessageId = (bytes[offset + 1] << 8) | bytes[offset];
// offset += 2;
// } else {
// throw FormatException("Invalid MessageId length");
// }
//
// // SpTotal (1 byte)
// if (bytes.length - offset >= 1) {
// message.SpTotal = bytes[offset];
// offset += 1;
// } else {
// throw FormatException("Invalid SpTotal length");
// }
//
// // SpIndex (1 byte)
// if (bytes.length - offset >= 1) {
// message.SpIndex = bytes[offset];
// offset += 1;
// } else {
// throw FormatException("Invalid SpIndex length");
// }
//
// // FromPeerId (44)
// if (bytes.length - offset >= 44) {
// message.FromPeerId =
// utf8.decode(bytes.sublist(offset, offset + 44)).trimRight();
// offset += 44;
// } else {
// throw FormatException("Invalid FromPeerId length");
// }
//
// // ToPeerId (44)
// if (bytes.length - offset >= 44) {
// message.ToPeerId =
// utf8.decode(bytes.sublist(offset, offset + 44)).trimRight();
// offset += 44;
// } else {
// throw FormatException("Invalid ToPeerId length");
// }
//
// // PayloadType (2 bytes, little-endian)
// if (bytes.length - offset >= 2) {
// message.PayloadType = (bytes[offset + 1] << 8) | bytes[offset];
// offset += 2;
// } else {
// throw FormatException("Invalid PayloadType length");
// }
//
// // PayloadCRC (2 bytes, little-endian)
// if (bytes.length - offset >= 2) {
// message.PayloadCRC = (bytes[offset + 1] << 8) | bytes[offset];
// offset += 2;
// } else {
// throw FormatException("Invalid PayloadCRC length");
// }
//
// // PayloadLength (4 bytes, little-endian)
// if (bytes.length - offset >= 4) {
// message.PayloadLength = (bytes[offset] |
// (bytes[offset + 1] << 8) |
// (bytes[offset + 2] << 16) |
// (bytes[offset + 3] << 24)); // little-endian
// offset += 4;
// } else {
// throw FormatException("Invalid PayloadLength length");
// }
//
// // Payload
// if (message.PayloadLength != null &&
// bytes.length - offset >= message.PayloadLength!) {
// final sublist = bytes.sublist(offset, offset + message.PayloadLength!);
// offset += message.PayloadLength!;
// message.Payload = _handlePayLoad(
// payloadType: message.PayloadType ?? 0,
// messageType: message.MessageType ?? 0,
// byte: sublist,
// offset: offset,
// PayloadLength: message.PayloadLength,
// spIndex: message.SpIndex,
// spTotal: message.SpTotal,
// messageId: message.MessageId,
// );
// } else {
// throw FormatException("Invalid Payload or PayloadLength");
// }
//
// return message;
// }
// payloadType序列化对应的payload结构体
static dynamic _handlePayLoad({
required int payloadType,

View File

@ -29,7 +29,6 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle
//
final GenericResp genericResp = scpMessage.Payload;
if (checkGenericRespSuccess(genericResp)) {
print('收到同意接听的回复');
//
startChartManage.stopTalkAcceptTimer();
//

View File

@ -156,7 +156,7 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle
void _handleVideoH264(TalkData talkData) {
final TalkDataH264Frame talkDataH264Frame = TalkDataH264Frame();
talkDataH264Frame.mergeFromBuffer(talkData.content);
AppLog.log('H264 TalkData :$talkDataH264Frame');
// AppLog.log('H264 TalkData :$talkDataH264Frame');
talkDataRepository.addTalkData(talkData);
}

View File

@ -21,7 +21,7 @@ class UdpTalkHangUpHandler extends ScpMessageBaseHandle
// //
// return;
// }
print('收到通话中挂断请求');
//
replySuccessMessage(scpMessage);
talkStatus.setHangingUpDuring();
@ -34,7 +34,7 @@ class UdpTalkHangUpHandler extends ScpMessageBaseHandle
talkePingOverTimeTimerManager.cancel();
talkDataOverTimeTimerManager.cancel();
EasyLoading.showToast('已挂断');
EasyLoading.showToast('已挂断'.tr);
Get.back();
}

View File

@ -28,7 +28,7 @@ class TalkDataOverTimeTimerManager {
//
static void _handleTalkeDataOverTime() {
EasyLoading.showToast('通话连接失败', duration: 2000.milliseconds);
EasyLoading.showToast('通话连接失败'.tr, duration: 2000.milliseconds);
//
StartChartManage().sendTalkHangupMessage();
StartChartManage().stopTalkPingMessageTimer();

View File

@ -28,7 +28,7 @@ class TalkePingOverTimeTimerManager {
//
static void _handleTalkePingOverTime() {
if (talkStatus.status == TalkStatus.answeredSuccessfully) {
EasyLoading.showToast('通话异常中断', duration: 2000.milliseconds);
EasyLoading.showToast('通话异常中断'.tr, duration: 2000.milliseconds);
//
StartChartManage().stopTalkPingMessageTimer();
StartChartManage().stopTalkExpectMessageTimer();

View File

@ -29,7 +29,7 @@ class TalkeRequestOverTimeTimerManager {
static void _handleTalkeRequestOverTime() {
if (talkStatus.status == TalkStatus.passiveCallWaitingAnswer ||
talkStatus.status == TalkStatus.proactivelyCallWaitingAnswer) {
EasyLoading.showToast('通话未接通,以挂断', duration: 2000.milliseconds);
EasyLoading.showToast('通话未接通,已挂断'.tr, duration: 2000.milliseconds);
//
StartChartManage().sendTalkRejectMessage();
talkStatus.setInitializationCompleted();

View File

@ -50,7 +50,6 @@ class TalkViewLogic extends BaseGetXController {
int audioFrameIntervalMs = 20; // 4522FPS
int minFrameIntervalMs = 30; // 33 FPS
int maxFrameIntervalMs = 100; // 1 FPS
// int maxFrameIntervalMs = 100; // 10 FPS
///
void _initFlutterPcmSound() {
@ -118,6 +117,17 @@ class TalkViewLogic extends BaseGetXController {
case TalkStatus.end:
_handleInvalidTalkStatus();
break;
case TalkStatus.answeredSuccessfully:
state.oneMinuteTimeTimer?.cancel(); //
state.oneMinuteTimeTimer ??=
Timer.periodic(const Duration(seconds: 1), (Timer t) {
state.oneMinuteTime.value++;
if (state.oneMinuteTime.value >= 60) {
t.cancel(); //
state.oneMinuteTime.value = 0;
}
});
break;
default:
//
break;
@ -160,12 +170,6 @@ class TalkViewLogic extends BaseGetXController {
///
void _adjustFrameInterval() {
int newFrameIntervalMs = frameIntervalMs;
if (state.networkStatus.value == NetworkStatus.lagging) {
bufferSize = 60; //
} else {
bufferSize = 40; //
}
if (state.videoBuffer.length < 10 && frameIntervalMs < maxFrameIntervalMs) {
//
frameIntervalMs += 5;
@ -303,7 +307,7 @@ class TalkViewLogic extends BaseGetXController {
bluetoothDeviceName: BlueManage().connectDeviceName,
openLockCommand: messageDetail,
);
showToast('已发送开门通知');
showToast('正在开锁中...'.tr);
}
int _getUTCNetTime() {
@ -442,7 +446,8 @@ class TalkViewLogic extends BaseGetXController {
_syncTimer = null; //
_audioTimer?.cancel();
_audioTimer = null; //
state.oneMinuteTimeTimer?.cancel();
state.oneMinuteTimeTimer = null;
stopProcessingAudio();
super.onClose();
}

View File

@ -110,13 +110,13 @@ class _TalkViewPageState extends State<TalkViewPage>
canPop: false,
child: RepaintBoundary(
key: state.globalKey,
child: Transform.rotate(
angle:
state.rotateAngle.value * (pi / 180), // 90
child: Transform.scale(
scale: scale, //
child: SizedBox.expand(
child: RotatedBox(
quarterTurns: -1,
child: Image.memory(
state.listData.value,
width: ScreenUtil().scaleWidth,
height: ScreenUtil().scaleHeight,
gaplessPlayback: true,
fit: BoxFit.cover,
filterQuality: FilterQuality.high,
@ -142,6 +142,34 @@ class _TalkViewPageState extends State<TalkViewPage>
style: TextStyle(color: Colors.black, fontSize: 26.sp),
))
: Container()),
Obx(
() => state.listData.value.isNotEmpty
? 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(
@ -197,7 +225,7 @@ class _TalkViewPageState extends State<TalkViewPage>
Icon(Icons.mic, color: Colors.white, size: 24.w),
SizedBox(width: 10.w),
Text(
'正在说话...',
'正在说话...'.tr,
style: TextStyle(
fontSize: 20.sp, color: Colors.white),
),
@ -342,13 +370,13 @@ class _TalkViewPageState extends State<TalkViewPage>
Colors.white,
longPress: () async {
if (state.talkStatus.value == TalkStatus.answeredSuccessfully) {
print('开始录音');
//
logic.startProcessingAudio();
state.isLongPressing.value = true;
}
},
longPressUp: () async {
print('停止录音');
//
logic.stopProcessingAudio();
state.isLongPressing.value = false;
},
@ -369,7 +397,7 @@ class _TalkViewPageState extends State<TalkViewPage>
}),
bottomBtnItemWidget(
'images/main/icon_lockDetail_monitoringUnlock.png',
'开锁',
'开锁'.tr,
AppColors.mainColor,
onClick: () {
// if (UDPManage().remoteUnlock == 1) {

View File

@ -37,8 +37,7 @@ class TalkViewState {
RxList<int> listAudioData = <int>[].obs; //
GlobalKey globalKey = GlobalKey();
late Timer oneMinuteTimeTimer =
Timer(const Duration(seconds: 1), () {}); // 60
Timer? oneMinuteTimeTimer; // 60
RxInt oneMinuteTime = 0.obs; //
// 10

View File

@ -36,6 +36,7 @@ const String starChartRegisterNodeInfo = 'starChartRegisterNodeInfo'; //星图
const String relayInfo = 'relayInfo'; //
const String lockNetWorkInfo = 'lockNetWorkInfo'; //
const String appVersionHistoryUrl = 'appVersionHistoryUrl'; //
class Storage {
factory Storage() => _instance;

View File

@ -1,7 +1,6 @@
import '../../login/login/app_get_version.dart';
import '../../network/api_repository.dart';
import '../storage.dart';
import 'customer_tool.dart';
import 'pay/wx_pay_tool.dart';
@ -13,12 +12,12 @@ class WechatManageTool {
static Future<void> getAppInfo(void Function() action) async {
// AppLog.log('AppFirstEnterHandle调用了获取App信息接口');
final GetAppInfo entity = await ApiRepository.to.getAppInfo();
if(entity.errorCode == 0) {
if (entity.errorCode == 0) {
Storage.setString(
appVersionHistoryUrl, entity.data?.appVersionHistoryUrl ?? '');
CustomerTool.init(entity.data?.wechatServiceUrl ?? '');
WxPayTool.setAssociationUrl(entity.data!.appSiteUrl!);
action();
}
}
}
}

View File

@ -401,7 +401,7 @@ packages:
source: hosted
version: "7.0.2"
dio:
dependency: transitive
dependency: "direct main"
description:
name: dio
sha256: "7d328c4d898a61efc3cd93655a0955858e29a0aa647f0f9e02d59b3bb275e2e8"
@ -1785,6 +1785,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.3.2"
video_thumbnail:
dependency: "direct main"
description:
name: video_thumbnail
sha256: "3455c189d3f0bb4e3fc2236475aa84fe598b9b2d0e08f43b9761f5bc44210016"
url: "https://pub.dev"
source: hosted
version: "0.5.3"
visibility_detector:
dependency: transitive
description:

View File

@ -269,7 +269,8 @@ dependencies:
# 图片预览
photo_view: ^0.15.0
provider: ^6.1.2
dio: ^4.0.6 # 网络请求库
video_thumbnail: ^0.5.3