From 6b3766f0d68c27d97d3a84433c47cdd012984a7e Mon Sep 17 00:00:00 2001 From: Liuyf Date: Thu, 19 Dec 2024 16:48:41 +0800 Subject: [PATCH 01/22] =?UTF-8?q?ci:=20=E5=8F=91=E5=B8=83=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=89=8Dsky=EF=BC=8Cxhj=E5=90=84=E8=87=AA=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E4=B8=80=E6=AC=A1api=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitlab-ci.yml | 2 ++ pre_build.sh | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7fc3fcae..f83413f7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -82,6 +82,8 @@ generate_git_tag: stage: generate_tag_or_version extends: .generate_tag_rule before_script: + - bash pre_build.sh xhj + - bash pre_build.sh sky - project_url=$(echo $CI_PROJECT_URL | sed 's/http:\/\///') - echo "project_url:$project_url" - git remote set-url origin http://gitlab-ci-token:${GITLAB_ACCESS_TOKEN}@$project_url.git diff --git a/pre_build.sh b/pre_build.sh index 532145a0..4737e9c7 100755 --- a/pre_build.sh +++ b/pre_build.sh @@ -105,6 +105,6 @@ echo -e "\n* 没有发现废弃 API,开始编译" # 编译命令 #flutter clean && flutter pub get -flutter build apk --flavor $environment -t $main_file +#flutter build apk --flavor $environment -t $main_file #flutter build ios --flavor $environment -t $main_file #flutter build appbundle --flavor $environment -t $main_file From 42197e7d19595f5cdd92a72b8c7baa3831088892 Mon Sep 17 00:00:00 2001 From: Liuyf Date: Thu, 19 Dec 2024 16:48:41 +0800 Subject: [PATCH 02/22] =?UTF-8?q?ci:=20=E5=8F=91=E5=B8=83=E7=89=88?= =?UTF-8?q?=E6=9C=AC=E5=89=8Dsky=EF=BC=8Cxhj=E5=90=84=E8=87=AA=E6=89=A7?= =?UTF-8?q?=E8=A1=8C=E4=B8=80=E6=AC=A1api=E6=A3=80=E6=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitlab-ci.yml | 2 ++ pre_build.sh | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 7fc3fcae..f83413f7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -82,6 +82,8 @@ generate_git_tag: stage: generate_tag_or_version extends: .generate_tag_rule before_script: + - bash pre_build.sh xhj + - bash pre_build.sh sky - project_url=$(echo $CI_PROJECT_URL | sed 's/http:\/\///') - echo "project_url:$project_url" - git remote set-url origin http://gitlab-ci-token:${GITLAB_ACCESS_TOKEN}@$project_url.git diff --git a/pre_build.sh b/pre_build.sh index 532145a0..4737e9c7 100755 --- a/pre_build.sh +++ b/pre_build.sh @@ -105,6 +105,6 @@ echo -e "\n* 没有发现废弃 API,开始编译" # 编译命令 #flutter clean && flutter pub get -flutter build apk --flavor $environment -t $main_file +#flutter build apk --flavor $environment -t $main_file #flutter build ios --flavor $environment -t $main_file #flutter build appbundle --flavor $environment -t $main_file From 558b9418cb57eedf4ade6e77c734392309812dcc Mon Sep 17 00:00:00 2001 From: liyi Date: Mon, 23 Dec 2024 18:52:58 +0800 Subject: [PATCH 03/22] =?UTF-8?q?fix:=E5=A2=9E=E5=8A=A0test.proto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/talk/startChart/proto/test.pb.dart | 82 ++++++++++++++++++++ lib/talk/startChart/proto/test.pbenum.dart | 11 +++ lib/talk/startChart/proto/test.pbjson.dart | 29 +++++++ lib/talk/startChart/proto/test.pbserver.dart | 14 ++++ 4 files changed, 136 insertions(+) create mode 100644 lib/talk/startChart/proto/test.pb.dart create mode 100644 lib/talk/startChart/proto/test.pbenum.dart create mode 100644 lib/talk/startChart/proto/test.pbjson.dart create mode 100644 lib/talk/startChart/proto/test.pbserver.dart diff --git a/lib/talk/startChart/proto/test.pb.dart b/lib/talk/startChart/proto/test.pb.dart new file mode 100644 index 00000000..2f27e4c1 --- /dev/null +++ b/lib/talk/startChart/proto/test.pb.dart @@ -0,0 +1,82 @@ +// +// Generated code. Do not modify. +// source: test.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +class TestStruct extends $pb.GeneratedMessage { + factory TestStruct({ + $core.int? statusA, + $core.int? statusB, + }) { + final $result = create(); + if (statusA != null) { + $result.statusA = statusA; + } + if (statusB != null) { + $result.statusB = statusB; + } + return $result; + } + TestStruct._() : super(); + factory TestStruct.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory TestStruct.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'TestStruct', package: const $pb.PackageName(_omitMessageNames ? '' : 'main'), createEmptyInstance: create) + ..a<$core.int>(1, _omitFieldNames ? '' : 'StatusA', $pb.PbFieldType.OU3, protoName: 'StatusA') + ..a<$core.int>(2, _omitFieldNames ? '' : 'StatusB', $pb.PbFieldType.OU3, protoName: 'StatusB') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + TestStruct clone() => TestStruct()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + TestStruct copyWith(void Function(TestStruct) updates) => super.copyWith((message) => updates(message as TestStruct)) as TestStruct; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static TestStruct create() => TestStruct._(); + TestStruct createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static TestStruct getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static TestStruct? _defaultInstance; + + @$pb.TagNumber(1) + $core.int get statusA => $_getIZ(0); + @$pb.TagNumber(1) + set statusA($core.int v) { $_setUnsignedInt32(0, v); } + @$pb.TagNumber(1) + $core.bool hasStatusA() => $_has(0); + @$pb.TagNumber(1) + void clearStatusA() => clearField(1); + + @$pb.TagNumber(2) + $core.int get statusB => $_getIZ(1); + @$pb.TagNumber(2) + set statusB($core.int v) { $_setUnsignedInt32(1, v); } + @$pb.TagNumber(2) + $core.bool hasStatusB() => $_has(1); + @$pb.TagNumber(2) + void clearStatusB() => clearField(2); +} + + +const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); +const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/lib/talk/startChart/proto/test.pbenum.dart b/lib/talk/startChart/proto/test.pbenum.dart new file mode 100644 index 00000000..27df3fe4 --- /dev/null +++ b/lib/talk/startChart/proto/test.pbenum.dart @@ -0,0 +1,11 @@ +// +// Generated code. Do not modify. +// source: test.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + diff --git a/lib/talk/startChart/proto/test.pbjson.dart b/lib/talk/startChart/proto/test.pbjson.dart new file mode 100644 index 00000000..3562951e --- /dev/null +++ b/lib/talk/startChart/proto/test.pbjson.dart @@ -0,0 +1,29 @@ +// +// Generated code. Do not modify. +// source: test.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:convert' as $convert; +import 'dart:core' as $core; +import 'dart:typed_data' as $typed_data; + +@$core.Deprecated('Use testStructDescriptor instead') +const TestStruct$json = { + '1': 'TestStruct', + '2': [ + {'1': 'StatusA', '3': 1, '4': 1, '5': 13, '10': 'StatusA'}, + {'1': 'StatusB', '3': 2, '4': 1, '5': 13, '10': 'StatusB'}, + ], +}; + +/// Descriptor for `TestStruct`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List testStructDescriptor = $convert.base64Decode( + 'CgpUZXN0U3RydWN0EhgKB1N0YXR1c0EYASABKA1SB1N0YXR1c0ESGAoHU3RhdHVzQhgCIAEoDV' + 'IHU3RhdHVzQg=='); + diff --git a/lib/talk/startChart/proto/test.pbserver.dart b/lib/talk/startChart/proto/test.pbserver.dart new file mode 100644 index 00000000..3ef60894 --- /dev/null +++ b/lib/talk/startChart/proto/test.pbserver.dart @@ -0,0 +1,14 @@ +// +// Generated code. Do not modify. +// source: test.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +export 'test.pb.dart'; + From f91f1714c8500388d4ecfadc128221581f519826 Mon Sep 17 00:00:00 2001 From: liyi Date: Tue, 24 Dec 2024 10:50:32 +0800 Subject: [PATCH 04/22] =?UTF-8?q?style:=E6=95=B4=E7=90=86udp=E8=BF=94?= =?UTF-8?q?=E5=9B=9E=E6=95=B0=E6=8D=AE=E7=9A=84=E5=8F=8D=E5=BA=8F=E5=88=97?= =?UTF-8?q?=E5=8C=96=E9=80=BB=E8=BE=91,=E4=BD=BF=E7=94=A8=E5=B7=A5?= =?UTF-8?q?=E5=8E=82=E6=A8=A1=E5=BC=8F=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/main/lockMian/lockMain/lockMain_page.dart | 41 ++- lib/talk/startChart/entity/scp_message.dart | 298 ++---------------- .../impl/udp_ble_passthrough_handler.dart | 17 + .../handle/impl/udp_echo_test_handler.dart | 39 ++- .../impl/udp_gateway_reset_handler.dart | 42 ++- .../impl/udp_gateway_transfer_handler.dart | 29 ++ .../handle/impl/udp_go_online_handler.dart | 17 + .../handle/impl/udp_heart_beat_handler.dart | 9 + .../impl/udp_remote_un_lock_handler.dart | 20 ++ .../handle/impl/udp_talk_accept_handler.dart | 35 +- .../handle/impl/udp_talk_data_handler.dart | 46 ++- .../handle/impl/udp_talk_expect_handler.dart | 26 ++ .../handle/impl/udp_talk_hangup_handler.dart | 33 +- .../handle/impl/udp_talk_ping_handler.dart | 29 ++ .../handle/impl/udp_talk_push_handler.dart | 29 ++ .../udp_talk_receiver_transfer_handler.dart | 35 +- .../handle/impl/udp_talk_reject_handler.dart | 33 +- .../handle/impl/udp_talk_request_handler.dart | 34 ++ .../impl/unknow_payload_type_handler.dart | 15 + .../handle/scp_message_base_handle.dart | 59 +++- .../startChart/handle/scp_message_handle.dart | 14 + lib/talk/startChart/start_chart_manage.dart | 5 +- 22 files changed, 576 insertions(+), 329 deletions(-) diff --git a/lib/main/lockMian/lockMain/lockMain_page.dart b/lib/main/lockMian/lockMain/lockMain_page.dart index babe6e59..a6697b2d 100755 --- a/lib/main/lockMian/lockMain/lockMain_page.dart +++ b/lib/main/lockMian/lockMain/lockMain_page.dart @@ -10,6 +10,7 @@ import 'package:star_lock/blue/blue_manage.dart'; import 'package:star_lock/main/lockMian/lockList/lockList_xhj_page.dart'; import 'package:star_lock/main/lockMian/lockMain/lockMain_state.dart'; import 'package:star_lock/talk/startChart/proto/talk_request.pb.dart'; +import 'package:star_lock/talk/startChart/proto/test.pb.dart'; import 'package:star_lock/talk/startChart/start_chart_manage.dart'; import 'package:star_lock/tools/noData.dart'; import 'package:star_lock/tools/submitBtn.dart'; @@ -107,13 +108,6 @@ class _StarLockMainPageState extends State ), onPressed: () { Scaffold.of(context).openDrawer(); - final writeToBuffer = TalkReq( - callerName: 'T9A_d9904c8c47c8', - callerType: TalkReq_CallerTypeE.Lock) - .writeToBuffer(); - final talkReq = TalkReq.fromBuffer(writeToBuffer); - - print('$talkReq\nwriteToBuffer:$writeToBuffer'); }, )), backgroundColor: AppColors.mainColor, @@ -230,21 +224,24 @@ class _StarLockMainPageState extends State height: 160.h, ), // _buildImage(), - SubmitBtn( - btnName: '发送回声测试'.tr, - onClick: () async { - String assetPath = 'assets/test.jpg'; // 替换为你的图片路径 - List imageBytes = await loadAssetImageAsBytes(assetPath); - // String testStr = - // '---天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈剑号巨阙珠称夜光果珍李柰菜重芥姜海咸河淡鳞潜羽翔龙师火帝鸟官人皇始制文字乃服衣裳推位让国有虞陶唐吊民伐罪周发殷汤坐朝问道垂拱平章爱育黎首臣伏戎羌遐迩体率宾归王鳴鳳翔兮神靈應和炫耀光芒照灼穹蒼迤邐岷峨嶢峻崢巍懿承緒劬勞育秩延佚賡孳蕃鑑識祗崇乾坤纘宵旰卿芮詒朔皎琲犂檄圮黯馭燎竈硎洮蠟胝攢囤嶇葺錡堯羲冕箕翬騏闡燮鸞蓊枘橐榘稟泗沂涖浹涔浣淇漱瀋潢瀾沚澮澆潦瀦瀌瀹灧炅焯燠燋爨熠焜煆烜燴炻熛燧牝牯犛犴牖牒疰疴痀癜痱癃皞皎盂盰盱盻眚眵瞋瞑瞷矙矧矬矰矞砟砉砥砣硇磑磲硤硭礌礞礡祀祚祜祧祼祺禋禡秭秣稃稞稹稷穈穄窀窆窠窸笄筇筌筅筮箦篑篯簟籀籝糅糗紵紽紾綃綈綬綣緜縞縰縻縴繢繇繙罟罾羝羶羸羼翊翕翥翡翳翽耖耜耠耱耦耧耩耨耬耵耶胂胼胬脘腽膦臢臬臾舂舄舡舸艋艏艟艤艚艨艪艭艴芏芊苣苴苕茌茱荄荃莛莪莶菰萁菸菽萸葶蒯蓍蓐蓬蓼蔌蔪蕈蕖蕙蕺蕻薷藦藨藭蘅蘧蘼虍虔虬虮虰虺虻蚨蚋蚱蛏蛘蜊蜍蜉蜣蜥蜩蜴蜱蜮蜾蝣蝤蝥螓螯螨蟒蟑蟛蠊蠋蠛蠡蠹衄衒衢衾袢袷裎裥裨裾褊褙褚褡褰褶襁襦襻觇觋觖觫觿訇訑訾詑詈詟詹誊誨誥誦誨諉諛謏謦譊譖譟譬譯譴讴讵讷诐诪诮诰诳诶谂谄谌谏谑谟谡谥谧谮谯谳谵豇豉豕豚豳豸貂貊貔賑賚賡贐赍赑赗赪赭赳趑趔趱趿跂跏跎跖跗跣跹跽踆踔踝踟踬踮踯蹀蹅蹇蹉蹐蹙蹦蹩躅躐躔躜躞軎軑軔軛軫軬軺輀輅輇輈輐輗輢輦輭輶轋轘轜辀辂辒辚辩迓迕迤迨迮逄逋逑逖逯遄遘遑遴遽邂邈邋邙邡邴邳邶郅郇郛郡郾鄌鄑鄘鄜鄞鄢鄣鄯鄹酃酆酈酖酗酘酢酤酴酹酽酾酾醅醊醑醚醢醪醭醮醯醵醴醶釃釅釐釜釡釴釸釾鉅鈦鈍鈹鈾鈿鉦鉬鉮鉻鉿銎銋銖銩銫銮銲鋈鋋鋌鋮鋯鋹鋻錎錡錣錤鍉鍐鍬鍱鍾鎏鎑鎒鎰鎵鎸鎿鏊鏖鏞鏟鏸鏹鏺鏽鐃鐋鐔鐛鐠鐣鐦鐭鐮鐯鐳鐴鐵鐼鐿鑌鑒鑔鑕鑞鑢鑬鑰鑱鑲鑴鑽鑾鑿钁钆钇钋钍钏钔钗钜钯钴钷钹钺钼钽钿铄铈铊铍铐铖铗铘铙铚铠铨铪铬铮铰铹铼铿锃锆锊锍锎锏锒锓锔锖锗锘锞锟锢锩锫锬锭锯锴锶锷锸锺锼锾镂镄镅镆镉镎镏镒镔镖镙镛镞镡镢镤镩镪镫镬镭镮镯镰镱镲镳镴镶镸长門閃閔閘閡闅闈闑闒闓闔闛闞闠闤闥闦闬闿阂阃阄阆阇阘阛阝队阡阯阱阪阽陀陂陉陔陘陞陟陧陬陲陴陶隃隋隍隒隓隗隦隰隱隳隵隶隽隿雎雋雐雚雝雟雤雩雯雱雿霈霅霌霎霏霗霙霛霝霡霣霤霧靂靅靆靉靎靏靐靕靗靛靡靺靻靽鞀鞉鞎鞑鞔鞖鞚鞝鞡鞤鞧鞨鞫鞬鞮鞯鞳鞴韁韃韈韉韋韐韒韓韗韙韝韟韢韡韣韦韧韨韩韪韫韬韭韮韯韲音頍頏頔頖頞頟頤頦頬頰頲頴頵頹頽頿顃顅顈顊顋顒顕顗顛顝顟顤顪顫顬顮顰顱顲顳顴页顶顷顸颃预颋颌颎颏颐频颓颔颕颙颖颛颟颡颢颣颤颥颦颧風颮颯颰颱颳颶颷颸颹颻颼颾飁飆飇飉飋飏飐飔飗飘飙飚飛飜飝飛飜飝飛飜飝飛飜飝雊霺霿靁靟鞲韞韭頫顴颿飌飡馺駃騑騜髐鬃鰩鳯鷊黴在古老的东方有一座美丽的城市名为锦绣锦绣城四季如春风景秀丽是人们心中的理想之地城中有一条清澈见底的小河河水潺潺流过滋养着两岸的花草树木河边有一座古老的石桥桥上雕刻着精美的图案见证了无数过往行人锦绣城的居民勤劳善良他们日出而作日落而息过着简单而幸福的生活城中有一位智者名叫慧心慧心先生博学多才深受人们的尊敬他常常在河边的亭子里给孩子们讲述历史故事传授知识和智慧孩子们围坐在他身边听得津津有味仿佛置身于一个个奇妙的世界在锦绣城的北边有一片茂密的森林森林里生活着各种各样的动物有活泼可爱的猴子它们在树上跳跃嬉戏有威武雄壮的老虎它们在林间巡视守护着森林的安宁还有许多不知名的鸟儿它们在枝头欢快地歌唱为森林增添了无限生机一天慧心先生带着几个学生走进森林进行一次生动的自然课他们观察着各种植物的生长了解它们的特性聆听鸟儿的鸣叫感受大自然的和谐学生们兴奋不已他们发现大自然是一个神奇的宝库蕴藏着无尽的奥秘在森林的深处有一片神秘的湖泊湖水碧绿如玉清澈见底传说中湖中住着一位美丽的水仙子她用神奇的力量保护着湖泊和周围的生灵慧心先生告诉学生们要爱护大自然与万物和谐共处才能得到大自然的馈赠锦绣城的居民们深知这一点他们珍惜每一滴水每一寸土地努力保护着这片美丽的家园他们相信只要人与自然和谐相处锦绣城将永远充满生机与活力锦绣城外群山环绕山间云雾缭绕宛如仙境山中有一处幽静的山谷谷中开满了五颜六色的花朵香气扑鼻引得蝴蝶翩翩起舞山谷中还有一条蜿蜒的小径两旁长满了奇花异草仿佛是一条通往神秘世界的通道慧心先生的学生们在一次探险中偶然发现了这个山谷他们惊叹于大自然的鬼斧神工纷纷拿出画笔将这美丽的景色描绘下来他们还发现了一种奇特的植物它的叶子呈现出透明的蓝色在阳光下闪闪发光仿佛蕴含着神秘的力量慧心先生告诉他们这种植物名为“梦幻草”只生长在特定的环境中非常稀有在锦绣城的东边有一片广阔的草原草原上绿草如茵牛羊成群草原上还有一座古老的风车风车的叶片随风旋转发出“吱呀吱呀”的声响仿佛在诉说着岁月的故事草原上的人们以放牧为生他们骑着骏马驰骋在广阔的天地间享受着自由自在的生活一天慧心先生带领学生们来到草原给他们上了一堂生动的地理课他指着远处的山脉告诉学生们山脉的形成过程以及它们对气候和生态环境的影响学生们听得入迷仿佛置身于大自然的怀抱中感受到了大自然的壮丽与神奇在锦绣城的南边有一片茂密的竹林竹林里竹子挺拔绿意盎然竹林中有一条清澈的小溪溪水潺潺流淌溪边长满了青苔竹林里还有一座精致的小亭子亭子的柱子上刻着优美的诗句让人感受到一种宁静与雅致慧心先生常常在竹林中的小亭子里静坐思考人生的哲理他告诉学生们竹子虽然柔弱但却有着坚韧不拔的精神无论遇到多大的困难都能顽强地生长学生们深受启发明白了在人生的道路上要像竹子一样勇敢地面对挑战坚持不懈地追求自己的理想锦绣城的居民们还擅长制作各种手工艺品他们的作品精美绝伦远近闻名有巧夺天工的陶瓷色彩斑斓形态各异有细腻精致的刺绣图案生动栩栩如生还有雕刻精美的木雕栩栩如生令人叹为观止这些手工艺品不仅展现了锦绣城居民的智慧和技艺也成为了他们与外界交流的纽带在锦绣城的中心有一座宏伟的宫殿宫殿的建筑风格独特气势恢宏宫殿的屋顶覆盖着金色的琉璃瓦在阳光下熠熠生辉宫殿的墙壁上雕刻着精美的图案栩栩如生仿佛在诉说着一个个古老的故事宫殿里住着一位英明的君主他以仁爱之心治理国家深受百姓的爱戴慧心先生常常受邀到宫殿中为君主出谋划策帮助他解决国家大事君主也非常尊重慧心先生的智慧常常向他请教治国之道在他们的共同努力下锦绣城国泰民安繁荣昌盛锦绣城的居民们还非常重视教育他们相信知识能够改变命运让孩子们拥有更加美好的未来城中有一座古老的书院书院的建筑古朴典雅环境优美书院里有许多博学的老师他们用心地教导学生传授知识和智慧学生们在这里努力学习汲取知识的养分,为将来的发展打下坚实的基础慧心先生也常常到书院授课他用自己的知识和智慧启迪学生们的思维引导他们去探索未知的世界学生们对慧心先生敬仰有加他们知道只有不断学习才能像慧心先生一样拥有渊博的知识和智慧为社会做出贡献锦绣城的故事还有很多这里的人们用自己的勤劳和智慧创造了一个充满生机与活力的美好家园他们相信只要人与自然和谐相处与他人携手共进锦绣城的明天一定会更加美好=='; - - // List imageBytes = utf8.encode(testStr).toList(); - // List imageBytes = utf8.encode(testStr).toList(); - // state.talkData.value = imageBytes; - // setState(() {}); - StartChartManage().sendEchoMessage(payload: imageBytes); - }, - ), + // SubmitBtn( + // btnName: '发送回声测试'.tr, + // onClick: () async { + // String assetPath = 'assets/test.jpg'; // 替换为你的图片路径 + // List imageBytes = await loadAssetImageAsBytes(assetPath); + // // String testStr = + // // '---天地玄黄宇宙洪荒日月盈昃辰宿列张寒来暑往秋收冬藏闰余成岁律吕调阳云腾致雨露结为霜金生丽水玉出昆冈剑号巨阙珠称夜光果珍李柰菜重芥姜海咸河淡鳞潜羽翔龙师火帝鸟官人皇始制文字乃服衣裳推位让国有虞陶唐吊民伐罪周发殷汤坐朝问道垂拱平章爱育黎首臣伏戎羌遐迩体率宾归王鳴鳳翔兮神靈應和炫耀光芒照灼穹蒼迤邐岷峨嶢峻崢巍懿承緒劬勞育秩延佚賡孳蕃鑑識祗崇乾坤纘宵旰卿芮詒朔皎琲犂檄圮黯馭燎竈硎洮蠟胝攢囤嶇葺錡堯羲冕箕翬騏闡燮鸞蓊枘橐榘稟泗沂涖浹涔浣淇漱瀋潢瀾沚澮澆潦瀦瀌瀹灧炅焯燠燋爨熠焜煆烜燴炻熛燧牝牯犛犴牖牒疰疴痀癜痱癃皞皎盂盰盱盻眚眵瞋瞑瞷矙矧矬矰矞砟砉砥砣硇磑磲硤硭礌礞礡祀祚祜祧祼祺禋禡秭秣稃稞稹稷穈穄窀窆窠窸笄筇筌筅筮箦篑篯簟籀籝糅糗紵紽紾綃綈綬綣緜縞縰縻縴繢繇繙罟罾羝羶羸羼翊翕翥翡翳翽耖耜耠耱耦耧耩耨耬耵耶胂胼胬脘腽膦臢臬臾舂舄舡舸艋艏艟艤艚艨艪艭艴芏芊苣苴苕茌茱荄荃莛莪莶菰萁菸菽萸葶蒯蓍蓐蓬蓼蔌蔪蕈蕖蕙蕺蕻薷藦藨藭蘅蘧蘼虍虔虬虮虰虺虻蚨蚋蚱蛏蛘蜊蜍蜉蜣蜥蜩蜴蜱蜮蜾蝣蝤蝥螓螯螨蟒蟑蟛蠊蠋蠛蠡蠹衄衒衢衾袢袷裎裥裨裾褊褙褚褡褰褶襁襦襻觇觋觖觫觿訇訑訾詑詈詟詹誊誨誥誦誨諉諛謏謦譊譖譟譬譯譴讴讵讷诐诪诮诰诳诶谂谄谌谏谑谟谡谥谧谮谯谳谵豇豉豕豚豳豸貂貊貔賑賚賡贐赍赑赗赪赭赳趑趔趱趿跂跏跎跖跗跣跹跽踆踔踝踟踬踮踯蹀蹅蹇蹉蹐蹙蹦蹩躅躐躔躜躞軎軑軔軛軫軬軺輀輅輇輈輐輗輢輦輭輶轋轘轜辀辂辒辚辩迓迕迤迨迮逄逋逑逖逯遄遘遑遴遽邂邈邋邙邡邴邳邶郅郇郛郡郾鄌鄑鄘鄜鄞鄢鄣鄯鄹酃酆酈酖酗酘酢酤酴酹酽酾酾醅醊醑醚醢醪醭醮醯醵醴醶釃釅釐釜釡釴釸釾鉅鈦鈍鈹鈾鈿鉦鉬鉮鉻鉿銎銋銖銩銫銮銲鋈鋋鋌鋮鋯鋹鋻錎錡錣錤鍉鍐鍬鍱鍾鎏鎑鎒鎰鎵鎸鎿鏊鏖鏞鏟鏸鏹鏺鏽鐃鐋鐔鐛鐠鐣鐦鐭鐮鐯鐳鐴鐵鐼鐿鑌鑒鑔鑕鑞鑢鑬鑰鑱鑲鑴鑽鑾鑿钁钆钇钋钍钏钔钗钜钯钴钷钹钺钼钽钿铄铈铊铍铐铖铗铘铙铚铠铨铪铬铮铰铹铼铿锃锆锊锍锎锏锒锓锔锖锗锘锞锟锢锩锫锬锭锯锴锶锷锸锺锼锾镂镄镅镆镉镎镏镒镔镖镙镛镞镡镢镤镩镪镫镬镭镮镯镰镱镲镳镴镶镸长門閃閔閘閡闅闈闑闒闓闔闛闞闠闤闥闦闬闿阂阃阄阆阇阘阛阝队阡阯阱阪阽陀陂陉陔陘陞陟陧陬陲陴陶隃隋隍隒隓隗隦隰隱隳隵隶隽隿雎雋雐雚雝雟雤雩雯雱雿霈霅霌霎霏霗霙霛霝霡霣霤霧靂靅靆靉靎靏靐靕靗靛靡靺靻靽鞀鞉鞎鞑鞔鞖鞚鞝鞡鞤鞧鞨鞫鞬鞮鞯鞳鞴韁韃韈韉韋韐韒韓韗韙韝韟韢韡韣韦韧韨韩韪韫韬韭韮韯韲音頍頏頔頖頞頟頤頦頬頰頲頴頵頹頽頿顃顅顈顊顋顒顕顗顛顝顟顤顪顫顬顮顰顱顲顳顴页顶顷顸颃预颋颌颎颏颐频颓颔颕颙颖颛颟颡颢颣颤颥颦颧風颮颯颰颱颳颶颷颸颹颻颼颾飁飆飇飉飋飏飐飔飗飘飙飚飛飜飝飛飜飝飛飜飝飛飜飝雊霺霿靁靟鞲韞韭頫顴颿飌飡馺駃騑騜髐鬃鰩鳯鷊黴在古老的东方有一座美丽的城市名为锦绣锦绣城四季如春风景秀丽是人们心中的理想之地城中有一条清澈见底的小河河水潺潺流过滋养着两岸的花草树木河边有一座古老的石桥桥上雕刻着精美的图案见证了无数过往行人锦绣城的居民勤劳善良他们日出而作日落而息过着简单而幸福的生活城中有一位智者名叫慧心慧心先生博学多才深受人们的尊敬他常常在河边的亭子里给孩子们讲述历史故事传授知识和智慧孩子们围坐在他身边听得津津有味仿佛置身于一个个奇妙的世界在锦绣城的北边有一片茂密的森林森林里生活着各种各样的动物有活泼可爱的猴子它们在树上跳跃嬉戏有威武雄壮的老虎它们在林间巡视守护着森林的安宁还有许多不知名的鸟儿它们在枝头欢快地歌唱为森林增添了无限生机一天慧心先生带着几个学生走进森林进行一次生动的自然课他们观察着各种植物的生长了解它们的特性聆听鸟儿的鸣叫感受大自然的和谐学生们兴奋不已他们发现大自然是一个神奇的宝库蕴藏着无尽的奥秘在森林的深处有一片神秘的湖泊湖水碧绿如玉清澈见底传说中湖中住着一位美丽的水仙子她用神奇的力量保护着湖泊和周围的生灵慧心先生告诉学生们要爱护大自然与万物和谐共处才能得到大自然的馈赠锦绣城的居民们深知这一点他们珍惜每一滴水每一寸土地努力保护着这片美丽的家园他们相信只要人与自然和谐相处锦绣城将永远充满生机与活力锦绣城外群山环绕山间云雾缭绕宛如仙境山中有一处幽静的山谷谷中开满了五颜六色的花朵香气扑鼻引得蝴蝶翩翩起舞山谷中还有一条蜿蜒的小径两旁长满了奇花异草仿佛是一条通往神秘世界的通道慧心先生的学生们在一次探险中偶然发现了这个山谷他们惊叹于大自然的鬼斧神工纷纷拿出画笔将这美丽的景色描绘下来他们还发现了一种奇特的植物它的叶子呈现出透明的蓝色在阳光下闪闪发光仿佛蕴含着神秘的力量慧心先生告诉他们这种植物名为“梦幻草”只生长在特定的环境中非常稀有在锦绣城的东边有一片广阔的草原草原上绿草如茵牛羊成群草原上还有一座古老的风车风车的叶片随风旋转发出“吱呀吱呀”的声响仿佛在诉说着岁月的故事草原上的人们以放牧为生他们骑着骏马驰骋在广阔的天地间享受着自由自在的生活一天慧心先生带领学生们来到草原给他们上了一堂生动的地理课他指着远处的山脉告诉学生们山脉的形成过程以及它们对气候和生态环境的影响学生们听得入迷仿佛置身于大自然的怀抱中感受到了大自然的壮丽与神奇在锦绣城的南边有一片茂密的竹林竹林里竹子挺拔绿意盎然竹林中有一条清澈的小溪溪水潺潺流淌溪边长满了青苔竹林里还有一座精致的小亭子亭子的柱子上刻着优美的诗句让人感受到一种宁静与雅致慧心先生常常在竹林中的小亭子里静坐思考人生的哲理他告诉学生们竹子虽然柔弱但却有着坚韧不拔的精神无论遇到多大的困难都能顽强地生长学生们深受启发明白了在人生的道路上要像竹子一样勇敢地面对挑战坚持不懈地追求自己的理想锦绣城的居民们还擅长制作各种手工艺品他们的作品精美绝伦远近闻名有巧夺天工的陶瓷色彩斑斓形态各异有细腻精致的刺绣图案生动栩栩如生还有雕刻精美的木雕栩栩如生令人叹为观止这些手工艺品不仅展现了锦绣城居民的智慧和技艺也成为了他们与外界交流的纽带在锦绣城的中心有一座宏伟的宫殿宫殿的建筑风格独特气势恢宏宫殿的屋顶覆盖着金色的琉璃瓦在阳光下熠熠生辉宫殿的墙壁上雕刻着精美的图案栩栩如生仿佛在诉说着一个个古老的故事宫殿里住着一位英明的君主他以仁爱之心治理国家深受百姓的爱戴慧心先生常常受邀到宫殿中为君主出谋划策帮助他解决国家大事君主也非常尊重慧心先生的智慧常常向他请教治国之道在他们的共同努力下锦绣城国泰民安繁荣昌盛锦绣城的居民们还非常重视教育他们相信知识能够改变命运让孩子们拥有更加美好的未来城中有一座古老的书院书院的建筑古朴典雅环境优美书院里有许多博学的老师他们用心地教导学生传授知识和智慧学生们在这里努力学习汲取知识的养分,为将来的发展打下坚实的基础慧心先生也常常到书院授课他用自己的知识和智慧启迪学生们的思维引导他们去探索未知的世界学生们对慧心先生敬仰有加他们知道只有不断学习才能像慧心先生一样拥有渊博的知识和智慧为社会做出贡献锦绣城的故事还有很多这里的人们用自己的勤劳和智慧创造了一个充满生机与活力的美好家园他们相信只要人与自然和谐相处与他人携手共进锦绣城的明天一定会更加美好=='; + // + // // List imageBytes = utf8.encode(testStr).toList(); + // // List imageBytes = utf8.encode(testStr).toList(); + // // state.talkData.value = imageBytes; + // // setState(() {}); + // String toPeerId = + // '2vzXdjdzipJBpWpJxhiRzCFXrDKk54t3YJ7EjYPSRuij'; + // StartChartManage() + // .sendEchoMessage(payload: imageBytes, toPeerId: toPeerId); + // }, + // ), if (F.isLite) Container() else diff --git a/lib/talk/startChart/entity/scp_message.dart b/lib/talk/startChart/entity/scp_message.dart index a2fbef53..c4f9ee07 100644 --- a/lib/talk/startChart/entity/scp_message.dart +++ b/lib/talk/startChart/entity/scp_message.dart @@ -1,32 +1,11 @@ import 'dart:convert'; -import 'package:get/get.dart'; -import 'package:protobuf/protobuf.dart'; +import 'dart:typed_data'; import 'package:star_lock/app_settings/app_settings.dart'; -import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; -import 'package:star_lock/talk/startChart/constant/payload_type_constant.dart'; -import 'package:star_lock/talk/startChart/entity/heartbeat_response.dart'; -import 'package:star_lock/talk/startChart/entity/login_response.dart'; -import 'package:star_lock/talk/startChart/handle/other/talk_data_repository.dart'; -import 'package:star_lock/talk/startChart/proto/ble_message.pb.dart'; -import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart'; -import 'package:star_lock/talk/startChart/proto/gateway_transfer.pb.dart'; -import 'package:star_lock/talk/startChart/proto/generic.pb.dart'; -import 'package:star_lock/talk/startChart/proto/remote_unlock.pb.dart'; -import 'package:star_lock/talk/startChart/proto/talk_accept.pbserver.dart'; -import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart'; -import 'package:star_lock/talk/startChart/proto/talk_expect.pb.dart'; -import 'package:star_lock/talk/startChart/proto/talk_hangup.pb.dart'; -import 'package:star_lock/talk/startChart/proto/talk_ping.pb.dart'; -import 'package:star_lock/talk/startChart/proto/talk_push.pbserver.dart'; -import 'package:star_lock/talk/startChart/proto/talk_receiver_transfer.pb.dart'; -import 'package:star_lock/talk/startChart/proto/talk_reject.pb.dart'; -import 'package:star_lock/talk/startChart/proto/talk_request.pb.dart'; +import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; +import 'package:star_lock/talk/startChart/handle/scp_message_handler_factory.dart'; + class ScpMessage { - /// 分包缓冲区 - // 存储每个 messageId 对应的分包数据 - static Map>> _packetBuffer = {}; - ScpMessage({ this.ProtocolFlag, this.MessageType, @@ -167,15 +146,16 @@ class ScpMessage { return bytesToHexString; } - static ScpMessage deserialize(List bytes) { + static ScpMessage deserialize(Uint8List bytes) { final message = ScpMessage(); int offset = 0; // Convert byte array to hex string with zero padding and without spaces - String hexString = - bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join(); + // String hexString = + // bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join(); + // + // // Log the hex string + // _log(text: '原始字节数组: $hexString'); - // Log the hex string - _log(text: '原始字节数组: $hexString'); // ProtocolFlag (4 bytes) if (bytes.length - offset >= 4) { message.ProtocolFlag = utf8.decode(bytes.sublist(offset, offset + 4)); @@ -264,7 +244,8 @@ class ScpMessage { // 处理其他类型的Payload if (message.PayloadLength != null && bytes.length - offset >= message.PayloadLength!) { - final sublist = bytes.sublist(offset, offset + message.PayloadLength!); + final Uint8List sublist = + bytes.sublist(offset, offset + message.PayloadLength!); offset += message.PayloadLength!; message.Payload = _handlePayLoad( payloadType: message.PayloadType ?? 0, @@ -276,9 +257,6 @@ class ScpMessage { spTotal: message.SpTotal, messageId: message.MessageId, ); - // if (message.Payload != null && message.Payload is List) { - // message.PayloadLength = message.Payload.length; - // } } else { throw FormatException("Invalid Payload or PayloadLength"); } @@ -290,7 +268,7 @@ class ScpMessage { static dynamic _handlePayLoad({ required int payloadType, required int messageType, - required List byte, + required Uint8List byte, int? offset, int? PayloadLength, int? spTotal, @@ -298,197 +276,20 @@ class ScpMessage { int? messageId, }) { try { - switch (payloadType) { - case PayloadTypeConstant.goOnline: - // 上线 - LoginResponse loginResp = LoginResponse.fromBytes(byte); - return loginResp; - case PayloadTypeConstant.heartbeat: - // 心跳 - HeartbeatResponse heartbeatResponse = - HeartbeatResponse.fromBytes(byte); - return heartbeatResponse; - case PayloadTypeConstant.echoTest: - // 回声测试 - if (spTotal != null && - spTotal > 1 && - messageId != null && - spIndex != null) { - // 分包处理 - return _handleFragmentedPayload( - messageId: messageId, - spTotal: spTotal, - spIndex: spIndex, - byte: byte, - payloadType: payloadType, - ); - } else { - // 如果 spTotal 为 1 或者没有分包信息,直接处理 byte 数据 - String payload = utf8.decode(byte); - return payload; - } - case PayloadTypeConstant.gatewayReset: - // 初始化网关 - if (messageType == MessageTypeConstant.Resp) { - final GatewayResetResp gatewayResetResp = - GatewayResetResp.fromBuffer(byte); - return gatewayResetResp; - } else if (messageType == MessageTypeConstant.Req) { - final GatewayResetReq gatewayResetReq = - GatewayResetReq.fromBuffer(byte); - return gatewayResetReq; - } else { - String payload = utf8.decode(byte); - return payload; - } - case PayloadTypeConstant.callRequest: - if (messageType == MessageTypeConstant.Resp) { - final GenericResp genericResp = GenericResp.fromBuffer(byte); - return genericResp; - } else if (messageType == MessageTypeConstant.Req) { - final TalkReq talkReq = TalkReq.fromBuffer(byte); - return talkReq; - } else { - String payload = utf8.decode(byte); - return payload; - } - case PayloadTypeConstant.talkAccept: - if (messageType == MessageTypeConstant.Resp) { - final GenericResp genericResp = GenericResp.fromBuffer(byte); - return genericResp; - } else if (messageType == MessageTypeConstant.Req) { - final TalkAcceptReq talkAccept = TalkAcceptReq.fromBuffer(byte); - return talkAccept; - } else { - String payload = utf8.decode(byte); - return payload; - } - case PayloadTypeConstant.gatewayTransfer: - if (messageType == MessageTypeConstant.Resp) { - final GenericResp genericResp = GenericResp.fromBuffer(byte); - return genericResp; - } else if (messageType == MessageTypeConstant.Req) { - final GatewayTransferReq gatewayTransferReq = - GatewayTransferReq.fromBuffer(byte); - return gatewayTransferReq; - } else { - String payload = utf8.decode(byte); - return payload; - } - case PayloadTypeConstant.blePassthrough: - final BleResp bleResp = BleResp.fromBuffer(byte); - return bleResp; - case PayloadTypeConstant.remoteUnlock: - if (messageType == MessageTypeConstant.Resp) { - final GenericResp genericResp = GenericResp.fromBuffer(byte); - return genericResp; - } else if (messageType == MessageTypeConstant.Req) { - final RemoteUnlockResp remoteUnlockResp = - RemoteUnlockResp.fromBuffer(byte); - return remoteUnlockResp; - } else { - String payload = utf8.decode(byte); - return payload; - } - case PayloadTypeConstant.talkReceiverTransfer: - if (messageType == MessageTypeConstant.Resp) { - final GenericResp genericResp = GenericResp.fromBuffer(byte); - return genericResp; - } else if (messageType == MessageTypeConstant.Req) { - final TalkReceiverTransfer talkReceiverTransfer = - TalkReceiverTransfer.fromBuffer(byte); - return talkReceiverTransfer; - } else { - String payload = utf8.decode(byte); - return payload; - } - case PayloadTypeConstant.talkPush: - if (messageType == MessageTypeConstant.Resp) { - final GenericResp genericResp = GenericResp.fromBuffer(byte); - return genericResp; - } else if (messageType == MessageTypeConstant.Req) { - final TalkPush talkPush = TalkPush.fromBuffer(byte); - return talkPush; - } else { - String payload = utf8.decode(byte); - return payload; - } - case PayloadTypeConstant.talkReject: - if (messageType == MessageTypeConstant.Resp) { - final GenericResp genericResp = GenericResp.fromBuffer(byte); - return genericResp; - } else if (messageType == MessageTypeConstant.Req) { - final TalkReject talkReject = TalkReject.fromBuffer(byte); - return talkReject; - } else { - String payload = utf8.decode(byte); - return payload; - } - case PayloadTypeConstant.talkPing: - if (messageType == MessageTypeConstant.Resp) { - final GenericResp genericResp = GenericResp.fromBuffer(byte); - return genericResp; - } else if (messageType == MessageTypeConstant.Req) { - final TalkPing talkPing = TalkPing.fromBuffer(byte); - return talkPing; - } else { - String payload = utf8.decode(byte); - return payload; - } - case PayloadTypeConstant.talkExpect: - if (messageType == MessageTypeConstant.Resp) { - final GenericResp genericResp = GenericResp.fromBuffer(byte); - return genericResp; - } else if (messageType == MessageTypeConstant.Req) { - final TalkExpect talkExpect = TalkExpect.fromBuffer(byte); - return talkExpect; - } else { - String payload = utf8.decode(byte); - return payload; - } - case PayloadTypeConstant.talkData: - if (messageType == MessageTypeConstant.Resp) { - final GenericResp genericResp = GenericResp.fromBuffer(byte); - return genericResp; - } else if (messageType == MessageTypeConstant.RealTimeData) { - // 回声测试 - if (spTotal != null && - spTotal > 1 && - messageId != null && - spIndex != null) { - // 分包处理 - return _handleFragmentedPayload( - messageId: messageId, - spTotal: spTotal, - spIndex: spIndex, - byte: byte, - payloadType: payloadType, - ); - } else { - // 没有分包直接解析 - final TalkData talkData = TalkData.fromBuffer(byte); - return talkData; - } - } else { - String payload = utf8.decode(byte); - return payload; - } - case PayloadTypeConstant.talkHangup: - if (messageType == MessageTypeConstant.Resp) { - final GenericResp genericResp = GenericResp.fromBuffer(byte); - return genericResp; - } else if (messageType == MessageTypeConstant.RealTimeData) { - final TalkHangup talkHangup = TalkHangup.fromBuffer(byte); - return talkHangup; - } else { - String payload = utf8.decode(byte); - return payload; - } - default: - print('❌未知的payloadType类型,按照字符串解析'); - String payload = utf8.decode(byte); - return payload; - } + // 构造工厂 + final ScpMessageHandler handler = + ScpMessageHandlerFactory.createHandler(payloadType); + // 处理荷载信息并返回 + return handler.deserializePayload( + payloadType: payloadType, + messageType: messageType, + byte: byte, + offset: offset, + PayloadLength: PayloadLength, + spTotal: spTotal, + spIndex: spIndex, + messageId: messageId, + ); } catch (e, stackTrace) { // 打印异常信息 _log(text: '❌反序列化udp数据时遇到错误----》$e'); @@ -515,49 +316,4 @@ class ScpMessage { static void _log({required String text}) { AppLog.log('=====${text}'); } - - /// 处理分包逻辑 - /// 如果没有收到所有包则返回null - static dynamic _handleFragmentedPayload({ - required int messageId, - required int spTotal, - required int spIndex, - required List byte, - required int payloadType, - }) { - // 初始化分包列表 - if (!_packetBuffer.containsKey(messageId)) { - _packetBuffer[messageId] = List.filled(spTotal, []); - } - - // 检查分包索引是否在合法范围内 - if (spIndex < 1 || spIndex > spTotal) { - print('Invalid spIndex: $spIndex for messageId: $messageId'); - return null; - } - - // 存储当前分包 - _packetBuffer[messageId]![spIndex - 1] = byte; - - // 检查是否接收到所有分包 - if (_packetBuffer[messageId]!.every((packet) => packet.isNotEmpty)) { - // 重组所有分包 - List completePayload = _packetBuffer[messageId]!.expand((packet) => packet).toList(); - - // 清除已重组的分包数据 - _packetBuffer.remove(messageId); - - // 解析完整的 payload - if (payloadType == PayloadTypeConstant.talkData) { - final TalkData talkData = TalkData.fromBuffer(completePayload); - return talkData; - } else { - String payload = utf8.decode(completePayload); - return payload; - } - } else { - // 如果分包尚未接收完全,返回 null 或其他指示符 - return null; - } - } } diff --git a/lib/talk/startChart/handle/impl/udp_ble_passthrough_handler.dart b/lib/talk/startChart/handle/impl/udp_ble_passthrough_handler.dart index 5cc8361b..a09f6aa2 100644 --- a/lib/talk/startChart/handle/impl/udp_ble_passthrough_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_ble_passthrough_handler.dart @@ -1,3 +1,5 @@ +import 'dart:typed_data'; + import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; import 'package:star_lock/appRouters.dart'; @@ -28,4 +30,19 @@ class UdpBlePassThroughHandler extends ScpMessageBaseHandle @override void handleRealTimeData(ScpMessage scpMessage) {} + + @override + deserializePayload( + {required int payloadType, + required int messageType, + required Uint8List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { + final BleResp bleResp = BleResp(); + bleResp.mergeFromBuffer(byte); + return bleResp; + } } diff --git a/lib/talk/startChart/handle/impl/udp_echo_test_handler.dart b/lib/talk/startChart/handle/impl/udp_echo_test_handler.dart index f8b446b3..75ecb89a 100644 --- a/lib/talk/startChart/handle/impl/udp_echo_test_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_echo_test_handler.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; +import 'dart:typed_data'; + import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; @@ -10,15 +13,11 @@ import '../../start_chart_manage.dart'; class UdpEchoTestHandler extends ScpMessageBaseHandle implements ScpMessageHandler { @override - void handleReq(ScpMessage scpMessage) { - // TODO: 收到回声测试请求 - } + void handleReq(ScpMessage scpMessage) {} @override void handleResp(ScpMessage scpMessage) { - // TODO: 收到回声测试回复 final List payload = scpMessage.Payload; - if (payload is String) { EasyLoading.showToast(scpMessage.Payload, duration: 2000.milliseconds); } else { @@ -32,4 +31,34 @@ class UdpEchoTestHandler extends ScpMessageBaseHandle @override void handleRealTimeData(ScpMessage scpMessage) {} + + @override + deserializePayload( + {required int payloadType, + required int messageType, + required Uint8List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { + // 回声测试 + if (spTotal != null && + spTotal > 1 && + messageId != null && + spIndex != null) { + // 分包处理 + return handleFragmentedPayload( + messageId: messageId, + spTotal: spTotal, + spIndex: spIndex, + byte: byte, + payloadType: payloadType, + ); + } else { + // 如果 spTotal 为 1 或者没有分包信息,直接处理 byte 数据 + String payload = utf8.decode(byte); + return payload; + } + } } diff --git a/lib/talk/startChart/handle/impl/udp_gateway_reset_handler.dart b/lib/talk/startChart/handle/impl/udp_gateway_reset_handler.dart index b02bb65b..1fcdd0c6 100644 --- a/lib/talk/startChart/handle/impl/udp_gateway_reset_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_gateway_reset_handler.dart @@ -1,5 +1,9 @@ +import 'dart:convert'; +import 'dart:typed_data'; + import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; +import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; @@ -7,17 +11,14 @@ import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart'; import '../../start_chart_manage.dart'; -class UdpGateWayResetHandler extends ScpMessageBaseHandle implements ScpMessageHandler { - - +class UdpGateWayResetHandler extends ScpMessageBaseHandle + implements ScpMessageHandler { @override void handleReq(ScpMessage scpMessage) { // TODO: 收到网关初始化请求 final GatewayResetResp gatewayResetResp = scpMessage.Payload; final status = gatewayResetResp.status; - if (status != null && status == GatewayResetResp_StatusE.OK) { - - } + if (status != null && status == GatewayResetResp_StatusE.OK) {} } @override @@ -26,12 +27,33 @@ class UdpGateWayResetHandler extends ScpMessageBaseHandle implements ScpMessageH } @override - void handleInvalidReq(ScpMessage scpMessage) { - - } + void handleInvalidReq(ScpMessage scpMessage) {} @override - void handleRealTimeData(ScpMessage scpMessage) { + void handleRealTimeData(ScpMessage scpMessage) {} + @override + deserializePayload( + {required int payloadType, + required int messageType, + required Uint8List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { + // 初始化网关 + if (messageType == MessageTypeConstant.Resp) { + final GatewayResetResp gatewayResetResp = + GatewayResetResp.fromBuffer(byte); + return gatewayResetResp; + } else if (messageType == MessageTypeConstant.Req) { + final GatewayResetReq gatewayResetReq = GatewayResetReq(); + gatewayResetReq.mergeFromBuffer(byte); + return gatewayResetReq; + } else { + String payload = utf8.decode(byte); + return payload; + } } } diff --git a/lib/talk/startChart/handle/impl/udp_gateway_transfer_handler.dart b/lib/talk/startChart/handle/impl/udp_gateway_transfer_handler.dart index 7eae5ae8..c5546e30 100644 --- a/lib/talk/startChart/handle/impl/udp_gateway_transfer_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_gateway_transfer_handler.dart @@ -1,9 +1,14 @@ +import 'dart:convert'; +import 'dart:typed_data'; + import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; +import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart'; +import 'package:star_lock/talk/startChart/proto/gateway_transfer.pb.dart'; import 'package:star_lock/talk/startChart/proto/generic.pb.dart'; import '../../start_chart_manage.dart'; @@ -29,4 +34,28 @@ class UdpGateWayTransferHandler extends ScpMessageBaseHandle @override void handleRealTimeData(ScpMessage scpMessage) {} + + @override + deserializePayload( + {required int payloadType, + required int messageType, + required Uint8List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { + if (messageType == MessageTypeConstant.Resp) { + final GenericResp genericResp = GenericResp(); + genericResp.mergeFromBuffer(byte); + return genericResp; + } else if (messageType == MessageTypeConstant.Req) { + final GatewayTransferReq gatewayTransferReq = GatewayTransferReq(); + gatewayTransferReq.mergeFromBuffer(byte); + return gatewayTransferReq; + } else { + String payload = utf8.decode(byte); + return payload; + } + } } diff --git a/lib/talk/startChart/handle/impl/udp_go_online_handler.dart b/lib/talk/startChart/handle/impl/udp_go_online_handler.dart index 7f703f4a..830301ed 100644 --- a/lib/talk/startChart/handle/impl/udp_go_online_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_go_online_handler.dart @@ -1,3 +1,5 @@ +import 'dart:typed_data'; + import 'package:star_lock/talk/startChart/constant/payload_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/login_response.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; @@ -35,4 +37,19 @@ class UdpGoOnlineHandler extends ScpMessageBaseHandle @override void handleRealTimeData(ScpMessage scpMessage) {} + + @override + deserializePayload( + {required int payloadType, + required int messageType, + required Uint8List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { + // 上线 + LoginResponse loginResp = LoginResponse.fromBytes(byte); + return loginResp; + } } diff --git a/lib/talk/startChart/handle/impl/udp_heart_beat_handler.dart b/lib/talk/startChart/handle/impl/udp_heart_beat_handler.dart index 82b8843b..ed866f63 100644 --- a/lib/talk/startChart/handle/impl/udp_heart_beat_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_heart_beat_handler.dart @@ -1,3 +1,5 @@ +import 'dart:typed_data'; + import 'package:star_lock/talk/startChart/constant/payload_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/heartbeat_response.dart'; import 'package:star_lock/talk/startChart/entity/login_response.dart'; @@ -39,4 +41,11 @@ class UdpHeartBeatHandler extends ScpMessageBaseHandle @override void handleRealTimeData(ScpMessage scpMessage) {} + + @override + deserializePayload({required int payloadType, required int messageType, required Uint8List byte, int? offset, int? PayloadLength, int? spTotal, int? spIndex, int? messageId}) { + // 心跳 + HeartbeatResponse heartbeatResponse = HeartbeatResponse.fromBytes(byte); + return heartbeatResponse; + } } diff --git a/lib/talk/startChart/handle/impl/udp_remote_un_lock_handler.dart b/lib/talk/startChart/handle/impl/udp_remote_un_lock_handler.dart index 4852ee40..03d24701 100644 --- a/lib/talk/startChart/handle/impl/udp_remote_un_lock_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_remote_un_lock_handler.dart @@ -1,6 +1,10 @@ +import 'dart:convert'; +import 'dart:typed_data'; + import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; import 'package:star_lock/appRouters.dart'; +import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; @@ -32,4 +36,20 @@ class UdpRemoteUnLockHandler extends ScpMessageBaseHandle implements ScpMessageH void handleRealTimeData(ScpMessage scpMessage) { } + + @override + deserializePayload({required int payloadType, required int messageType, required Uint8List byte, int? offset, int? PayloadLength, int? spTotal, int? spIndex, int? messageId}) { + if (messageType == MessageTypeConstant.Resp) { + final GenericResp genericResp = GenericResp(); + genericResp.mergeFromBuffer(byte); + return genericResp; + } else if (messageType == MessageTypeConstant.Req) { + final RemoteUnlockResp remoteUnlockResp = RemoteUnlockResp(); + remoteUnlockResp.mergeFromBuffer(byte); + return remoteUnlockResp; + } else { + String payload = utf8.decode(byte); + return payload; + } + } } diff --git a/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart index 0b9510a3..d16f80fd 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart @@ -1,10 +1,15 @@ +import 'dart:convert'; +import 'dart:typed_data'; + import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; +import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart'; import 'package:star_lock/talk/startChart/proto/generic.pb.dart'; +import 'package:star_lock/talk/startChart/proto/talk_accept.pb.dart'; import '../../start_chart_manage.dart'; @@ -22,12 +27,12 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle } @override - void handleResp(ScpMessage scpMessage) { + void handleResp(ScpMessage scpMessage) { // 收到同意接听回复 final GenericResp genericResp = scpMessage.Payload; if (checkGenericRespSuccess(genericResp)) { // 延迟2秒后启动监听 - Future.delayed(Duration(seconds: 4), () { + Future.delayed(Duration(seconds: 5), () { // 启动通话保持定时器 _handleStartTalkPing(); // 启动发送预期数据请求 @@ -49,6 +54,30 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle @override void handleRealTimeData(ScpMessage scpMessage) {} + @override + deserializePayload( + {required int payloadType, + required int messageType, + required Uint8List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { + if (messageType == MessageTypeConstant.Resp) { + final GenericResp genericResp = GenericResp(); + genericResp.mergeFromBuffer(byte); + return genericResp; + } else if (messageType == MessageTypeConstant.Req) { + final TalkAcceptReq talkAccept = TalkAcceptReq(); + talkAccept.mergeFromBuffer(byte); + return talkAccept; + } else { + String payload = utf8.decode(byte); + return payload; + } + } + // 启动通话保持 void _handleStartTalkPing() { // 启动通话保持 @@ -85,4 +114,6 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle // 启动发送预期数据定时器 startChartManage.startTalkExpectTimer(); } + + } diff --git a/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart index 51527d9d..9649533e 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart @@ -1,7 +1,9 @@ +import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; +import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; @@ -61,5 +63,47 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle void _handleVideoImage(TalkData talkData) {} /// 处理g711音频数据 - void _handleVideoG711(TalkData talkData) {} + void _handleVideoG711(TalkData talkData) { + talkDataRepository.addTalkData(talkData); + } + + @override + deserializePayload( + {required int payloadType, + required int messageType, + required Uint8List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { + if (messageType == MessageTypeConstant.Resp) { + final GenericResp genericResp = GenericResp(); + genericResp.mergeFromBuffer(byte); + return genericResp; + } else if (messageType == MessageTypeConstant.RealTimeData) { + // 回声测试 + if (spTotal != null && + spTotal > 1 && + messageId != null && + spIndex != null) { + // 分包处理 + return handleFragmentedPayload( + messageId: messageId, + spTotal: spTotal, + spIndex: spIndex, + byte: byte, + payloadType: payloadType, + ); + } else { + // 没有分包直接解析 + final TalkData talkData = TalkData(); + talkData.mergeFromBuffer(byte); + return talkData; + } + } else { + String payload = utf8.decode(byte); + return payload; + } + } } diff --git a/lib/talk/startChart/handle/impl/udp_talk_expect_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_expect_handler.dart index f7fc3c85..2dda6dab 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_expect_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_expect_handler.dart @@ -1,8 +1,10 @@ +import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter/services.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; +import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; @@ -42,4 +44,28 @@ class UdpTalkExpectHandler extends ScpMessageBaseHandle @override void handleRealTimeData(ScpMessage scpMessage) {} + + @override + deserializePayload( + {required int payloadType, + required int messageType, + required Uint8List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { + if (messageType == MessageTypeConstant.Resp) { + final GenericResp genericResp = GenericResp(); + genericResp.mergeFromBuffer(byte); + return genericResp; + } else if (messageType == MessageTypeConstant.Req) { + final TalkExpect talkExpect = TalkExpect(); + talkExpect.mergeFromBuffer(byte); + return talkExpect; + } else { + String payload = utf8.decode(byte); + return payload; + } + } } diff --git a/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart index d590ee9c..1bebcddc 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart @@ -1,10 +1,15 @@ +import 'dart:convert'; +import 'dart:typed_data'; + import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; +import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart'; import 'package:star_lock/talk/startChart/proto/generic.pb.dart'; +import 'package:star_lock/talk/startChart/proto/talk_hangup.pb.dart'; import '../../start_chart_manage.dart'; @@ -39,12 +44,32 @@ class UdpTalkHangUpHandler extends ScpMessageBaseHandle } @override - void handleInvalidReq(ScpMessage scpMessage) { - - } + void handleInvalidReq(ScpMessage scpMessage) {} @override - void handleRealTimeData(ScpMessage scpMessage) { + void handleRealTimeData(ScpMessage scpMessage) {} + @override + deserializePayload( + {required int payloadType, + required int messageType, + required Uint8List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { + if (messageType == MessageTypeConstant.Resp) { + final GenericResp genericResp = GenericResp(); + genericResp.mergeFromBuffer(byte); + return genericResp; + } else if (messageType == MessageTypeConstant.RealTimeData) { + final TalkHangup talkHangup = TalkHangup(); + talkHangup.mergeFromBuffer(byte); + return talkHangup; + } else { + String payload = utf8.decode(byte); + return payload; + } } } diff --git a/lib/talk/startChart/handle/impl/udp_talk_ping_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_ping_handler.dart index 0c952f48..42b7021a 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_ping_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_ping_handler.dart @@ -1,8 +1,13 @@ +import 'dart:convert'; +import 'dart:typed_data'; + +import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; import 'package:star_lock/talk/startChart/proto/generic.pb.dart'; +import 'package:star_lock/talk/startChart/proto/talk_ping.pb.dart'; class UdpTalkPingHandler extends ScpMessageBaseHandle implements ScpMessageHandler { @@ -26,4 +31,28 @@ class UdpTalkPingHandler extends ScpMessageBaseHandle @override void handleRealTimeData(ScpMessage scpMessage) {} + + @override + deserializePayload( + {required int payloadType, + required int messageType, + required Uint8List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { + if (messageType == MessageTypeConstant.Resp) { + final GenericResp genericResp = GenericResp(); + genericResp.mergeFromBuffer(byte); + return genericResp; + } else if (messageType == MessageTypeConstant.Req) { + final TalkPing talkPing = TalkPing(); + talkPing.mergeFromBuffer(byte); + return talkPing; + } else { + String payload = utf8.decode(byte); + return payload; + } + } } diff --git a/lib/talk/startChart/handle/impl/udp_talk_push_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_push_handler.dart index b7872213..15ecf86e 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_push_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_push_handler.dart @@ -1,10 +1,15 @@ +import 'dart:convert'; +import 'dart:typed_data'; + import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; +import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart'; import 'package:star_lock/talk/startChart/proto/generic.pb.dart'; +import 'package:star_lock/talk/startChart/proto/talk_push.pb.dart'; import '../../start_chart_manage.dart'; @@ -27,4 +32,28 @@ class UdpTalkPushHandler extends ScpMessageBaseHandle @override void handleRealTimeData(ScpMessage scpMessage) {} + + @override + deserializePayload( + {required int payloadType, + required int messageType, + required Uint8List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { + if (messageType == MessageTypeConstant.Resp) { + final GenericResp genericResp = GenericResp(); + genericResp.mergeFromBuffer(byte); + return genericResp; + } else if (messageType == MessageTypeConstant.Req) { + final TalkPush talkPush = TalkPush(); + talkPush.mergeFromBuffer(byte); + return talkPush; + } else { + String payload = utf8.decode(byte); + return payload; + } + } } diff --git a/lib/talk/startChart/handle/impl/udp_talk_receiver_transfer_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_receiver_transfer_handler.dart index 6e662383..168fde66 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_receiver_transfer_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_receiver_transfer_handler.dart @@ -1,17 +1,20 @@ +import 'dart:convert'; +import 'dart:typed_data'; + import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; +import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart'; import 'package:star_lock/talk/startChart/proto/generic.pb.dart'; +import 'package:star_lock/talk/startChart/proto/talk_receiver_transfer.pb.dart'; import '../../start_chart_manage.dart'; class UdpTalkReceiverTransferHandler extends ScpMessageBaseHandle implements ScpMessageHandler { - - @override void handleReq(ScpMessage scpMessage) { // TODO: 对讲接听者转移请求 @@ -23,12 +26,32 @@ class UdpTalkReceiverTransferHandler extends ScpMessageBaseHandle } @override - void handleInvalidReq(ScpMessage scpMessage) { - - } + void handleInvalidReq(ScpMessage scpMessage) {} @override - void handleRealTimeData(ScpMessage scpMessage) { + void handleRealTimeData(ScpMessage scpMessage) {} + @override + deserializePayload( + {required int payloadType, + required int messageType, + required Uint8List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { + if (messageType == MessageTypeConstant.Resp) { + final GenericResp genericResp = GenericResp(); + genericResp.mergeFromBuffer(byte); + return genericResp; + } else if (messageType == MessageTypeConstant.Req) { + final TalkReceiverTransfer talkReceiverTransfer = TalkReceiverTransfer(); + talkReceiverTransfer.mergeFromBuffer(byte); + return talkReceiverTransfer; + } else { + String payload = utf8.decode(byte); + return payload; + } } } diff --git a/lib/talk/startChart/handle/impl/udp_talk_reject_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_reject_handler.dart index 3aab2976..ffcf75da 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_reject_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_reject_handler.dart @@ -1,10 +1,15 @@ +import 'dart:convert'; +import 'dart:typed_data'; + import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; +import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart'; import 'package:star_lock/talk/startChart/proto/generic.pb.dart'; +import 'package:star_lock/talk/startChart/proto/talk_reject.pb.dart'; import '../../start_chart_manage.dart'; @@ -36,12 +41,32 @@ class UdpTalkRejectHandler extends ScpMessageBaseHandle } @override - void handleInvalidReq(ScpMessage scpMessage) { - - } + void handleInvalidReq(ScpMessage scpMessage) {} @override - void handleRealTimeData(ScpMessage scpMessage) { + void handleRealTimeData(ScpMessage scpMessage) {} + @override + deserializePayload( + {required int payloadType, + required int messageType, + required Uint8List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { + if (messageType == MessageTypeConstant.Resp) { + final GenericResp genericResp = GenericResp(); + genericResp.mergeFromBuffer(byte); + return genericResp; + } else if (messageType == MessageTypeConstant.Req) { + final TalkReject talkReject = TalkReject(); + talkReject.mergeFromBuffer(byte); + return talkReject; + } else { + String payload = utf8.decode(byte); + return payload; + } } } diff --git a/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart index 45aaf970..02588870 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart @@ -1,3 +1,6 @@ +import 'dart:convert'; +import 'dart:typed_data'; + import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:get/get.dart'; @@ -9,6 +12,7 @@ import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart'; import 'package:star_lock/talk/startChart/proto/generic.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_request.pb.dart'; +import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; import 'package:star_lock/tools/storage.dart'; import '../../start_chart_manage.dart'; @@ -17,6 +21,10 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle implements ScpMessageHandler { @override void handleReq(ScpMessage scpMessage) { + if (talkStatus == TalkStatus.waitingAnswer) { + // 如果已经是等待接听了,就不在处理剩下的请求 + return; + } // 收到对讲请求 final TalkReq talkReq = scpMessage.Payload; // 回复收到对讲成功的消息 @@ -29,6 +37,8 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle startChartManage.ToPeerId = scpMessage.FromPeerId!; // 处理收到接听请求后的事件 _talkRequestEvent(talkObjectName: talkReq.callerName); + // 设置为等待接听状态 + talkStatus.setWaitingAnswer(); } @override @@ -77,4 +87,28 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle '${'收到来自'.tr}($talkObjectName)${'锁的呼叫'.tr}。', platformChannelSpecifics, payload: 'item x'); } + + @override + deserializePayload( + {required int payloadType, + required int messageType, + required Uint8List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { + if (messageType == MessageTypeConstant.Resp) { + final GenericResp genericResp = GenericResp(); + genericResp.mergeFromBuffer(byte); + return genericResp; + } else if (messageType == MessageTypeConstant.Req) { + final TalkReq talkReq = TalkReq(); + talkReq.mergeFromBuffer(byte); + return talkReq; + } else { + String payload = utf8.decode(byte); + return payload; + } + } } diff --git a/lib/talk/startChart/handle/impl/unknow_payload_type_handler.dart b/lib/talk/startChart/handle/impl/unknow_payload_type_handler.dart index 62c99f35..4aee3b03 100644 --- a/lib/talk/startChart/handle/impl/unknow_payload_type_handler.dart +++ b/lib/talk/startChart/handle/impl/unknow_payload_type_handler.dart @@ -1,3 +1,5 @@ +import 'dart:typed_data'; + import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart'; @@ -27,4 +29,17 @@ class UnKnowPayloadTypeHandler extends ScpMessageBaseHandle void handleRealTimeData(ScpMessage scpMessage) { AppLog.log('❌未知的payloadType类型---》${scpMessage}'); } + + @override + deserializePayload( + {required int payloadType, + required int messageType, + required Uint8List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { + AppLog.log('❌未知的payloadType类型---》'); + } } diff --git a/lib/talk/startChart/handle/scp_message_base_handle.dart b/lib/talk/startChart/handle/scp_message_base_handle.dart index 06597f71..c825f972 100644 --- a/lib/talk/startChart/handle/scp_message_base_handle.dart +++ b/lib/talk/startChart/handle/scp_message_base_handle.dart @@ -1,20 +1,28 @@ +import 'dart:convert'; + import 'package:audioplayers/audioplayers.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/talk/other/audio_player_manager.dart'; +import 'package:star_lock/talk/startChart/constant/payload_type_constant.dart'; import 'package:star_lock/talk/startChart/constant/udp_constant.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/handle/other/overtime_timer_manger.dart'; import 'package:star_lock/talk/startChart/handle/other/talk_data_repository.dart'; import 'package:star_lock/talk/startChart/proto/generic.pb.dart'; +import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart'; import 'package:star_lock/talk/startChart/start_chart_manage.dart'; import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; class ScpMessageBaseHandle { final startChartManage = StartChartManage(); + /// 分包缓冲区 + // 存储每个 messageId 对应的分包数据 + static Map>> _packetBuffer = {}; + // 通话数据流的单例流数据处理类 final TalkDataRepository talkDataRepository = TalkDataRepository.instance; @@ -31,7 +39,7 @@ class ScpMessageBaseHandle { ); // 回复成功消息 - void replySuccessMessage(ScpMessage scpMessage){ + void replySuccessMessage(ScpMessage scpMessage) { startChartManage.sendGenericRespSuccessMessage( ToPeerId: scpMessage.FromPeerId!, FromPeerId: scpMessage.ToPeerId!, @@ -63,4 +71,53 @@ class ScpMessageBaseHandle { void stopRingtone() async { await audioManager.stopRingtone(); } + + /// 处理分包逻辑 + /// 如果没有收到所有包则返回null + dynamic handleFragmentedPayload({ + required int messageId, + required int spTotal, + required int spIndex, + required List byte, + required int payloadType, + }) { + // 初始化分包列表 + if (!_packetBuffer.containsKey(messageId)) { + _packetBuffer[messageId] = List.filled(spTotal, []); + } + + // 检查分包索引是否在合法范围内 + if (spIndex < 1 || spIndex > spTotal) { + print('Invalid spIndex: $spIndex for messageId: $messageId'); + return null; + } + + // 存储当前分包 + _packetBuffer[messageId]![spIndex - 1] = byte; + + // 检查是否接收到所有分包 + if (_packetBuffer[messageId]!.every((packet) => packet.isNotEmpty)) { + // 重组所有分包 + List completePayload = + _packetBuffer[messageId]!.expand((packet) => packet).toList(); + + // 清除已重组的分包数据 + _packetBuffer.remove(messageId); + + // 解析完整的 payload + if (payloadType == PayloadTypeConstant.talkData) { + final TalkData talkData = TalkData(); + talkData.mergeFromBuffer(completePayload); + return talkData; + } else if (payloadType == PayloadTypeConstant.echoTest) { + return completePayload; + } else { + String payload = utf8.decode(completePayload); + return payload; + } + } else { + // 如果分包尚未接收完全,返回 null 或其他指示符 + return null; + } + } } diff --git a/lib/talk/startChart/handle/scp_message_handle.dart b/lib/talk/startChart/handle/scp_message_handle.dart index c7a32916..e43ac425 100644 --- a/lib/talk/startChart/handle/scp_message_handle.dart +++ b/lib/talk/startChart/handle/scp_message_handle.dart @@ -1,3 +1,5 @@ +import 'dart:typed_data'; + import 'package:star_lock/talk/startChart/entity/scp_message.dart'; abstract class ScpMessageHandler { @@ -12,4 +14,16 @@ abstract class ScpMessageHandler { // 无效请求 void handleInvalidReq(ScpMessage scpMessage); + + // 处理荷载 + dynamic deserializePayload({ + required int payloadType, + required int messageType, + required Uint8List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId, + }); } diff --git a/lib/talk/startChart/start_chart_manage.dart b/lib/talk/startChart/start_chart_manage.dart index 790d6ea5..080bf818 100644 --- a/lib/talk/startChart/start_chart_manage.dart +++ b/lib/talk/startChart/start_chart_manage.dart @@ -285,8 +285,7 @@ class StartChartManage { } // 发送回声测试消息 - void sendEchoMessage({required List payload}) async { - String toPeerId = '7SDLN3XMNAQvVAJ2yrbLFfJkKQdgfBrpa8wV7s7TXYcu'; + void sendEchoMessage({required List payload,required String toPeerId}) async { // 计算需要分多少个包发送 final int totalPackets = (payload.length / _maxPayloadSize).ceil(); @@ -764,7 +763,7 @@ class StartChartManage { _handleUdpResultData(deserialize); } if (deserialize.PayloadType != PayloadTypeConstant.heartbeat) { - _log(text: 'Udp收到结构体数据---》$deserialize'); + // _log(text: 'Udp收到结构体数据---》$deserialize'); // _log(text: 'text---》${utf8.decode(deserialize.Payload)}'); } } From 136da703cc36a74e53260ca75b0238078a76f13b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E9=AD=8F=E5=B0=91=E9=98=B3?= <786612630@qq.com> Date: Tue, 24 Dec 2024 14:11:54 +0800 Subject: [PATCH 05/22] =?UTF-8?q?fix:=201=E3=80=81=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E8=B7=9F=E9=9A=8F=E7=B3=BB=E7=BB=9F=E5=8A=9F=E8=83=BD=E3=80=82?= =?UTF-8?q?2=E3=80=81=E4=BF=AE=E5=A4=8D=E7=BD=91=E5=85=B3bug=E3=80=823?= =?UTF-8?q?=E3=80=81=E4=BF=AE=E5=A4=8D=E5=85=B6=E4=BB=96bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner.xcodeproj/project.pbxproj | 8 +- .../xcshareddata/xcschemes/sky.xcscheme | 2 +- .../xcshareddata/xcschemes/xhj.xcscheme | 2 +- ios/Runner/InfoPlist.xcstrings | 120 ++++++++++++++++++ ios/Runner/InfoPlist_sky.xcstrings | 23 ++++ ios/Runner/info_sky.plist | 2 +- ios/Runner/info_xhj.plist | 2 +- lan/lan_ar.json | 3 +- lan/lan_bg.json | 3 +- lan/lan_bn.json | 3 +- lan/lan_cs.json | 3 +- lan/lan_da.json | 3 +- lan/lan_de.json | 3 +- lan/lan_el.json | 3 +- lan/lan_en.json | 3 +- lan/lan_es.json | 3 +- lan/lan_et.json | 3 +- lan/lan_fi.json | 3 +- lan/lan_fr.json | 3 +- lan/lan_he.json | 3 +- lan/lan_hk.json | 37 +++--- lan/lan_hr.json | 3 +- lan/lan_hu.json | 3 +- lan/lan_id.json | 3 +- lan/lan_it.json | 3 +- lan/lan_ja.json | 3 +- lan/lan_keys.json | 3 +- lan/lan_kk.json | 3 +- lan/lan_ko.json | 3 +- lan/lan_lt.json | 3 +- lan/lan_ms.json | 3 +- lan/lan_nl.json | 3 +- lan/lan_pl.json | 3 +- lan/lan_pt.json | 3 +- lan/lan_ro.json | 3 +- lan/lan_ru.json | 3 +- lan/lan_sk.json | 3 +- lan/lan_sr_cyrl.json | 3 +- lan/lan_sv.json | 3 +- lan/lan_th.json | 3 +- lan/lan_tr.json | 3 +- lan/lan_tw.json | 4 +- lan/lan_uk.json | 3 +- lan/lan_vi.json | 3 +- lan/lan_zh.json | 3 +- lib/app.dart | 45 ++++--- lib/blue/blue_manage.dart | 4 +- .../lockMain/xhj/lockMain_xhj_logic.dart | 1 + lib/main_local.dart | 2 +- .../gatewayConnectionLockList_entity.dart | 14 +- .../gatewayConnectionLockList_page.dart | 2 +- .../mineMultiLanguage_logic.dart | 19 ++- .../mineMultiLanguage_page.dart | 5 +- .../mineMultiLanguage_state.dart | 4 +- lib/mine/mineSet/mineSet/mineSet_page.dart | 36 ++++-- lib/mine/mineSet/mineSet/mineSet_state.dart | 14 +- lib/tools/commonItem.dart | 6 +- lib/translations/current_locale_tool.dart | 104 ++++++++++++--- lib/translations/translation_message.dart | 2 +- 59 files changed, 421 insertions(+), 145 deletions(-) create mode 100644 ios/Runner/InfoPlist_sky.xcstrings diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index e988c153..13002562 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -7,6 +7,7 @@ objects = { /* Begin PBXBuildFile section */ + 04BFE7152D14105500A48EC9 /* InfoPlist_sky.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 04BFE7142D14105500A48EC9 /* InfoPlist_sky.xcstrings */; }; 04C1FC572D13F1A2000C959E /* InfoPlist.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 04C1FC562D13F1A2000C959E /* InfoPlist.xcstrings */; }; 04D0CC262D06CE570042EF10 /* launchImage_xhj.png in Resources */ = {isa = PBXBuildFile; fileRef = 82B657662C919BDF0079121C /* launchImage_xhj.png */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; @@ -99,6 +100,7 @@ /* Begin PBXFileReference section */ 0420903B2C0EEAA50073E654 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Main.strings"; sourceTree = ""; }; 04BFC4482BCFE05100688FCA /* RunnerRelease-xhj.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "RunnerRelease-xhj.entitlements"; sourceTree = ""; }; + 04BFE7142D14105500A48EC9 /* InfoPlist_sky.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = InfoPlist_sky.xcstrings; sourceTree = ""; }; 04C1FC562D13F1A2000C959E /* InfoPlist.xcstrings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json.xcstrings; path = InfoPlist.xcstrings; sourceTree = ""; }; 0BEB3ADCCEC961E2916B9004 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 0D02C18E16914A687A4A1AC2 /* devDebug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = devDebug.xcconfig; path = Flutter/devDebug.xcconfig; sourceTree = ""; }; @@ -366,6 +368,7 @@ 82BD91212ADA72360018E523 /* CommonDefine.h */, 76C3321F45DA6AE46ED83204 /* RunnerDebug-dev.entitlements */, 16D90D130794DF74DA656999 /* RunnerRelease-sky.entitlements */, + 04BFE7142D14105500A48EC9 /* InfoPlist_sky.xcstrings */, ); path = Runner; sourceTree = ""; @@ -504,6 +507,7 @@ 805C745A7C79091E65665B96 /* devDebug.xcconfig in Resources */, E0B3E9EE2D04B36C00907A95 /* info_sky.plist in Resources */, D415555B0C61C5422202D037 /* devProfile.xcconfig in Resources */, + 04BFE7152D14105500A48EC9 /* InfoPlist_sky.xcstrings in Resources */, 8A77CDE0EDBCACCE22C29A9E /* devRelease.xcconfig in Resources */, F0A7A6EF7D83CA92324D9C20 /* preDebug.xcconfig in Resources */, E0A496CF2CA30CEF00E376BB /* skyPreviewRelease.xcconfig in Resources */, @@ -704,7 +708,7 @@ CODE_SIGN_ENTITLEMENTS = "Runner/RunnerRelease-sky.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = SF86QP26TZ; + DEVELOPMENT_TEAM = NAQ5PL2DYC; INFOPLIST_FILE = Runner/info_sky.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -1089,7 +1093,7 @@ CODE_SIGN_ENTITLEMENTS = "Runner/RunnerDebug-dev.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = SF86QP26TZ; + DEVELOPMENT_TEAM = NAQ5PL2DYC; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/sky.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/sky.xcscheme index 36a76d0e..018f20fb 100755 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/sky.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/sky.xcscheme @@ -24,7 +24,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - $(BUNDLE_DISPLAY_NAME) + $(BUNDLE_NAME) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/ios/Runner/info_xhj.plist b/ios/Runner/info_xhj.plist index d8ed7c28..e2b52aaa 100755 --- a/ios/Runner/info_xhj.plist +++ b/ios/Runner/info_xhj.plist @@ -7,7 +7,7 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - $(BUNDLE_DISPLAY_NAME) + $(BUNDLE_NAME) CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/lan/lan_ar.json b/lan/lan_ar.json index de21a723..44cffeca 100644 --- a/lan/lan_ar.json +++ b/lan/lan_ar.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "اتبع النظام" } \ No newline at end of file diff --git a/lan/lan_bg.json b/lan/lan_bg.json index e4cbd2e3..6567d1c8 100644 --- a/lan/lan_bg.json +++ b/lan/lan_bg.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Следете система" } \ No newline at end of file diff --git a/lan/lan_bn.json b/lan/lan_bn.json index 94eb47cf..1542a4ab 100644 --- a/lan/lan_bn.json +++ b/lan/lan_bn.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "এক্ফক্লোসিস্টেম" } \ No newline at end of file diff --git a/lan/lan_cs.json b/lan/lan_cs.json index a4f55b06..4954aa0f 100644 --- a/lan/lan_cs.json +++ b/lan/lan_cs.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Sledovat systém" } \ No newline at end of file diff --git a/lan/lan_da.json b/lan/lan_da.json index d5e1d294..ee31f06f 100644 --- a/lan/lan_da.json +++ b/lan/lan_da.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Følg systemet" } \ No newline at end of file diff --git a/lan/lan_de.json b/lan/lan_de.json index 9b95837e..ef466e35 100644 --- a/lan/lan_de.json +++ b/lan/lan_de.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "System folgen" } \ No newline at end of file diff --git a/lan/lan_el.json b/lan/lan_el.json index 6c459d12..8615448e 100644 --- a/lan/lan_el.json +++ b/lan/lan_el.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Ακολουθήστε το σύστημα" } \ No newline at end of file diff --git a/lan/lan_en.json b/lan/lan_en.json index 2bc8eeaf..e203f2c4 100644 --- a/lan/lan_en.json +++ b/lan/lan_en.json @@ -1121,5 +1121,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Follow system" } diff --git a/lan/lan_es.json b/lan/lan_es.json index 79c49887..6be6e12e 100644 --- a/lan/lan_es.json +++ b/lan/lan_es.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Seguir sistema" } \ No newline at end of file diff --git a/lan/lan_et.json b/lan/lan_et.json index 23cbb933..36e7dbd6 100644 --- a/lan/lan_et.json +++ b/lan/lan_et.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Süsteemi jälgimine" } \ No newline at end of file diff --git a/lan/lan_fi.json b/lan/lan_fi.json index 9141b427..be6b3412 100644 --- a/lan/lan_fi.json +++ b/lan/lan_fi.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Seuraa järjestelmää" } \ No newline at end of file diff --git a/lan/lan_fr.json b/lan/lan_fr.json index 43debbe2..41653eba 100644 --- a/lan/lan_fr.json +++ b/lan/lan_fr.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Suivre le système" } \ No newline at end of file diff --git a/lan/lan_he.json b/lan/lan_he.json index c0bd0c01..964fde7b 100644 --- a/lan/lan_he.json +++ b/lan/lan_he.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "מערכת מעקב" } \ No newline at end of file diff --git a/lan/lan_hk.json b/lan/lan_hk.json index 977e1f37..d7ad3f6c 100644 --- a/lan/lan_hk.json +++ b/lan/lan_hk.json @@ -1046,39 +1046,39 @@ "英文": "英语", "简体中文": "简体中文", "繁体中文": "繁體中文", - "法语": "法语", + "法语": "Français", "俄语": "Русский", - "德语": "多伊奇", + "德语": "Deutsch", "日语": "日本語", - "韩语": "嘻嘻哈哈", - "意大利语": "意大利语", + "韩语": "한국어", + "意大利语": "Italiano", "乌克兰语": "Українська", - "葡萄牙语": "葡萄牙商业银行", - "西班牙语": "西班牙语", + "葡萄牙语": "Português", + "西班牙语": "Español", "阿拉伯语": "العربية", "越南语": "Tiếng Việt", - "马来语": "马来语", - "荷兰语": "荷兰语", - "罗马尼亚语": "罗马ă", + "马来语": "Bahasa Melayu", + "荷兰语": "Nederlands", + "罗马尼亚语": "Română", "立陶宛语": "Lietuvių", - "瑞典语": "瑞典语", + "瑞典语": "Svenska", "爱沙尼亚语": "Eesti", - "波兰语": "波兰语", - "斯洛伐克语": "懒散的人č艾娜", - "捷克语": "Češ蒂娜", + "波兰语": "Polski", + "斯洛伐克语": "Slovenčina", + "捷克语": "Čeština", "希腊语": "Ελληνικά", "希伯来语": "עברית", "塞尔维亚语": "Српски", "土耳其语": "Turkce", - "匈牙利语": "马札尔人的", + "匈牙利语": "Magyar", "保加利亚语": "Български", "哈萨克斯坦语": "Қазақ", "孟加拉语": "বাংলা", "克罗地亚语": "Hrvatski", "泰语": "ไทย", - "印度尼西亚语": "印度尼西亚语", - "芬兰语": "芬兰语", - "丹麦语": "丹麦语", + "印度尼西亚语": "Bahasa Indonesia", + "芬兰语": "Suomi", + "丹麦语": "Dansk", "重置后,该锁的掌静脉都将被删除哦,确认要重置吗?": "重置后,锁上的掌骨静脉会被删除。你确定要重置吗?", "在线": "在线", "离线": "离线", @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "跟踪系统" } \ No newline at end of file diff --git a/lan/lan_hr.json b/lan/lan_hr.json index d4dd8fd3..557389ba 100644 --- a/lan/lan_hr.json +++ b/lan/lan_hr.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Prati sistem:" } \ No newline at end of file diff --git a/lan/lan_hu.json b/lan/lan_hu.json index bed7ca9a..58ad7bbf 100644 --- a/lan/lan_hu.json +++ b/lan/lan_hu.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Follow system" } \ No newline at end of file diff --git a/lan/lan_id.json b/lan/lan_id.json index e494a7b5..71306282 100644 --- a/lan/lan_id.json +++ b/lan/lan_id.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Sistem mengikuti" } \ No newline at end of file diff --git a/lan/lan_it.json b/lan/lan_it.json index 2434772c..93144303 100644 --- a/lan/lan_it.json +++ b/lan/lan_it.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Seguire il sistema" } \ No newline at end of file diff --git a/lan/lan_ja.json b/lan/lan_ja.json index be431bac..39576260 100644 --- a/lan/lan_ja.json +++ b/lan/lan_ja.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "システムに従う" } \ No newline at end of file diff --git a/lan/lan_keys.json b/lan/lan_keys.json index a26d6864..1656967f 100755 --- a/lan/lan_keys.json +++ b/lan/lan_keys.json @@ -1121,5 +1121,6 @@ "月简称": "月简称", "日简称": "日简称", "时简称": "时简称", - "分简称": "分简称" + "分简称": "分简称", + "跟随系统": "跟随系统" } diff --git a/lan/lan_kk.json b/lan/lan_kk.json index 1c8871ae..ca429781 100644 --- a/lan/lan_kk.json +++ b/lan/lan_kk.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Жүйені қолдану" } \ No newline at end of file diff --git a/lan/lan_ko.json b/lan/lan_ko.json index 589b5633..11ff733a 100644 --- a/lan/lan_ko.json +++ b/lan/lan_ko.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "시스템을 따르십시오" } \ No newline at end of file diff --git a/lan/lan_lt.json b/lan/lan_lt.json index 84e203e1..1c203b01 100644 --- a/lan/lan_lt.json +++ b/lan/lan_lt.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Sekti sistema" } \ No newline at end of file diff --git a/lan/lan_ms.json b/lan/lan_ms.json index d6b27146..3d3a59e7 100644 --- a/lan/lan_ms.json +++ b/lan/lan_ms.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Ikut system" } \ No newline at end of file diff --git a/lan/lan_nl.json b/lan/lan_nl.json index 82ed9aac..07669fa1 100644 --- a/lan/lan_nl.json +++ b/lan/lan_nl.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Systeem volgen" } \ No newline at end of file diff --git a/lan/lan_pl.json b/lan/lan_pl.json index 7ca0add8..b50c37e6 100644 --- a/lan/lan_pl.json +++ b/lan/lan_pl.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Śledź system" } \ No newline at end of file diff --git a/lan/lan_pt.json b/lan/lan_pt.json index 5660f8cf..a08af884 100644 --- a/lan/lan_pt.json +++ b/lan/lan_pt.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Siga o sistema" } \ No newline at end of file diff --git a/lan/lan_ro.json b/lan/lan_ro.json index 18926d79..6d572f88 100644 --- a/lan/lan_ro.json +++ b/lan/lan_ro.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Urmează sistemul:" } \ No newline at end of file diff --git a/lan/lan_ru.json b/lan/lan_ru.json index 5fb8f68b..08faace8 100644 --- a/lan/lan_ru.json +++ b/lan/lan_ru.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Следуйте системе" } \ No newline at end of file diff --git a/lan/lan_sk.json b/lan/lan_sk.json index f1d7df2d..cadd2541 100644 --- a/lan/lan_sk.json +++ b/lan/lan_sk.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Sledovať systém" } \ No newline at end of file diff --git a/lan/lan_sr_cyrl.json b/lan/lan_sr_cyrl.json index be13221a..e79136b2 100644 --- a/lan/lan_sr_cyrl.json +++ b/lan/lan_sr_cyrl.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Пратите систем" } \ No newline at end of file diff --git a/lan/lan_sv.json b/lan/lan_sv.json index 0a1bbc5b..9235a008 100644 --- a/lan/lan_sv.json +++ b/lan/lan_sv.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Följ system" } \ No newline at end of file diff --git a/lan/lan_th.json b/lan/lan_th.json index 94dda514..e4020c44 100644 --- a/lan/lan_th.json +++ b/lan/lan_th.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "ระบบติดตามผล" } \ No newline at end of file diff --git a/lan/lan_tr.json b/lan/lan_tr.json index cd306451..04d9df57 100644 --- a/lan/lan_tr.json +++ b/lan/lan_tr.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Sistemi takip et" } \ No newline at end of file diff --git a/lan/lan_tw.json b/lan/lan_tw.json index 38ea07af..39b35337 100644 --- a/lan/lan_tw.json +++ b/lan/lan_tw.json @@ -1118,6 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" - + "分简称": "M", + "跟随系统": "跟蹤系統" } \ No newline at end of file diff --git a/lan/lan_uk.json b/lan/lan_uk.json index 18d523a5..7bef26fa 100644 --- a/lan/lan_uk.json +++ b/lan/lan_uk.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Система стеження за" } \ No newline at end of file diff --git a/lan/lan_vi.json b/lan/lan_vi.json index 3fac30ad..2584157d 100644 --- a/lan/lan_vi.json +++ b/lan/lan_vi.json @@ -1118,5 +1118,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "Hệ thống theo dõi" } \ No newline at end of file diff --git a/lan/lan_zh.json b/lan/lan_zh.json index 20d398f7..522b21da 100755 --- a/lan/lan_zh.json +++ b/lan/lan_zh.json @@ -1122,5 +1122,6 @@ "月简称": "M", "日简称": "D", "时简称": "H", - "分简称": "M" + "分简称": "M", + "跟随系统": "跟随系统" } diff --git a/lib/app.dart b/lib/app.dart index fad7b31d..bc3b4bff 100755 --- a/lib/app.dart +++ b/lib/app.dart @@ -33,9 +33,14 @@ class MyApp extends StatefulWidget { class _MyAppState extends State with WidgetsBindingObserver, BaseWidget { @override Widget build(BuildContext context) { - return ScreenUtilInit( - designSize: const Size(585, 1265), - builder: (BuildContext w, Widget? a) => _initMaterialApp()); + return FutureBuilder( + future: Get.updateLocale(CurrentLocaleTool.getCurrentLocale()), + builder: (BuildContext context, AsyncSnapshot snapshot) { + return ScreenUtilInit( + designSize: const Size(585, 1265), + builder: (BuildContext w, Widget? a) => _initMaterialApp()); + }, + ); } GetMaterialApp _initMaterialApp() { @@ -60,23 +65,23 @@ class _MyAppState extends State with WidgetsBindingObserver, BaseWidget { GlobalWidgetsLocalizations.delegate, ], // localeResolutionCallback用于在启动时动态确定应该使用哪种语言和地区。 - localeResolutionCallback: - (Locale? locale, Iterable supportedLocales) { - AppLog.log( - 'System device locale: ${Get.deviceLocale} locale:$locale'); - // if (widget.isLogin) { - // 登录之后调用存储的语言 - locale = CurrentLocaleTool.getCurrentLocale(); - // } else { - // // 没登录之前调用系统的语言 - // locale = Get.deviceLocale; - // } - locale = CurrentLocaleTool.convertLocale(locale); - AppLog.log('localeResolutionCallback locale: $locale'); - AppManager() - .setLanCode(code: '${locale.languageCode}_${locale.countryCode}'); - return Locale(locale.languageCode, locale.countryCode); - }, + // localeResolutionCallback: + // (Locale? locale, Iterable supportedLocales) { + // AppLog.log( + // 'System device locale: ${Get.deviceLocale} locale:$locale'); + // // if (widget.isLogin) { + // // 登录之后调用存储的语言 + // locale = CurrentLocaleTool.getCurrentLocale(); + // // } else { + // // // 没登录之前调用系统的语言 + // // locale = Get.deviceLocale; + // // } + // locale = CurrentLocaleTool.convertLocale(locale); + // AppLog.log('localeResolutionCallback locale: $locale'); + // AppManager() + // .setLanCode(code: '${locale.languageCode}_${locale.countryCode}'); + // return Locale(locale.languageCode, locale.countryCode); + // }, // 用来指定应用当前使用的语言和地区。它定义了应用在启动时加载的默认语言环境。 locale: CurrentLocaleTool.getCurrentLocale(), // locale: Get.deviceLocale, diff --git a/lib/blue/blue_manage.dart b/lib/blue/blue_manage.dart index eea8f7bc..97873775 100755 --- a/lib/blue/blue_manage.dart +++ b/lib/blue/blue_manage.dart @@ -213,9 +213,7 @@ class BlueManage { message: '指定设备名称的扫描蓝牙设备 监听扫描结果失败', detail: '打印失败问题 e:${e.toString()}', upload: false); - AppLog.log( - '扫描失败:$e', - ); + AppLog.log('扫描失败:$e'); }); FlutterBluePlus.cancelWhenScanComplete(subscription); await completer.future; diff --git a/lib/main/lockMian/lockMain/xhj/lockMain_xhj_logic.dart b/lib/main/lockMian/lockMain/xhj/lockMain_xhj_logic.dart index 71b0f5db..6085bbdd 100755 --- a/lib/main/lockMian/lockMain/xhj/lockMain_xhj_logic.dart +++ b/lib/main/lockMian/lockMain/xhj/lockMain_xhj_logic.dart @@ -31,6 +31,7 @@ class LockMainXHJLogic extends BaseGetXController { final String languageCodeAndCountryCode = entity.data!.lang!; if (languageCodeAndCountryCode.isEmpty) { await StoreService.to.saveLanguageCode(''); + await Get.updateLocale(Get.deviceLocale!); } else if (languageCodeAndCountryCode.contains('-')) { final List parts = languageCodeAndCountryCode.split('-'); final Locale locale = Locale(parts[0], parts[1]); diff --git a/lib/main_local.dart b/lib/main_local.dart index cacb90f6..bd256362 100755 --- a/lib/main_local.dart +++ b/lib/main_local.dart @@ -4,7 +4,7 @@ import 'flavors.dart'; import 'main.dart' as runner; Future main() async { - F.appFlavor = Flavor.local; + F.appFlavor = Flavor.sky; // AppLog.log('local调用了main函数'); await runner.main(); } diff --git a/lib/mine/gateway/gatewayConnectionLock/gatewayConnectionLockList_entity.dart b/lib/mine/gateway/gatewayConnectionLock/gatewayConnectionLockList_entity.dart index 30517dc5..1edb363e 100644 --- a/lib/mine/gateway/gatewayConnectionLock/gatewayConnectionLockList_entity.dart +++ b/lib/mine/gateway/gatewayConnectionLock/gatewayConnectionLockList_entity.dart @@ -1,5 +1,4 @@ class GatewayConnectionLockListEntity { - GatewayConnectionLockListEntity( {this.errorCode, this.description, this.errorMsg, this.data}); @@ -27,7 +26,6 @@ class GatewayConnectionLockListEntity { } class Data { - Data({this.list, this.total, this.pageNo, this.pageSize}); Data.fromJson(Map json) { @@ -59,23 +57,23 @@ class Data { } class GatewayConnectionLockItemEntity { - - GatewayConnectionLockItemEntity({this.lockId, this.lockAlias, this.rssi}); + GatewayConnectionLockItemEntity( + {this.lockId, this.lockAlias, this.gatewayRssi}); GatewayConnectionLockItemEntity.fromJson(Map json) { lockId = json['lockId']; lockAlias = json['lockAlias']; - rssi = json['rssi']; + gatewayRssi = json['gatewayRssi']; } int? lockId; String? lockAlias; - int? rssi; + int? gatewayRssi; Map toJson() { final Map data = {}; data['lockId'] = lockId; data['lockAlias'] = lockAlias; - data['rssi'] = rssi; + data['gatewayRssi'] = gatewayRssi; return data; } -} \ No newline at end of file +} diff --git a/lib/mine/gateway/gatewayConnectionLock/gatewayConnectionLockList_page.dart b/lib/mine/gateway/gatewayConnectionLock/gatewayConnectionLockList_page.dart index 803d8baa..7eaaa516 100755 --- a/lib/mine/gateway/gatewayConnectionLock/gatewayConnectionLockList_page.dart +++ b/lib/mine/gateway/gatewayConnectionLock/gatewayConnectionLockList_page.dart @@ -56,7 +56,7 @@ class _GatewayConnectionLockListPageState return _gatewayConnectionLockListItem( 'images/mine/icon_mine_gatewaySignal_prompt.png', entity.lockAlias ?? '', - '信号强'.tr, () { + entity.gatewayRssi.toString(), () { // Navigator.pushNamed(context, Routers.gatewayDetailPage); }); }) diff --git a/lib/mine/mineMultiLanguage/mineMultiLanguage_logic.dart b/lib/mine/mineMultiLanguage/mineMultiLanguage_logic.dart index 459615f6..c63773bb 100755 --- a/lib/mine/mineMultiLanguage/mineMultiLanguage_logic.dart +++ b/lib/mine/mineMultiLanguage/mineMultiLanguage_logic.dart @@ -15,22 +15,27 @@ class MineMultiLanguageLogic extends GetxController { //更新个人信息 Future updateUserLangInfo(Locale l) async { - // AppLog.log('lanTypeTitle: $lanTypeTitle'); + AppLog.log('selet: ${l.toString()}}'); String lang = l.toString(); if (state.currentLanguageType.value == LanguageType.system) { lang = ''; } + String currentLanguage = StoreService.to.getLanguageCode()!; + await StoreService.to.saveLanguageCode(lang); final PasswordKeyListEntity entity = await ApiRepository.to.updateUserLangInfo(lang: lang); if (entity.errorCode!.codeIsSuccessful) { - await changeLanguage(l); + await Get.updateLocale(l); + AppLog.log('saveLocale: ${l.toString()}}'); eventBus.fire(ChangeLanguageBlockLastLanguageEvent()); + } else { + await StoreService.to.saveLanguageCode(currentLanguage); } } - Future changeLanguage(Locale l) async { - await Get.updateLocale(l); - await StoreService.to.saveLanguageCode( - CurrentLocaleTool.convertLocale(Get.locale!).toString()); - } + // Future changeLanguage(Locale l) async { + // await StoreService.to.saveLanguageCode(l.toString()); + // await Get.updateLocale(l); + // AppLog.log('saveLocale: ${l.toString()}}'); + // } } diff --git a/lib/mine/mineMultiLanguage/mineMultiLanguage_page.dart b/lib/mine/mineMultiLanguage/mineMultiLanguage_page.dart index 87e1c395..86e75fe5 100755 --- a/lib/mine/mineMultiLanguage/mineMultiLanguage_page.dart +++ b/lib/mine/mineMultiLanguage/mineMultiLanguage_page.dart @@ -36,7 +36,8 @@ class _MineMultiLanguagePageState extends State { final String lanTypeTitle = ExtensionLanguageType.fromLocale(state.seletLocale) .lanTitle; - Get.back(result: {'currentLanguage': lanTypeTitle}); + Get.back( + result: {'currentLanguage': state.seletLocale.toString()}); }, child: Text( '保存'.tr, @@ -59,7 +60,7 @@ class _MineMultiLanguagePageState extends State { final List l = []; l.add( CommonItem( - leftTitel: '跟随系统', + leftTitel: '跟随系统'.tr, rightTitle: '', isHaveLine: true, isHaveDirection: false, diff --git a/lib/mine/mineMultiLanguage/mineMultiLanguage_state.dart b/lib/mine/mineMultiLanguage/mineMultiLanguage_state.dart index c424b8e9..a91eba99 100755 --- a/lib/mine/mineMultiLanguage/mineMultiLanguage_state.dart +++ b/lib/mine/mineMultiLanguage/mineMultiLanguage_state.dart @@ -15,7 +15,9 @@ class MineMultiLanguageState { } Rx currentLanguageType = - ExtensionLanguageType.fromLocale(Get.locale!).obs; + StoreService.to.getLanguageCode()!.isEmpty + ? LanguageType.system.obs + : ExtensionLanguageType.fromLocale(Get.locale!).obs; late Locale seletLocale; } diff --git a/lib/mine/mineSet/mineSet/mineSet_page.dart b/lib/mine/mineSet/mineSet/mineSet_page.dart index 3cb15f4e..e0835c62 100755 --- a/lib/mine/mineSet/mineSet/mineSet_page.dart +++ b/lib/mine/mineSet/mineSet/mineSet_page.dart @@ -2,17 +2,14 @@ import 'dart:io'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/widgets.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; -import 'package:get/get_state_manager/get_state_manager.dart'; import 'package:permission_handler/permission_handler.dart'; import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/flavors.dart'; import 'package:star_lock/mine/mineSet/mineSet/mineSet_logic.dart'; import 'package:star_lock/mine/mineSet/mineSet/mineSet_state.dart'; -import 'package:star_lock/tools/langue/langue_tool.dart'; import 'package:star_lock/tools/wechat/customer_tool.dart'; import '../../../appRouters.dart'; import '../../../app_settings/app_colors.dart'; @@ -22,7 +19,8 @@ import '../../../tools/submitBtn.dart'; import '../../../tools/titleAppBar.dart'; import '../../../tools/wechat/wechatManageTool.dart'; import '../../../tools/wechat/wx_push_miniProgram/wx_push_miniProgram.dart'; -import '../../../translations/trans_lib.dart'; +import '../../../translations/app_dept.dart'; +import '../../../translations/current_locale_tool.dart'; class MineSetPage extends StatefulWidget { MineSetPage({Key? key, this.showAppBar = true, this.showAbout = false}) @@ -303,10 +301,10 @@ class _MineSetPageState extends State isHaveDirection: true, action: () async { // Get.toNamed(Routers.mineMultiLanguagePage); - await Get.toNamed(Routers.mineMultiLanguagePage)!.then( - (value) => { - state.currentLanguage.value = value['currentLanguage'] - }); + await Get.toNamed(Routers.mineMultiLanguagePage)!.then((value) { + state.currentLanguage.value = value['currentLanguage']; + setState(() {}); + }); }); }), /* 2024-01-12 会议确定去掉“锁屏” by DaisyWu @@ -516,11 +514,23 @@ class _MineSetPageState extends State Expanded( child: Text(leftTitle!, style: TextStyle(fontSize: 22.sp))), SizedBox(width: 10.w), - Image.asset( - 'images/icon_right_grey.png', - width: 12.w, - height: 21.w, - ), + if (CurrentLocaleTool.getCurrentLocaleString() == + ExtensionLanguageType.fromLanguageType(LanguageType.hebrew) + .toString() || + CurrentLocaleTool.getCurrentLocaleString() == + ExtensionLanguageType.fromLanguageType(LanguageType.arabic) + .toString()) + Image.asset( + 'images/icon_left_grey.png', + width: 21.w, + height: 21.w, + ) + else + Image.asset( + 'images/icon_right_grey.png', + width: 12.w, + height: 21.w, + ), SizedBox(width: 5.w), ], ), diff --git a/lib/mine/mineSet/mineSet/mineSet_state.dart b/lib/mine/mineSet/mineSet/mineSet_state.dart index cae4519d..4962ff92 100755 --- a/lib/mine/mineSet/mineSet/mineSet_state.dart +++ b/lib/mine/mineSet/mineSet/mineSet_state.dart @@ -1,8 +1,10 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:get/get.dart'; +import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/mine/minePersonInfo/minePersonInfoPage/minePersonInfo_entity.dart'; import 'package:star_lock/mine/mineSet/mineSet/userSettingInfoEntity.dart'; +import '../../../tools/store_service.dart'; import '../../../translations/app_dept.dart'; import '../../../translations/current_locale_tool.dart'; @@ -24,10 +26,14 @@ class MineSetState { /// 获取翻译后的国家名称 String get currentLanguageName { - return ExtensionLanguageType.fromLocale( - CurrentLocaleTool.getCurrentLocaleWithLanguageCode( - currentLanguage.value)) - .lanTitle; + AppLog.log( + 'currentLanguage.value:${currentLanguage.value} fsgdsgddfg:${CurrentLocaleTool.getCurrentLocaleWithLanguageCode(currentLanguage.value)}'); + return StoreService.to.getLanguageCode()!.isEmpty + ? '跟随系统'.tr + : ExtensionLanguageType.fromLocale(CurrentLocaleTool.convertLocale( + CurrentLocaleTool.getCurrentLocaleWithLanguageCode( + currentLanguage.value))) + .lanTitle; } RxBool isAmazonAlexa = false.obs; //亚马逊Alexa diff --git a/lib/tools/commonItem.dart b/lib/tools/commonItem.dart index 30e36dd9..2a9d1c1d 100755 --- a/lib/tools/commonItem.dart +++ b/lib/tools/commonItem.dart @@ -93,13 +93,11 @@ class CommonItem extends StatelessWidget { if (CurrentLocaleTool.getCurrentLocaleString() == ExtensionLanguageType.fromLanguageType( LanguageType.hebrew) - .toLanguageTag() || + .toString() || CurrentLocaleTool.getCurrentLocaleString() == ExtensionLanguageType.fromLanguageType( LanguageType.arabic) - .toLanguageTag() || - rightTitle == '希伯来语'.tr || - rightTitle == '阿拉伯语'.tr) + .toString()) Image.asset( 'images/icon_left_grey.png', width: 21.w, diff --git a/lib/translations/current_locale_tool.dart b/lib/translations/current_locale_tool.dart index cc883bd4..f9793967 100644 --- a/lib/translations/current_locale_tool.dart +++ b/lib/translations/current_locale_tool.dart @@ -8,8 +8,6 @@ import 'app_dept.dart'; class CurrentLocaleTool { /// 获取当前语言的Locale字符串,没有的话获取系统的 static String getCurrentLocaleString() { - AppLog.log( - '11111StoreService.to.getLanguageCode():${StoreService.to.getLanguageCode()}'); final Locale locale = StoreService.to.getLanguageCode()!.isNotEmpty ? appDept.deptSupportedLocales .where((Locale element) => @@ -21,14 +19,12 @@ class CurrentLocaleTool { : Get.deviceLocale!; // Get.deviceLocale; final String languageCode = convertLocale(locale).toString(); AppLog.log( - '11111locale.toString(): ${locale.toString()} locale: $locale languageCode:$languageCode'); + '11111locale.toString(): ${locale.toString()} locale: $locale languageCode:$languageCode 从本地获取code:${StoreService.to.getLanguageCode()}'); return languageCode; } /// 获取当前储存的Locale,没有的话获取系统的 static Locale getCurrentLocale() { - AppLog.log( - '222StoreService.to.getLanguageCode():${StoreService.to.getLanguageCode()}'); final Locale locale = StoreService.to.getLanguageCode()!.isNotEmpty ? appDept.deptSupportedLocales .where((Locale element) => @@ -40,7 +36,7 @@ class CurrentLocaleTool { : Get.deviceLocale!; // Get.deviceLocale; final Locale getLocale = convertLocale(locale); AppLog.log( - '222locale.toString(): ${locale.toString()} locale: $locale getLocale:$getLocale'); + '222locale.toString(): ${locale.toString()} locale: $locale getLocale:$getLocale 从本地获取code:${StoreService.to.getLanguageCode()}'); return getLocale; } @@ -55,23 +51,95 @@ class CurrentLocaleTool { } } - return const Locale('en', 'US'); + return Get.deviceLocale!; } /// 国际化中文繁体及中文的转化 static Locale convertLocale(Locale locale) { - if (locale.languageCode == 'zh') { - if (locale.scriptCode == 'Hans') { - // 简体中文 - return const Locale('zh', 'CN'); - } else if (locale.scriptCode == 'Hant') { - // 繁体中文 - if (locale.countryCode == 'CN') { - return const Locale('zh', 'TW'); - } else if (locale.countryCode == 'HK') { - return const Locale('zh', 'HK'); + switch (locale.languageCode) { + case 'zh': + if (locale.scriptCode == 'Hans') { + // 简体中文 + return const Locale('zh', 'CN'); + } else if (locale.scriptCode == 'Hant') { + // 繁体中文 + if (locale.countryCode == 'CN') { + return const Locale('zh', 'TW'); + } else if (locale.countryCode == 'HK') { + return const Locale('zh', 'HK'); + } } - } + break; + case 'en': + return const Locale('en', 'US'); + case 'fr': + return const Locale('fr', 'FR'); + case 'ru': + return const Locale('ru', 'RU'); + case 'de': + return const Locale('de', 'DE'); + case 'ja': + return const Locale('ja', 'JP'); + case 'ko': + return const Locale('ko', 'KR'); + case 'it': + return const Locale('it', 'IT'); + case 'uk': + return const Locale('uk', 'UA'); + case 'pt': + return const Locale('pt', 'PT'); + case 'es': + return const Locale('es', 'ES'); + case 'ar': + return const Locale('ar', 'SA'); + case 'vi': + return const Locale('vi', 'VN'); + case 'ms': + return const Locale('ms', 'MY'); + case 'nl': + return const Locale('nl', 'NL'); + case 'ro': + return const Locale('ro', 'RO'); + case 'lt': + return const Locale('lt', 'LT'); + case 'sv': + return const Locale('sv', 'SE'); + case 'et': + return const Locale('et', 'EE'); + case 'pl': + return const Locale('pl', 'PL'); + case 'sk': + return const Locale('sk', 'SK'); + case 'cs': + return const Locale('cs', 'CZ'); + case 'el': + return const Locale('el', 'GR'); + case 'he': + return const Locale('he', 'IL'); + case 'sr': + return const Locale('sr', 'RS'); + case 'tr': + return const Locale('tr', 'TR'); + case 'hu': + return const Locale('hu', 'HU'); + case 'bg': + return const Locale('bg', 'BG'); + case 'kk': + return const Locale('kk', 'KZ'); + case 'bn': + return const Locale('bn', 'BD'); + case 'hr': + return const Locale('hr', 'BA'); + case 'th': + return const Locale('th', 'TH'); + case 'id': + return const Locale('id', 'ID'); + case 'fi': + return const Locale('fi', 'FI'); + case 'da': + return const Locale('da', 'DK'); + default: + return locale; } // 默认返回 Locale 的完整格式 return locale; diff --git a/lib/translations/translation_message.dart b/lib/translations/translation_message.dart index eafecdf9..b20ed713 100755 --- a/lib/translations/translation_message.dart +++ b/lib/translations/translation_message.dart @@ -6,7 +6,7 @@ class TranslationMessage extends Translations { 'zh_CN': TranslationLoader.zhDic, 'en_US': TranslationLoader.enDic, 'zh_TW': TranslationLoader.twDic, - 'hk_HK': TranslationLoader.hkDic, + 'zh_HK': TranslationLoader.hkDic, 'fr_FR': TranslationLoader.frDic, 'ru_RU': TranslationLoader.ruDic, 'de_DE': TranslationLoader.deDic, From a20206dfafd3b0254743553df0d55d51cb76af54 Mon Sep 17 00:00:00 2001 From: liyi Date: Tue, 24 Dec 2024 14:20:30 +0800 Subject: [PATCH 06/22] =?UTF-8?q?fix:=E8=B0=83=E6=95=B4=E6=98=9F=E5=9B=BE?= =?UTF-8?q?=E4=B8=8A=E7=BA=BF=E6=B6=88=E6=81=AF=E5=8C=85=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/talk/startChart/command/message_command.dart | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/lib/talk/startChart/command/message_command.dart b/lib/talk/startChart/command/message_command.dart index 40038f1a..8f3ea1d5 100644 --- a/lib/talk/startChart/command/message_command.dart +++ b/lib/talk/startChart/command/message_command.dart @@ -21,8 +21,7 @@ class MessageCommand { static int _maxIntValue = 9223372036854775807; // Dart 中 int 的最大值 // 获取并递增消息ID - static int getNextMessageId(String toPeerId, {bool increment = true}) { - + static int getNextMessageId(String toPeerId, {bool increment = true}) { if (_messageIdMap.containsKey(toPeerId)) { if (increment) { _messageIdMap[toPeerId] = _messageIdMap[toPeerId]! + 1; @@ -51,7 +50,6 @@ class MessageCommand { SpIndex: 1, FromPeerId: FromPeerId, ToPeerId: ToPeerId, - Payload: [0], PayloadCRC: 0, PayloadLength: 0, PayloadType: PayloadTypeConstant.goOnline, From 4ab5d77473e54813ba03eb7110fa737f98c3f495 Mon Sep 17 00:00:00 2001 From: liyi Date: Tue, 24 Dec 2024 14:20:59 +0800 Subject: [PATCH 07/22] =?UTF-8?q?fix:=E5=A2=9E=E5=8A=A0=E7=BB=84=E5=8C=85?= =?UTF-8?q?=E6=97=B6=E7=9A=84=E6=9C=80=E5=A4=A7=E7=BB=84=E5=8C=85=E8=B6=85?= =?UTF-8?q?=E6=97=B6=E6=97=B6=E9=97=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../handle/scp_message_base_handle.dart | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/lib/talk/startChart/handle/scp_message_base_handle.dart b/lib/talk/startChart/handle/scp_message_base_handle.dart index c825f972..430578bf 100644 --- a/lib/talk/startChart/handle/scp_message_base_handle.dart +++ b/lib/talk/startChart/handle/scp_message_base_handle.dart @@ -1,3 +1,4 @@ +import 'dart:async'; import 'dart:convert'; import 'package:audioplayers/audioplayers.dart'; @@ -22,6 +23,8 @@ class ScpMessageBaseHandle { /// 分包缓冲区 // 存储每个 messageId 对应的分包数据 static Map>> _packetBuffer = {}; + final Map _packetTimers = {}; + final Duration _timeoutDuration = Duration(seconds: 10); // 分包组包最大超时时间 // 通话数据流的单例流数据处理类 final TalkDataRepository talkDataRepository = TalkDataRepository.instance; @@ -74,7 +77,7 @@ class ScpMessageBaseHandle { /// 处理分包逻辑 /// 如果没有收到所有包则返回null - dynamic handleFragmentedPayload({ + dynamic handleFragmentedPayload({ required int messageId, required int spTotal, required int spIndex, @@ -84,6 +87,7 @@ class ScpMessageBaseHandle { // 初始化分包列表 if (!_packetBuffer.containsKey(messageId)) { _packetBuffer[messageId] = List.filled(spTotal, []); + _startTimer(messageId); } // 检查分包索引是否在合法范围内 @@ -102,7 +106,7 @@ class ScpMessageBaseHandle { _packetBuffer[messageId]!.expand((packet) => packet).toList(); // 清除已重组的分包数据 - _packetBuffer.remove(messageId); + _clearPacketData(messageId); // 解析完整的 payload if (payloadType == PayloadTypeConstant.talkData) { @@ -120,4 +124,18 @@ class ScpMessageBaseHandle { return null; } } + + // 启动定时器 + void _startTimer(int messageId) { + _packetTimers[messageId]?.cancel(); + _packetTimers[messageId] = Timer(_timeoutDuration, () { + _clearPacketData(messageId); + }); + } + + // 清除分包数据和定时器 + void _clearPacketData(int messageId) { + _packetBuffer.remove(messageId); + _packetTimers.remove(messageId)?.cancel(); + } } From d642d79aa14ef6ceb510b90cf0d3fdbf8836d729 Mon Sep 17 00:00:00 2001 From: liyi Date: Tue, 24 Dec 2024 14:21:49 +0800 Subject: [PATCH 08/22] =?UTF-8?q?fix:=E5=A2=9E=E5=8A=A0=E6=94=B6=E5=88=B0T?= =?UTF-8?q?alkData=E4=B9=8B=E5=90=8E=E7=9A=84=E5=A4=84=E7=90=86=E6=96=B9?= =?UTF-8?q?=E6=B3=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../star_chart_h264/star_chart_logic.dart | 41 ++++++++++++++ .../star_chart_h264/star_chart_page.dart | 54 ++++++++++++++----- .../star_chart_h264/star_chart_state.dart | 5 ++ .../handle/impl/udp_talk_accept_handler.dart | 20 ++++--- .../handle/impl/udp_talk_data_handler.dart | 16 ++++-- lib/talk/startChart/start_chart_manage.dart | 16 +++--- 6 files changed, 117 insertions(+), 35 deletions(-) diff --git a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart index dd5f1a89..5b41c798 100644 --- a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart +++ b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart @@ -1,5 +1,7 @@ import 'dart:async'; +import 'package:flutter/foundation.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_pcm_sound/flutter_pcm_sound.dart'; import 'package:flutter_voice_processor/flutter_voice_processor.dart'; import 'package:get/get.dart'; import 'package:permission_handler/permission_handler.dart'; @@ -7,6 +9,8 @@ import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/blue/io_tool/manager_event_bus.dart'; import 'package:star_lock/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart'; import 'package:star_lock/talk/startChart/events/talk_status_change_event.dart'; +import 'package:star_lock/talk/startChart/handle/other/talk_data_repository.dart'; +import 'package:star_lock/talk/startChart/proto/talk_data.pbenum.dart'; import 'package:star_lock/talk/startChart/start_chart_manage.dart'; import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; import 'package:star_lock/tools/baseGetXController.dart'; @@ -21,7 +25,14 @@ class StarChartLogic extends BaseGetXController { @override void onReady() { super.onReady(); + // 初始化音频播放设备,采样率为 44100 Hz,单声道 + FlutterPcmSound.setup(sampleRate: 44100, channelCount: 1, ); + + // 设置音频数据的供给阈值,假设我们使用一秒钟的供给阈值 + int feedThreshold = 44100 * 1; // 44100 个样本,相当于一秒钟的数据量 + FlutterPcmSound.setFeedThreshold(feedThreshold); _getTalkStatusRefreshUIAction(); + _startListenTalkData(); } void _getTalkStatusRefreshUIAction() { @@ -36,8 +47,11 @@ class StarChartLogic extends BaseGetXController { state.talkStatus.value == TalkStatus.notTalkPing.index || state.talkStatus.value == TalkStatus.end.index) { _cancelTimers(); + stopProcessing(); + state.listPhotoData.value = Uint8List(0); // 状态错误,返回页面 Get.back(); + return; } @@ -47,6 +61,33 @@ class StarChartLogic extends BaseGetXController { }); } + // 监听音视频数据流 + void _startListenTalkData() { + state.talkDataRepository.talkDataStream.listen((talkData) { + final contentType = talkData.contentType; + // 判断数据类型,进行分发处理 + switch (contentType) { + case TalkData_ContentTypeE.G711: + _playG711Data(talkData.content); + break; + } + }); + } + + /// 播放音频数据 + Future _playG711Data(List audioData) async { + final PcmArrayInt16 fromList = PcmArrayInt16.fromList(audioData); + + await FlutterPcmSound.feed(fromList); + FlutterPcmSound.play(); + } + + void _stopPlayG711Data() { + FlutterPcmSound.pause(); + FlutterPcmSound.clear(); + FlutterPcmSound.stop(); + } + void _startCallTimer() { state.oneMinuteTimeTimer.cancel(); state.oneMinuteTimeTimer = diff --git a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart index 8508c58e..b0678082 100644 --- a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart +++ b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart @@ -15,6 +15,7 @@ import 'package:star_lock/app_settings/app_colors.dart'; import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart'; import 'package:star_lock/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart'; +import 'package:star_lock/talk/startChart/proto/talk_data.pbenum.dart'; import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; import 'package:star_lock/talk/startChart/webView/h264_web_view.dart'; import 'package:star_lock/talk/udp/udp_manage.dart'; @@ -265,19 +266,48 @@ class _StarChartPageState extends State { } void _getTVDataRefreshUIAction() { - state.getTVDataRefreshUIEvent = eventBus - .on() - .listen((GetTVDataRefreshUI event) async { - if (event.tvList.isNotEmpty && event.tvList.length > 100) { - final Uint8List imageData = Uint8List.fromList(event.tvList); - if (!listEquals(state.listPhotoData.value, imageData)) { - state.listPhotoData.value = imageData; - state.shouldUpdateUI.value = true; - if (state.shouldUpdateUI.value) { - setState(() {}); - state.shouldUpdateUI.value = false; + // state.getTVDataRefreshUIEvent = eventBus + // .on() + // .listen((GetTVDataRefreshUI event) async { + // if (event.tvList.isNotEmpty && event.tvList.length > 100) { + // final Uint8List imageData = Uint8List.fromList(event.tvList); + // if (!listEquals(state.listPhotoData.value, imageData)) { + // state.listPhotoData.value = imageData; + // state.shouldUpdateUI.value = true; + // if (state.shouldUpdateUI.value) { + // setState(() {}); + // state.shouldUpdateUI.value = false; + // } + // } + // } + // }); + state.talkDataRepository.talkDataStream.listen((talkData) { + final contentType = talkData.contentType; + // 判断数据类型,进行分发处理 + switch (contentType) { + case TalkData_ContentTypeE.Image: + state.listPhotoData.value = Uint8List.fromList(talkData.content); + if (talkData.content.isNotEmpty && talkData.content.length > 100) { + // 比较新旧数据是否相同 + final Uint8List imageData = Uint8List.fromList(talkData.content); + if (!listEquals(state.listPhotoData.value, imageData)) { + // 更新状态 + state.listPhotoData.value = imageData; + // 设置标志为true,表示需要更新UI + state.shouldUpdateUI.value = true; + // WidgetsBinding.instance.addPostFrameCallback((_) { + // 调用setState方法之前检查标志,只有当标志为true时才更新UI + if (state.shouldUpdateUI.value) { + setState(() { + // 更新UI + }); + // 更新完UI后将标志重新设置为false + state.shouldUpdateUI.value = false; + } + // }); + } } - } + break; } }); } diff --git a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart index 4a8ddfc4..c0aa5e69 100644 --- a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart +++ b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_voice_processor/flutter_voice_processor.dart'; import 'package:get/get.dart'; import 'package:network_info_plus/network_info_plus.dart'; +import 'package:star_lock/talk/startChart/handle/other/talk_data_repository.dart'; import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; import '../../../../tools/storage.dart'; @@ -54,4 +55,8 @@ class StarChartState { RxInt openDoorSeconds = 0.obs; RxInt talkStatus = 0.obs; //星图对讲状态 + + // 通话数据流的单例流数据处理类 + final TalkDataRepository talkDataRepository = TalkDataRepository.instance; + } diff --git a/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart index d16f80fd..295e2c02 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart @@ -31,8 +31,8 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle // 收到同意接听回复 final GenericResp genericResp = scpMessage.Payload; if (checkGenericRespSuccess(genericResp)) { - // 延迟2秒后启动监听 - Future.delayed(Duration(seconds: 5), () { + // 延迟一秒启动定时器判断 + Future.delayed(Duration(seconds: 1), () { // 启动通话保持定时器 _handleStartTalkPing(); // 启动发送预期数据请求 @@ -57,13 +57,13 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle @override deserializePayload( {required int payloadType, - required int messageType, - required Uint8List byte, - int? offset, - int? PayloadLength, - int? spTotal, - int? spIndex, - int? messageId}) { + required int messageType, + required Uint8List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { if (messageType == MessageTypeConstant.Resp) { final GenericResp genericResp = GenericResp(); genericResp.mergeFromBuffer(byte); @@ -114,6 +114,4 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle // 启动发送预期数据定时器 startChartManage.startTalkExpectTimer(); } - - } diff --git a/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart index 9649533e..98767822 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart @@ -3,6 +3,7 @@ import 'dart:typed_data'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; +import 'package:star_lock/talk/call/g711.dart'; import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart'; @@ -25,13 +26,12 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle @override void handleRealTimeData(ScpMessage scpMessage) { + // 收到数据后调用更新,防止定时器超时 + talkDataOverTimeTimerManager.receiveMessage(); if (scpMessage.Payload != null) { final TalkData talkData = scpMessage.Payload; // 处理音视频数据 _handleTalkData(talkData: talkData); - print('talkData:$talkData'); - // 收到数据后调用更新,防止定时器超时 - talkDataOverTimeTimerManager.receiveMessage(); } } @@ -64,7 +64,15 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle /// 处理g711音频数据 void _handleVideoG711(TalkData talkData) { - talkDataRepository.addTalkData(talkData); + try { + final g711Data = talkData.content; + // 转pcm数据 + List pcmBytes = G711().convertList(g711Data); + talkData.content = pcmBytes; + talkDataRepository.addTalkData(talkData); + } catch (e) { + print('Error decoding G.711 to PCM: $e'); + } } @override diff --git a/lib/talk/startChart/start_chart_manage.dart b/lib/talk/startChart/start_chart_manage.dart index 080bf818..0b3d22d4 100644 --- a/lib/talk/startChart/start_chart_manage.dart +++ b/lib/talk/startChart/start_chart_manage.dart @@ -126,7 +126,6 @@ class StartChartManage { FromPeerId = requestStarChartRegisterNode.peer!.id ?? ''; bindUserStarchart(); } - } //绑定星图配置 @@ -158,7 +157,6 @@ class StartChartManage { if (relayInfoEntity.client_addr != null) { localPublicHost = relayInfoEntity.client_addr!; } - if (relayInfoEntity.relay_list != null && relayInfoEntity.relay_list!.length > 0) { for (int i = 0; i <= relayInfoEntity.relay_list!.length; i++) { @@ -220,7 +218,6 @@ class StartChartManage { // 发送上线消息 Future _sendOnlineMessage() async { - _log(text: '发送上线消息,是否已经上线:$isOnlineStartChartServer'); if (isOnlineStartChartServer) { _log(text: '星图已上线,请勿重复发送上线消息'); return; @@ -285,8 +282,8 @@ class StartChartManage { } // 发送回声测试消息 - void sendEchoMessage({required List payload,required String toPeerId}) async { - + void sendEchoMessage( + {required List payload, required String toPeerId}) async { // 计算需要分多少个包发送 final int totalPackets = (payload.length / _maxPayloadSize).ceil(); // 循环遍历 @@ -763,7 +760,10 @@ class StartChartManage { _handleUdpResultData(deserialize); } if (deserialize.PayloadType != PayloadTypeConstant.heartbeat) { - // _log(text: 'Udp收到结构体数据---》$deserialize'); + if (deserialize.Payload != null) { + _log(text: 'Udp收到结构体数据---》$deserialize'); + } + // _log(text: 'text---》${utf8.decode(deserialize.Payload)}'); } } @@ -791,8 +791,8 @@ class StartChartManage { } else { handler.handleInvalidReq(scpMessage); } - } catch (e) { - // _log(text: '❌ 处理udp返回数据时遇到错误---> $e'); + } catch (e, stackTrace) { + _log(text: '❌ 处理udp返回数据时遇到错误---> $e\n,$stackTrace'); } } From 1108d4c66508ecdb9e1414fd9a0f97c3a964ad5a Mon Sep 17 00:00:00 2001 From: liyi Date: Tue, 24 Dec 2024 15:17:44 +0800 Subject: [PATCH 09/22] =?UTF-8?q?fix:=E6=9B=B4=E6=96=B0talk=5Fdata.proto?= =?UTF-8?q?=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/talk/startChart/proto/talk_data.pb.dart | 17 +++++++++++++++++ lib/talk/startChart/proto/talk_data.pbjson.dart | 6 ++++-- lib/talk/startChart/proto/talk_data.proto | 5 +++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/lib/talk/startChart/proto/talk_data.pb.dart b/lib/talk/startChart/proto/talk_data.pb.dart index 8097a685..d39cd33e 100644 --- a/lib/talk/startChart/proto/talk_data.pb.dart +++ b/lib/talk/startChart/proto/talk_data.pb.dart @@ -23,6 +23,7 @@ class TalkData extends $pb.GeneratedMessage { factory TalkData({ TalkData_ContentTypeE? contentType, $core.List<$core.int>? content, + $core.int? durationMs, }) { final $result = create(); if (contentType != null) { @@ -31,6 +32,9 @@ class TalkData extends $pb.GeneratedMessage { if (content != null) { $result.content = content; } + if (durationMs != null) { + $result.durationMs = durationMs; + } return $result; } TalkData._() : super(); @@ -40,6 +44,7 @@ class TalkData extends $pb.GeneratedMessage { static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'TalkData', package: const $pb.PackageName(_omitMessageNames ? '' : 'main'), createEmptyInstance: create) ..e(1, _omitFieldNames ? '' : 'ContentType', $pb.PbFieldType.OE, protoName: 'ContentType', defaultOrMaker: TalkData_ContentTypeE.Image, valueOf: TalkData_ContentTypeE.valueOf, enumValues: TalkData_ContentTypeE.values) ..a<$core.List<$core.int>>(2, _omitFieldNames ? '' : 'Content', $pb.PbFieldType.OY, protoName: 'Content') + ..a<$core.int>(3, _omitFieldNames ? '' : 'DurationMs', $pb.PbFieldType.OU3, protoName: 'DurationMs') ..hasRequiredFields = false ; @@ -81,6 +86,18 @@ class TalkData extends $pb.GeneratedMessage { $core.bool hasContent() => $_has(1); @$pb.TagNumber(2) void clearContent() => clearField(2); + + /// 时间 毫秒,例如第一帧视频,就是0ms,第2秒的第一帧,就是1000ms + /// 该字段仅用于协调音视频同步,而不是用于影响音视频播放时机 + /// 对于对讲场景,应该根据网络延迟和设备性能自行决定缓冲时长,在满足播放条件时立即进行渲染。 + @$pb.TagNumber(3) + $core.int get durationMs => $_getIZ(2); + @$pb.TagNumber(3) + set durationMs($core.int v) { $_setUnsignedInt32(2, v); } + @$pb.TagNumber(3) + $core.bool hasDurationMs() => $_has(2); + @$pb.TagNumber(3) + void clearDurationMs() => clearField(3); } diff --git a/lib/talk/startChart/proto/talk_data.pbjson.dart b/lib/talk/startChart/proto/talk_data.pbjson.dart index a9beab46..096262dc 100644 --- a/lib/talk/startChart/proto/talk_data.pbjson.dart +++ b/lib/talk/startChart/proto/talk_data.pbjson.dart @@ -19,6 +19,7 @@ const TalkData$json = { '2': [ {'1': 'ContentType', '3': 1, '4': 1, '5': 14, '6': '.main.TalkData.ContentTypeE', '10': 'ContentType'}, {'1': 'Content', '3': 2, '4': 1, '5': 12, '10': 'Content'}, + {'1': 'DurationMs', '3': 3, '4': 1, '5': 13, '10': 'DurationMs'}, ], '4': [TalkData_ContentTypeE$json], }; @@ -36,6 +37,7 @@ const TalkData_ContentTypeE$json = { /// Descriptor for `TalkData`. Decode as a `google.protobuf.DescriptorProto`. final $typed_data.Uint8List talkDataDescriptor = $convert.base64Decode( 'CghUYWxrRGF0YRI9CgtDb250ZW50VHlwZRgBIAEoDjIbLm1haW4uVGFsa0RhdGEuQ29udGVudF' - 'R5cGVFUgtDb250ZW50VHlwZRIYCgdDb250ZW50GAIgASgMUgdDb250ZW50Ii0KDENvbnRlbnRU' - 'eXBlRRIJCgVJbWFnZRAAEggKBEgyNjQQARIICgRHNzExEAI='); + 'R5cGVFUgtDb250ZW50VHlwZRIYCgdDb250ZW50GAIgASgMUgdDb250ZW50Eh4KCkR1cmF0aW9u' + 'TXMYAyABKA1SCkR1cmF0aW9uTXMiLQoMQ29udGVudFR5cGVFEgkKBUltYWdlEAASCAoESDI2NB' + 'ABEggKBEc3MTEQAg=='); diff --git a/lib/talk/startChart/proto/talk_data.proto b/lib/talk/startChart/proto/talk_data.proto index 88b51996..5dcb93a0 100644 --- a/lib/talk/startChart/proto/talk_data.proto +++ b/lib/talk/startChart/proto/talk_data.proto @@ -14,4 +14,9 @@ message TalkData { }; ContentTypeE ContentType = 1; bytes Content = 2; + // 时间 毫秒,例如第一帧视频,就是0ms,第2秒的第一帧,就是1000ms + // 该字段仅用于协调音视频同步,而不是用于影响音视频播放时机 + // 对于对讲场景,应该根据网络延迟和设备性能自行决定缓冲时长,在满足播放条件时立即进行渲染。 + uint32 DurationMs = 3; + } From a029c4b4e04a94de514bfd75aa55ba9fec32e6de Mon Sep 17 00:00:00 2001 From: liyi Date: Tue, 24 Dec 2024 15:38:36 +0800 Subject: [PATCH 10/22] =?UTF-8?q?fix:=E6=9B=B4=E6=96=B0=E7=BB=84=E5=8C=85?= =?UTF-8?q?=E7=BC=93=E5=86=B2=E5=8C=BA=E7=9A=84key?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../star_chart_h264/star_chart_logic.dart | 18 +++++----- .../star_chart_h264/star_chart_page.dart | 2 +- .../star_chart_h264/star_chart_state.dart | 2 +- .../startChart/command/message_command.dart | 11 +++--- .../startChart/constant/udp_constant.dart | 3 ++ .../handle/impl/udp_talk_accept_handler.dart | 8 ++--- .../handle/impl/udp_talk_data_handler.dart | 9 +---- .../handle/impl/udp_talk_request_handler.dart | 3 ++ .../handle/other/overtime_timer_manger.dart | 5 +-- .../handle/scp_message_base_handle.dart | 36 ++++++++++--------- lib/talk/startChart/start_chart_manage.dart | 1 + 11 files changed, 51 insertions(+), 47 deletions(-) diff --git a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart index 5b41c798..7cc18cbc 100644 --- a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart +++ b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart @@ -25,12 +25,6 @@ class StarChartLogic extends BaseGetXController { @override void onReady() { super.onReady(); - // 初始化音频播放设备,采样率为 44100 Hz,单声道 - FlutterPcmSound.setup(sampleRate: 44100, channelCount: 1, ); - - // 设置音频数据的供给阈值,假设我们使用一秒钟的供给阈值 - int feedThreshold = 44100 * 1; // 44100 个样本,相当于一秒钟的数据量 - FlutterPcmSound.setFeedThreshold(feedThreshold); _getTalkStatusRefreshUIAction(); _startListenTalkData(); } @@ -75,8 +69,16 @@ class StarChartLogic extends BaseGetXController { } /// 播放音频数据 - Future _playG711Data(List audioData) async { - final PcmArrayInt16 fromList = PcmArrayInt16.fromList(audioData); + Future _playG711Data(List pcmData) async { + // 将 PCM 数据转换为 PcmArrayInt16 + final PcmArrayInt16 fromList = PcmArrayInt16.fromList(pcmData); + + // 初始化音频播放设备,确保采样率和声道数与 PCM 数据匹配 + await FlutterPcmSound.setup(sampleRate: 8000, channelCount: 1); + + // 设置音频数据的供给阈值 + FlutterPcmSound.setFeedThreshold(8000 ~/ 2); // 根据需要调整 + // final PcmArrayInt16 fromList = PcmArrayInt16.fromList(audioData); await FlutterPcmSound.feed(fromList); FlutterPcmSound.play(); diff --git a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart index b0678082..f8669c97 100644 --- a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart +++ b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart @@ -316,7 +316,7 @@ class _StarChartPageState extends State { void dispose() { super.dispose(); logic.stopProcessing(); - state.getTVDataRefreshUIEvent!.cancel(); + // state.getTVDataRefreshUIEvent!.cancel(); } Widget _buildTalkView({required bool isMpeg4}) { diff --git a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart index c0aa5e69..25512f62 100644 --- a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart +++ b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart @@ -14,7 +14,7 @@ class StarChartState { RxBool isOpenVoice = false.obs; int udpSendDataFrameNumber = 0; // 帧序号 // var isSenderAudioData = false.obs;// 是否要发送音频数据 - StreamSubscription? getTVDataRefreshUIEvent; //收到视频流数据 + // StreamSubscription? getTVDataRefreshUIEvent; //收到视频流数据 RxBool shouldUpdateUI = false.obs; //是否需要更新UI Future userMobileIP = NetworkInfo().getWifiIP(); diff --git a/lib/talk/startChart/command/message_command.dart b/lib/talk/startChart/command/message_command.dart index 8f3ea1d5..00093af7 100644 --- a/lib/talk/startChart/command/message_command.dart +++ b/lib/talk/startChart/command/message_command.dart @@ -4,6 +4,7 @@ import 'dart:typed_data'; import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/startChart/constant/payload_type_constant.dart'; import 'package:star_lock/talk/startChart/constant/protocol_flag_constant.dart'; +import 'package:star_lock/talk/startChart/constant/udp_constant.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart'; import 'package:star_lock/talk/startChart/proto/generic.pb.dart'; @@ -17,6 +18,7 @@ import 'package:star_lock/talk/startChart/proto/talk_request.pb.dart'; class MessageCommand { // 全局字典,用于存储每个 ToPeerId 对应的当前 messageId + // 单个会话的messageId需要递增 static Map _messageIdMap = {}; static int _maxIntValue = 9223372036854775807; // Dart 中 int 的最大值 @@ -322,8 +324,8 @@ class MessageCommand { int? MessageId, }) { final genericResp = GenericResp(); - genericResp.message = 'ok'; - genericResp.code = 0; + genericResp.message = UdpConstant.genericRespSuccessMsg; + genericResp.code = UdpConstant.genericRespSuccessCode; final payload = genericResp.writeToBuffer(); ScpMessage message = ScpMessage( ProtocolFlag: ProtocolFlagConstant.scp01, @@ -348,10 +350,11 @@ class MessageCommand { required String ToPeerId, required int PayloadType, int? MessageId, + String? errorMessageText, }) { final genericResp = GenericResp(); - genericResp.message = 'error'; - genericResp.code = -1; + genericResp.message = errorMessageText ?? UdpConstant.genericRespErrorMsg; + genericResp.code = UdpConstant.genericRespErrorCode; final payload = genericResp.writeToBuffer(); ScpMessage message = ScpMessage( ProtocolFlag: ProtocolFlagConstant.scp01, diff --git a/lib/talk/startChart/constant/udp_constant.dart b/lib/talk/startChart/constant/udp_constant.dart index 39c6e524..f696ac92 100644 --- a/lib/talk/startChart/constant/udp_constant.dart +++ b/lib/talk/startChart/constant/udp_constant.dart @@ -2,4 +2,7 @@ class UdpConstant{ // generic成功响应 static const int genericRespSuccessCode = 0; static const String genericRespSuccessMsg = 'ok'; + // generic失败响应 + static const int genericRespErrorCode = -1; + static const String genericRespErrorMsg = 'error'; } \ No newline at end of file diff --git a/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart index 295e2c02..40bfaea3 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart @@ -31,12 +31,12 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle // 收到同意接听回复 final GenericResp genericResp = scpMessage.Payload; if (checkGenericRespSuccess(genericResp)) { - // 延迟一秒启动定时器判断 + // 启动发送预期数据请求 + _handleStartSendTalkExpectDataRequest(); Future.delayed(Duration(seconds: 1), () { + print('启动定时器判断'); // 启动通话保持定时器 _handleStartTalkPing(); - // 启动发送预期数据请求 - _handleStartSendTalkExpectDataRequest(); // 启动通话数据检查的定时器 _handleCheckTalkDataTimer(); }); @@ -78,7 +78,7 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle } } - // 启动通话保持 + // 启动通话保持,判断x秒内是否收到通话保持 void _handleStartTalkPing() { // 启动通话保持 startChartManage.startTalkPingMessageTimer(); diff --git a/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart index 98767822..856b84ce 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart @@ -85,11 +85,7 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle int? spTotal, int? spIndex, int? messageId}) { - if (messageType == MessageTypeConstant.Resp) { - final GenericResp genericResp = GenericResp(); - genericResp.mergeFromBuffer(byte); - return genericResp; - } else if (messageType == MessageTypeConstant.RealTimeData) { + if (messageType == MessageTypeConstant.RealTimeData) { // 回声测试 if (spTotal != null && spTotal > 1 && @@ -109,9 +105,6 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle talkData.mergeFromBuffer(byte); return talkData; } - } else { - String payload = utf8.decode(byte); - return payload; } } } diff --git a/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart index 02588870..f8f66c23 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart @@ -1,6 +1,7 @@ import 'dart:convert'; import 'dart:typed_data'; +import 'package:flutter/services.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:get/get.dart'; @@ -65,6 +66,8 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle Routers.starChartPage, arguments: {'lockId': '111'}, ); + // 触发轻微震动反馈 + HapticFeedback.lightImpact(); } // 收到来电请求时进行本地通知 diff --git a/lib/talk/startChart/handle/other/overtime_timer_manger.dart b/lib/talk/startChart/handle/other/overtime_timer_manger.dart index c813dcaf..16add8ad 100644 --- a/lib/talk/startChart/handle/other/overtime_timer_manger.dart +++ b/lib/talk/startChart/handle/other/overtime_timer_manger.dart @@ -12,9 +12,6 @@ class OverTimeTimerManager { // 超时时间(以秒为单位),是 final 的,因此必须在构造函数中初始化 final int timeoutInSeconds; - // 默认超时时间(以秒为单位) - static const int defaultTimeoutInSeconds = 5; - // 超时回调函数 TimeoutCallback? _onTimeout; @@ -24,7 +21,7 @@ class OverTimeTimerManager { _timer = Timer(Duration(seconds: timeoutInSeconds), () { // 超时回调方法 _onTimeout?.call(); - // 清楚定时器 + // 清除定时器 _cancelTimer(); }); } diff --git a/lib/talk/startChart/handle/scp_message_base_handle.dart b/lib/talk/startChart/handle/scp_message_base_handle.dart index 430578bf..5db47166 100644 --- a/lib/talk/startChart/handle/scp_message_base_handle.dart +++ b/lib/talk/startChart/handle/scp_message_base_handle.dart @@ -22,8 +22,8 @@ class ScpMessageBaseHandle { /// 分包缓冲区 // 存储每个 messageId 对应的分包数据 - static Map>> _packetBuffer = {}; - final Map _packetTimers = {}; + static Map>> _packetBuffer = {}; + final Map _packetTimers = {}; final Duration _timeoutDuration = Duration(seconds: 10); // 分包组包最大超时时间 // 通话数据流的单例流数据处理类 @@ -85,28 +85,30 @@ class ScpMessageBaseHandle { required int payloadType, }) { // 初始化分包列表 + String key = '$messageId-$payloadType'; if (!_packetBuffer.containsKey(messageId)) { - _packetBuffer[messageId] = List.filled(spTotal, []); - _startTimer(messageId); + _packetBuffer[key] = List.filled(spTotal, []); + _startTimer(key); } // 检查分包索引是否在合法范围内 if (spIndex < 1 || spIndex > spTotal) { - print('Invalid spIndex: $spIndex for messageId: $messageId'); + print( + 'Invalid spTotal: $spTotal spIndex: $spIndex for messageId: $messageId'); return null; } // 存储当前分包 - _packetBuffer[messageId]![spIndex - 1] = byte; + _packetBuffer[key]![spIndex - 1] = byte; // 检查是否接收到所有分包 - if (_packetBuffer[messageId]!.every((packet) => packet.isNotEmpty)) { + if (_packetBuffer[key]!.every((packet) => packet.isNotEmpty)) { // 重组所有分包 List completePayload = - _packetBuffer[messageId]!.expand((packet) => packet).toList(); + _packetBuffer[key]!.expand((packet) => packet).toList(); - // 清除已重组的分包数据 - _clearPacketData(messageId); + // 清除已重组和超时的分包数据 + _clearPacketData(key); // 解析完整的 payload if (payloadType == PayloadTypeConstant.talkData) { @@ -126,16 +128,16 @@ class ScpMessageBaseHandle { } // 启动定时器 - void _startTimer(int messageId) { - _packetTimers[messageId]?.cancel(); - _packetTimers[messageId] = Timer(_timeoutDuration, () { - _clearPacketData(messageId); + void _startTimer(String key) { + _packetTimers[key]?.cancel(); + _packetTimers[key] = Timer(_timeoutDuration, () { + _clearPacketData(key); }); } // 清除分包数据和定时器 - void _clearPacketData(int messageId) { - _packetBuffer.remove(messageId); - _packetTimers.remove(messageId)?.cancel(); + void _clearPacketData(String key) { + _packetBuffer.remove(key); + _packetTimers.remove(key)?.cancel(); } } diff --git a/lib/talk/startChart/start_chart_manage.dart b/lib/talk/startChart/start_chart_manage.dart index 0b3d22d4..22ff1c64 100644 --- a/lib/talk/startChart/start_chart_manage.dart +++ b/lib/talk/startChart/start_chart_manage.dart @@ -296,6 +296,7 @@ class StartChartManage { // 取到分包数据 List packet = payload.sublist(start, end); + // 分包数据不递增messageID final messageId = MessageCommand.getNextMessageId(toPeerId, increment: false); // 组装分包数据 From 8b30af9ceae1c92608747489088639781dcdd007 Mon Sep 17 00:00:00 2001 From: Liuyf Date: Tue, 24 Dec 2024 15:43:54 +0800 Subject: [PATCH 11/22] =?UTF-8?q?feat=EF=BC=9Aapp=E5=90=8D=E7=A7=B0?= =?UTF-8?q?=E5=9B=BD=E9=99=85=E5=8C=96=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner.xcodeproj/project.pbxproj | 16 ++++++++-------- ...nfoPlist_sky.xcstrings => info_sky.xcstrings} | 0 .../{InfoPlist.xcstrings => info_xhj.xcstrings} | 0 3 files changed, 8 insertions(+), 8 deletions(-) rename ios/Runner/{InfoPlist_sky.xcstrings => info_sky.xcstrings} (100%) rename ios/Runner/{InfoPlist.xcstrings => info_xhj.xcstrings} (100%) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 13002562..e6da6496 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -7,8 +7,8 @@ objects = { /* Begin PBXBuildFile section */ - 04BFE7152D14105500A48EC9 /* InfoPlist_sky.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 04BFE7142D14105500A48EC9 /* InfoPlist_sky.xcstrings */; }; - 04C1FC572D13F1A2000C959E /* InfoPlist.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 04C1FC562D13F1A2000C959E /* InfoPlist.xcstrings */; }; + 04BFE7152D14105500A48EC9 /* info_sky.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 04BFE7142D14105500A48EC9 /* info_sky.xcstrings */; }; + 04C1FC572D13F1A2000C959E /* info_xhj.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 04C1FC562D13F1A2000C959E /* info_xhj.xcstrings */; }; 04D0CC262D06CE570042EF10 /* launchImage_xhj.png in Resources */ = {isa = PBXBuildFile; fileRef = 82B657662C919BDF0079121C /* launchImage_xhj.png */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 25B2A1422F9A2CCCBBCBBB97 /* skyRelease.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 35F02D170492750B437D6AB6 /* skyRelease.xcconfig */; }; @@ -100,8 +100,8 @@ /* Begin PBXFileReference section */ 0420903B2C0EEAA50073E654 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Main.strings"; sourceTree = ""; }; 04BFC4482BCFE05100688FCA /* RunnerRelease-xhj.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "RunnerRelease-xhj.entitlements"; sourceTree = ""; }; - 04BFE7142D14105500A48EC9 /* InfoPlist_sky.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = InfoPlist_sky.xcstrings; sourceTree = ""; }; - 04C1FC562D13F1A2000C959E /* InfoPlist.xcstrings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json.xcstrings; path = InfoPlist.xcstrings; sourceTree = ""; }; + 04BFE7142D14105500A48EC9 /* info_sky.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = info_sky.xcstrings; sourceTree = ""; }; + 04C1FC562D13F1A2000C959E /* info_xhj.xcstrings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json.xcstrings; path = info_xhj.xcstrings; sourceTree = ""; }; 0BEB3ADCCEC961E2916B9004 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 0D02C18E16914A687A4A1AC2 /* devDebug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = devDebug.xcconfig; path = Flutter/devDebug.xcconfig; sourceTree = ""; }; 126D1370182AB44291C67A10 /* Pods-Runner.dev-release-sky.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.dev-release-sky.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.dev-release-sky.xcconfig"; sourceTree = ""; }; @@ -345,7 +345,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( - 04C1FC562D13F1A2000C959E /* InfoPlist.xcstrings */, + 04C1FC562D13F1A2000C959E /* info_xhj.xcstrings */, E0B3E9EC2D04B36C00907A95 /* info_dev.plist */, E0B3E9EB2D04B36C00907A95 /* info_pre.plist */, E0B3E9EA2D04B36C00907A95 /* info_sky.plist */, @@ -368,7 +368,7 @@ 82BD91212ADA72360018E523 /* CommonDefine.h */, 76C3321F45DA6AE46ED83204 /* RunnerDebug-dev.entitlements */, 16D90D130794DF74DA656999 /* RunnerRelease-sky.entitlements */, - 04BFE7142D14105500A48EC9 /* InfoPlist_sky.xcstrings */, + 04BFE7142D14105500A48EC9 /* info_sky.xcstrings */, ); path = Runner; sourceTree = ""; @@ -507,14 +507,14 @@ 805C745A7C79091E65665B96 /* devDebug.xcconfig in Resources */, E0B3E9EE2D04B36C00907A95 /* info_sky.plist in Resources */, D415555B0C61C5422202D037 /* devProfile.xcconfig in Resources */, - 04BFE7152D14105500A48EC9 /* InfoPlist_sky.xcstrings in Resources */, + 04BFE7152D14105500A48EC9 /* info_sky.xcstrings in Resources */, 8A77CDE0EDBCACCE22C29A9E /* devRelease.xcconfig in Resources */, F0A7A6EF7D83CA92324D9C20 /* preDebug.xcconfig in Resources */, E0A496CF2CA30CEF00E376BB /* skyPreviewRelease.xcconfig in Resources */, 9C453CBFAB0703DFA762337C /* preProfile.xcconfig in Resources */, 44827AC367F1EAB110A97660 /* preRelease.xcconfig in Resources */, D7EF77645AB1C3CEEA536468 /* skyDebug.xcconfig in Resources */, - 04C1FC572D13F1A2000C959E /* InfoPlist.xcstrings in Resources */, + 04C1FC572D13F1A2000C959E /* info_xhj.xcstrings in Resources */, 7B54002BF45E5D8B295B6447 /* skyProfile.xcconfig in Resources */, 25B2A1422F9A2CCCBBCBBB97 /* skyRelease.xcconfig in Resources */, ADF948FD9EE8BD1AE71F0984 /* xhjDebug.xcconfig in Resources */, diff --git a/ios/Runner/InfoPlist_sky.xcstrings b/ios/Runner/info_sky.xcstrings similarity index 100% rename from ios/Runner/InfoPlist_sky.xcstrings rename to ios/Runner/info_sky.xcstrings diff --git a/ios/Runner/InfoPlist.xcstrings b/ios/Runner/info_xhj.xcstrings similarity index 100% rename from ios/Runner/InfoPlist.xcstrings rename to ios/Runner/info_xhj.xcstrings From 5a4de444e232ee14ee0a4fd402a8f85db6875108 Mon Sep 17 00:00:00 2001 From: ci_bot Date: Tue, 24 Dec 2024 15:50:18 +0800 Subject: [PATCH 12/22] =?UTF-8?q?other:=20=E8=AF=81=E4=B9=A6=E9=97=AE?= =?UTF-8?q?=E9=A2=98=E4=BF=AE=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner.xcodeproj/project.pbxproj | 63 ++++++++++++++++++++-------- 1 file changed, 45 insertions(+), 18 deletions(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index e6da6496..86f5d276 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -707,8 +707,10 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = "Runner/RunnerRelease-sky.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = NAQ5PL2DYC; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NAQ5PL2DYC; INFOPLIST_FILE = Runner/info_sky.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -718,6 +720,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.starlock.lock.local; PRODUCT_NAME = Runner; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Debug_com.starlock.lock.local.mobileprovision; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -802,8 +805,10 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = "Runner/RunnerRelease-xhj.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = SF86QP26TZ; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NAQ5PL2DYC; INFOPLIST_FILE = Runner/info_xhj.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -813,6 +818,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.starlock.lock.local; PRODUCT_NAME = Runner; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Debug_com.starlock.lock.local.mobileprovision; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -826,8 +832,10 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = "Runner/RunnerRelease-sky.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = NAQ5PL2DYC; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NAQ5PL2DYC; INFOPLIST_FILE = Runner/info_sky.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -836,6 +844,7 @@ ); PRODUCT_NAME = Runner; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Appstore_com.skychip.lock.mobileprovision; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -849,8 +858,10 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = "Runner/RunnerRelease-xhj.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = P8997RW3V8; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = P8997RW3V8; INFOPLIST_FILE = Runner/info_xhj.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -860,6 +871,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.xhjcn.lock; PRODUCT_NAME = Runner; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Appstore_com.xhjcn.lock.mobileprovision; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -1092,8 +1104,10 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = "Runner/RunnerDebug-dev.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = NAQ5PL2DYC; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NAQ5PL2DYC; INFOPLIST_FILE = Runner/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -1103,6 +1117,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.starlock.lock.local; PRODUCT_NAME = Runner; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Debug_com.starlock.lock.local.mobileprovision; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -1187,8 +1202,10 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = "Runner/RunnerRelease-sky.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = NAQ5PL2DYC; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NAQ5PL2DYC; INFOPLIST_FILE = Runner/info_sky.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -1198,6 +1215,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.skychip.lock.pre; PRODUCT_NAME = Runner; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Adhoc_com.skychip.lock.pre.mobileprovision; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -1282,8 +1300,10 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = "Runner/RunnerRelease-sky.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = NAQ5PL2DYC; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = NAQ5PL2DYC; INFOPLIST_FILE = Runner/info_sky.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -1293,6 +1313,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.skychip.lock.dev; PRODUCT_NAME = Runner; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Adhoc_com.skychip.lock.dev.mobileprovision; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -1377,8 +1398,10 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = "Runner/RunnerRelease-xhj.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = P8997RW3V8; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = P8997RW3V8; INFOPLIST_FILE = Runner/info_xhj.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -1388,6 +1411,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.xhjcn.lock.pre; PRODUCT_NAME = Runner; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Adhoc_com.xhjcn.lock.pre.mobileprovision; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; @@ -1472,8 +1496,10 @@ buildSettings = { CODE_SIGN_ENTITLEMENTS = "Runner/RunnerRelease-xhj.entitlements"; CODE_SIGN_IDENTITY = "Apple Development"; - CODE_SIGN_STYLE = Automatic; - DEVELOPMENT_TEAM = P8997RW3V8; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution"; + CODE_SIGN_STYLE = Manual; + DEVELOPMENT_TEAM = ""; + "DEVELOPMENT_TEAM[sdk=iphoneos*]" = P8997RW3V8; INFOPLIST_FILE = Runner/info_xhj.plist; IPHONEOS_DEPLOYMENT_TARGET = 12.0; LD_RUNPATH_SEARCH_PATHS = ( @@ -1483,6 +1509,7 @@ PRODUCT_BUNDLE_IDENTIFIER = com.xhjcn.lock.dev; PRODUCT_NAME = Runner; PROVISIONING_PROFILE_SPECIFIER = ""; + "PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = Adhoc_com.xhjcn.lock.dev.mobileprovision; SUPPORTED_PLATFORMS = "iphoneos iphonesimulator"; SUPPORTS_MACCATALYST = NO; SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO; From 742d7b4f1bf3994c8da7e5c49ac7726102d231c5 Mon Sep 17 00:00:00 2001 From: Liuyf Date: Tue, 24 Dec 2024 16:02:55 +0800 Subject: [PATCH 13/22] =?UTF-8?q?other:=20=E9=BB=98=E8=AE=A4=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E5=A5=BD=E6=9C=AC=E5=9C=B0=E8=B0=83=E8=AF=95=E7=8E=AF?= =?UTF-8?q?=E5=A2=83?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner.xcodeproj/xcshareddata/xcschemes/sky.xcscheme | 2 +- ios/Runner.xcodeproj/xcshareddata/xcschemes/xhj.xcscheme | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/sky.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/sky.xcscheme index 018f20fb..b2589df9 100755 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/sky.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/sky.xcscheme @@ -24,7 +24,7 @@ Date: Tue, 24 Dec 2024 16:21:44 +0800 Subject: [PATCH 14/22] =?UTF-8?q?fix:=E8=A1=A5=E5=85=85=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../star_chart_h264/star_chart_logic.dart | 13 +++++-------- .../handle/impl/udp_talk_accept_handler.dart | 4 ++-- .../handle/impl/udp_talk_request_handler.dart | 4 ++-- .../startChart/handle/scp_message_base_handle.dart | 4 ++-- 4 files changed, 11 insertions(+), 14 deletions(-) diff --git a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart index 7cc18cbc..f9884a7d 100644 --- a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart +++ b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart @@ -25,6 +25,11 @@ class StarChartLogic extends BaseGetXController { @override void onReady() { super.onReady(); + // 初始化音频播放设备,确保采样率和声道数与 PCM 数据匹配 + FlutterPcmSound.setup(sampleRate: 8000, channelCount: 1); + + // 设置音频数据的供给阈值 + FlutterPcmSound.setFeedThreshold(8000 ~/ 2); // 根据需要调整 _getTalkStatusRefreshUIAction(); _startListenTalkData(); } @@ -72,14 +77,6 @@ class StarChartLogic extends BaseGetXController { Future _playG711Data(List pcmData) async { // 将 PCM 数据转换为 PcmArrayInt16 final PcmArrayInt16 fromList = PcmArrayInt16.fromList(pcmData); - - // 初始化音频播放设备,确保采样率和声道数与 PCM 数据匹配 - await FlutterPcmSound.setup(sampleRate: 8000, channelCount: 1); - - // 设置音频数据的供给阈值 - FlutterPcmSound.setFeedThreshold(8000 ~/ 2); // 根据需要调整 - // final PcmArrayInt16 fromList = PcmArrayInt16.fromList(audioData); - await FlutterPcmSound.feed(fromList); FlutterPcmSound.play(); } diff --git a/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart index 40bfaea3..c29dbc84 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart @@ -31,12 +31,12 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle // 收到同意接听回复 final GenericResp genericResp = scpMessage.Payload; if (checkGenericRespSuccess(genericResp)) { - // 启动发送预期数据请求 - _handleStartSendTalkExpectDataRequest(); Future.delayed(Duration(seconds: 1), () { print('启动定时器判断'); // 启动通话保持定时器 _handleStartTalkPing(); + // 启动发送预期数据请求 + _handleStartSendTalkExpectDataRequest(); // 启动通话数据检查的定时器 _handleCheckTalkDataTimer(); }); diff --git a/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart index f8f66c23..eb6a0cdd 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart @@ -66,8 +66,8 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle Routers.starChartPage, arguments: {'lockId': '111'}, ); - // 触发轻微震动反馈 - HapticFeedback.lightImpact(); + // 触发震动反馈 + HapticFeedback.vibrate(); } // 收到来电请求时进行本地通知 diff --git a/lib/talk/startChart/handle/scp_message_base_handle.dart b/lib/talk/startChart/handle/scp_message_base_handle.dart index 5db47166..1d13809e 100644 --- a/lib/talk/startChart/handle/scp_message_base_handle.dart +++ b/lib/talk/startChart/handle/scp_message_base_handle.dart @@ -33,12 +33,12 @@ class ScpMessageBaseHandle { // 通话保持超时监听定时器管理 final talkePingOverTimeTimerManager = OverTimeTimerManager( - timeoutInSeconds: 5, + timeoutInSeconds: 15, ); // 通话数据超时定时器 final talkDataOverTimeTimerManager = OverTimeTimerManager( - timeoutInSeconds: 3, + timeoutInSeconds: 13, ); // 回复成功消息 From cdbd0c3d036a1eed501813d1c3fde1695807f7c9 Mon Sep 17 00:00:00 2001 From: liyi Date: Wed, 25 Dec 2024 09:41:46 +0800 Subject: [PATCH 15/22] =?UTF-8?q?fix:=E8=A1=A5=E5=85=85=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../star_chart_h264/star_chart_logic.dart | 19 ++++ .../star_chart_h264/star_chart_page.dart | 94 +++++++++---------- .../handle/impl/udp_talk_data_handler.dart | 9 ++ .../handle/scp_message_base_handle.dart | 11 ++- lib/talk/startChart/start_chart_manage.dart | 26 ++++- 5 files changed, 105 insertions(+), 54 deletions(-) diff --git a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart index f9884a7d..88e37fea 100644 --- a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart +++ b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart @@ -22,6 +22,9 @@ class StarChartLogic extends BaseGetXController { /// 收到Talk发送的状态 StreamSubscription? _getTalkStatusRefreshUIEvent; + int startTime = DateTime.now().millisecondsSinceEpoch; + + @override void onReady() { super.onReady(); @@ -73,6 +76,22 @@ class StarChartLogic extends BaseGetXController { }); } + void syncPlay() { + int currentTime = DateTime.now().millisecondsSinceEpoch - startTime; + + // 播放音频 + // while (audioBuffer.isNotEmpty && audioBuffer.first.durationMs <= currentTime) { + // TalkData audioData = audioBuffer.removeAt(0); + // playAudio(audioData.content); + // } + // + // // 播放视频 + // while (videoBuffer.isNotEmpty && videoBuffer.first.durationMs <= currentTime) { + // TalkData videoData = videoBuffer.removeAt(0); + // playVideo(videoData.content); + // } + } + /// 播放音频数据 Future _playG711Data(List pcmData) async { // 将 PCM 数据转换为 PcmArrayInt16 diff --git a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart index f8669c97..a7854c0c 100644 --- a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart +++ b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart @@ -38,7 +38,7 @@ class _StarChartPageState extends State { super.initState(); initAsync(); - _getTVDataRefreshUIAction(); + // _getTVDataRefreshUIAction(); } Future initAsync() async { @@ -265,52 +265,52 @@ class _StarChartPageState extends State { } } - void _getTVDataRefreshUIAction() { - // state.getTVDataRefreshUIEvent = eventBus - // .on() - // .listen((GetTVDataRefreshUI event) async { - // if (event.tvList.isNotEmpty && event.tvList.length > 100) { - // final Uint8List imageData = Uint8List.fromList(event.tvList); - // if (!listEquals(state.listPhotoData.value, imageData)) { - // state.listPhotoData.value = imageData; - // state.shouldUpdateUI.value = true; - // if (state.shouldUpdateUI.value) { - // setState(() {}); - // state.shouldUpdateUI.value = false; - // } - // } - // } - // }); - state.talkDataRepository.talkDataStream.listen((talkData) { - final contentType = talkData.contentType; - // 判断数据类型,进行分发处理 - switch (contentType) { - case TalkData_ContentTypeE.Image: - state.listPhotoData.value = Uint8List.fromList(talkData.content); - if (talkData.content.isNotEmpty && talkData.content.length > 100) { - // 比较新旧数据是否相同 - final Uint8List imageData = Uint8List.fromList(talkData.content); - if (!listEquals(state.listPhotoData.value, imageData)) { - // 更新状态 - state.listPhotoData.value = imageData; - // 设置标志为true,表示需要更新UI - state.shouldUpdateUI.value = true; - // WidgetsBinding.instance.addPostFrameCallback((_) { - // 调用setState方法之前检查标志,只有当标志为true时才更新UI - if (state.shouldUpdateUI.value) { - setState(() { - // 更新UI - }); - // 更新完UI后将标志重新设置为false - state.shouldUpdateUI.value = false; - } - // }); - } - } - break; - } - }); - } + // void _getTVDataRefreshUIAction() { + // // state.getTVDataRefreshUIEvent = eventBus + // // .on() + // // .listen((GetTVDataRefreshUI event) async { + // // if (event.tvList.isNotEmpty && event.tvList.length > 100) { + // // final Uint8List imageData = Uint8List.fromList(event.tvList); + // // if (!listEquals(state.listPhotoData.value, imageData)) { + // // state.listPhotoData.value = imageData; + // // state.shouldUpdateUI.value = true; + // // if (state.shouldUpdateUI.value) { + // // setState(() {}); + // // state.shouldUpdateUI.value = false; + // // } + // // } + // // } + // // }); + // state.talkDataRepository.talkDataStream.listen((talkData) { + // final contentType = talkData.contentType; + // // 判断数据类型,进行分发处理 + // switch (contentType) { + // case TalkData_ContentTypeE.Image: + // state.listPhotoData.value = Uint8List.fromList(talkData.content); + // if (talkData.content.isNotEmpty && talkData.content.length > 100) { + // // 比较新旧数据是否相同 + // final Uint8List imageData = Uint8List.fromList(talkData.content); + // if (!listEquals(state.listPhotoData.value, imageData)) { + // // 更新状态 + // state.listPhotoData.value = imageData; + // // 设置标志为true,表示需要更新UI + // state.shouldUpdateUI.value = true; + // // WidgetsBinding.instance.addPostFrameCallback((_) { + // // 调用setState方法之前检查标志,只有当标志为true时才更新UI + // if (state.shouldUpdateUI.value) { + // setState(() { + // // 更新UI + // }); + // // 更新完UI后将标志重新设置为false + // state.shouldUpdateUI.value = false; + // } + // // }); + // } + // } + // break; + // } + // }); + // } @override void dispose() { diff --git a/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart index 856b84ce..20d6d1f0 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart @@ -30,11 +30,20 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle talkDataOverTimeTimerManager.receiveMessage(); if (scpMessage.Payload != null) { final TalkData talkData = scpMessage.Payload; + print('talkData: ${listToHexString(talkData.content)}'); + // 处理音视频数据 _handleTalkData(talkData: talkData); } } + String listToHexString(List intList) { + // 将整数列表转换为十六进制字符串列表 + List hexList = intList.map((num) => num.toRadixString(16)).toList(); + // 将十六进制字符串列表连接成一个字符串,没有空格 + return hexList.join(''); + } + void _handleTalkData({required TalkData talkData}) { if (talkData == null) return; final contentType = talkData.contentType; diff --git a/lib/talk/startChart/handle/scp_message_base_handle.dart b/lib/talk/startChart/handle/scp_message_base_handle.dart index 1d13809e..a54fb927 100644 --- a/lib/talk/startChart/handle/scp_message_base_handle.dart +++ b/lib/talk/startChart/handle/scp_message_base_handle.dart @@ -115,12 +115,13 @@ class ScpMessageBaseHandle { final TalkData talkData = TalkData(); talkData.mergeFromBuffer(completePayload); return talkData; - } else if (payloadType == PayloadTypeConstant.echoTest) { - return completePayload; - } else { - String payload = utf8.decode(completePayload); - return payload; } + // if (payloadType == PayloadTypeConstant.echoTest) { + // return completePayload; + // } else { + // String payload = utf8.decode(completePayload); + // return payload; + // } } else { // 如果分包尚未接收完全,返回 null 或其他指示符 return null; diff --git a/lib/talk/startChart/start_chart_manage.dart b/lib/talk/startChart/start_chart_manage.dart index 22ff1c64..d146a42a 100644 --- a/lib/talk/startChart/start_chart_manage.dart +++ b/lib/talk/startChart/start_chart_manage.dart @@ -330,6 +330,14 @@ class StartChartManage { // 发送同意接听消息 void sendTalkAcceptMessage() async { + if (talkStatus.status == TalkStatus.duringCall) { + _log(text: '已经在通话中,请勿重复发送同意接听消息'); + return; + } + if (talkStatus.status != TalkStatus.waitingAnswer) { + _log(text: '当前未处于等待接听状态, 无法发送同意接听消息'); + return; + } final message = MessageCommand.talkAcceptMessage( ToPeerId: ToPeerId, FromPeerId: FromPeerId, @@ -341,6 +349,10 @@ class StartChartManage { // 发送拒绝接听消息 void sendTalkRejectMessage() async { + if (talkStatus.status != TalkStatus.waitingAnswer) { + _log(text: '当前未处于等待接听状态, 无法发送拒绝接听消息'); + return; + } final message = MessageCommand.talkRejectMessage( ToPeerId: ToPeerId, FromPeerId: FromPeerId, @@ -351,6 +363,10 @@ class StartChartManage { // 发送期望接受消息 void sendTalkExpectMessage({required TalkExpect talkExpect}) async { + if (talkStatus.status != TalkStatus.duringCall) { + _log(text: '当前未处于接听状态, 无法发送期望接受数据消息'); + return; + } final message = MessageCommand.talkExpectMessage( ToPeerId: ToPeerId, FromPeerId: FromPeerId, @@ -391,6 +407,10 @@ class StartChartManage { // 发送通话保持消息 Future sendTalkPingMessage( {required String ToPeerId, required String FromPeerId}) async { + if (talkStatus.status != TalkStatus.duringCall) { + _log(text: '当前未处于接听状态, 无法发送通话保持消息'); + return; + } final message = MessageCommand.talkPingMessage( ToPeerId: ToPeerId, FromPeerId: FromPeerId, @@ -401,6 +421,10 @@ class StartChartManage { // 发送通话中挂断消息 Future sendTalkHangupMessage() async { + if (talkStatus.status != TalkStatus.duringCall) { + _log(text: '当前未处于接听状态, 无法发送通话中挂断消息'); + return; + } final message = MessageCommand.talkHangupMessage( ToPeerId: ToPeerId, FromPeerId: FromPeerId, @@ -475,7 +499,6 @@ class StartChartManage { StarChartRegisterNodeEntity starChartRegisterNodeEntity) async { if (starChartRegisterNodeEntity != null) { await Storage.saveStarChartRegisterNodeInfo(starChartRegisterNodeEntity); - _log(text: '注册成功'); } } @@ -764,7 +787,6 @@ class StartChartManage { if (deserialize.Payload != null) { _log(text: 'Udp收到结构体数据---》$deserialize'); } - // _log(text: 'text---》${utf8.decode(deserialize.Payload)}'); } } From 9d31bca9e07c9eea874bde9f245b22a60ca4d9d6 Mon Sep 17 00:00:00 2001 From: Liuyf Date: Wed, 25 Dec 2024 10:46:01 +0800 Subject: [PATCH 16/22] =?UTF-8?q?feat=EF=BC=9AApp=E5=90=8D=E7=A7=B0?= =?UTF-8?q?=E5=9B=BD=E9=99=85=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner.xcodeproj/project.pbxproj | 69 ++++++++-- ios/Runner/en.lproj/InfoPlist.strings | 16 ++- ios/Runner/en.lproj/InfoPlist_sky.strings | 18 +++ ios/Runner/en.lproj/InfoPlist_xhj.strings | 18 +++ ios/Runner/info_xhj.xcstrings | 120 ------------------ ios/Runner/zh-Hans.lproj/InfoPlist.strings | 16 ++- .../zh-Hans.lproj/InfoPlist_sky.strings | 18 +++ .../zh-Hans.lproj/InfoPlist_xhj.strings | 18 +++ 8 files changed, 159 insertions(+), 134 deletions(-) create mode 100644 ios/Runner/en.lproj/InfoPlist_sky.strings create mode 100644 ios/Runner/en.lproj/InfoPlist_xhj.strings create mode 100644 ios/Runner/zh-Hans.lproj/InfoPlist_sky.strings create mode 100644 ios/Runner/zh-Hans.lproj/InfoPlist_xhj.strings diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 86f5d276..f14bf9c6 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -7,8 +7,9 @@ objects = { /* Begin PBXBuildFile section */ - 04BFE7152D14105500A48EC9 /* info_sky.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 04BFE7142D14105500A48EC9 /* info_sky.xcstrings */; }; - 04C1FC572D13F1A2000C959E /* info_xhj.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = 04C1FC562D13F1A2000C959E /* info_xhj.xcstrings */; }; + 04717D692D1B97E100089BD3 /* InfoPlist_sky.strings in Resources */ = {isa = PBXBuildFile; fileRef = 04717D672D1B97E100089BD3 /* InfoPlist_sky.strings */; }; + 04717D6D2D1B983300089BD3 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 04717D6B2D1B983200089BD3 /* InfoPlist.strings */; }; + 04717D712D1B9B5A00089BD3 /* InfoPlist_xhj.strings in Resources */ = {isa = PBXBuildFile; fileRef = 04717D6F2D1B9B5A00089BD3 /* InfoPlist_xhj.strings */; }; 04D0CC262D06CE570042EF10 /* launchImage_xhj.png in Resources */ = {isa = PBXBuildFile; fileRef = 82B657662C919BDF0079121C /* launchImage_xhj.png */; }; 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 25B2A1422F9A2CCCBBCBBB97 /* skyRelease.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 35F02D170492750B437D6AB6 /* skyRelease.xcconfig */; }; @@ -99,9 +100,13 @@ /* Begin PBXFileReference section */ 0420903B2C0EEAA50073E654 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Main.strings"; sourceTree = ""; }; + 04717D682D1B97E100089BD3 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist_sky.strings"; sourceTree = ""; }; + 04717D6A2D1B97E800089BD3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist_sky.strings; sourceTree = ""; }; + 04717D6C2D1B983200089BD3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; + 04717D6E2D1B983900089BD3 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = ""; }; + 04717D702D1B9B5A00089BD3 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist_xhj.strings"; sourceTree = ""; }; + 04717D722D1B9B5D00089BD3 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist_xhj.strings; sourceTree = ""; }; 04BFC4482BCFE05100688FCA /* RunnerRelease-xhj.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "RunnerRelease-xhj.entitlements"; sourceTree = ""; }; - 04BFE7142D14105500A48EC9 /* info_sky.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = info_sky.xcstrings; sourceTree = ""; }; - 04C1FC562D13F1A2000C959E /* info_xhj.xcstrings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json.xcstrings; path = info_xhj.xcstrings; sourceTree = ""; }; 0BEB3ADCCEC961E2916B9004 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 0D02C18E16914A687A4A1AC2 /* devDebug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = devDebug.xcconfig; path = Flutter/devDebug.xcconfig; sourceTree = ""; }; 126D1370182AB44291C67A10 /* Pods-Runner.dev-release-sky.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.dev-release-sky.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.dev-release-sky.xcconfig"; sourceTree = ""; }; @@ -345,7 +350,7 @@ 97C146F01CF9000F007C117D /* Runner */ = { isa = PBXGroup; children = ( - 04C1FC562D13F1A2000C959E /* info_xhj.xcstrings */, + 04717D6B2D1B983200089BD3 /* InfoPlist.strings */, E0B3E9EC2D04B36C00907A95 /* info_dev.plist */, E0B3E9EB2D04B36C00907A95 /* info_pre.plist */, E0B3E9EA2D04B36C00907A95 /* info_sky.plist */, @@ -368,7 +373,8 @@ 82BD91212ADA72360018E523 /* CommonDefine.h */, 76C3321F45DA6AE46ED83204 /* RunnerDebug-dev.entitlements */, 16D90D130794DF74DA656999 /* RunnerRelease-sky.entitlements */, - 04BFE7142D14105500A48EC9 /* info_sky.xcstrings */, + 04717D672D1B97E100089BD3 /* InfoPlist_sky.strings */, + 04717D6F2D1B9B5A00089BD3 /* InfoPlist_xhj.strings */, ); path = Runner; sourceTree = ""; @@ -434,6 +440,7 @@ 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, + 04717D732D1B9CCA00089BD3 /* ShellScript */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, @@ -490,12 +497,14 @@ 04D0CC262D06CE570042EF10 /* launchImage_xhj.png in Resources */, 82F1ED1C2BE8BA8F00265D59 /* BioAuthEngine.bundle in Resources */, 82B657692C919DA70079121C /* launchImage_sky.png in Resources */, + 04717D712D1B9B5A00089BD3 /* InfoPlist_xhj.strings in Resources */, 82F1ED1A2BE8BA7D00265D59 /* OCRXMedia.bundle in Resources */, 82F1ED182BE8BA6900265D59 /* APBToygerFacadeSuitable.bundle in Resources */, 82F1ED162BE8BA2000265D59 /* APBToygerFacade.bundle in Resources */, E0B3E9ED2D04B36C00907A95 /* info_xhj.plist in Resources */, 82F1ED142BE8B9F400265D59 /* ToygerService.bundle in Resources */, 82C026542AE8AC6D0011FE6A /* AliyunEmasServices-Info.plist in Resources */, + 04717D6D2D1B983300089BD3 /* InfoPlist.strings in Resources */, E0A496C92CA30C2C00E376BB /* xhjDevRelease.xcconfig in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, E0A496CD2CA30CA900E376BB /* skyDevRelease.xcconfig in Resources */, @@ -507,16 +516,15 @@ 805C745A7C79091E65665B96 /* devDebug.xcconfig in Resources */, E0B3E9EE2D04B36C00907A95 /* info_sky.plist in Resources */, D415555B0C61C5422202D037 /* devProfile.xcconfig in Resources */, - 04BFE7152D14105500A48EC9 /* info_sky.xcstrings in Resources */, 8A77CDE0EDBCACCE22C29A9E /* devRelease.xcconfig in Resources */, F0A7A6EF7D83CA92324D9C20 /* preDebug.xcconfig in Resources */, E0A496CF2CA30CEF00E376BB /* skyPreviewRelease.xcconfig in Resources */, 9C453CBFAB0703DFA762337C /* preProfile.xcconfig in Resources */, 44827AC367F1EAB110A97660 /* preRelease.xcconfig in Resources */, D7EF77645AB1C3CEEA536468 /* skyDebug.xcconfig in Resources */, - 04C1FC572D13F1A2000C959E /* info_xhj.xcstrings in Resources */, 7B54002BF45E5D8B295B6447 /* skyProfile.xcconfig in Resources */, 25B2A1422F9A2CCCBBCBBB97 /* skyRelease.xcconfig in Resources */, + 04717D692D1B97E100089BD3 /* InfoPlist_sky.strings in Resources */, ADF948FD9EE8BD1AE71F0984 /* xhjDebug.xcconfig in Resources */, E0B3E9F02D04B36D00907A95 /* info_dev.plist in Resources */, EE0A7A61CF36BC83ACA6EE3E /* xhjProfile.xcconfig in Resources */, @@ -531,6 +539,24 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 04717D732D1B9CCA00089BD3 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n# env\nCONFIGURATION_NAME=\"${CONFIGURATION}\"\necho \"-----===${CONFIGURATION_NAME}===------\"\nSTRINGS_FILE=\"\"\nif [[ \"$CONFIGURATION_NAME\" == *\"sky\"* ]]; then\n STRINGS_FILE=\"InfoPlist_sky.strings\"\nelif [[ \"$CONFIGURATION_NAME\" == *\"xhj\"* ]]; then\n STRINGS_FILE=\"InfoPlist_xhj.strings\"\nelse\n STRINGS_FILE=\"InfoPlist.strings\"\nfi\necho \"-----===${STRINGS_FILE}===------\"\necho \"=====${SRCROOT}====\"\n# 替换目标目录中的 InfoPlist.strings\nfor lang in $(ls \"${SRCROOT}/Runner\" | grep .lproj); do\n echo \"-----===${lang}===------\"\n cp \"${SRCROOT}/Runner/${lang}/${STRINGS_FILE}\" \"${SRCROOT}/Runner/${lang}/InfoPlist.strings\"\ndone\n"; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; @@ -618,6 +644,33 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXVariantGroup section */ + 04717D672D1B97E100089BD3 /* InfoPlist_sky.strings */ = { + isa = PBXVariantGroup; + children = ( + 04717D682D1B97E100089BD3 /* zh-Hans */, + 04717D6A2D1B97E800089BD3 /* en */, + ); + name = InfoPlist_sky.strings; + sourceTree = ""; + }; + 04717D6B2D1B983200089BD3 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 04717D6C2D1B983200089BD3 /* en */, + 04717D6E2D1B983900089BD3 /* zh-Hans */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 04717D6F2D1B9B5A00089BD3 /* InfoPlist_xhj.strings */ = { + isa = PBXVariantGroup; + children = ( + 04717D702D1B9B5A00089BD3 /* zh-Hans */, + 04717D722D1B9B5D00089BD3 /* en */, + ); + name = InfoPlist_xhj.strings; + sourceTree = ""; + }; 97C146FA1CF9000F007C117D /* Main.storyboard */ = { isa = PBXVariantGroup; children = ( diff --git a/ios/Runner/en.lproj/InfoPlist.strings b/ios/Runner/en.lproj/InfoPlist.strings index 42bf8e95..4dcbac90 100644 --- a/ios/Runner/en.lproj/InfoPlist.strings +++ b/ios/Runner/en.lproj/InfoPlist.strings @@ -1,8 +1,18 @@ /* - InfoPlist.strings + InfoPlist_xhj.strings Runner - Created by mac on 2024/11/21. + Created by Mac on 2024/12/25. */ -"AppName"="Star Lock"; +CFBundleName="StarLock Pro"; +CFBundleDisplayName="StarLock Pro"; +NSBluetoothAlwaysUsageDescription="The app uses bluetooth to find, connect and transfer data between different devices"; +NSBluetoothPeripheralUsageDescription="The app uses bluetooth to find, connect and transfer data between different devices"; +NSCameraUsageDescription="The application requests the camera to take photos for avatar upload and face authentication"; +NSContactsUsageDescription="Access the address book to send electronic keys to contacts in the address book"; +NSLocationAlwaysAndWhenInUseUsageDescription="updated location information when applying in the front and backend"; +NSLocationAlwaysUsageDescription="updated location information when the app is in the background"; +NSLocationWhenInUseUsageDescription="updated location information when the app is in the foreground"; +NSMicrophoneUsageDescription="request microphone for calls"; +NSPhotoLibraryUsageDescription="request photo album for avatar upload"; diff --git a/ios/Runner/en.lproj/InfoPlist_sky.strings b/ios/Runner/en.lproj/InfoPlist_sky.strings new file mode 100644 index 00000000..f735249c --- /dev/null +++ b/ios/Runner/en.lproj/InfoPlist_sky.strings @@ -0,0 +1,18 @@ +/* + InfoPlist_sky.strings + Runner + + Created by Mac on 2024/12/25. + +*/ +CFBundleName="TTLock Pro"; +CFBundleDisplayName="TTLock Pro"; +NSBluetoothAlwaysUsageDescription="The app uses bluetooth to find, connect and transfer data between different devices"; +NSBluetoothPeripheralUsageDescription="The app uses bluetooth to find, connect and transfer data between different devices"; +NSCameraUsageDescription="The application requests the camera to take photos for avatar upload and face authentication"; +NSContactsUsageDescription="Access the address book to send electronic keys to contacts in the address book"; +NSLocationAlwaysAndWhenInUseUsageDescription="updated location information when applying in the front and backend"; +NSLocationAlwaysUsageDescription="updated location information when the app is in the background"; +NSLocationWhenInUseUsageDescription="updated location information when the app is in the foreground"; +NSMicrophoneUsageDescription="request microphone for calls"; +NSPhotoLibraryUsageDescription="request photo album for avatar upload"; diff --git a/ios/Runner/en.lproj/InfoPlist_xhj.strings b/ios/Runner/en.lproj/InfoPlist_xhj.strings new file mode 100644 index 00000000..4dcbac90 --- /dev/null +++ b/ios/Runner/en.lproj/InfoPlist_xhj.strings @@ -0,0 +1,18 @@ +/* + InfoPlist_xhj.strings + Runner + + Created by Mac on 2024/12/25. + +*/ +CFBundleName="StarLock Pro"; +CFBundleDisplayName="StarLock Pro"; +NSBluetoothAlwaysUsageDescription="The app uses bluetooth to find, connect and transfer data between different devices"; +NSBluetoothPeripheralUsageDescription="The app uses bluetooth to find, connect and transfer data between different devices"; +NSCameraUsageDescription="The application requests the camera to take photos for avatar upload and face authentication"; +NSContactsUsageDescription="Access the address book to send electronic keys to contacts in the address book"; +NSLocationAlwaysAndWhenInUseUsageDescription="updated location information when applying in the front and backend"; +NSLocationAlwaysUsageDescription="updated location information when the app is in the background"; +NSLocationWhenInUseUsageDescription="updated location information when the app is in the foreground"; +NSMicrophoneUsageDescription="request microphone for calls"; +NSPhotoLibraryUsageDescription="request photo album for avatar upload"; diff --git a/ios/Runner/info_xhj.xcstrings b/ios/Runner/info_xhj.xcstrings index d037360b..449697b7 100644 --- a/ios/Runner/info_xhj.xcstrings +++ b/ios/Runner/info_xhj.xcstrings @@ -17,126 +17,6 @@ } } } - }, - "CFBundleName" : { - "comment" : "Bundle name", - "extractionState" : "extracted_with_value", - "localizations" : { - "zh-Hans" : { - "stringUnit" : { - "state" : "new", - "value" : "星锁-local" - } - } - } - }, - "NSBluetoothAlwaysUsageDescription" : { - "comment" : "Privacy - Bluetooth Always Usage Description", - "extractionState" : "extracted_with_value", - "localizations" : { - "zh-Hans" : { - "stringUnit" : { - "state" : "new", - "value" : "The app uses bluetooth to find, connect and transfer data between different devices" - } - } - } - }, - "NSBluetoothPeripheralUsageDescription" : { - "comment" : "Privacy - Bluetooth Peripheral Usage Description", - "extractionState" : "extracted_with_value", - "localizations" : { - "zh-Hans" : { - "stringUnit" : { - "state" : "new", - "value" : "The app uses bluetooth to find, connect and transfer data between different devices" - } - } - } - }, - "NSCameraUsageDescription" : { - "comment" : "Privacy - Camera Usage Description", - "extractionState" : "extracted_with_value", - "localizations" : { - "zh-Hans" : { - "stringUnit" : { - "state" : "new", - "value" : "应用请求相机,以便于拍摄照片,用于头像上传及人脸认证" - } - } - } - }, - "NSContactsUsageDescription" : { - "comment" : "Privacy - Contacts Usage Description", - "extractionState" : "extracted_with_value", - "localizations" : { - "zh-Hans" : { - "stringUnit" : { - "state" : "new", - "value" : "Reason we need access to the contact list" - } - } - } - }, - "NSLocationAlwaysAndWhenInUseUsageDescription" : { - "comment" : "Privacy - Location Always and When In Use Usage Description", - "extractionState" : "extracted_with_value", - "localizations" : { - "zh-Hans" : { - "stringUnit" : { - "state" : "new", - "value" : "应用在前台和后台的时候可以搜到更新的位置信息" - } - } - } - }, - "NSLocationAlwaysUsageDescription" : { - "comment" : "Privacy - Location Always Usage Description", - "extractionState" : "extracted_with_value", - "localizations" : { - "zh-Hans" : { - "stringUnit" : { - "state" : "new", - "value" : "应用在后台的时候可以搜到更新的位置信息" - } - } - } - }, - "NSLocationWhenInUseUsageDescription" : { - "comment" : "Privacy - Location When In Use Usage Description", - "extractionState" : "extracted_with_value", - "localizations" : { - "zh-Hans" : { - "stringUnit" : { - "state" : "new", - "value" : "应用在前台的时候可以搜到更新的位置信息" - } - } - } - }, - "NSMicrophoneUsageDescription" : { - "comment" : "Privacy - Microphone Usage Description", - "extractionState" : "extracted_with_value", - "localizations" : { - "zh-Hans" : { - "stringUnit" : { - "state" : "new", - "value" : "应用请求麦克风用来通话" - } - } - } - }, - "NSPhotoLibraryUsageDescription" : { - "comment" : "Privacy - Photo Library Usage Description", - "extractionState" : "extracted_with_value", - "localizations" : { - "zh-Hans" : { - "stringUnit" : { - "state" : "new", - "value" : "应用请求相册用于头像上传" - } - } - } } }, "version" : "1.0" diff --git a/ios/Runner/zh-Hans.lproj/InfoPlist.strings b/ios/Runner/zh-Hans.lproj/InfoPlist.strings index 5b0ee9b4..60876642 100644 --- a/ios/Runner/zh-Hans.lproj/InfoPlist.strings +++ b/ios/Runner/zh-Hans.lproj/InfoPlist.strings @@ -1,8 +1,18 @@ /* - InfoPlist.strings + InfoPlist_xhj.strings Runner - Created by mac on 2024/11/21. + Created by Mac on 2024/12/25. */ -"AppName"="星锁"; +CFBundleName="星星锁"; +CFBundleDisplayName="星星锁"; +NSBluetoothAlwaysUsageDescription="该应用程序使用蓝牙在不同设备之间查找、连接和传输数据"; +NSBluetoothPeripheralUsageDescription="该应用程序使用蓝牙在不同设备之间查找、连接和传输数据"; +NSCameraUsageDescription="应用请求相机,以便于拍摄照片,用于头像上传及人脸认证"; +NSContactsUsageDescription="应用访问通讯录,用于给通讯录中的联系人发送电子钥匙"; +NSLocationAlwaysAndWhenInUseUsageDescription="应用在前台和后台的时候可以搜到更新的位置信息"; +NSLocationAlwaysUsageDescription="应用在后台的时候可以搜到更新的位置信息"; +NSLocationWhenInUseUsageDescription="应用在前台的时候可以搜到更新的位置信息"; +NSMicrophoneUsageDescription="应用请求麦克风用来通话"; +NSPhotoLibraryUsageDescription="应用请求相册用于头像上传"; diff --git a/ios/Runner/zh-Hans.lproj/InfoPlist_sky.strings b/ios/Runner/zh-Hans.lproj/InfoPlist_sky.strings new file mode 100644 index 00000000..94277ec2 --- /dev/null +++ b/ios/Runner/zh-Hans.lproj/InfoPlist_sky.strings @@ -0,0 +1,18 @@ +/* + InfoPlist_sky.strings + Runner + + Created by Mac on 2024/12/25. + +*/ +CFBundleName="锁通通"; +CFBundleDisplayName="锁通通"; +NSBluetoothAlwaysUsageDescription="该应用程序使用蓝牙在不同设备之间查找、连接和传输数据"; +NSBluetoothPeripheralUsageDescription="该应用程序使用蓝牙在不同设备之间查找、连接和传输数据"; +NSCameraUsageDescription="应用请求相机,以便于拍摄照片,用于头像上传及人脸认证"; +NSContactsUsageDescription="应用访问通讯录,用于给通讯录中的联系人发送电子钥匙"; +NSLocationAlwaysAndWhenInUseUsageDescription="应用在前台和后台的时候可以搜到更新的位置信息"; +NSLocationAlwaysUsageDescription="应用在后台的时候可以搜到更新的位置信息"; +NSLocationWhenInUseUsageDescription="应用在前台的时候可以搜到更新的位置信息"; +NSMicrophoneUsageDescription="应用请求麦克风用来通话"; +NSPhotoLibraryUsageDescription="应用请求相册用于头像上传"; diff --git a/ios/Runner/zh-Hans.lproj/InfoPlist_xhj.strings b/ios/Runner/zh-Hans.lproj/InfoPlist_xhj.strings new file mode 100644 index 00000000..60876642 --- /dev/null +++ b/ios/Runner/zh-Hans.lproj/InfoPlist_xhj.strings @@ -0,0 +1,18 @@ +/* + InfoPlist_xhj.strings + Runner + + Created by Mac on 2024/12/25. + +*/ +CFBundleName="星星锁"; +CFBundleDisplayName="星星锁"; +NSBluetoothAlwaysUsageDescription="该应用程序使用蓝牙在不同设备之间查找、连接和传输数据"; +NSBluetoothPeripheralUsageDescription="该应用程序使用蓝牙在不同设备之间查找、连接和传输数据"; +NSCameraUsageDescription="应用请求相机,以便于拍摄照片,用于头像上传及人脸认证"; +NSContactsUsageDescription="应用访问通讯录,用于给通讯录中的联系人发送电子钥匙"; +NSLocationAlwaysAndWhenInUseUsageDescription="应用在前台和后台的时候可以搜到更新的位置信息"; +NSLocationAlwaysUsageDescription="应用在后台的时候可以搜到更新的位置信息"; +NSLocationWhenInUseUsageDescription="应用在前台的时候可以搜到更新的位置信息"; +NSMicrophoneUsageDescription="应用请求麦克风用来通话"; +NSPhotoLibraryUsageDescription="应用请求相册用于头像上传"; From d7611bdc128d138e694879056860009c0c0ec09d Mon Sep 17 00:00:00 2001 From: Liuyf Date: Wed, 25 Dec 2024 11:01:04 +0800 Subject: [PATCH 17/22] =?UTF-8?q?feat=EF=BC=9AApp=E5=90=8D=E7=A7=B0?= =?UTF-8?q?=E5=9B=BD=E9=99=85=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner.xcodeproj/project.pbxproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index f14bf9c6..238827f8 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -555,7 +555,7 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n# env\nCONFIGURATION_NAME=\"${CONFIGURATION}\"\necho \"-----===${CONFIGURATION_NAME}===------\"\nSTRINGS_FILE=\"\"\nif [[ \"$CONFIGURATION_NAME\" == *\"sky\"* ]]; then\n STRINGS_FILE=\"InfoPlist_sky.strings\"\nelif [[ \"$CONFIGURATION_NAME\" == *\"xhj\"* ]]; then\n STRINGS_FILE=\"InfoPlist_xhj.strings\"\nelse\n STRINGS_FILE=\"InfoPlist.strings\"\nfi\necho \"-----===${STRINGS_FILE}===------\"\necho \"=====${SRCROOT}====\"\n# 替换目标目录中的 InfoPlist.strings\nfor lang in $(ls \"${SRCROOT}/Runner\" | grep .lproj); do\n echo \"-----===${lang}===------\"\n cp \"${SRCROOT}/Runner/${lang}/${STRINGS_FILE}\" \"${SRCROOT}/Runner/${lang}/InfoPlist.strings\"\ndone\n"; + shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n# env\nCONFIGURATION_NAME=\"${CONFIGURATION}\"\necho \"-----===${CONFIGURATION_NAME}===------\"\nSTRINGS_FILE=\"\"\nif [[ \"$CONFIGURATION_NAME\" == *\"sky\"* ]]; then\n STRINGS_FILE=\"InfoPlist_sky.strings\"\nelif [[ \"$CONFIGURATION_NAME\" == *\"xhj\"* ]]; then\n STRINGS_FILE=\"InfoPlist_xhj.strings\"\nelse\n STRINGS_FILE=\"InfoPlist.strings\"\nfi\necho \"-----===${STRINGS_FILE}===------\"\necho \"=====${SRCROOT}====\"\nif [[ \"$STRINGS_FILE\" == \"InfoPlist.strings\" ]]; then\n echo \"=====use Default InfoPlist.strings====\"\n exit 0;\nfi\n# 替换目标目录中的 InfoPlist.strings\nfor lang in $(ls \"${SRCROOT}/Runner\" | grep .lproj); do\n echo \"-----===${lang}===------\"\n cp \"${SRCROOT}/Runner/${lang}/${STRINGS_FILE}\" \"${SRCROOT}/Runner/${lang}/InfoPlist.strings\"\ndone\n"; }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; From 5c351ccc7b9790f19078b7dc6b9b37de3b2dcda1 Mon Sep 17 00:00:00 2001 From: Liuyf Date: Wed, 25 Dec 2024 11:04:54 +0800 Subject: [PATCH 18/22] =?UTF-8?q?feat=EF=BC=9AApp=E6=9C=AC=E5=9C=B0?= =?UTF-8?q?=E8=B0=83=E8=AF=95=E5=90=8D=E7=A7=B0=E5=9B=BD=E9=99=85=E5=8C=96?= =?UTF-8?q?=E9=80=82=E9=85=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ios/Runner/en.lproj/InfoPlist.strings | 4 ++-- ios/Runner/zh-Hans.lproj/InfoPlist.strings | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/ios/Runner/en.lproj/InfoPlist.strings b/ios/Runner/en.lproj/InfoPlist.strings index 4dcbac90..3d31da66 100644 --- a/ios/Runner/en.lproj/InfoPlist.strings +++ b/ios/Runner/en.lproj/InfoPlist.strings @@ -5,8 +5,8 @@ Created by Mac on 2024/12/25. */ -CFBundleName="StarLock Pro"; -CFBundleDisplayName="StarLock Pro"; +CFBundleName="StarLock"; +CFBundleDisplayName="StarLock"; NSBluetoothAlwaysUsageDescription="The app uses bluetooth to find, connect and transfer data between different devices"; NSBluetoothPeripheralUsageDescription="The app uses bluetooth to find, connect and transfer data between different devices"; NSCameraUsageDescription="The application requests the camera to take photos for avatar upload and face authentication"; diff --git a/ios/Runner/zh-Hans.lproj/InfoPlist.strings b/ios/Runner/zh-Hans.lproj/InfoPlist.strings index 60876642..c6a9457d 100644 --- a/ios/Runner/zh-Hans.lproj/InfoPlist.strings +++ b/ios/Runner/zh-Hans.lproj/InfoPlist.strings @@ -5,8 +5,8 @@ Created by Mac on 2024/12/25. */ -CFBundleName="星星锁"; -CFBundleDisplayName="星星锁"; +CFBundleName="星锁"; +CFBundleDisplayName="星锁"; NSBluetoothAlwaysUsageDescription="该应用程序使用蓝牙在不同设备之间查找、连接和传输数据"; NSBluetoothPeripheralUsageDescription="该应用程序使用蓝牙在不同设备之间查找、连接和传输数据"; NSCameraUsageDescription="应用请求相机,以便于拍摄照片,用于头像上传及人脸认证"; From f98d1ab05c06795b23f638349d60a9e42de0ad30 Mon Sep 17 00:00:00 2001 From: liyi Date: Wed, 25 Dec 2024 18:30:10 +0800 Subject: [PATCH 19/22] =?UTF-8?q?fix:=E5=A2=9E=E5=8A=A0=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E6=A8=A1=E6=8B=9F=E5=AF=B9=E8=AE=B2=E6=9C=8D=E5=8A=A1=E7=9A=84?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=B9=B6=E6=98=BE=E7=A4=BA=E5=88=B0=E5=B1=8F?= =?UTF-8?q?=E5=B9=95=E4=B8=8A=E3=80=81=E5=A2=9E=E5=8A=A0=E5=BC=82=E5=B8=B8?= =?UTF-8?q?=E7=B1=BB=E3=80=81=E5=A2=9E=E5=8A=A0=E5=8F=91=E9=80=81=E6=B6=88?= =?UTF-8?q?=E6=81=AF=E5=89=8D=E7=9A=84=E7=8A=B6=E6=80=81=E5=88=A4=E6=96=AD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../star_chart_h264/star_chart_logic.dart | 21 ++- .../star_chart_h264/star_chart_page.dart | 162 +++++++----------- .../star_chart_h264/star_chart_state.dart | 3 + .../constant/exception_constant.dart | 3 + lib/talk/startChart/entity/scp_message.dart | 25 ++- .../start_chart_message_exception.dart | 10 ++ .../start_chart_talk_status_exception.dart | 10 ++ .../impl/udp_ble_passthrough_handler.dart | 2 +- .../handle/impl/udp_echo_test_handler.dart | 2 +- .../impl/udp_gateway_reset_handler.dart | 2 +- .../impl/udp_gateway_transfer_handler.dart | 2 +- .../handle/impl/udp_go_online_handler.dart | 2 +- .../handle/impl/udp_heart_beat_handler.dart | 2 +- .../impl/udp_remote_un_lock_handler.dart | 2 +- .../handle/impl/udp_talk_accept_handler.dart | 4 +- .../handle/impl/udp_talk_data_handler.dart | 105 ++++++++---- .../handle/impl/udp_talk_expect_handler.dart | 2 +- .../handle/impl/udp_talk_hangup_handler.dart | 2 +- .../handle/impl/udp_talk_ping_handler.dart | 2 +- .../handle/impl/udp_talk_push_handler.dart | 2 +- .../udp_talk_receiver_transfer_handler.dart | 2 +- .../handle/impl/udp_talk_reject_handler.dart | 2 +- .../handle/impl/udp_talk_request_handler.dart | 2 +- .../impl/unknow_payload_type_handler.dart | 2 +- .../handle/scp_message_base_handle.dart | 23 +-- .../startChart/handle/scp_message_handle.dart | 2 +- lib/talk/startChart/proto/talk_data.pb.dart | 3 +- lib/talk/startChart/proto/talk_data.proto | 10 +- lib/talk/startChart/start_chart_manage.dart | 22 +-- .../startChart/start_chart_talk_status.dart | 7 + 30 files changed, 239 insertions(+), 201 deletions(-) create mode 100644 lib/talk/startChart/constant/exception_constant.dart create mode 100644 lib/talk/startChart/exception/start_chart_message_exception.dart create mode 100644 lib/talk/startChart/exception/start_chart_talk_status_exception.dart diff --git a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart index 88e37fea..a5f91c0a 100644 --- a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart +++ b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart @@ -24,7 +24,6 @@ class StarChartLogic extends BaseGetXController { int startTime = DateTime.now().millisecondsSinceEpoch; - @override void onReady() { super.onReady(); @@ -51,6 +50,8 @@ class StarChartLogic extends BaseGetXController { _cancelTimers(); stopProcessing(); state.listPhotoData.value = Uint8List(0); + // 停止播放音频 + _stopPlayG711Data(); // 状态错误,返回页面 Get.back(); @@ -72,6 +73,10 @@ class StarChartLogic extends BaseGetXController { case TalkData_ContentTypeE.G711: _playG711Data(talkData.content); break; + case TalkData_ContentTypeE.Image: + // 收到视频数据 + state.listPhotoData.value = Uint8List.fromList(talkData.content); + break; } }); } @@ -107,16 +112,18 @@ class StarChartLogic extends BaseGetXController { } void _startCallTimer() { + if (state.oneMinuteTimeTimer.isActive) return; + state.oneMinuteTimeTimer.cancel(); state.oneMinuteTimeTimer = Timer.periodic(const Duration(seconds: 1), (Timer t) { state.oneMinuteTime.value++; - if (state.oneMinuteTime.value >= 60) { - t.cancel(); - initiateHangUpCommand(); - AppLog.log('通话时间超过60秒,自动挂断'); - state.oneMinuteTime.value = 0; - } + // if (state.oneMinuteTime.value >= 60) { + // t.cancel(); + // initiateHangUpCommand(); + // AppLog.log('通话时间超过60秒,自动挂断'); + // state.oneMinuteTime.value = 0; + // } }); } diff --git a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart index a7854c0c..1717c207 100644 --- a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart +++ b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart @@ -38,7 +38,6 @@ class _StarChartPageState extends State { super.initState(); initAsync(); - // _getTVDataRefreshUIAction(); } Future initAsync() async { @@ -265,52 +264,12 @@ class _StarChartPageState extends State { } } - // void _getTVDataRefreshUIAction() { - // // state.getTVDataRefreshUIEvent = eventBus - // // .on() - // // .listen((GetTVDataRefreshUI event) async { - // // if (event.tvList.isNotEmpty && event.tvList.length > 100) { - // // final Uint8List imageData = Uint8List.fromList(event.tvList); - // // if (!listEquals(state.listPhotoData.value, imageData)) { - // // state.listPhotoData.value = imageData; - // // state.shouldUpdateUI.value = true; - // // if (state.shouldUpdateUI.value) { - // // setState(() {}); - // // state.shouldUpdateUI.value = false; - // // } - // // } - // // } - // // }); - // state.talkDataRepository.talkDataStream.listen((talkData) { - // final contentType = talkData.contentType; - // // 判断数据类型,进行分发处理 - // switch (contentType) { - // case TalkData_ContentTypeE.Image: - // state.listPhotoData.value = Uint8List.fromList(talkData.content); - // if (talkData.content.isNotEmpty && talkData.content.length > 100) { - // // 比较新旧数据是否相同 - // final Uint8List imageData = Uint8List.fromList(talkData.content); - // if (!listEquals(state.listPhotoData.value, imageData)) { - // // 更新状态 - // state.listPhotoData.value = imageData; - // // 设置标志为true,表示需要更新UI - // state.shouldUpdateUI.value = true; - // // WidgetsBinding.instance.addPostFrameCallback((_) { - // // 调用setState方法之前检查标志,只有当标志为true时才更新UI - // if (state.shouldUpdateUI.value) { - // setState(() { - // // 更新UI - // }); - // // 更新完UI后将标志重新设置为false - // state.shouldUpdateUI.value = false; - // } - // // }); - // } - // } - // break; - // } - // }); - // } + String listToHexString(List intList) { + // 将整数列表转换为十六进制字符串列表 + List hexList = intList.map((num) => num.toRadixString(16)).toList(); + // 将十六进制字符串列表连接成一个字符串,没有空格 + return hexList.join(''); + } @override void dispose() { @@ -324,58 +283,67 @@ class _StarChartPageState extends State { } Widget _buildMpeg4TalkView() { - return Stack( - children: [ - Image.memory( - state.listPhotoData.value, - gaplessPlayback: true, - width: 1.sw, - height: 1.sh, - fit: BoxFit.cover, - filterQuality: FilterQuality.high, - errorBuilder: - (BuildContext context, Object error, StackTrace? stackTrace) { - return Container(color: Colors.transparent); - }, - ), - Positioned( - top: ScreenUtil().statusBarHeight + 30.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: [ - Text('$min:$sec', - style: TextStyle(fontSize: 26.sp, color: Colors.white)), - ], - ); - }), - ), - Positioned( - bottom: 10.w, - child: Container( - width: 1.sw - 30.w * 2, - margin: EdgeInsets.all(30.w), - decoration: BoxDecoration( - color: const Color(0xC83C3F41), - borderRadius: BorderRadius.circular(20.h), - ), - child: Column( - children: [ - SizedBox(height: 20.h), - buildTopButtons(), - SizedBox(height: 20.h), - buildBottomButtons(), - SizedBox(height: 20.h), - ], + return Obx( + () => Stack( + children: [ + state.listPhotoData.value.isNotEmpty + ? Image.memory( + state.listPhotoData.value, + gaplessPlayback: true, + width: 1.sw, + height: 1.sh, + fit: BoxFit.cover, + filterQuality: FilterQuality.high, + errorBuilder: (BuildContext context, Object error, + StackTrace? stackTrace) { + return Container(color: Colors.transparent); + }, + ) + : Image.asset( + 'images/main/monitorBg.png', + width: 1.sw, + height: 1.sh, + fit: BoxFit.cover, + ), + Positioned( + top: ScreenUtil().statusBarHeight + 30.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: [ + Text('$min:$sec', + style: TextStyle(fontSize: 26.sp, color: Colors.white)), + ], + ); + }), + ), + Positioned( + bottom: 10.w, + child: Container( + width: 1.sw - 30.w * 2, + margin: EdgeInsets.all(30.w), + decoration: BoxDecoration( + color: const Color(0xC83C3F41), + borderRadius: BorderRadius.circular(20.h), + ), + child: Column( + children: [ + SizedBox(height: 20.h), + buildTopButtons(), + SizedBox(height: 20.h), + buildBottomButtons(), + SizedBox(height: 20.h), + ], + ), ), ), - ), - ], + ], + ), ); } diff --git a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart index 25512f62..68268c85 100644 --- a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart +++ b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart @@ -56,6 +56,9 @@ class StarChartState { RxInt talkStatus = 0.obs; //星图对讲状态 + // 获取 StartChartTalkStatus 的唯一实例 + StartChartTalkStatus talkStatusInstance = StartChartTalkStatus.instance; + // 通话数据流的单例流数据处理类 final TalkDataRepository talkDataRepository = TalkDataRepository.instance; diff --git a/lib/talk/startChart/constant/exception_constant.dart b/lib/talk/startChart/constant/exception_constant.dart new file mode 100644 index 00000000..e53fb85c --- /dev/null +++ b/lib/talk/startChart/constant/exception_constant.dart @@ -0,0 +1,3 @@ +class ExceptionConstant{ + static const String relay = 'relay'; +} \ No newline at end of file diff --git a/lib/talk/startChart/entity/scp_message.dart b/lib/talk/startChart/entity/scp_message.dart index c4f9ee07..f80db8a6 100644 --- a/lib/talk/startChart/entity/scp_message.dart +++ b/lib/talk/startChart/entity/scp_message.dart @@ -1,10 +1,10 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:star_lock/app_settings/app_settings.dart'; +import 'package:star_lock/talk/startChart/exception/start_chart_message_exception.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handler_factory.dart'; - class ScpMessage { ScpMessage({ this.ProtocolFlag, @@ -149,12 +149,13 @@ class ScpMessage { static ScpMessage deserialize(Uint8List bytes) { final message = ScpMessage(); int offset = 0; - // Convert byte array to hex string with zero padding and without spaces + // String hexString = // bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join(); - // - // // Log the hex string - // _log(text: '原始字节数组: $hexString'); + // // _log(text: 'result bytes hex: ${hexString}'); + // _log( + // text: + // '\n result bytes hex: ${hexString} \n payload hex: ${hexString.substring(194)}'); // ProtocolFlag (4 bytes) if (bytes.length - offset >= 4) { @@ -244,8 +245,7 @@ class ScpMessage { // 处理其他类型的Payload if (message.PayloadLength != null && bytes.length - offset >= message.PayloadLength!) { - final Uint8List sublist = - bytes.sublist(offset, offset + message.PayloadLength!); + final sublist = bytes.sublist(offset, offset + message.PayloadLength!); offset += message.PayloadLength!; message.Payload = _handlePayLoad( payloadType: message.PayloadType ?? 0, @@ -268,7 +268,7 @@ class ScpMessage { static dynamic _handlePayLoad({ required int payloadType, required int messageType, - required Uint8List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, @@ -280,7 +280,7 @@ class ScpMessage { final ScpMessageHandler handler = ScpMessageHandlerFactory.createHandler(payloadType); // 处理荷载信息并返回 - return handler.deserializePayload( + final payload = handler.deserializePayload( payloadType: payloadType, messageType: messageType, byte: byte, @@ -290,12 +290,9 @@ class ScpMessage { spIndex: spIndex, messageId: messageId, ); + return payload; } catch (e, stackTrace) { - // 打印异常信息 - _log(text: '❌反序列化udp数据时遇到错误----》$e'); - // 打印堆栈跟踪信息 - _log(text: '堆栈跟踪:\n$stackTrace'); - return ''; + throw StartChartMessageException('❌反序列化udp数据时遇到错误----》$e \n$stackTrace'); } } diff --git a/lib/talk/startChart/exception/start_chart_message_exception.dart b/lib/talk/startChart/exception/start_chart_message_exception.dart new file mode 100644 index 00000000..fa0065c2 --- /dev/null +++ b/lib/talk/startChart/exception/start_chart_message_exception.dart @@ -0,0 +1,10 @@ +class StartChartMessageException implements Exception { + final String message; + + StartChartMessageException(this.message); + + @override + String toString() { + return 'StartChartMessageException{message: $message}'; + } +} diff --git a/lib/talk/startChart/exception/start_chart_talk_status_exception.dart b/lib/talk/startChart/exception/start_chart_talk_status_exception.dart new file mode 100644 index 00000000..6620b177 --- /dev/null +++ b/lib/talk/startChart/exception/start_chart_talk_status_exception.dart @@ -0,0 +1,10 @@ +class StartChartTalkStatusException implements Exception { + final String message; + + StartChartTalkStatusException(this.message); + + @override + String toString() { + return 'StartChartTalkStatusException{message: $message}'; + } +} diff --git a/lib/talk/startChart/handle/impl/udp_ble_passthrough_handler.dart b/lib/talk/startChart/handle/impl/udp_ble_passthrough_handler.dart index a09f6aa2..de492782 100644 --- a/lib/talk/startChart/handle/impl/udp_ble_passthrough_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_ble_passthrough_handler.dart @@ -35,7 +35,7 @@ class UdpBlePassThroughHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required Uint8List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/impl/udp_echo_test_handler.dart b/lib/talk/startChart/handle/impl/udp_echo_test_handler.dart index 75ecb89a..a814992c 100644 --- a/lib/talk/startChart/handle/impl/udp_echo_test_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_echo_test_handler.dart @@ -36,7 +36,7 @@ class UdpEchoTestHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required Uint8List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/impl/udp_gateway_reset_handler.dart b/lib/talk/startChart/handle/impl/udp_gateway_reset_handler.dart index 1fcdd0c6..b0a54741 100644 --- a/lib/talk/startChart/handle/impl/udp_gateway_reset_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_gateway_reset_handler.dart @@ -36,7 +36,7 @@ class UdpGateWayResetHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required Uint8List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/impl/udp_gateway_transfer_handler.dart b/lib/talk/startChart/handle/impl/udp_gateway_transfer_handler.dart index c5546e30..705c5ffe 100644 --- a/lib/talk/startChart/handle/impl/udp_gateway_transfer_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_gateway_transfer_handler.dart @@ -39,7 +39,7 @@ class UdpGateWayTransferHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required Uint8List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/impl/udp_go_online_handler.dart b/lib/talk/startChart/handle/impl/udp_go_online_handler.dart index 830301ed..7899adc9 100644 --- a/lib/talk/startChart/handle/impl/udp_go_online_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_go_online_handler.dart @@ -42,7 +42,7 @@ class UdpGoOnlineHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required Uint8List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/impl/udp_heart_beat_handler.dart b/lib/talk/startChart/handle/impl/udp_heart_beat_handler.dart index ed866f63..4e6caec1 100644 --- a/lib/talk/startChart/handle/impl/udp_heart_beat_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_heart_beat_handler.dart @@ -43,7 +43,7 @@ class UdpHeartBeatHandler extends ScpMessageBaseHandle void handleRealTimeData(ScpMessage scpMessage) {} @override - deserializePayload({required int payloadType, required int messageType, required Uint8List byte, int? offset, int? PayloadLength, int? spTotal, int? spIndex, int? messageId}) { + deserializePayload({required int payloadType, required int messageType, required List byte, int? offset, int? PayloadLength, int? spTotal, int? spIndex, int? messageId}) { // 心跳 HeartbeatResponse heartbeatResponse = HeartbeatResponse.fromBytes(byte); return heartbeatResponse; diff --git a/lib/talk/startChart/handle/impl/udp_remote_un_lock_handler.dart b/lib/talk/startChart/handle/impl/udp_remote_un_lock_handler.dart index 03d24701..9e037284 100644 --- a/lib/talk/startChart/handle/impl/udp_remote_un_lock_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_remote_un_lock_handler.dart @@ -38,7 +38,7 @@ class UdpRemoteUnLockHandler extends ScpMessageBaseHandle implements ScpMessageH } @override - deserializePayload({required int payloadType, required int messageType, required Uint8List byte, int? offset, int? PayloadLength, int? spTotal, int? spIndex, int? messageId}) { + deserializePayload({required int payloadType, required int messageType, required List byte, int? offset, int? PayloadLength, int? spTotal, int? spIndex, int? messageId}) { if (messageType == MessageTypeConstant.Resp) { final GenericResp genericResp = GenericResp(); genericResp.mergeFromBuffer(byte); diff --git a/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart index c29dbc84..a3c1fe8c 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart @@ -44,7 +44,7 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle stopRingtone(); // 设置状态为接听中 talkStatus.setAnsweredSuccessfully(); - talkStatus.setDuringCall(); + talkStatus.setWaitingData(); } } @@ -58,7 +58,7 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required Uint8List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart index 20d6d1f0..8d94787d 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart @@ -30,10 +30,45 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle talkDataOverTimeTimerManager.receiveMessage(); if (scpMessage.Payload != null) { final TalkData talkData = scpMessage.Payload; - print('talkData: ${listToHexString(talkData.content)}'); - // 处理音视频数据 _handleTalkData(talkData: talkData); + // 设置状态为接听中 + talkStatus.setDuringCall(); + } + } + + @override + deserializePayload( + {required int payloadType, + required int messageType, + required List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { + if (messageType == MessageTypeConstant.RealTimeData) { + print( + '收到音视频数据:${byte.length} messageId:$messageId spTotal:$spTotal spIndex:$spIndex PayloadLength:$PayloadLength'); + // 回声测试 + if (spTotal != null && + spTotal > 1 && + messageId != null && + spIndex != null) { + // 分包处理 + return handleFragmentedPayload( + messageId: messageId, + spTotal: spTotal, + spIndex: spIndex, + byte: byte, + payloadType: payloadType, + ); + } else { + // 没有分包直接解析 + final TalkData talkData = TalkData(); + talkData.mergeFromBuffer(byte); + return talkData; + } } } @@ -69,7 +104,15 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle } /// 处理图片数据 - void _handleVideoImage(TalkData talkData) {} + void _handleVideoImage(TalkData talkData) { + final List processCompletePayload = + _processCompletePayload(Uint8List.fromList(talkData.content)); + // 循环发送每一帧的数据 + processCompletePayload.forEach((element) { + talkData.content = element; + talkDataRepository.addTalkData(talkData); + }); + } /// 处理g711音频数据 void _handleVideoG711(TalkData talkData) { @@ -84,36 +127,36 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle } } - @override - deserializePayload( - {required int payloadType, - required int messageType, - required Uint8List byte, - int? offset, - int? PayloadLength, - int? spTotal, - int? spIndex, - int? messageId}) { - if (messageType == MessageTypeConstant.RealTimeData) { - // 回声测试 - if (spTotal != null && - spTotal > 1 && - messageId != null && - spIndex != null) { - // 分包处理 - return handleFragmentedPayload( - messageId: messageId, - spTotal: spTotal, - spIndex: spIndex, - byte: byte, - payloadType: payloadType, - ); + /// 查找完整的帧数据 + List _processCompletePayload(Uint8List payload) { + // 存储找到的所有完整帧 + List frames = []; + + // 寻找完整帧 (0xFFD8 开始, 0xFFD9 结束) + int startIdx = payload.indexOf(0xFF); + while (startIdx != -1 && startIdx + 1 < payload.length) { + if (payload[startIdx + 1] == 0xD8) { + // 找到帧的起始标志 0xFFD8 + int endIdx = startIdx + 2; + while (endIdx < payload.length - 1) { + endIdx = payload.indexOf(0xFF, endIdx); + if (endIdx == -1) break; + if (endIdx + 1 < payload.length && payload[endIdx + 1] == 0xD9) { + // 找到帧的结束标志 0xFFD9 + Uint8List frame = payload.sublist(startIdx, endIdx + 2); + frames.add(frame); + startIdx = endIdx + 2; // 继续寻找下一个帧 + break; + } else { + endIdx += 1; // 继续寻找结束标志 + } + } } else { - // 没有分包直接解析 - final TalkData talkData = TalkData(); - talkData.mergeFromBuffer(byte); - return talkData; + startIdx = payload.indexOf(0xFF, startIdx + 1); // 寻找下一个起始标志 } } + + // 返回找到的所有完整帧 + return frames; } } diff --git a/lib/talk/startChart/handle/impl/udp_talk_expect_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_expect_handler.dart index 2dda6dab..6bdf44e7 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_expect_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_expect_handler.dart @@ -49,7 +49,7 @@ class UdpTalkExpectHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required Uint8List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart index 1bebcddc..dfe227ef 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart @@ -53,7 +53,7 @@ class UdpTalkHangUpHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required Uint8List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/impl/udp_talk_ping_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_ping_handler.dart index 42b7021a..95c9f243 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_ping_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_ping_handler.dart @@ -36,7 +36,7 @@ class UdpTalkPingHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required Uint8List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/impl/udp_talk_push_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_push_handler.dart index 15ecf86e..addcedbc 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_push_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_push_handler.dart @@ -37,7 +37,7 @@ class UdpTalkPushHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required Uint8List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/impl/udp_talk_receiver_transfer_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_receiver_transfer_handler.dart index 168fde66..154afbac 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_receiver_transfer_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_receiver_transfer_handler.dart @@ -35,7 +35,7 @@ class UdpTalkReceiverTransferHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required Uint8List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/impl/udp_talk_reject_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_reject_handler.dart index ffcf75da..96e722ac 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_reject_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_reject_handler.dart @@ -50,7 +50,7 @@ class UdpTalkRejectHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required Uint8List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart index eb6a0cdd..af67d4b6 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart @@ -95,7 +95,7 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required Uint8List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/impl/unknow_payload_type_handler.dart b/lib/talk/startChart/handle/impl/unknow_payload_type_handler.dart index 4aee3b03..29665244 100644 --- a/lib/talk/startChart/handle/impl/unknow_payload_type_handler.dart +++ b/lib/talk/startChart/handle/impl/unknow_payload_type_handler.dart @@ -34,7 +34,7 @@ class UnKnowPayloadTypeHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required Uint8List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/scp_message_base_handle.dart b/lib/talk/startChart/handle/scp_message_base_handle.dart index a54fb927..de51229b 100644 --- a/lib/talk/startChart/handle/scp_message_base_handle.dart +++ b/lib/talk/startChart/handle/scp_message_base_handle.dart @@ -1,5 +1,6 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:typed_data'; import 'package:audioplayers/audioplayers.dart'; import 'package:flutter_local_notifications/flutter_local_notifications.dart'; @@ -19,6 +20,7 @@ import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; class ScpMessageBaseHandle { final startChartManage = StartChartManage(); + final List _buffer = []; /// 分包缓冲区 // 存储每个 messageId 对应的分包数据 @@ -33,12 +35,12 @@ class ScpMessageBaseHandle { // 通话保持超时监听定时器管理 final talkePingOverTimeTimerManager = OverTimeTimerManager( - timeoutInSeconds: 15, + timeoutInSeconds: 55, ); // 通话数据超时定时器 final talkDataOverTimeTimerManager = OverTimeTimerManager( - timeoutInSeconds: 13, + timeoutInSeconds: 53, ); // 回复成功消息 @@ -86,7 +88,7 @@ class ScpMessageBaseHandle { }) { // 初始化分包列表 String key = '$messageId-$payloadType'; - if (!_packetBuffer.containsKey(messageId)) { + if (!_packetBuffer.containsKey(key)) { _packetBuffer[key] = List.filled(spTotal, []); _startTimer(key); } @@ -104,24 +106,17 @@ class ScpMessageBaseHandle { // 检查是否接收到所有分包 if (_packetBuffer[key]!.every((packet) => packet.isNotEmpty)) { // 重组所有分包 - List completePayload = - _packetBuffer[key]!.expand((packet) => packet).toList(); - + Uint8List completePayload = Uint8List.fromList( + _packetBuffer[key]!.expand((packet) => packet).toList()); // 清除已重组和超时的分包数据 _clearPacketData(key); - // 解析完整的 payload + // 使用重组的包构造成TalkData if (payloadType == PayloadTypeConstant.talkData) { - final TalkData talkData = TalkData(); + final talkData = TalkData(); talkData.mergeFromBuffer(completePayload); return talkData; } - // if (payloadType == PayloadTypeConstant.echoTest) { - // return completePayload; - // } else { - // String payload = utf8.decode(completePayload); - // return payload; - // } } else { // 如果分包尚未接收完全,返回 null 或其他指示符 return null; diff --git a/lib/talk/startChart/handle/scp_message_handle.dart b/lib/talk/startChart/handle/scp_message_handle.dart index e43ac425..352b5f3f 100644 --- a/lib/talk/startChart/handle/scp_message_handle.dart +++ b/lib/talk/startChart/handle/scp_message_handle.dart @@ -19,7 +19,7 @@ abstract class ScpMessageHandler { dynamic deserializePayload({ required int payloadType, required int messageType, - required Uint8List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/proto/talk_data.pb.dart b/lib/talk/startChart/proto/talk_data.pb.dart index d39cd33e..8fd25269 100644 --- a/lib/talk/startChart/proto/talk_data.pb.dart +++ b/lib/talk/startChart/proto/talk_data.pb.dart @@ -17,7 +17,7 @@ import 'talk_data.pbenum.dart'; export 'talk_data.pbenum.dart'; -/// 注意这个包不应该使用Req,而应该只用单向发送类型,不等待响应。 +/// 注意这个包不应该使用请求响应(Req/Resp),应该用单向发送类型(RealTimeData),不等待响应。 /// 在未收到对方的Ping,或者其他情况,即停止发送。 class TalkData extends $pb.GeneratedMessage { factory TalkData({ @@ -78,6 +78,7 @@ class TalkData extends $pb.GeneratedMessage { @$pb.TagNumber(1) void clearContentType() => clearField(1); + /// 音视频数据,例如PCM的字节,或者H264的字节,或者图片的字节 @$pb.TagNumber(2) $core.List<$core.int> get content => $_getN(1); @$pb.TagNumber(2) diff --git a/lib/talk/startChart/proto/talk_data.proto b/lib/talk/startChart/proto/talk_data.proto index 5dcb93a0..9c47ac05 100644 --- a/lib/talk/startChart/proto/talk_data.proto +++ b/lib/talk/startChart/proto/talk_data.proto @@ -3,7 +3,7 @@ syntax = "proto3"; package main; option go_package = "./spb/talk"; -// 注意这个包不应该使用Req,而应该只用单向发送类型,不等待响应。 +// 注意这个包不应该使用请求响应(Req/Resp),应该用单向发送类型(RealTimeData),不等待响应。 // 在未收到对方的Ping,或者其他情况,即停止发送。 message TalkData { // 内容类型枚举: 一张图传;一帧H264;一段G711 @@ -13,10 +13,10 @@ message TalkData { G711 = 2; }; ContentTypeE ContentType = 1; + // 音视频数据,例如PCM的字节,或者H264的字节,或者图片的字节 bytes Content = 2; // 时间 毫秒,例如第一帧视频,就是0ms,第2秒的第一帧,就是1000ms - // 该字段仅用于协调音视频同步,而不是用于影响音视频播放时机 - // 对于对讲场景,应该根据网络延迟和设备性能自行决定缓冲时长,在满足播放条件时立即进行渲染。 - uint32 DurationMs = 3; - + // 该字段仅用于协调音视频同步,而不是用于影响音视频播放时机 + // 对于对讲场景,应该根据网络延迟和设备性能自行决定缓冲时长,在满足播放条件时立即进行渲染。 + uint32 DurationMs = 3; } diff --git a/lib/talk/startChart/start_chart_manage.dart b/lib/talk/startChart/start_chart_manage.dart index d146a42a..e7de70a6 100644 --- a/lib/talk/startChart/start_chart_manage.dart +++ b/lib/talk/startChart/start_chart_manage.dart @@ -21,6 +21,7 @@ import 'package:star_lock/talk/startChart/entity/relay_info_entity.dart'; import 'package:star_lock/talk/startChart/entity/report_information_data.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/entity/star_chart_register_node_entity.dart'; +import 'package:star_lock/talk/startChart/exception/start_chart_message_exception.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handler_factory.dart'; import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart'; @@ -363,10 +364,6 @@ class StartChartManage { // 发送期望接受消息 void sendTalkExpectMessage({required TalkExpect talkExpect}) async { - if (talkStatus.status != TalkStatus.duringCall) { - _log(text: '当前未处于接听状态, 无法发送期望接受数据消息'); - return; - } final message = MessageCommand.talkExpectMessage( ToPeerId: ToPeerId, FromPeerId: FromPeerId, @@ -407,10 +404,6 @@ class StartChartManage { // 发送通话保持消息 Future sendTalkPingMessage( {required String ToPeerId, required String FromPeerId}) async { - if (talkStatus.status != TalkStatus.duringCall) { - _log(text: '当前未处于接听状态, 无法发送通话保持消息'); - return; - } final message = MessageCommand.talkPingMessage( ToPeerId: ToPeerId, FromPeerId: FromPeerId, @@ -474,7 +467,8 @@ class StartChartManage { var result = await _udpSocket?.send( message, InternetAddress(remoteHost), remotePort); if (result != message.length) { - AppLog.log('❌Udp send data error----> $result ${message.length}'); + throw StartChartMessageException( + '❌Udp send data error----> $result ${message.length}'); // _udpSocket = null; } } @@ -663,7 +657,7 @@ class StartChartManage { .signPKCS1v15Bytes(signData, fastRsa.Hash.SHA256, pemPrivateKey); resultSign = hex.encode(result); } catch (e) { - _log(text: '❌--->上报信息生成签名时出现错误: $e'); + throw StartChartMessageException('❌--->上报信息生成签名时出现错误: $e'); e.printError(); } return resultSign ?? ''; @@ -785,14 +779,14 @@ class StartChartManage { } if (deserialize.PayloadType != PayloadTypeConstant.heartbeat) { if (deserialize.Payload != null) { - _log(text: 'Udp收到结构体数据---》$deserialize'); + // _log(text: 'Udp收到结构体数据---》$deserialize'); } // _log(text: 'text---》${utf8.decode(deserialize.Payload)}'); } } } catch (e, stackTrace) { - _log(text: '❌ Udp result data error ----> $e'); - _log(text: '堆栈跟踪:\n$stackTrace'); + throw StartChartMessageException( + '❌ Udp result data error ----> $e\n,$stackTrace'); } } }); @@ -815,7 +809,7 @@ class StartChartManage { handler.handleInvalidReq(scpMessage); } } catch (e, stackTrace) { - _log(text: '❌ 处理udp返回数据时遇到错误---> $e\n,$stackTrace'); + throw StartChartMessageException('❌ 处理udp返回数据时遇到错误---> $e\n,$stackTrace'); } } diff --git a/lib/talk/startChart/start_chart_talk_status.dart b/lib/talk/startChart/start_chart_talk_status.dart index 4fd0a363..6c8b5f11 100644 --- a/lib/talk/startChart/start_chart_talk_status.dart +++ b/lib/talk/startChart/start_chart_talk_status.dart @@ -64,6 +64,13 @@ class StartChartTalkStatus { // 可以在这里添加特定于 "waitingAnswer" 状态的逻辑 } + /// 设置状态为等待数据 + void setWaitingData() { + _setStatus(TalkStatus.waitingData); + // 可以在这里添加特定于 "waitingAnswer" 状态的逻辑 + } + + /// 设置状态为通话中 void setDuringCall() { _setStatus(TalkStatus.duringCall); From 44ba4c2920df32c85187db934eb5347ab95e86b8 Mon Sep 17 00:00:00 2001 From: liyi Date: Fri, 27 Dec 2024 13:35:07 +0800 Subject: [PATCH 20/22] =?UTF-8?q?fix:=E8=B0=83=E6=95=B4=E5=BF=83=E8=B7=B3?= =?UTF-8?q?=E5=8C=85=E8=A7=A3=E6=9E=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../startChart/entity/heartbeat_response.dart | 44 +++++--- .../proto/talk_data_h264_frame.pb.dart | 102 ++++++++++++++++++ .../proto/talk_data_h264_frame.pbenum.dart | 36 +++++++ .../proto/talk_data_h264_frame.pbjson.dart | 43 ++++++++ .../proto/talk_data_h264_frame.pbserver.dart | 14 +++ 5 files changed, 226 insertions(+), 13 deletions(-) create mode 100644 lib/talk/startChart/proto/talk_data_h264_frame.pb.dart create mode 100644 lib/talk/startChart/proto/talk_data_h264_frame.pbenum.dart create mode 100644 lib/talk/startChart/proto/talk_data_h264_frame.pbjson.dart create mode 100644 lib/talk/startChart/proto/talk_data_h264_frame.pbserver.dart diff --git a/lib/talk/startChart/entity/heartbeat_response.dart b/lib/talk/startChart/entity/heartbeat_response.dart index 4b71ca0e..19fa3c91 100644 --- a/lib/talk/startChart/entity/heartbeat_response.dart +++ b/lib/talk/startChart/entity/heartbeat_response.dart @@ -1,37 +1,55 @@ class HeartbeatResponse { int? statusCode; int? nextPingTime; + String? clientAddr; HeartbeatResponse({ this.statusCode, this.nextPingTime, + this.clientAddr, }); factory HeartbeatResponse.fromBytes(List bytes) { final message = HeartbeatResponse(); int offset = 0; - // Set default value for statusCode - message.statusCode = 0; // 或者其他默认值 - - // Check if the entire array has at least 2 bytes for nextPingTime - if (bytes.length < 2) { + // 检查是否有足够的数据来解析状态码 + if (bytes.length < 1) { throw FormatException("Insufficient data for HeartbeatResponse"); } - // nextPingTime (2 bytes, little-endian) - if (bytes.length - offset >= 2) { - message.nextPingTime = (bytes[offset + 1] << 8) | bytes[offset]; - offset += 2; - } else { - throw FormatException("Invalid nextPingTime length"); + // 解析状态码 (1 byte) + message.statusCode = bytes[offset]; + offset += 1; + + // 检查是否有足够的数据来解析 nextPingTime + if (bytes.length < offset + 2) { + throw FormatException("Insufficient data for nextPingTime"); } + // 解析 nextPingTime (2 bytes, little-endian) + message.nextPingTime = (bytes[offset + 1] << 8) | bytes[offset]; + offset += 2; + + // 解析 clientAddr (null-terminated string) + final clientAddrBytes = []; + while (offset < bytes.length && bytes[offset] != 0) { + clientAddrBytes.add(bytes[offset]); + offset++; + } + + // 跳过 null terminator + if (offset < bytes.length && bytes[offset] == 0) { + offset++; + } + + message.clientAddr = String.fromCharCodes(clientAddrBytes); + return message; } @override String toString() { - return 'HeartbeatResponse{statusCode: $statusCode, nextPingTime: $nextPingTime}'; + return 'HeartbeatResponse{statusCode: $statusCode, nextPingTime: $nextPingTime, clientAddr: $clientAddr}'; } -} +} \ No newline at end of file diff --git a/lib/talk/startChart/proto/talk_data_h264_frame.pb.dart b/lib/talk/startChart/proto/talk_data_h264_frame.pb.dart new file mode 100644 index 00000000..4a77a31a --- /dev/null +++ b/lib/talk/startChart/proto/talk_data_h264_frame.pb.dart @@ -0,0 +1,102 @@ +// +// Generated code. Do not modify. +// source: talk_data_h264_frame.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +import 'talk_data_h264_frame.pbenum.dart'; + +export 'talk_data_h264_frame.pbenum.dart'; + +class TalkDataH264Frame extends $pb.GeneratedMessage { + factory TalkDataH264Frame({ + $core.int? frameSeq, + TalkDataH264Frame_FrameTypeE? frameType, + $core.List<$core.int>? frameData, + }) { + final $result = create(); + if (frameSeq != null) { + $result.frameSeq = frameSeq; + } + if (frameType != null) { + $result.frameType = frameType; + } + if (frameData != null) { + $result.frameData = frameData; + } + return $result; + } + TalkDataH264Frame._() : super(); + factory TalkDataH264Frame.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory TalkDataH264Frame.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'TalkDataH264Frame', package: const $pb.PackageName(_omitMessageNames ? '' : 'main'), createEmptyInstance: create) + ..a<$core.int>(1, _omitFieldNames ? '' : 'FrameSeq', $pb.PbFieldType.OU3, protoName: 'FrameSeq') + ..e(2, _omitFieldNames ? '' : 'FrameType', $pb.PbFieldType.OE, protoName: 'FrameType', defaultOrMaker: TalkDataH264Frame_FrameTypeE.NONE, valueOf: TalkDataH264Frame_FrameTypeE.valueOf, enumValues: TalkDataH264Frame_FrameTypeE.values) + ..a<$core.List<$core.int>>(3, _omitFieldNames ? '' : 'FrameData', $pb.PbFieldType.OY, protoName: 'FrameData') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + TalkDataH264Frame clone() => TalkDataH264Frame()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + TalkDataH264Frame copyWith(void Function(TalkDataH264Frame) updates) => super.copyWith((message) => updates(message as TalkDataH264Frame)) as TalkDataH264Frame; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static TalkDataH264Frame create() => TalkDataH264Frame._(); + TalkDataH264Frame createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static TalkDataH264Frame getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static TalkDataH264Frame? _defaultInstance; + + /// 帧序号 seq + @$pb.TagNumber(1) + $core.int get frameSeq => $_getIZ(0); + @$pb.TagNumber(1) + set frameSeq($core.int v) { $_setUnsignedInt32(0, v); } + @$pb.TagNumber(1) + $core.bool hasFrameSeq() => $_has(0); + @$pb.TagNumber(1) + void clearFrameSeq() => clearField(1); + + @$pb.TagNumber(2) + TalkDataH264Frame_FrameTypeE get frameType => $_getN(1); + @$pb.TagNumber(2) + set frameType(TalkDataH264Frame_FrameTypeE v) { setField(2, v); } + @$pb.TagNumber(2) + $core.bool hasFrameType() => $_has(1); + @$pb.TagNumber(2) + void clearFrameType() => clearField(2); + + /// 帧数据 + @$pb.TagNumber(3) + $core.List<$core.int> get frameData => $_getN(2); + @$pb.TagNumber(3) + set frameData($core.List<$core.int> v) { $_setBytes(2, v); } + @$pb.TagNumber(3) + $core.bool hasFrameData() => $_has(2); + @$pb.TagNumber(3) + void clearFrameData() => clearField(3); +} + + +const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); +const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/lib/talk/startChart/proto/talk_data_h264_frame.pbenum.dart b/lib/talk/startChart/proto/talk_data_h264_frame.pbenum.dart new file mode 100644 index 00000000..fb124137 --- /dev/null +++ b/lib/talk/startChart/proto/talk_data_h264_frame.pbenum.dart @@ -0,0 +1,36 @@ +// +// Generated code. Do not modify. +// source: talk_data_h264_frame.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +/// 帧类型,对于H264,I帧、P帧, +/// 未知为NONE?有时候发送方不知道,或者渲染方可以从帧数据中解析 +class TalkDataH264Frame_FrameTypeE extends $pb.ProtobufEnum { + static const TalkDataH264Frame_FrameTypeE NONE = TalkDataH264Frame_FrameTypeE._(0, _omitEnumNames ? '' : 'NONE'); + static const TalkDataH264Frame_FrameTypeE I = TalkDataH264Frame_FrameTypeE._(1, _omitEnumNames ? '' : 'I'); + static const TalkDataH264Frame_FrameTypeE P = TalkDataH264Frame_FrameTypeE._(2, _omitEnumNames ? '' : 'P'); + + static const $core.List values = [ + NONE, + I, + P, + ]; + + static final $core.Map<$core.int, TalkDataH264Frame_FrameTypeE> _byValue = $pb.ProtobufEnum.initByValue(values); + static TalkDataH264Frame_FrameTypeE? valueOf($core.int value) => _byValue[value]; + + const TalkDataH264Frame_FrameTypeE._($core.int v, $core.String n) : super(v, n); +} + + +const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/lib/talk/startChart/proto/talk_data_h264_frame.pbjson.dart b/lib/talk/startChart/proto/talk_data_h264_frame.pbjson.dart new file mode 100644 index 00000000..bc7b066c --- /dev/null +++ b/lib/talk/startChart/proto/talk_data_h264_frame.pbjson.dart @@ -0,0 +1,43 @@ +// +// Generated code. Do not modify. +// source: talk_data_h264_frame.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:convert' as $convert; +import 'dart:core' as $core; +import 'dart:typed_data' as $typed_data; + +@$core.Deprecated('Use talkDataH264FrameDescriptor instead') +const TalkDataH264Frame$json = { + '1': 'TalkDataH264Frame', + '2': [ + {'1': 'FrameSeq', '3': 1, '4': 1, '5': 13, '10': 'FrameSeq'}, + {'1': 'FrameType', '3': 2, '4': 1, '5': 14, '6': '.main.TalkDataH264Frame.FrameTypeE', '10': 'FrameType'}, + {'1': 'FrameData', '3': 3, '4': 1, '5': 12, '10': 'FrameData'}, + ], + '4': [TalkDataH264Frame_FrameTypeE$json], +}; + +@$core.Deprecated('Use talkDataH264FrameDescriptor instead') +const TalkDataH264Frame_FrameTypeE$json = { + '1': 'FrameTypeE', + '2': [ + {'1': 'NONE', '2': 0}, + {'1': 'I', '2': 1}, + {'1': 'P', '2': 2}, + ], +}; + +/// Descriptor for `TalkDataH264Frame`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List talkDataH264FrameDescriptor = $convert.base64Decode( + 'ChFUYWxrRGF0YUgyNjRGcmFtZRIaCghGcmFtZVNlcRgBIAEoDVIIRnJhbWVTZXESQAoJRnJhbW' + 'VUeXBlGAIgASgOMiIubWFpbi5UYWxrRGF0YUgyNjRGcmFtZS5GcmFtZVR5cGVFUglGcmFtZVR5' + 'cGUSHAoJRnJhbWVEYXRhGAMgASgMUglGcmFtZURhdGEiJAoKRnJhbWVUeXBlRRIICgROT05FEA' + 'ASBQoBSRABEgUKAVAQAg=='); + diff --git a/lib/talk/startChart/proto/talk_data_h264_frame.pbserver.dart b/lib/talk/startChart/proto/talk_data_h264_frame.pbserver.dart new file mode 100644 index 00000000..6377dfd8 --- /dev/null +++ b/lib/talk/startChart/proto/talk_data_h264_frame.pbserver.dart @@ -0,0 +1,14 @@ +// +// Generated code. Do not modify. +// source: talk_data_h264_frame.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names +// ignore_for_file: deprecated_member_use_from_same_package, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +export 'talk_data_h264_frame.pb.dart'; + From 133f8634487672b76f82e9a939c24ff597dc043c Mon Sep 17 00:00:00 2001 From: liyi Date: Fri, 27 Dec 2024 13:35:56 +0800 Subject: [PATCH 21/22] =?UTF-8?q?fix:=E5=A2=9E=E5=8A=A0=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E6=92=AD=E6=94=BE=E9=80=BB=E8=BE=91=E3=80=81=E8=B0=83=E6=95=B4?= =?UTF-8?q?proto=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/appRouters.dart | 8 +- lib/blue/io_tool/manager_event_bus.dart | 8 + .../star_chart_h264/star_chart_logic.dart | 276 ------------ .../star_chart_h264/star_chart_page.dart | 394 ------------------ .../star_chart_h264/star_chart_state.dart | 65 --- lib/main/lockMian/lockMain/lockMain_page.dart | 6 + .../startChart/command/message_command.dart | 2 +- lib/talk/startChart/constant/talk_status.dart | 18 + lib/talk/startChart/entity/scp_message.dart | 2 +- .../events/talk_status_change_event.dart | 13 - .../handle/impl/udp_heart_beat_handler.dart | 10 +- .../handle/impl/udp_talk_accept_handler.dart | 14 +- .../handle/impl/udp_talk_data_handler.dart | 39 +- .../handle/impl/udp_talk_expect_handler.dart | 9 +- .../handle/impl/udp_talk_hangup_handler.dart | 18 +- .../handle/impl/udp_talk_reject_handler.dart | 9 +- .../handle/impl/udp_talk_request_handler.dart | 17 +- .../handle/other/talk_data_repository.dart | 2 +- .../handle/scp_message_base_handle.dart | 14 +- .../proto/talk_data_h264_frame.proto | 18 + lib/talk/startChart/proto/talk_expect.pb.dart | 150 ++++++- .../startChart/proto/talk_expect.pbenum.dart | 36 +- .../startChart/proto/talk_expect.pbjson.dart | 66 ++- lib/talk/startChart/proto/talk_expect.proto | 44 +- lib/talk/startChart/start_chart_manage.dart | 28 +- .../startChart/start_chart_talk_status.dart | 125 ++---- .../views/talkView/talk_view_logic.dart | 291 +++++++++++++ .../views/talkView/talk_view_page.dart | 373 +++++++++++++++++ .../views/talkView/talk_view_state.dart | 68 +++ 29 files changed, 1139 insertions(+), 984 deletions(-) delete mode 100644 lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart delete mode 100644 lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart delete mode 100644 lib/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart create mode 100644 lib/talk/startChart/constant/talk_status.dart delete mode 100644 lib/talk/startChart/events/talk_status_change_event.dart create mode 100644 lib/talk/startChart/proto/talk_data_h264_frame.proto create mode 100644 lib/talk/startChart/views/talkView/talk_view_logic.dart create mode 100644 lib/talk/startChart/views/talkView/talk_view_page.dart create mode 100644 lib/talk/startChart/views/talkView/talk_view_state.dart diff --git a/lib/appRouters.dart b/lib/appRouters.dart index 1c460c84..50aec2f6 100755 --- a/lib/appRouters.dart +++ b/lib/appRouters.dart @@ -36,7 +36,6 @@ import 'package:star_lock/main/lockDetail/messageWarn/msgNotification/msgNotific import 'package:star_lock/main/lockDetail/messageWarn/msgNotification/nDaysUnopened/nDaysUnopened_page.dart'; import 'package:star_lock/main/lockDetail/messageWarn/msgNotification/openDoorNotify/openDoorNotify_page.dart'; import 'package:star_lock/main/lockDetail/messageWarn/notificationMode/notificationMode_page.dart'; -import 'package:star_lock/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart'; import 'package:star_lock/main/lockDetail/palm/addPalm/addPalm_page.dart'; import 'package:star_lock/main/lockDetail/palm/palmList/palmList_page.dart'; import 'package:star_lock/main/lockDetail/passwordKey/passwordKeyDetailChangeDate/passwordKeyDetailChangeDate_page.dart'; @@ -61,6 +60,7 @@ import 'package:star_lock/mine/mineSet/transferSmartLock/transferSmartLockList/t import 'package:star_lock/mine/valueAddedServices/advancedFeaturesWeb/advancedFeaturesWeb_page.dart'; import 'package:star_lock/mine/valueAddedServices/advancedFunctionRecord/advancedFunctionRecord_page.dart'; import 'package:star_lock/mine/valueAddedServices/valueAddedServicesRecord/value_added_services_record_page.dart'; +import 'package:star_lock/talk/startChart/views/talkView/talk_view_page.dart'; import 'common/safetyVerification/safetyVerification_page.dart'; import 'login/forgetPassword/starLock_forgetPassword_page.dart'; @@ -514,6 +514,7 @@ abstract class Routers { static const String googleHomePage = '/googleHomePage'; //GoogleHome static const String doubleLockLinkPage = '/doubleLockLinkPage'; //双锁联动 static const String starChartPage = '/starChartPage'; //星图 + static const String starChartTalkView = '/starChartTalkView'; //星图对讲页面 } abstract class AppRouters { @@ -747,7 +748,8 @@ abstract class AppRouters { GetPage( name: Routers.lockTimePage, page: () => const LockTimePage(), - ), // 诊断 + ), + // 诊断 GetPage( name: Routers.diagnosePage, page: () => const DiagnosePage(), @@ -1192,6 +1194,6 @@ abstract class AppRouters { name: Routers.doubleLockLinkPage, page: () => const DoubleLockLinkPage()), GetPage( - name: Routers.starChartPage, page: () => const StarChartPage()), + name: Routers.starChartTalkView, page: () => const TalkViewPage()), ]; } diff --git a/lib/blue/io_tool/manager_event_bus.dart b/lib/blue/io_tool/manager_event_bus.dart index f85b9bc7..566a1899 100755 --- a/lib/blue/io_tool/manager_event_bus.dart +++ b/lib/blue/io_tool/manager_event_bus.dart @@ -21,4 +21,12 @@ class EventBusManager { eventBusFir(dynamic event) { eventBus?.fire(event); } + + // 发送事件 + void fireEvent(dynamic event) { + eventBus?.fire(event); + } + + // 获取 EventBus 实例 + EventBus? get bus => eventBus; } diff --git a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart deleted file mode 100644 index a5f91c0a..00000000 --- a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart +++ /dev/null @@ -1,276 +0,0 @@ -import 'dart:async'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_pcm_sound/flutter_pcm_sound.dart'; -import 'package:flutter_voice_processor/flutter_voice_processor.dart'; -import 'package:get/get.dart'; -import 'package:permission_handler/permission_handler.dart'; -import 'package:star_lock/app_settings/app_settings.dart'; -import 'package:star_lock/blue/io_tool/manager_event_bus.dart'; -import 'package:star_lock/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart'; -import 'package:star_lock/talk/startChart/events/talk_status_change_event.dart'; -import 'package:star_lock/talk/startChart/handle/other/talk_data_repository.dart'; -import 'package:star_lock/talk/startChart/proto/talk_data.pbenum.dart'; -import 'package:star_lock/talk/startChart/start_chart_manage.dart'; -import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; -import 'package:star_lock/tools/baseGetXController.dart'; -import 'package:star_lock/tools/eventBusEventManage.dart'; - -class StarChartLogic extends BaseGetXController { - final StarChartState state = StarChartState(); - - /// 收到Talk发送的状态 - StreamSubscription? _getTalkStatusRefreshUIEvent; - - int startTime = DateTime.now().millisecondsSinceEpoch; - - @override - void onReady() { - super.onReady(); - // 初始化音频播放设备,确保采样率和声道数与 PCM 数据匹配 - FlutterPcmSound.setup(sampleRate: 8000, channelCount: 1); - - // 设置音频数据的供给阈值 - FlutterPcmSound.setFeedThreshold(8000 ~/ 2); // 根据需要调整 - _getTalkStatusRefreshUIAction(); - _startListenTalkData(); - } - - void _getTalkStatusRefreshUIAction() { - _getTalkStatusRefreshUIEvent = EventBusManager() - .eventBus! - .on() - .listen((TalkStatusChangeEvent event) async { - state.talkStatus.value = event.newStatus.index; - state.oneMinuteTime.value = 0; - if (state.talkStatus.value == TalkStatus.rejected.index || - state.talkStatus.value == TalkStatus.notTalkData.index || - state.talkStatus.value == TalkStatus.notTalkPing.index || - state.talkStatus.value == TalkStatus.end.index) { - _cancelTimers(); - stopProcessing(); - state.listPhotoData.value = Uint8List(0); - // 停止播放音频 - _stopPlayG711Data(); - // 状态错误,返回页面 - Get.back(); - - return; - } - - if (state.talkStatus.value == TalkStatus.duringCall.index) { - _startCallTimer(); - } - }); - } - - // 监听音视频数据流 - void _startListenTalkData() { - state.talkDataRepository.talkDataStream.listen((talkData) { - final contentType = talkData.contentType; - // 判断数据类型,进行分发处理 - switch (contentType) { - case TalkData_ContentTypeE.G711: - _playG711Data(talkData.content); - break; - case TalkData_ContentTypeE.Image: - // 收到视频数据 - state.listPhotoData.value = Uint8List.fromList(talkData.content); - break; - } - }); - } - - void syncPlay() { - int currentTime = DateTime.now().millisecondsSinceEpoch - startTime; - - // 播放音频 - // while (audioBuffer.isNotEmpty && audioBuffer.first.durationMs <= currentTime) { - // TalkData audioData = audioBuffer.removeAt(0); - // playAudio(audioData.content); - // } - // - // // 播放视频 - // while (videoBuffer.isNotEmpty && videoBuffer.first.durationMs <= currentTime) { - // TalkData videoData = videoBuffer.removeAt(0); - // playVideo(videoData.content); - // } - } - - /// 播放音频数据 - Future _playG711Data(List pcmData) async { - // 将 PCM 数据转换为 PcmArrayInt16 - final PcmArrayInt16 fromList = PcmArrayInt16.fromList(pcmData); - await FlutterPcmSound.feed(fromList); - FlutterPcmSound.play(); - } - - void _stopPlayG711Data() { - FlutterPcmSound.pause(); - FlutterPcmSound.clear(); - FlutterPcmSound.stop(); - } - - void _startCallTimer() { - if (state.oneMinuteTimeTimer.isActive) return; - - state.oneMinuteTimeTimer.cancel(); - state.oneMinuteTimeTimer = - Timer.periodic(const Duration(seconds: 1), (Timer t) { - state.oneMinuteTime.value++; - // if (state.oneMinuteTime.value >= 60) { - // t.cancel(); - // initiateHangUpCommand(); - // AppLog.log('通话时间超过60秒,自动挂断'); - // state.oneMinuteTime.value = 0; - // } - }); - } - - void _cancelTimers() { - state.oneMinuteTimeTimer.cancel(); - } - - // 发起接听命令 - void initiateAnswerCommand() { - StartChartManage().sendTalkAcceptMessage(); - } - - // 发起挂断命令 - void initiateHangUpCommand() { - _cancelTimers(); - if (state.talkStatus.value == TalkStatus.duringCall.index) { - // 如果是通话中就挂断 - StartChartManage().sendTalkHangupMessage(); - } else { - // 拒绝 - StartChartManage().sendTalkRejectMessage(); - } - Get.back(); - } - - Future _onFrame(List frame) async { - state.allFrames.add(frame); - final List concatenatedFrames = concatenateFrames(state.allFrames); - AppLog.log('pcm数据:$concatenatedFrames'); - - final List pcmBytes = listLinearToULaw(frame); - //发起发送录音命令 - // StartChartManage().sendTalkAudioMessage(pcmBytes); - } - - List listLinearToULaw(List pcmList) { - return pcmList.map(linearToULaw).toList(); - } - - List concatenateFrames(List> frames) { - return frames.expand((frame) => frame).toList(); - } - - Future startProcessing() async { - // state.isButtonDisabled.value = true; - // - // state.voiceProcessor?.addFrameListener(_onFrame); - // state.voiceProcessor?.addErrorListener(_onError); - // try { - // if (await state.voiceProcessor?.hasRecordAudioPermission() ?? false) { - // await state.voiceProcessor?.start(state.frameLength, state.sampleRate); - // state.isProcessing.value = - // await state.voiceProcessor?.isRecording() ?? false; - // } else { - // state.errorMessage.value = 'Recording permission not granted'; - // } - // } on PlatformException catch (ex) { - // state.errorMessage.value = 'Failed to start recorder: $ex'; - // } finally { - // state.isButtonDisabled.value = false; - // } - } - - void _onError(VoiceProcessorException error) { - state.errorMessage.value = error.message!; - } - - Future stopProcessing() async { - // 检查 voiceProcessor 是否已经初始化 - // if (state.voiceProcessor == null) { - // state.errorMessage.value = 'Voice processor is not initialized.'; - // return; - // } - // - // state.isButtonDisabled.value = true; - // try { - // await state.voiceProcessor?.stop(); - // state.voiceProcessor?.removeFrameListener(_onFrame); - // state.udpSendDataFrameNumber = 0; - // } on PlatformException catch (ex) { - // state.errorMessage.value = 'Failed to stop recorder: $ex'; - // } finally { - // state.isProcessing.value = - // await state.voiceProcessor?.isRecording() ?? false; - // state.isButtonDisabled.value = false; - // } - } - - int linearToULaw(int pcmVal) { - int mask; - int seg; - int uval; - - if (pcmVal < 0) { - pcmVal = 0x84 - pcmVal; - mask = 0x7F; - } else { - pcmVal += 0x84; - mask = 0xFF; - } - - seg = search(pcmVal); - if (seg >= 8) { - return 0x7F ^ mask; - } else { - uval = seg << 4; - uval |= (pcmVal >> (seg + 3)) & 0xF; - return uval ^ mask; - } - } - - int search(int val) { - final List table = [ - 0xFF, - 0x1FF, - 0x3FF, - 0x7FF, - 0xFFF, - 0x1FFF, - 0x3FFF, - 0x7FFF - ]; - for (int i = 0; i < table.length; i++) { - if (val <= table[i]) { - return i; - } - } - return table.length; - } - - Future getPermissionStatus() async { - const Permission permission = Permission.microphone; - final PermissionStatus status = await permission.status; - if (status.isGranted) { - return true; - } else if (status.isDenied || status.isRestricted) { - await requestPermission(permission); - } else if (status.isPermanentlyDenied) { - openAppSettings(); - } - return false; - } - - Future requestPermission(Permission permission) async { - final PermissionStatus status = await permission.request(); - if (status.isPermanentlyDenied) { - openAppSettings(); - } - } -} diff --git a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart deleted file mode 100644 index 1717c207..00000000 --- a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_page.dart +++ /dev/null @@ -1,394 +0,0 @@ -import 'dart:async'; -import 'dart:convert'; -import 'dart:io'; -import 'dart:ui' as ui; - -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/rendering.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_screenutil/flutter_screenutil.dart'; -import 'package:get/get.dart'; -import 'package:image_gallery_saver/image_gallery_saver.dart'; -import 'package:path_provider/path_provider.dart'; -import 'package:star_lock/app_settings/app_colors.dart'; -import 'package:star_lock/app_settings/app_settings.dart'; -import 'package:star_lock/main/lockDetail/monitoring/star_chart_h264/star_chart_logic.dart'; -import 'package:star_lock/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart'; -import 'package:star_lock/talk/startChart/proto/talk_data.pbenum.dart'; -import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; -import 'package:star_lock/talk/startChart/webView/h264_web_view.dart'; -import 'package:star_lock/talk/udp/udp_manage.dart'; -import 'package:star_lock/tools/eventBusEventManage.dart'; -import 'package:star_lock/tools/showTFView.dart'; - -class StarChartPage extends StatefulWidget { - const StarChartPage({Key? key}) : super(key: key); - - @override - State createState() => _StarChartPageState(); -} - -class _StarChartPageState extends State { - final StarChartLogic logic = Get.put(StarChartLogic()); - final StarChartState state = Get.find().state; - - @override - void initState() { - super.initState(); - - initAsync(); - } - - Future initAsync() async { - await requestMicrophonePermission(); - } - - @override - Widget build(BuildContext context) { - return PopScope( - canPop: false, - child: RepaintBoundary( - key: state.globalKey, - child: Container( - width: 1.sw, - height: 1.sh, - color: Colors.transparent, - child: _buildTalkView(isMpeg4: true), - ), - ), - ); - } - - Widget buildTopButtons() { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - buildIconButton( - icon: state.isOpenVoice.value - ? 'images/main/icon_lockDetail_monitoringCloseVoice.png' - : 'images/main/icon_lockDetail_monitoringOpenVoice.png', - onTap: () { - state.isOpenVoice.value = !state.isOpenVoice.value; - }, - ), - SizedBox(width: 60.w), - buildIconButton( - icon: 'images/main/icon_lockDetail_monitoringScreenshot.png', - onTap: captureAndSavePng, - ), - SizedBox(width: 60.w), - buildIconButton( - icon: 'images/main/icon_lockDetail_monitoringScreenRecording.png', - onTap: () { - // Get.toNamed(Routers.monitoringRealTimeScreenPage); - }, - ), - ], - ); - } - - Widget buildBottomButtons() { - return Row( - mainAxisAlignment: MainAxisAlignment.spaceEvenly, - children: [ - buildAnswerButton(), - buildIconButton( - icon: 'images/main/icon_lockDetail_hangUp.png', - label: '挂断'.tr, - color: Colors.red, - onTap: () async { - logic.initiateHangUpCommand(); - }, - ), - buildIconButton( - icon: 'images/main/icon_lockDetail_monitoringUnlock.png', - label: '开锁'.tr, - color: AppColors.mainColor, - onTap: () { - if (UDPManage().remoteUnlock == 1) { - showDeletPasswordAlertDialog(context); - } else { - logic.showToast('请在锁设置中开启远程开锁'.tr); - } - }, - ), - ], - ); - } - - Widget buildAnswerButton() { - return Obx(() { - // final bool isDuringCall = - // state.talkStatus.value == TalkStatus.duringCall.index; - return buildIconButton( - icon: state.talkStatus.value == TalkStatus.duringCall.index - ? 'images/main/icon_lockDetail_monitoringUnTalkback.png' - : 'images/main/icon_lockDetail_monitoringAnswerCalls.png', - label: state.talkStatus.value == TalkStatus.duringCall.index - ? '长按说话'.tr - : '接听'.tr, - onTap: () async { - if (state.talkStatus.value == TalkStatus.waitingAnswer.index) { - logic.initiateAnswerCommand(); - setState(() {}); - } - }, - onLongPress: () { - state.listAudioData.value = []; - logic.startProcessing(); - }, - onLongPressUp: () { - logic.stopProcessing(); - }, - ); - }); - } - - Widget buildIconButton({ - required String icon, - String? label, - Color color = Colors.white, - required Function() onTap, - Function()? onLongPress, - Function()? onLongPressUp, - }) { - final double wh = 80.w; - return GestureDetector( - onTap: onTap, - onLongPress: onLongPress, - onLongPressUp: onLongPressUp, - child: SizedBox( - height: 140.h, - child: Column( - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Container( - width: wh, - height: wh, - decoration: BoxDecoration( - color: color, - borderRadius: BorderRadius.circular((wh + 10.w * 2) / 2), - ), - padding: EdgeInsets.all(20.w), - child: Image.asset(icon, fit: BoxFit.fitWidth), - ), - if (label != null) ...[ - SizedBox(height: 20.w), - Expanded( - child: Text( - label, - style: TextStyle(fontSize: 20.sp, color: Colors.white), - textAlign: TextAlign.center, - ), - ), - ], - ], - ), - ), - ); - } - - void showDeletPasswordAlertDialog(BuildContext context) { - showDialog( - barrierDismissible: false, - context: context, - builder: (BuildContext context) { - return ShowTFView( - title: '请输入6位数字开锁密码'.tr, - tipTitle: '', - controller: state.passwordTF, - inputFormatters: [ - LengthLimitingTextInputFormatter(6), //限制长度 - FilteringTextInputFormatter.allow(RegExp('[0-9]')), - ], - sureClick: () async { - if (state.passwordTF.text.isEmpty) { - logic.showToast('请输入开锁密码'.tr); - return; - } - final List numbers = []; - final List lockIDData = utf8.encode(state.passwordTF.text); - numbers.addAll(lockIDData); - for (int i = 0; i < 6 - lockIDData.length; i++) { - numbers.add(0); - } - //todo: 开门暂未实现 - // logic.udpOpenDoorAction(numbers); - }, - cancelClick: () { - Get.back(); - }, - ); - }, - ); - } - - Future requestMicrophonePermission() async { - await logic.getPermissionStatus().then((bool value) async { - if (!value) { - return; - } - }); - } - - Future captureAndSavePng() async { - try { - if (state.globalKey.currentContext == null) { - AppLog.log('截图失败: 未找到当前上下文'); - return; - } - 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); - if (byteData == null) { - AppLog.log('截图失败: 图像数据为空'); - return; - } - final Uint8List pngBytes = byteData.buffer.asUint8List(); - - final Directory directory = await getApplicationDocumentsDirectory(); - final String imagePath = '${directory.path}/screenshot.png'; - - final File imgFile = File(imagePath); - await imgFile.writeAsBytes(pngBytes); - - await ImageGallerySaver.saveFile(imagePath); - - AppLog.log('截图保存路径: $imagePath'); - logic.showToast('截图已保存到相册'.tr); - } catch (e) { - AppLog.log('截图失败: $e'); - } - } - - String listToHexString(List intList) { - // 将整数列表转换为十六进制字符串列表 - List hexList = intList.map((num) => num.toRadixString(16)).toList(); - // 将十六进制字符串列表连接成一个字符串,没有空格 - return hexList.join(''); - } - - @override - void dispose() { - super.dispose(); - logic.stopProcessing(); - // state.getTVDataRefreshUIEvent!.cancel(); - } - - Widget _buildTalkView({required bool isMpeg4}) { - return isMpeg4 ? _buildMpeg4TalkView() : _buildH264TalkView(); - } - - Widget _buildMpeg4TalkView() { - return Obx( - () => Stack( - children: [ - state.listPhotoData.value.isNotEmpty - ? Image.memory( - state.listPhotoData.value, - gaplessPlayback: true, - width: 1.sw, - height: 1.sh, - fit: BoxFit.cover, - filterQuality: FilterQuality.high, - errorBuilder: (BuildContext context, Object error, - StackTrace? stackTrace) { - return Container(color: Colors.transparent); - }, - ) - : Image.asset( - 'images/main/monitorBg.png', - width: 1.sw, - height: 1.sh, - fit: BoxFit.cover, - ), - Positioned( - top: ScreenUtil().statusBarHeight + 30.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: [ - Text('$min:$sec', - style: TextStyle(fontSize: 26.sp, color: Colors.white)), - ], - ); - }), - ), - Positioned( - bottom: 10.w, - child: Container( - width: 1.sw - 30.w * 2, - margin: EdgeInsets.all(30.w), - decoration: BoxDecoration( - color: const Color(0xC83C3F41), - borderRadius: BorderRadius.circular(20.h), - ), - child: Column( - children: [ - SizedBox(height: 20.h), - buildTopButtons(), - SizedBox(height: 20.h), - buildBottomButtons(), - SizedBox(height: 20.h), - ], - ), - ), - ), - ], - ), - ); - } - - Widget _buildH264TalkView() { - return Stack( - children: [ - H264WebView(), - Positioned( - top: ScreenUtil().statusBarHeight + 30.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: [ - Text('$min:$sec', - style: TextStyle(fontSize: 26.sp, color: Colors.white)), - ], - ); - }), - ), - Positioned( - bottom: 10.w, - child: Container( - width: 1.sw - 30.w * 2, - margin: EdgeInsets.all(30.w), - decoration: BoxDecoration( - color: const Color(0xC83C3F41), - borderRadius: BorderRadius.circular(20.h), - ), - child: Column( - children: [ - SizedBox(height: 20.h), - buildTopButtons(), - SizedBox(height: 20.h), - buildBottomButtons(), - SizedBox(height: 20.h), - ], - ), - ), - ), - ], - ); - } -} diff --git a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart b/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart deleted file mode 100644 index 68268c85..00000000 --- a/lib/main/lockDetail/monitoring/star_chart_h264/star_chart_state.dart +++ /dev/null @@ -1,65 +0,0 @@ -import 'dart:async'; -import 'dart:typed_data'; - -import 'package:flutter/material.dart'; -import 'package:flutter_voice_processor/flutter_voice_processor.dart'; -import 'package:get/get.dart'; -import 'package:network_info_plus/network_info_plus.dart'; -import 'package:star_lock/talk/startChart/handle/other/talk_data_repository.dart'; -import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; - -import '../../../../tools/storage.dart'; - -class StarChartState { - RxBool isOpenVoice = false.obs; - int udpSendDataFrameNumber = 0; // 帧序号 - // var isSenderAudioData = false.obs;// 是否要发送音频数据 - // StreamSubscription? getTVDataRefreshUIEvent; //收到视频流数据 - RxBool shouldUpdateUI = false.obs; //是否需要更新UI - - Future userMobileIP = NetworkInfo().getWifiIP(); - Future userUid = Storage.getUid(); - - // RxInt udpStatus = - // 0.obs; //0:初始状态 1:等待监视 2: 3:监视中 4:呼叫成功 5:主角通话中 6:被叫通话 8:被叫通话中 9:长按说话 - TextEditingController passwordTF = TextEditingController(); - - Rx listPhotoData = Uint8List(0).obs; //得到的视频流字节数据 - RxList listAudioData = [].obs; //得到的音频流字节数据 - -//录音相关 - late VoiceProcessor? voiceProcessor; - RxBool isProcessing = false.obs; //是否正在处理音频数据 - RxBool isButtonDisabled = false.obs; //是否禁用按钮 - final int frameLength = 320; //音视频帧长度为320 - final int sampleRate = 8000; //音频采样率为8000 - RxString errorMessage = ''.obs; - List> allFrames = >[]; - - GlobalKey globalKey = GlobalKey(); - - late Timer oneMinuteTimeTimer = - Timer(const Duration(seconds: 1), () {}); // 定时器超过60秒关闭当前界面 - RxInt oneMinuteTime = 0.obs; // 定时器秒数 - - // 定时器如果发送了接听的命令 而没收到回复就每秒重复发送10次 - late Timer answerTimer = Timer(const Duration(seconds: 1), () {}); //接听命令定时器 - RxInt answerSeconds = 0.obs; - RxBool isClickAnswer = false.obs; //是否点击了接听按钮 - - late Timer hangUpTimer = Timer(const Duration(seconds: 1), () {}); //挂断命令定时器 - RxInt hangUpSeconds = 0.obs; - RxBool isClickHangUp = false.obs; //是否点击了挂断按钮 - - late Timer openDoorTimer = Timer(const Duration(seconds: 1), () {}); //开门命令定时器 - RxInt openDoorSeconds = 0.obs; - - RxInt talkStatus = 0.obs; //星图对讲状态 - - // 获取 StartChartTalkStatus 的唯一实例 - StartChartTalkStatus talkStatusInstance = StartChartTalkStatus.instance; - - // 通话数据流的单例流数据处理类 - final TalkDataRepository talkDataRepository = TalkDataRepository.instance; - -} diff --git a/lib/main/lockMian/lockMain/lockMain_page.dart b/lib/main/lockMian/lockMain/lockMain_page.dart index a6697b2d..ebcdd7ff 100755 --- a/lib/main/lockMian/lockMain/lockMain_page.dart +++ b/lib/main/lockMian/lockMain/lockMain_page.dart @@ -9,6 +9,8 @@ import 'package:star_lock/app_settings/app_colors.dart'; import 'package:star_lock/blue/blue_manage.dart'; import 'package:star_lock/main/lockMian/lockList/lockList_xhj_page.dart'; import 'package:star_lock/main/lockMian/lockMain/lockMain_state.dart'; +import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart'; +import 'package:star_lock/talk/startChart/proto/talk_expect.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_request.pb.dart'; import 'package:star_lock/talk/startChart/proto/test.pb.dart'; import 'package:star_lock/talk/startChart/start_chart_manage.dart'; @@ -72,6 +74,10 @@ class _StarLockMainPageState extends State _initLoadDataAction(); } + String bufferToHexString(Uint8List buffer) { + return buffer.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join(); + } + @override void didChangeDependencies() { super.didChangeDependencies(); diff --git a/lib/talk/startChart/command/message_command.dart b/lib/talk/startChart/command/message_command.dart index 00093af7..86184110 100644 --- a/lib/talk/startChart/command/message_command.dart +++ b/lib/talk/startChart/command/message_command.dart @@ -295,7 +295,7 @@ class MessageCommand { static List talkExpectMessage({ required String FromPeerId, required String ToPeerId, - required TalkExpect talkExpect, + required TalkExpectReq talkExpect, int? MessageId, }) { final payload = talkExpect.writeToBuffer(); diff --git a/lib/talk/startChart/constant/talk_status.dart b/lib/talk/startChart/constant/talk_status.dart new file mode 100644 index 00000000..73f43a82 --- /dev/null +++ b/lib/talk/startChart/constant/talk_status.dart @@ -0,0 +1,18 @@ + + +enum TalkStatus { + none, // 无状态 + waitingAnswer, // 等待接听 + answeredSuccessfully, // 接听成功 + waitingData, // 等待数据 + duringCall, // 通话中 + hangingUpDuring, // 通话中挂断 + rejected, // 被拒绝 + uninitialized, // 未初始化 + initializationCompleted, // 初始化完成 + notTalkData, // 暂无通话数据 + notTalkPing, // 暂无通话保持 + error, // 错误状态 + end, // 结束 +} + diff --git a/lib/talk/startChart/entity/scp_message.dart b/lib/talk/startChart/entity/scp_message.dart index f80db8a6..2820ab61 100644 --- a/lib/talk/startChart/entity/scp_message.dart +++ b/lib/talk/startChart/entity/scp_message.dart @@ -155,7 +155,7 @@ class ScpMessage { // // _log(text: 'result bytes hex: ${hexString}'); // _log( // text: - // '\n result bytes hex: ${hexString} \n payload hex: ${hexString.substring(194)}'); + // '\n result bytes hex: ${hexString} \n payload hex: ${hexString.substring(210)}'); // ProtocolFlag (4 bytes) if (bytes.length - offset >= 4) { diff --git a/lib/talk/startChart/events/talk_status_change_event.dart b/lib/talk/startChart/events/talk_status_change_event.dart deleted file mode 100644 index aea113c2..00000000 --- a/lib/talk/startChart/events/talk_status_change_event.dart +++ /dev/null @@ -1,13 +0,0 @@ -import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; - -class TalkStatusChangeEvent { - final TalkStatus oldStatus; - final TalkStatus newStatus; - - TalkStatusChangeEvent(this.oldStatus, this.newStatus); - - @override - String toString() { - return "TalkStatusChangeEvent: ${oldStatus.name} -> ${newStatus.name}"; - } -} diff --git a/lib/talk/startChart/handle/impl/udp_heart_beat_handler.dart b/lib/talk/startChart/handle/impl/udp_heart_beat_handler.dart index 4e6caec1..5f0a6031 100644 --- a/lib/talk/startChart/handle/impl/udp_heart_beat_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_heart_beat_handler.dart @@ -43,7 +43,15 @@ class UdpHeartBeatHandler extends ScpMessageBaseHandle void handleRealTimeData(ScpMessage scpMessage) {} @override - deserializePayload({required int payloadType, required int messageType, required List byte, int? offset, int? PayloadLength, int? spTotal, int? spIndex, int? messageId}) { + deserializePayload( + {required int payloadType, + required int messageType, + required List byte, + int? offset, + int? PayloadLength, + int? spTotal, + int? spIndex, + int? messageId}) { // 心跳 HeartbeatResponse heartbeatResponse = HeartbeatResponse.fromBytes(byte); return heartbeatResponse; diff --git a/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart index a3c1fe8c..f6e932e1 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart @@ -2,6 +2,7 @@ import 'dart:convert'; import 'dart:typed_data'; import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:flutter_pcm_sound/flutter_pcm_sound.dart'; import 'package:get/get.dart'; import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; @@ -17,13 +18,8 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle implements ScpMessageHandler { @override void handleReq(ScpMessage scpMessage) { - print('收到同意接听请求'); // 回复同意接听消息 - startChartManage.sendGenericRespSuccessMessage( - ToPeerId: scpMessage.FromPeerId!, - FromPeerId: scpMessage.ToPeerId!, - PayloadType: scpMessage.PayloadType!, - ); + replySuccessMessage(scpMessage); } @override @@ -32,7 +28,6 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle final GenericResp genericResp = scpMessage.Payload; if (checkGenericRespSuccess(genericResp)) { Future.delayed(Duration(seconds: 1), () { - print('启动定时器判断'); // 启动通话保持定时器 _handleStartTalkPing(); // 启动发送预期数据请求 @@ -42,9 +37,8 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle }); // 停止播放铃声 stopRingtone(); - // 设置状态为接听中 + // 设置状态为接听成功 talkStatus.setAnsweredSuccessfully(); - talkStatus.setWaitingData(); } } @@ -58,7 +52,7 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart index 8d94787d..bfcdbc7d 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_data_handler.dart @@ -1,8 +1,11 @@ import 'dart:convert'; +import 'dart:io'; import 'dart:typed_data'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:get/get.dart'; +import 'package:path_provider/path_provider.dart'; +import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/talk/call/g711.dart'; import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; @@ -32,11 +35,13 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle final TalkData talkData = scpMessage.Payload; // 处理音视频数据 _handleTalkData(talkData: talkData); - // 设置状态为接听中 - talkStatus.setDuringCall(); } } + String bufferToHexString(List buffer) { + return buffer.map((byte) => byte.toRadixString(16).padLeft(2, '0')).join(); + } + @override deserializePayload( {required int payloadType, @@ -47,9 +52,9 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle int? spTotal, int? spIndex, int? messageId}) { + // AppLog.log( + // '没有组包之前的每一个包的数据:${byte.length} messageId:$messageId spTotal:$spTotal spIndex:$spIndex PayloadLength:$PayloadLength,byte:${bufferToHexString(byte)}'); if (messageType == MessageTypeConstant.RealTimeData) { - print( - '收到音视频数据:${byte.length} messageId:$messageId spTotal:$spTotal spIndex:$spIndex PayloadLength:$PayloadLength'); // 回声测试 if (spTotal != null && spTotal > 1 && @@ -104,13 +109,15 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle } /// 处理图片数据 - void _handleVideoImage(TalkData talkData) { + void _handleVideoImage(TalkData talkData) async { final List processCompletePayload = - _processCompletePayload(Uint8List.fromList(talkData.content)); - // 循环发送每一帧的数据 + await _processCompletePayload(Uint8List.fromList(talkData.content)); + // AppLog.log('得到完整的帧:${processCompletePayload.length}'); // 循环发送每一帧的数据 processCompletePayload.forEach((element) { talkData.content = element; talkDataRepository.addTalkData(talkData); + // 设置状态为接听中 + talkStatus.setDuringCall(); }); } @@ -122,26 +129,30 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle List pcmBytes = G711().convertList(g711Data); talkData.content = pcmBytes; talkDataRepository.addTalkData(talkData); + // 设置状态为接听中 + talkStatus.setDuringCall(); } catch (e) { print('Error decoding G.711 to PCM: $e'); } } - /// 查找完整的帧数据 - List _processCompletePayload(Uint8List payload) { + Future> _processCompletePayload(Uint8List payload) async { // 存储找到的所有完整帧 List frames = []; // 寻找完整帧 (0xFFD8 开始, 0xFFD9 结束) - int startIdx = payload.indexOf(0xFF); - while (startIdx != -1 && startIdx + 1 < payload.length) { + int startIdx = 0; + while (startIdx < payload.length - 1) { + // 找到帧的起始标志 0xFFD8 + startIdx = payload.indexOf(0xFF, startIdx); + if (startIdx == -1 || startIdx + 1 >= payload.length) break; if (payload[startIdx + 1] == 0xD8) { // 找到帧的起始标志 0xFFD8 int endIdx = startIdx + 2; while (endIdx < payload.length - 1) { endIdx = payload.indexOf(0xFF, endIdx); - if (endIdx == -1) break; - if (endIdx + 1 < payload.length && payload[endIdx + 1] == 0xD9) { + if (endIdx == -1 || endIdx + 1 >= payload.length) break; + if (payload[endIdx + 1] == 0xD9) { // 找到帧的结束标志 0xFFD9 Uint8List frame = payload.sublist(startIdx, endIdx + 2); frames.add(frame); @@ -152,7 +163,7 @@ class UdpTalkDataHandler extends ScpMessageBaseHandle } } } else { - startIdx = payload.indexOf(0xFF, startIdx + 1); // 寻找下一个起始标志 + startIdx += 1; // 继续寻找下一个起始标志 } } diff --git a/lib/talk/startChart/handle/impl/udp_talk_expect_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_expect_handler.dart index 6bdf44e7..69469f47 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_expect_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_expect_handler.dart @@ -20,7 +20,7 @@ class UdpTalkExpectHandler extends ScpMessageBaseHandle @override void handleReq(ScpMessage scpMessage) { // 收到预期音视频数据请求 - final TalkExpect talkExpect = scpMessage.Payload; + final TalkExpectReq talkExpect = scpMessage.Payload; print('收到预期音视频数据请求:$talkExpect'); // 回复请求 @@ -49,18 +49,21 @@ class UdpTalkExpectHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, int? spIndex, int? messageId}) { if (messageType == MessageTypeConstant.Resp) { + // final TalkExpectResp talkExpectResp = TalkExpectResp(); + // talkExpectResp.mergeFromBuffer(byte); + // return talkExpectResp; final GenericResp genericResp = GenericResp(); genericResp.mergeFromBuffer(byte); return genericResp; } else if (messageType == MessageTypeConstant.Req) { - final TalkExpect talkExpect = TalkExpect(); + final TalkExpectReq talkExpect = TalkExpectReq(); talkExpect.mergeFromBuffer(byte); return talkExpect; } else { diff --git a/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart index dfe227ef..f7e39e2a 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart @@ -1,24 +1,25 @@ import 'dart:convert'; -import 'dart:typed_data'; -import 'package:flutter_easyloading/flutter_easyloading.dart'; -import 'package:get/get.dart'; import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; +import 'package:star_lock/talk/startChart/constant/talk_status.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; -import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart'; + import 'package:star_lock/talk/startChart/proto/generic.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_hangup.pb.dart'; + import '../../start_chart_manage.dart'; class UdpTalkHangUpHandler extends ScpMessageBaseHandle implements ScpMessageHandler { @override void handleReq(ScpMessage scpMessage) { - // 通话中挂断请求 - print('收到通话中挂断请求'); + if (talkStatus.status != TalkStatus.duringCall) { + // 如果不是接听中,不处理通话中挂断请求 + return; + } startChartManage.sendGenericRespSuccessMessage( ToPeerId: scpMessage.FromPeerId!, FromPeerId: scpMessage.ToPeerId!, @@ -28,18 +29,15 @@ class UdpTalkHangUpHandler extends ScpMessageBaseHandle startChartManage.stopTalkPingMessageTimer(); startChartManage.stopTalkExpectMessageTimer(); talkStatus.setHangingUpDuring(); - talkStatus.setEnd(); stopRingtone(); } @override void handleResp(ScpMessage scpMessage) { - print('收到通话中挂断回复'); // 停止发送通话保持的命令 startChartManage.stopTalkPingMessageTimer(); startChartManage.stopTalkExpectMessageTimer(); talkStatus.setHangingUpDuring(); - talkStatus.setEnd(); stopRingtone(); } @@ -53,7 +51,7 @@ class UdpTalkHangUpHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/impl/udp_talk_reject_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_reject_handler.dart index 96e722ac..07dfe4f6 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_reject_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_reject_handler.dart @@ -25,19 +25,16 @@ class UdpTalkRejectHandler extends ScpMessageBaseHandle ); startChartManage.stopTalkPingMessageTimer(); startChartManage.stopTalkExpectMessageTimer(); - talkStatus.setRejected(); stopRingtone(); - talkStatus.setEnd(); + // 收到接听拒绝回复 + talkStatus.setRejected(); } @override void handleResp(ScpMessage scpMessage) { - // 收到接听拒绝回复 - talkStatus.setRejected(); startChartManage.stopTalkPingMessageTimer(); startChartManage.stopTalkExpectMessageTimer(); stopRingtone(); - talkStatus.setEnd(); } @override @@ -50,7 +47,7 @@ class UdpTalkRejectHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart index af67d4b6..20e649fa 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart @@ -1,22 +1,18 @@ import 'dart:convert'; -import 'dart:typed_data'; import 'package:flutter/services.dart'; -import 'package:flutter_easyloading/flutter_easyloading.dart'; + import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:get/get.dart'; import 'package:star_lock/appRouters.dart'; import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; +import 'package:star_lock/talk/startChart/constant/talk_status.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_base_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; import 'package:star_lock/talk/startChart/proto/gateway_reset.pb.dart'; import 'package:star_lock/talk/startChart/proto/generic.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_request.pb.dart'; -import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; -import 'package:star_lock/tools/storage.dart'; - -import '../../start_chart_manage.dart'; class UdpTalkRequestHandler extends ScpMessageBaseHandle implements ScpMessageHandler { @@ -38,8 +34,6 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle startChartManage.ToPeerId = scpMessage.FromPeerId!; // 处理收到接听请求后的事件 _talkRequestEvent(talkObjectName: talkReq.callerName); - // 设置为等待接听状态 - talkStatus.setWaitingAnswer(); } @override @@ -63,11 +57,12 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle _showTalkRequestNotification(talkObjectName: talkObjectName); // 收到呼叫请求,跳转到接听页面 Get.toNamed( - Routers.starChartPage, - arguments: {'lockId': '111'}, + Routers.starChartTalkView, ); // 触发震动反馈 HapticFeedback.vibrate(); + // 设置为等待接听状态 + talkStatus.setWaitingAnswer(); } // 收到来电请求时进行本地通知 @@ -95,7 +90,7 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle deserializePayload( {required int payloadType, required int messageType, - required List byte, + required List byte, int? offset, int? PayloadLength, int? spTotal, diff --git a/lib/talk/startChart/handle/other/talk_data_repository.dart b/lib/talk/startChart/handle/other/talk_data_repository.dart index a86382c7..8eedbc3f 100644 --- a/lib/talk/startChart/handle/other/talk_data_repository.dart +++ b/lib/talk/startChart/handle/other/talk_data_repository.dart @@ -19,7 +19,7 @@ class TalkDataRepository { Stream get talkDataStream => _talkDataStreamController.stream; // 提供一个方法来添加 TalkData 到 Stream - void addTalkData(TalkData talkData) { + void addTalkData(TalkData talkData) async { _talkDataStreamController.add(talkData); } diff --git a/lib/talk/startChart/handle/scp_message_base_handle.dart b/lib/talk/startChart/handle/scp_message_base_handle.dart index de51229b..63e639dd 100644 --- a/lib/talk/startChart/handle/scp_message_base_handle.dart +++ b/lib/talk/startChart/handle/scp_message_base_handle.dart @@ -31,16 +31,19 @@ class ScpMessageBaseHandle { // 通话数据流的单例流数据处理类 final TalkDataRepository talkDataRepository = TalkDataRepository.instance; + // 获取 StartChartTalkStatus 的唯一实例 + final StartChartTalkStatus talkStatus = StartChartTalkStatus.instance; + final audioManager = AudioPlayerManager(); // 通话保持超时监听定时器管理 final talkePingOverTimeTimerManager = OverTimeTimerManager( - timeoutInSeconds: 55, + timeoutInSeconds: 260, ); // 通话数据超时定时器 final talkDataOverTimeTimerManager = OverTimeTimerManager( - timeoutInSeconds: 53, + timeoutInSeconds: 260, ); // 回复成功消息 @@ -52,9 +55,6 @@ class ScpMessageBaseHandle { ); } - // 获取 StartChartTalkStatus 的唯一实例 - StartChartTalkStatus talkStatus = StartChartTalkStatus.instance; - bool checkGenericRespSuccess(GenericResp genericResp) { if (genericResp == null) return false; final code = genericResp.code; @@ -95,8 +95,8 @@ class ScpMessageBaseHandle { // 检查分包索引是否在合法范围内 if (spIndex < 1 || spIndex > spTotal) { - print( - 'Invalid spTotal: $spTotal spIndex: $spIndex for messageId: $messageId'); + // print( + // 'Invalid spTotal: $spTotal spIndex: $spIndex for messageId: $messageId'); return null; } diff --git a/lib/talk/startChart/proto/talk_data_h264_frame.proto b/lib/talk/startChart/proto/talk_data_h264_frame.proto new file mode 100644 index 00000000..09815d16 --- /dev/null +++ b/lib/talk/startChart/proto/talk_data_h264_frame.proto @@ -0,0 +1,18 @@ +syntax = "proto3"; +package main; +option go_package = "./spb/talk"; + +message TalkDataH264Frame { + // 帧序号 seq + uint32 FrameSeq = 1; + // 帧类型,对于H264,I帧、P帧, + // 未知为NONE?有时候发送方不知道,或者渲染方可以从帧数据中解析 + enum FrameTypeE { + NONE = 0; + I = 1; + P = 2; + }; + FrameTypeE FrameType = 2; + // 帧数据 + bytes FrameData = 3; +} diff --git a/lib/talk/startChart/proto/talk_expect.pb.dart b/lib/talk/startChart/proto/talk_expect.pb.dart index 2f624153..835baa3c 100644 --- a/lib/talk/startChart/proto/talk_expect.pb.dart +++ b/lib/talk/startChart/proto/talk_expect.pb.dart @@ -17,10 +17,11 @@ import 'talk_expect.pbenum.dart'; export 'talk_expect.pbenum.dart'; -class TalkExpect extends $pb.GeneratedMessage { - factory TalkExpect({ - $core.Iterable? videoType, - $core.Iterable? audioType, +/// 预期接收为渲染方发送,含义为:“我可以理解这些格式,你看看你方便提供什么格式” +class TalkExpectReq extends $pb.GeneratedMessage { + factory TalkExpectReq({ + $core.Iterable? videoType, + $core.Iterable? audioType, }) { final $result = create(); if (videoType != null) { @@ -31,13 +32,13 @@ class TalkExpect extends $pb.GeneratedMessage { } return $result; } - TalkExpect._() : super(); - factory TalkExpect.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); - factory TalkExpect.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + TalkExpectReq._() : super(); + factory TalkExpectReq.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory TalkExpectReq.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); - static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'TalkExpect', package: const $pb.PackageName(_omitMessageNames ? '' : 'main'), createEmptyInstance: create) - ..pc(1, _omitFieldNames ? '' : 'VideoType', $pb.PbFieldType.KE, protoName: 'VideoType', valueOf: TalkExpect_VideoTypeE.valueOf, enumValues: TalkExpect_VideoTypeE.values, defaultEnumValue: TalkExpect_VideoTypeE.NONE_V) - ..pc(2, _omitFieldNames ? '' : 'AudioType', $pb.PbFieldType.KE, protoName: 'AudioType', valueOf: TalkExpect_AudioTypeE.valueOf, enumValues: TalkExpect_AudioTypeE.values, defaultEnumValue: TalkExpect_AudioTypeE.NONE_A) + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'TalkExpectReq', package: const $pb.PackageName(_omitMessageNames ? '' : 'main'), createEmptyInstance: create) + ..pc(1, _omitFieldNames ? '' : 'VideoType', $pb.PbFieldType.KE, protoName: 'VideoType', valueOf: VideoTypeE.valueOf, enumValues: VideoTypeE.values, defaultEnumValue: VideoTypeE.NONE_V) + ..pc(2, _omitFieldNames ? '' : 'AudioType', $pb.PbFieldType.KE, protoName: 'AudioType', valueOf: AudioTypeE.valueOf, enumValues: AudioTypeE.values, defaultEnumValue: AudioTypeE.NONE_A) ..hasRequiredFields = false ; @@ -45,30 +46,141 @@ class TalkExpect extends $pb.GeneratedMessage { 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' 'Will be removed in next major version') - TalkExpect clone() => TalkExpect()..mergeFromMessage(this); + TalkExpectReq clone() => TalkExpectReq()..mergeFromMessage(this); @$core.Deprecated( 'Using this can add significant overhead to your binary. ' 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' 'Will be removed in next major version') - TalkExpect copyWith(void Function(TalkExpect) updates) => super.copyWith((message) => updates(message as TalkExpect)) as TalkExpect; + TalkExpectReq copyWith(void Function(TalkExpectReq) updates) => super.copyWith((message) => updates(message as TalkExpectReq)) as TalkExpectReq; $pb.BuilderInfo get info_ => _i; @$core.pragma('dart2js:noInline') - static TalkExpect create() => TalkExpect._(); - TalkExpect createEmptyInstance() => create(); - static $pb.PbList createRepeated() => $pb.PbList(); + static TalkExpectReq create() => TalkExpectReq._(); + TalkExpectReq createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); @$core.pragma('dart2js:noInline') - static TalkExpect getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); - static TalkExpect? _defaultInstance; + static TalkExpectReq getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static TalkExpectReq? _defaultInstance; /// 如果接收到NONE的话,意味着对方关闭了喇叭或者视频显示,发送方应该停止发送 /// 支持的类型是数组 @$pb.TagNumber(1) - $core.List get videoType => $_getList(0); + $core.List get videoType => $_getList(0); @$pb.TagNumber(2) - $core.List get audioType => $_getList(1); + $core.List get audioType => $_getList(1); +} + +/// 这是音视频提供方的回应,含义为:“马上我就会为你发送这个格式的音视频,你准备好解析器吧” +class TalkExpectResp extends $pb.GeneratedMessage { + factory TalkExpectResp({ + $core.int? width, + $core.int? height, + $core.int? rotate, + VideoTypeE? videoType, + AudioTypeE? audioType, + }) { + final $result = create(); + if (width != null) { + $result.width = width; + } + if (height != null) { + $result.height = height; + } + if (rotate != null) { + $result.rotate = rotate; + } + if (videoType != null) { + $result.videoType = videoType; + } + if (audioType != null) { + $result.audioType = audioType; + } + return $result; + } + TalkExpectResp._() : super(); + factory TalkExpectResp.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory TalkExpectResp.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'TalkExpectResp', package: const $pb.PackageName(_omitMessageNames ? '' : 'main'), createEmptyInstance: create) + ..a<$core.int>(1, _omitFieldNames ? '' : 'Width', $pb.PbFieldType.OU3, protoName: 'Width') + ..a<$core.int>(2, _omitFieldNames ? '' : 'Height', $pb.PbFieldType.OU3, protoName: 'Height') + ..a<$core.int>(3, _omitFieldNames ? '' : 'Rotate', $pb.PbFieldType.OU3, protoName: 'Rotate') + ..e(4, _omitFieldNames ? '' : 'VideoType', $pb.PbFieldType.OE, protoName: 'VideoType', defaultOrMaker: VideoTypeE.NONE_V, valueOf: VideoTypeE.valueOf, enumValues: VideoTypeE.values) + ..e(5, _omitFieldNames ? '' : 'AudioType', $pb.PbFieldType.OE, protoName: 'AudioType', defaultOrMaker: AudioTypeE.NONE_A, valueOf: AudioTypeE.valueOf, enumValues: AudioTypeE.values) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + TalkExpectResp clone() => TalkExpectResp()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + TalkExpectResp copyWith(void Function(TalkExpectResp) updates) => super.copyWith((message) => updates(message as TalkExpectResp)) as TalkExpectResp; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static TalkExpectResp create() => TalkExpectResp._(); + TalkExpectResp createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static TalkExpectResp getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static TalkExpectResp? _defaultInstance; + + /// 宽度,单位像素 + @$pb.TagNumber(1) + $core.int get width => $_getIZ(0); + @$pb.TagNumber(1) + set width($core.int v) { $_setUnsignedInt32(0, v); } + @$pb.TagNumber(1) + $core.bool hasWidth() => $_has(0); + @$pb.TagNumber(1) + void clearWidth() => clearField(1); + + /// 高度,单位像素 + @$pb.TagNumber(2) + $core.int get height => $_getIZ(1); + @$pb.TagNumber(2) + set height($core.int v) { $_setUnsignedInt32(1, v); } + @$pb.TagNumber(2) + $core.bool hasHeight() => $_has(1); + @$pb.TagNumber(2) + void clearHeight() => clearField(2); + + /// 旋转角度 默认0 + @$pb.TagNumber(3) + $core.int get rotate => $_getIZ(2); + @$pb.TagNumber(3) + set rotate($core.int v) { $_setUnsignedInt32(2, v); } + @$pb.TagNumber(3) + $core.bool hasRotate() => $_has(2); + @$pb.TagNumber(3) + void clearRotate() => clearField(3); + + /// 即将发送的音视频格式 + @$pb.TagNumber(4) + VideoTypeE get videoType => $_getN(3); + @$pb.TagNumber(4) + set videoType(VideoTypeE v) { setField(4, v); } + @$pb.TagNumber(4) + $core.bool hasVideoType() => $_has(3); + @$pb.TagNumber(4) + void clearVideoType() => clearField(4); + + @$pb.TagNumber(5) + AudioTypeE get audioType => $_getN(4); + @$pb.TagNumber(5) + set audioType(AudioTypeE v) { setField(5, v); } + @$pb.TagNumber(5) + $core.bool hasAudioType() => $_has(4); + @$pb.TagNumber(5) + void clearAudioType() => clearField(5); } diff --git a/lib/talk/startChart/proto/talk_expect.pbenum.dart b/lib/talk/startChart/proto/talk_expect.pbenum.dart index ef51cbcd..d6b34250 100644 --- a/lib/talk/startChart/proto/talk_expect.pbenum.dart +++ b/lib/talk/startChart/proto/talk_expect.pbenum.dart @@ -14,39 +14,43 @@ import 'dart:core' as $core; import 'package:protobuf/protobuf.dart' as $pb; /// 视频类型 -class TalkExpect_VideoTypeE extends $pb.ProtobufEnum { - static const TalkExpect_VideoTypeE NONE_V = TalkExpect_VideoTypeE._(0, _omitEnumNames ? '' : 'NONE_V'); - static const TalkExpect_VideoTypeE H264 = TalkExpect_VideoTypeE._(1, _omitEnumNames ? '' : 'H264'); - static const TalkExpect_VideoTypeE IMAGE = TalkExpect_VideoTypeE._(2, _omitEnumNames ? '' : 'IMAGE'); +class VideoTypeE extends $pb.ProtobufEnum { + static const VideoTypeE NONE_V = VideoTypeE._(0, _omitEnumNames ? '' : 'NONE_V'); + static const VideoTypeE H264 = VideoTypeE._(1, _omitEnumNames ? '' : 'H264'); + static const VideoTypeE IMAGE = VideoTypeE._(2, _omitEnumNames ? '' : 'IMAGE'); + static const VideoTypeE VP8 = VideoTypeE._(3, _omitEnumNames ? '' : 'VP8'); - static const $core.List values = [ + static const $core.List values = [ NONE_V, H264, IMAGE, + VP8, ]; - static final $core.Map<$core.int, TalkExpect_VideoTypeE> _byValue = $pb.ProtobufEnum.initByValue(values); - static TalkExpect_VideoTypeE? valueOf($core.int value) => _byValue[value]; + static final $core.Map<$core.int, VideoTypeE> _byValue = $pb.ProtobufEnum.initByValue(values); + static VideoTypeE? valueOf($core.int value) => _byValue[value]; - const TalkExpect_VideoTypeE._($core.int v, $core.String n) : super(v, n); + const VideoTypeE._($core.int v, $core.String n) : super(v, n); } /// 音频类型 -class TalkExpect_AudioTypeE extends $pb.ProtobufEnum { - static const TalkExpect_AudioTypeE NONE_A = TalkExpect_AudioTypeE._(0, _omitEnumNames ? '' : 'NONE_A'); - static const TalkExpect_AudioTypeE AAC = TalkExpect_AudioTypeE._(1, _omitEnumNames ? '' : 'AAC'); - static const TalkExpect_AudioTypeE G711 = TalkExpect_AudioTypeE._(2, _omitEnumNames ? '' : 'G711'); +class AudioTypeE extends $pb.ProtobufEnum { + static const AudioTypeE NONE_A = AudioTypeE._(0, _omitEnumNames ? '' : 'NONE_A'); + static const AudioTypeE AAC = AudioTypeE._(1, _omitEnumNames ? '' : 'AAC'); + static const AudioTypeE G711 = AudioTypeE._(2, _omitEnumNames ? '' : 'G711'); + static const AudioTypeE OPUS = AudioTypeE._(3, _omitEnumNames ? '' : 'OPUS'); - static const $core.List values = [ + static const $core.List values = [ NONE_A, AAC, G711, + OPUS, ]; - static final $core.Map<$core.int, TalkExpect_AudioTypeE> _byValue = $pb.ProtobufEnum.initByValue(values); - static TalkExpect_AudioTypeE? valueOf($core.int value) => _byValue[value]; + static final $core.Map<$core.int, AudioTypeE> _byValue = $pb.ProtobufEnum.initByValue(values); + static AudioTypeE? valueOf($core.int value) => _byValue[value]; - const TalkExpect_AudioTypeE._($core.int v, $core.String n) : super(v, n); + const AudioTypeE._($core.int v, $core.String n) : super(v, n); } diff --git a/lib/talk/startChart/proto/talk_expect.pbjson.dart b/lib/talk/startChart/proto/talk_expect.pbjson.dart index 61ace4ca..89cfcd51 100644 --- a/lib/talk/startChart/proto/talk_expect.pbjson.dart +++ b/lib/talk/startChart/proto/talk_expect.pbjson.dart @@ -13,40 +13,66 @@ import 'dart:convert' as $convert; import 'dart:core' as $core; import 'dart:typed_data' as $typed_data; -@$core.Deprecated('Use talkExpectDescriptor instead') -const TalkExpect$json = { - '1': 'TalkExpect', - '2': [ - {'1': 'VideoType', '3': 1, '4': 3, '5': 14, '6': '.main.TalkExpect.VideoTypeE', '10': 'VideoType'}, - {'1': 'AudioType', '3': 2, '4': 3, '5': 14, '6': '.main.TalkExpect.AudioTypeE', '10': 'AudioType'}, - ], - '4': [TalkExpect_VideoTypeE$json, TalkExpect_AudioTypeE$json], -}; - -@$core.Deprecated('Use talkExpectDescriptor instead') -const TalkExpect_VideoTypeE$json = { +@$core.Deprecated('Use videoTypeEDescriptor instead') +const VideoTypeE$json = { '1': 'VideoTypeE', '2': [ {'1': 'NONE_V', '2': 0}, {'1': 'H264', '2': 1}, {'1': 'IMAGE', '2': 2}, + {'1': 'VP8', '2': 3}, ], }; -@$core.Deprecated('Use talkExpectDescriptor instead') -const TalkExpect_AudioTypeE$json = { +/// Descriptor for `VideoTypeE`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List videoTypeEDescriptor = $convert.base64Decode( + 'CgpWaWRlb1R5cGVFEgoKBk5PTkVfVhAAEggKBEgyNjQQARIJCgVJTUFHRRACEgcKA1ZQOBAD'); + +@$core.Deprecated('Use audioTypeEDescriptor instead') +const AudioTypeE$json = { '1': 'AudioTypeE', '2': [ {'1': 'NONE_A', '2': 0}, {'1': 'AAC', '2': 1}, {'1': 'G711', '2': 2}, + {'1': 'OPUS', '2': 3}, ], }; -/// Descriptor for `TalkExpect`. Decode as a `google.protobuf.DescriptorProto`. -final $typed_data.Uint8List talkExpectDescriptor = $convert.base64Decode( - 'CgpUYWxrRXhwZWN0EjkKCVZpZGVvVHlwZRgBIAMoDjIbLm1haW4uVGFsa0V4cGVjdC5WaWRlb1' - 'R5cGVFUglWaWRlb1R5cGUSOQoJQXVkaW9UeXBlGAIgAygOMhsubWFpbi5UYWxrRXhwZWN0LkF1' - 'ZGlvVHlwZUVSCUF1ZGlvVHlwZSItCgpWaWRlb1R5cGVFEgoKBk5PTkVfVhAAEggKBEgyNjQQAR' - 'IJCgVJTUFHRRACIisKCkF1ZGlvVHlwZUUSCgoGTk9ORV9BEAASBwoDQUFDEAESCAoERzcxMRAC'); +/// Descriptor for `AudioTypeE`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List audioTypeEDescriptor = $convert.base64Decode( + 'CgpBdWRpb1R5cGVFEgoKBk5PTkVfQRAAEgcKA0FBQxABEggKBEc3MTEQAhIICgRPUFVTEAM='); + +@$core.Deprecated('Use talkExpectReqDescriptor instead') +const TalkExpectReq$json = { + '1': 'TalkExpectReq', + '2': [ + {'1': 'VideoType', '3': 1, '4': 3, '5': 14, '6': '.main.VideoTypeE', '10': 'VideoType'}, + {'1': 'AudioType', '3': 2, '4': 3, '5': 14, '6': '.main.AudioTypeE', '10': 'AudioType'}, + ], +}; + +/// Descriptor for `TalkExpectReq`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List talkExpectReqDescriptor = $convert.base64Decode( + 'Cg1UYWxrRXhwZWN0UmVxEi4KCVZpZGVvVHlwZRgBIAMoDjIQLm1haW4uVmlkZW9UeXBlRVIJVm' + 'lkZW9UeXBlEi4KCUF1ZGlvVHlwZRgCIAMoDjIQLm1haW4uQXVkaW9UeXBlRVIJQXVkaW9UeXBl'); + +@$core.Deprecated('Use talkExpectRespDescriptor instead') +const TalkExpectResp$json = { + '1': 'TalkExpectResp', + '2': [ + {'1': 'Width', '3': 1, '4': 1, '5': 13, '10': 'Width'}, + {'1': 'Height', '3': 2, '4': 1, '5': 13, '10': 'Height'}, + {'1': 'Rotate', '3': 3, '4': 1, '5': 13, '10': 'Rotate'}, + {'1': 'VideoType', '3': 4, '4': 1, '5': 14, '6': '.main.VideoTypeE', '10': 'VideoType'}, + {'1': 'AudioType', '3': 5, '4': 1, '5': 14, '6': '.main.AudioTypeE', '10': 'AudioType'}, + ], +}; + +/// Descriptor for `TalkExpectResp`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List talkExpectRespDescriptor = $convert.base64Decode( + 'Cg5UYWxrRXhwZWN0UmVzcBIUCgVXaWR0aBgBIAEoDVIFV2lkdGgSFgoGSGVpZ2h0GAIgASgNUg' + 'ZIZWlnaHQSFgoGUm90YXRlGAMgASgNUgZSb3RhdGUSLgoJVmlkZW9UeXBlGAQgASgOMhAubWFp' + 'bi5WaWRlb1R5cGVFUglWaWRlb1R5cGUSLgoJQXVkaW9UeXBlGAUgASgOMhAubWFpbi5BdWRpb1' + 'R5cGVFUglBdWRpb1R5cGU='); diff --git a/lib/talk/startChart/proto/talk_expect.proto b/lib/talk/startChart/proto/talk_expect.proto index c7ee69d5..9a73d13f 100644 --- a/lib/talk/startChart/proto/talk_expect.proto +++ b/lib/talk/startChart/proto/talk_expect.proto @@ -3,21 +3,39 @@ syntax = "proto3"; package main; option go_package = "./spb/talk"; -message TalkExpect { - // 视频类型 - enum VideoTypeE { - NONE_V = 0; - H264 = 1; - IMAGE = 2; - } - // 音频类型 - enum AudioTypeE { - NONE_A = 0; - AAC = 1; - G711 = 2; - } +// 视频类型 +enum VideoTypeE { + NONE_V = 0; + H264 = 1; // 也称为 AVC,采用了多种高效的编码技术,如帧间压缩、运动估计、变换编码等,能够在保持较高画质的同时减小文件大小,具有广泛的兼容性和硬件支持 + IMAGE = 2; // 一种简单的视频压缩格式,它将每一帧视频都压缩成单独的JPEG图像帧 经常用于低延迟的视频流应用 如网络摄像头、数字视频录像机、监控视频。它也常用于一些老旧的设备中。 + VP8 = 3; // 由Google开发,开源,软件编码器,WebRTC默认编码器 广泛用于WebRTC视频会议、YouTube视频播放等。VP8 已被Google广泛推广,尤其在HTML5视频和实时视频应用中有着广泛应用。 +} +// 音频类型 +enum AudioTypeE { + NONE_A = 0; + AAC = 1; // AAC 是一种有损压缩音频编解码器,广泛应用于多媒体和流媒体传输。 + G711 = 2; // 无损压缩算法,使用8 kHz的采样率,每个采样点使用8位数据。虽然压缩效率低但非常高的音质,广泛应用于电话和VoIP中。 + OPUS = 3; // 开源,WebRTC默认编码器 低延迟、广泛的比特率范围(从6 kbps到510 kbps)以及适应性强的特性,特别适合用于实时通信(如VoIP)和高质量的音乐流媒体。 +} + +// 预期接收为渲染方发送,含义为:“我可以理解这些格式,你看看你方便提供什么格式” +message TalkExpectReq { // 如果接收到NONE的话,意味着对方关闭了喇叭或者视频显示,发送方应该停止发送 // 支持的类型是数组 repeated VideoTypeE VideoType = 1; repeated AudioTypeE AudioType = 2; } + +// 这是音视频提供方的回应,含义为:“马上我就会为你发送这个格式的音视频,你准备好解析器吧” +message TalkExpectResp { + // 宽度,单位像素 + uint32 Width = 1; + // 高度,单位像素 + uint32 Height = 2; + // 旋转角度 默认0 + uint32 Rotate = 3; + + // 即将发送的音视频格式 + VideoTypeE VideoType = 4; + AudioTypeE AudioType = 5; +} diff --git a/lib/talk/startChart/start_chart_manage.dart b/lib/talk/startChart/start_chart_manage.dart index e7de70a6..83485db6 100644 --- a/lib/talk/startChart/start_chart_manage.dart +++ b/lib/talk/startChart/start_chart_manage.dart @@ -12,11 +12,13 @@ import 'package:star_lock/flavors.dart'; import 'package:star_lock/login/login/entity/LoginEntity.dart'; import 'package:star_lock/network/api_repository.dart'; import 'package:star_lock/network/start_chart_api.dart'; +import 'package:star_lock/talk/other/audio_player_manager.dart'; import 'package:star_lock/talk/startChart/command/message_command.dart'; import 'package:star_lock/talk/startChart/constant/ip_constant.dart'; import 'package:star_lock/talk/startChart/constant/listen_addr_type_constant.dart'; import 'package:star_lock/talk/startChart/constant/message_type_constant.dart'; import 'package:star_lock/talk/startChart/constant/payload_type_constant.dart'; +import 'package:star_lock/talk/startChart/constant/talk_status.dart'; import 'package:star_lock/talk/startChart/entity/relay_info_entity.dart'; import 'package:star_lock/talk/startChart/entity/report_information_data.dart'; import 'package:star_lock/talk/startChart/entity/scp_message.dart'; @@ -26,6 +28,7 @@ import 'package:star_lock/talk/startChart/handle/scp_message_handle.dart'; import 'package:star_lock/talk/startChart/handle/scp_message_handler_factory.dart'; import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_expect.pb.dart'; +import 'package:star_lock/talk/startChart/proto/talk_expect.pbserver.dart'; import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; import 'package:star_lock/tools/baseGetXController.dart'; import 'package:star_lock/tools/deviceInfo_utils.dart'; @@ -83,9 +86,9 @@ class StartChartManage { final int _maxPayloadSize = 8 * 1024; // 分包大小 // 默认通话的期望数据格式 - TalkExpect defaultTalkExpect = TalkExpect( - videoType: [TalkExpect_VideoTypeE.IMAGE], - audioType: [TalkExpect_AudioTypeE.G711], + TalkExpectReq defaultTalkExpect = TalkExpectReq( + videoType: [VideoTypeE.IMAGE], + audioType: [AudioTypeE.G711], ); // 默认通话数据 @@ -345,7 +348,9 @@ class StartChartManage { MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: true), ); await _sendMessage(message: message); + // 设置状态为等待接听 talkStatus.setWaitingAnswer(); + _log(text: '发送同意接听消息'); } // 发送拒绝接听消息 @@ -360,10 +365,15 @@ class StartChartManage { MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: true), ); await _sendMessage(message: message); + + // 设置状态为拒绝 + StartChartTalkStatus.instance.setRejected(); + // 停止播放铃声 + AudioPlayerManager().stopRingtone(); } // 发送期望接受消息 - void sendTalkExpectMessage({required TalkExpect talkExpect}) async { + void sendTalkExpectMessage({required TalkExpectReq talkExpect}) async { final message = MessageCommand.talkExpectMessage( ToPeerId: ToPeerId, FromPeerId: FromPeerId, @@ -424,6 +434,11 @@ class StartChartManage { MessageId: MessageCommand.getNextMessageId(ToPeerId, increment: true), ); await _sendMessage(message: message); + + // 设置状态为通话中挂断 + StartChartTalkStatus.instance.setHangingUpDuring(); + // 停止播放铃声 + AudioPlayerManager().stopRingtone(); } // 重新上线 @@ -785,8 +800,7 @@ class StartChartManage { } } } catch (e, stackTrace) { - throw StartChartMessageException( - '❌ Udp result data error ----> $e\n,$stackTrace'); + throw StartChartMessageException('$e\n,$stackTrace'); } } }); @@ -958,7 +972,7 @@ class StartChartManage { } /// 修改预期接收到的数据 - void changeTalkExpectDataType({required TalkExpect talkExpect}) { + void changeTalkExpectDataType({required TalkExpectReq talkExpect}) { defaultTalkExpect = talkExpect; } diff --git a/lib/talk/startChart/start_chart_talk_status.dart b/lib/talk/startChart/start_chart_talk_status.dart index 6c8b5f11..7f4a7f2a 100644 --- a/lib/talk/startChart/start_chart_talk_status.dart +++ b/lib/talk/startChart/start_chart_talk_status.dart @@ -1,131 +1,70 @@ -// 定义视频对讲的状态枚举 +import 'dart:async'; import 'package:star_lock/blue/io_tool/manager_event_bus.dart'; -import 'package:star_lock/talk/startChart/events/talk_status_change_event.dart'; - -enum TalkStatus { - waitingAnswer, // 等待接听 - answeredSuccessfully, // 接听成功 - waitingData, // 等待数据 - duringCall, // 通话中 - hangingUpDuring, // 通话中挂断 - rejected, // 被拒绝 - uninitialized, // 未初始化 - initializationCompleted, // 初始化完成 - notTalkData, // 暂无通话数据 - notTalkPing, // 暂无通话保持 - error, // 错误状态 - end, // 结束 -} +import 'package:star_lock/talk/startChart/constant/talk_status.dart'; class StartChartTalkStatus { // 私有字段,用于存储当前状态 TalkStatus _status = TalkStatus.uninitialized; // 私有化默认构造函数,防止外部创建实例 - StartChartTalkStatus._( - {TalkStatus initialStatus = TalkStatus.uninitialized}) { - _status = initialStatus; - } + StartChartTalkStatus._(); // 静态私有字段,存储唯一的实例 static final StartChartTalkStatus _instance = StartChartTalkStatus._(); - // 静态工厂构造函数,返回唯一的实例 + // 提供一个静态方法来获取单例实例 static StartChartTalkStatus get instance => _instance; + // 创建一个 StreamController 用于状态变化 + final StreamController _statusStreamController = + StreamController.broadcast(); + + // 提供一个方法来获取 Stream + Stream get statusStream => _statusStreamController.stream; + // 获取当前状态的 getter 方法 TalkStatus get status => _status; - // 通用的 set 方法(仍然保留) - set status(TalkStatus newStatus) { - _setStatus(newStatus); - } - // 内部方法,用于更新状态并触发状态变化事件 void _setStatus(TalkStatus newStatus) { if (_status == newStatus) return; // 如果状态没有变化,直接返回 - print("对讲状态变化: ${_status.name} -> ${newStatus.name}"); + print("对讲状态变化: ${_status} -> ${newStatus}"); + // 更新状态 _status = newStatus; - // 触发状态变化的事件或执行其他操作 - _onStatusChanged(newStatus); + // 触发状态变化事件 + _statusStreamController.add(_status); } - // 状态变化时的回调方法(可选) - void _onStatusChanged(TalkStatus newStatus) { - // 发布状态变化事件 - EventBusManager().eventBus!.fire(TalkStatusChangeEvent(_status, newStatus)); - } + // 提供状态设置方法 + void setWaitingAnswer() => _setStatus(TalkStatus.waitingAnswer); - /// 设置状态为等待接听 - void setWaitingAnswer() { - _setStatus(TalkStatus.waitingAnswer); - // 可以在这里添加特定于 "waitingAnswer" 状态的逻辑 - } + void setWaitingData() => _setStatus(TalkStatus.waitingData); - /// 设置状态为等待数据 - void setWaitingData() { - _setStatus(TalkStatus.waitingData); - // 可以在这里添加特定于 "waitingAnswer" 状态的逻辑 - } + void setDuringCall() => _setStatus(TalkStatus.duringCall); + void setRejected() => _setStatus(TalkStatus.rejected); - /// 设置状态为通话中 - void setDuringCall() { - _setStatus(TalkStatus.duringCall); - // 可以在这里添加特定于 "duringCall" 状态的逻辑 - } + void setUninitialized() => _setStatus(TalkStatus.uninitialized); - /// 设置状态为被拒绝 - void setRejected() { - _setStatus(TalkStatus.rejected); - // 可以在这里添加特定于 "rejected" 状态的逻辑 - } + void setInitializationCompleted() => + _setStatus(TalkStatus.initializationCompleted); - /// 设置状态为未初始化 - void setUninitialized() { - _setStatus(TalkStatus.uninitialized); - // 可以在这里添加特定于 "uninitialized" 状态的逻辑 - } + void setNotTalkData() => _setStatus(TalkStatus.notTalkData); - /// 设置状态为初始化完成 - void setInitializationCompleted() { - _setStatus(TalkStatus.initializationCompleted); - // 可以为 "initializationCompleted" 状态添加特定的逻辑 - } + void setNotTalkPing() => _setStatus(TalkStatus.notTalkPing); - /// 设置状态为暂无通话数据 - void setNotTalkData() { - _setStatus(TalkStatus.notTalkData); - // 可以在这里添加特定于 "notTalkData" 状态的逻辑 - } + void setError() => _setStatus(TalkStatus.error); - /// 设置状态为暂无通话保持 - void setNotTalkPing() { - _setStatus(TalkStatus.notTalkPing); - // 可以在这里添加特定于 "notTalkPing" 状态的逻辑 - } + void setHangingUpDuring() => _setStatus(TalkStatus.hangingUpDuring); - /// 设置状态为错误 - void setError() { - _setStatus(TalkStatus.error); - // 可以在这里添加特定于 "error" 状态的逻辑 - } + void setAnsweredSuccessfully() => _setStatus(TalkStatus.answeredSuccessfully); - /// 设置状态为通话中挂断 - void setHangingUpDuring() { - _setStatus(TalkStatus.hangingUpDuring); - // 可以在这里添加特定于 "hangingUpDuring" 状态的逻辑 - } /// 设置状态为接听成功 - void setAnsweredSuccessfully() { - _setStatus(TalkStatus.answeredSuccessfully); - // 可以在这里添加特定于 "hangingUpDuring" 状态的逻辑 - } + void setEnd() => _setStatus(TalkStatus.end); - /// 设置状态为结束 - void setEnd() { - _setStatus(TalkStatus.end); - // 可以在这里添加特定于 "end" 状态的逻辑 + // 提供一个方法来关闭 StreamController + void dispose() { + _statusStreamController.close(); } } diff --git a/lib/talk/startChart/views/talkView/talk_view_logic.dart b/lib/talk/startChart/views/talkView/talk_view_logic.dart new file mode 100644 index 00000000..264374bb --- /dev/null +++ b/lib/talk/startChart/views/talkView/talk_view_logic.dart @@ -0,0 +1,291 @@ +import 'dart:async'; +import 'dart:io'; +import 'dart:math'; + +import 'package:flutter/services.dart'; +import 'package:flutter_pcm_sound/flutter_pcm_sound.dart'; +import 'package:flutter_voice_processor/flutter_voice_processor.dart'; +import 'package:get/get.dart'; +import 'package:permission_handler/permission_handler.dart'; +import 'package:star_lock/app_settings/app_settings.dart'; +import 'package:star_lock/blue/io_tool/manager_event_bus.dart'; +import 'package:star_lock/talk/call/callTalk.dart'; +import 'package:star_lock/talk/startChart/constant/talk_status.dart'; +import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart'; +import 'package:star_lock/talk/startChart/proto/talk_data.pbenum.dart'; +import 'package:star_lock/talk/startChart/start_chart_manage.dart'; +import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; +import 'package:star_lock/talk/startChart/views/talkView/talk_view_state.dart'; + +import '../../../../talk/call/g711.dart'; +import '../../../../talk/udp/udp_manage.dart'; +import '../../../../talk/udp/udp_senderManage.dart'; +import '../../../../tools/baseGetXController.dart'; +import '../../../../tools/eventBusEventManage.dart'; + +class TalkViewLogic extends BaseGetXController { + final TalkViewState state = TalkViewState(); + + Timer? _syncTimer; + int _startTime = 0; + final int bufferSize = 22; // 缓冲区大小(以帧为单位) + final List frameTimestamps = []; + int frameIntervalMs = 45; // 初始帧间隔设置为45毫秒(约22FPS) + int minFrameIntervalMs = 30; // 最小帧间隔(约33 FPS) + int maxFrameIntervalMs = 100; // 最大帧间隔(约10 FPS) + + /// 收到Talk发送的状态 + StreamSubscription? _getTalkStatusRefreshUIEvent; + + void _initFlutterPcmSound() { + const int sampleRate = 44100; + FlutterPcmSound.setLogLevel(LogLevel.verbose); + FlutterPcmSound.setup(sampleRate: sampleRate, channelCount: 2); + // 设置 feed 阈值 + if (Platform.isAndroid) { + FlutterPcmSound.setFeedThreshold(-1); // Android 平台的特殊处理 + } else { + FlutterPcmSound.setFeedThreshold(sampleRate ~/ 32); // 非 Android 平台的处理 + } + } + + /// 挂断 + void udpHangUpAction() async { + if (state.talkStatus.value == TalkStatus.duringCall) { + // 如果是通话中就挂断 + StartChartManage().sendTalkHangupMessage(); + } else { + // 拒绝 + StartChartManage().sendTalkRejectMessage(); + } + Get.back(); + } + + // 发起接听命令 + void initiateAnswerCommand() { + StartChartManage().sendTalkAcceptMessage(); + } + + void _updateFps(List frameTimestamps) { + final int now = DateTime.now().millisecondsSinceEpoch; + // 移除超过1秒的时间戳 + frameTimestamps.removeWhere((timestamp) => now - timestamp > 1000); + + // 计算 FPS + final double fps = frameTimestamps.length.toDouble(); + + // 更新 FPS + state.fps.value = fps; + } + + // 监听音视频数据流 + void _startListenTalkData() { + state.talkDataRepository.talkDataStream.listen((talkData) { + final contentType = talkData.contentType; + // 判断数据类型,进行分发处理 + switch (contentType) { + case TalkData_ContentTypeE.G711: + // state.audioBuffer.add(talkData); + if (state.audioBuffer.length < 60) { + // 假设缓冲区大小为60帧 + state.audioBuffer.add(talkData); + } + break; + case TalkData_ContentTypeE.Image: + // state.videoBuffer.add(talkData); + // 增加视频缓冲区大小 + if (state.videoBuffer.length < 60) { + // 假设缓冲区大小为60帧 + state.videoBuffer.add(talkData); + } + break; + } + }); + } + + /// 监听对讲状态 + void _startListenTalkStatus() { + state.startChartTalkStatus.statusStream.listen((talkStatus) { + state.talkStatus.value = talkStatus; + switch (talkStatus) { + case TalkStatus.rejected: + case TalkStatus.hangingUpDuring: + case TalkStatus.notTalkData: + case TalkStatus.notTalkPing: + case TalkStatus.end: + _handleInvalidTalkStatus(); + break; + default: + // 其他状态的处理 + break; + } + }); + } + + void _playAudioData(TalkData talkData) { + // 将 PCM 数据转换为 PcmArrayInt16 + final PcmArrayInt16 fromList = PcmArrayInt16.fromList(talkData.content); + FlutterPcmSound.feed(fromList); + if (!state.isPlaying.value) { + FlutterPcmSound.play(); + state.isPlaying.value = true; + } + } + + void _playVideoData(TalkData talkData) { + state.listData.value = Uint8List.fromList(talkData.content); + } + + void _startPlayback() { + int frameIntervalMs = 45; // 初始帧间隔设置为45毫秒(约22FPS) + + Future.delayed(Duration(milliseconds: 800), () { + _startTime = DateTime.now().millisecondsSinceEpoch; + _syncTimer ??= + Timer.periodic(Duration(milliseconds: frameIntervalMs), (timer) { + final currentTime = DateTime.now().millisecondsSinceEpoch; + final elapsedTime = currentTime - _startTime; + + // 根据 elapsedTime 同步音频和视频 + // AppLog.log('Elapsed Time: $elapsedTime ms'); + // 动态调整帧间隔 + _adjustFrameInterval(); + + // 播放合适的音频帧 + if (state.audioBuffer.isNotEmpty && + state.audioBuffer.first.durationMs <= elapsedTime) { + _playAudioData(state.audioBuffer.removeAt(0)); + } + + // 播放合适的视频帧 + // 跳帧策略:如果缓冲区中有多个帧,且它们的时间戳都在当前时间之前,则播放最新的帧 + while (state.videoBuffer.isNotEmpty && + state.videoBuffer.first.durationMs <= elapsedTime) { + // 如果有多个帧,移除旧的帧,保持最新的帧 + if (state.videoBuffer.length > 1) { + state.videoBuffer.removeAt(0); + } else { + // 记录当前时间戳 + frameTimestamps.add(DateTime.now().millisecondsSinceEpoch); + // 计算并更新 FPS + _updateFps(frameTimestamps); + _playVideoData(state.videoBuffer.removeAt(0)); + } + } + }); + }); + } + + /// 动态调整帧间隔 + void _adjustFrameInterval() { + if (state.videoBuffer.length < 10 && frameIntervalMs < maxFrameIntervalMs) { + // 如果缓冲区较小且帧间隔小于最大值,则增加帧间隔 + frameIntervalMs += 5; + } else if (state.videoBuffer.length > 20 && + frameIntervalMs > minFrameIntervalMs) { + // 如果缓冲区较大且帧间隔大于最小值,则减少帧间隔 + frameIntervalMs -= 5; + } + _syncTimer?.cancel(); + _syncTimer = + Timer.periodic(Duration(milliseconds: frameIntervalMs), (timer) { + final currentTime = DateTime.now().millisecondsSinceEpoch; + final elapsedTime = currentTime - _startTime; + + // 播放合适的音频帧 + if (state.audioBuffer.isNotEmpty && + state.audioBuffer.first.durationMs <= elapsedTime) { + _playAudioData(state.audioBuffer.removeAt(0)); + } + + // 播放合适的视频帧 + // 跳帧策略:如果缓冲区中有多个帧,且它们的时间戳都在当前时间之前,则播放最新的帧 + while (state.videoBuffer.isNotEmpty && + state.videoBuffer.first.durationMs <= elapsedTime) { + // 如果有多个帧,移除旧的帧,保持最新的帧 + if (state.videoBuffer.length > 1) { + state.videoBuffer.removeAt(0); + } else { + // 记录当前时间戳 + frameTimestamps.add(DateTime.now().millisecondsSinceEpoch); + // 计算并更新 FPS + _updateFps(frameTimestamps); + _playVideoData(state.videoBuffer.removeAt(0)); + } + } + }); + } + + /// 停止播放音频 + void _stopPlayG711Data() async { + print('停止播放'); + await FlutterPcmSound.pause(); + await FlutterPcmSound.stop(); + await FlutterPcmSound.clear(); + } + + /// 开门 + udpOpenDoorAction(List list) async {} + + Future getPermissionStatus() async { + final Permission permission = Permission.microphone; + //granted 通过,denied 被拒绝,permanentlyDenied 拒绝且不在提示 + final PermissionStatus status = await permission.status; + if (status.isGranted) { + return true; + } else if (status.isDenied) { + requestPermission(permission); + } else if (status.isPermanentlyDenied) { + openAppSettings(); + } else if (status.isRestricted) { + requestPermission(permission); + } else {} + return false; + } + + ///申请权限 + void requestPermission(Permission permission) async { + final PermissionStatus status = await permission.request(); + if (status.isPermanentlyDenied) { + openAppSettings(); + } + } + + @override + void onReady() { + super.onReady(); + } + + @override + void onInit() { + super.onInit(); + + // 监听音视频数据流 + _startListenTalkData(); + // 监听对讲状态 + _startListenTalkStatus(); + // 在没有监听成功之前赋值一遍状态 + // *** 由于页面会在状态变化之后才会初始化,导致识别不到最新的状态,在这里手动赋值 *** + state.talkStatus.value = state.startChartTalkStatus.status; + + _initFlutterPcmSound(); + + _startPlayback(); + } + + @override + void onClose() { + _stopPlayG711Data(); + state.listData.value = Uint8List(0); + _syncTimer?.cancel(); + } + + /// 处理无效通话状态 + void _handleInvalidTalkStatus() { + state.listData.value = Uint8List(0); + // 停止播放音频 + _stopPlayG711Data(); + // 状态错误,返回页面 + Get.back(); + } +} diff --git a/lib/talk/startChart/views/talkView/talk_view_page.dart b/lib/talk/startChart/views/talkView/talk_view_page.dart new file mode 100644 index 00000000..db49b6ba --- /dev/null +++ b/lib/talk/startChart/views/talkView/talk_view_page.dart @@ -0,0 +1,373 @@ +import 'dart:async'; +import 'dart:convert'; + +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:get/get.dart'; +import 'package:star_lock/app_settings/app_settings.dart'; +import 'package:star_lock/main/lockDetail/realTimePicture/realTimePicture_state.dart'; +import 'package:star_lock/talk/call/callTalk.dart'; +import 'package:star_lock/talk/startChart/constant/talk_status.dart'; +import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; +import 'package:star_lock/talk/startChart/views/talkView/talk_view_logic.dart'; +import 'package:star_lock/talk/startChart/views/talkView/talk_view_state.dart'; + +import '../../../../app_settings/app_colors.dart'; +import '../../../../tools/showTFView.dart'; + +class TalkViewPage extends StatefulWidget { + const TalkViewPage({Key? key}) : super(key: key); + + @override + State createState() => _TalkViewPageState(); +} + +class _TalkViewPageState extends State + with TickerProviderStateMixin { + final TalkViewLogic logic = Get.put(TalkViewLogic()); + final TalkViewState state = Get.find().state; + + @override + void initState() { + super.initState(); + + //写一个定时器,三十秒后页面自动返回 + // state.autoBackTimer = Timer(const Duration(seconds: 30), Get.back); + + state.animationController = AnimationController( + vsync: this, // 确保使用的TickerProvider是当前Widget + duration: const Duration(seconds: 1), + ); + + state.animationController.repeat(); + //动画开始、结束、向前移动或向后移动时会调用StatusListener + state.animationController.addStatusListener((AnimationStatus status) { + // AppLog.log("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 SizedBox( + width: 1.sw, + height: 1.sh, + child: Stack( + alignment: Alignment.center, + children: [ + Obx( + () => state.listData.value.isEmpty + ? Image.asset( + 'images/main/monitorBg.png', + width: ScreenUtil().screenWidth, + height: ScreenUtil().screenHeight, + fit: BoxFit.cover, + ) + : Image.memory( + state.listData.value, + gaplessPlayback: true, + width: 1.sw, + height: 1.sh, + fit: BoxFit.cover, + filterQuality: FilterQuality.high, + errorBuilder: ( + BuildContext context, + Object error, + StackTrace? stackTrace, + ) { + return Container(color: Colors.transparent); + }, + ), + ), + Obx(() => state.listData.value.isEmpty + ? Positioned( + bottom: 300.h, + child: Text( + '正在创建安全连接...'.tr, + style: TextStyle(color: Colors.black, fontSize: 26.sp), + )) + : 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: [ + SizedBox(height: 20.h), + bottomTopBtnWidget(), + SizedBox(height: 20.h), + bottomBottomBtnWidget(), + SizedBox(height: 20.h), + ], + ), + ), + ), + Positioned( + top: 100.h, + left: 10.w, + child: Obx( + () => Text( + 'FPS:${state.fps.value}', + style: TextStyle(fontSize: 30.sp, color: Colors.orange,fontWeight: FontWeight.bold), + ), + ), + ), + Obx(() => state.listData.value.isEmpty + ? buildRotationTransition() + : Container()) + ], + ), + ); + } + + Widget bottomTopBtnWidget() { + return Row(mainAxisAlignment: MainAxisAlignment.center, children: [ + // 打开关闭声音 + GestureDetector( + onTap: () { + state.isOpenVoice.value = !state.isOpenVoice.value; + }, + 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_monitoringCloseVoice.png') + : const AssetImage( + 'images/main/icon_lockDetail_monitoringOpenVoice.png'))), + ), + ), + SizedBox(width: 50.w), + // 截图 + GestureDetector( + onTap: () { + // Get.toNamed(Routers.monitoringRealTimeScreenPage); + }, + 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: () { + // Get.toNamed(Routers.monitoringRealTimeScreenPage); + }, + 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: () { + logic.showToast('功能暂未开放'.tr); + }, + child: Image( + width: 28.w, + height: 28.w, + fit: BoxFit.fill, + image: const AssetImage( + 'images/main/icon_lockDetail_rectangle.png'))) + ]); + } + + Widget bottomBottomBtnWidget() { + return Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + children: [ + // 接听 + Obx( + () => bottomBtnItemWidget( + getAnswerBtnImg(), + getAnswerBtnName(), + Colors.white, + longPress: () async {}, + longPressUp: () async {}, + onClick: () async { + if (state.talkStatus.value == TalkStatus.waitingAnswer) { + // 接听 + logic.initiateAnswerCommand(); + } + }, + ), + ), + bottomBtnItemWidget( + 'images/main/icon_lockDetail_hangUp.png', '挂断'.tr, Colors.red, + onClick: () { + // 挂断 + logic.udpHangUpAction(); + }), + bottomBtnItemWidget( + 'images/main/icon_lockDetail_monitoringUnlock.png', + '开锁', + AppColors.mainColor, + onClick: () {}, + ) + ]); + } + + String getAnswerBtnImg() { + switch (state.talkStatus.value) { + case TalkStatus.waitingAnswer: + return 'images/main/icon_lockDetail_monitoringAnswerCalls.png'; + case TalkStatus.answeredSuccessfully: + case TalkStatus.duringCall: + return 'images/main/icon_lockDetail_monitoringUnTalkback.png'; + default: + return 'images/main/icon_lockDetail_monitoringAnswerCalls.png'; + } + } + + String getAnswerBtnName() { + switch (state.talkStatus.value) { + case TalkStatus.waitingAnswer: + return '接听'.tr; + case TalkStatus.answeredSuccessfully: + case TalkStatus.duringCall: + 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: 140.h, + child: Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Container( + width: wh, + height: 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), + Expanded( + child: Text(name, + style: TextStyle(fontSize: 20.sp, color: Colors.white), + textAlign: TextAlign.center)) + ], + )), + ); + } + + void showDeletPasswordAlertDialog(BuildContext context) { + showDialog( + barrierDismissible: false, + context: context, + builder: (BuildContext context) { + return ShowTFView( + title: '请输入六位数字开锁密码'.tr, + tipTitle: '', + controller: state.passwordTF, + inputFormatters: [ + LengthLimitingTextInputFormatter(6), //限制长度 + FilteringTextInputFormatter.allow(RegExp('[0-9]')), + ], + sureClick: () async { + // //发送删除锁请求 + // if (state.passwordTF.text.isEmpty) { + // logic.showToast('请输入开锁密码'.tr); + // return; + // } + // + // // List numbers = state.passwordTF.text.split('').map((char) => int.parse(char)).toList(); + // // 开锁 + // // lockID + // List numbers = []; + // List lockIDData = utf8.encode(state.passwordTF.text); + // numbers.addAll(lockIDData); + // // topBytes = getFixedLengthList(lockIDData, 20 - lockIDData.length); + // for (int i = 0; i < 6 - lockIDData.length; i++) { + // numbers.add(0); + // } + }, + cancelClick: () { + Get.back(); + }, + ); + }, + ); + } + + //旋转动画 + 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(); + state.realTimePicTimer.cancel(); + state.autoBackTimer.cancel(); + CallTalk().finishAVData(); + super.dispose(); + } +} diff --git a/lib/talk/startChart/views/talkView/talk_view_state.dart b/lib/talk/startChart/views/talkView/talk_view_state.dart new file mode 100644 index 00000000..403ede77 --- /dev/null +++ b/lib/talk/startChart/views/talkView/talk_view_state.dart @@ -0,0 +1,68 @@ +import 'dart:async'; +import 'dart:typed_data'; + +import 'package:flutter/material.dart'; +import 'package:flutter_voice_processor/flutter_voice_processor.dart'; +import 'package:get/get.dart'; +import 'package:get/get_rx/get_rx.dart'; +import 'package:network_info_plus/network_info_plus.dart'; +import 'package:star_lock/talk/startChart/constant/talk_status.dart'; +import 'package:star_lock/talk/startChart/handle/other/talk_data_repository.dart'; +import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart'; +import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; + +import '../../../../tools/storage.dart'; + +class TalkViewState { + RxBool isOpenVoice = false.obs; + int udpSendDataFrameNumber = 0; // 帧序号 + // var isSenderAudioData = false.obs;// 是否要发送音频数据 + + Future userMobileIP = NetworkInfo().getWifiIP(); + Future userUid = Storage.getUid(); + + RxInt udpStatus = + 0.obs; //0:初始状态 1:等待监视 2: 3:监视中 4:呼叫成功 5:主角通话中 6:被叫通话 8:被叫通话中 9:长按说话 + TextEditingController passwordTF = TextEditingController(); + + Rx listData = Uint8List(0).obs; //得到的视频流字节数据 + RxList listAudioData = [].obs; //得到的音频流字节数据 + + late final VoiceProcessor? voiceProcessor; + + late Timer oneMinuteTimeTimer = + Timer(const Duration(seconds: 1), () {}); // 定时器超过60秒关闭当前界面 + RxInt oneMinuteTime = 0.obs; // 定时器秒数 + + // 定时器如果发送了接听的命令 而没收到回复就每秒重复发送10次 + late Timer answerTimer; + late Timer hangUpTimer; + late Timer openDoorTimer; + late AnimationController animationController; + + RxDouble fps = 0.0.obs; // 添加 FPS 计数 + + + late Timer autoBackTimer = + Timer(const Duration(seconds: 1), () {}); //发送30秒监视后自动返回 + late Timer realTimePicTimer = + Timer(const Duration(seconds: 1), () {}); //监视命令定时器 + RxInt elapsedSeconds = 0.obs; + + + List audioBuffer = [].obs; + List videoBuffer = [].obs; + + + RxBool isPlaying = false.obs; // 是否开始播放 + + + Rx talkStatus = TalkStatus.none.obs; //星图对讲状态 + + // 获取 startChartTalkStatus 的唯一实例 + final StartChartTalkStatus startChartTalkStatus = + StartChartTalkStatus.instance; + + // 通话数据流的单例流数据处理类 + final TalkDataRepository talkDataRepository = TalkDataRepository.instance; +} From c865db7a9f17da25f28463af2dcb2a6a0c25014a Mon Sep 17 00:00:00 2001 From: liyi Date: Sat, 28 Dec 2024 14:58:01 +0800 Subject: [PATCH 22/22] =?UTF-8?q?fix:=E5=A2=9E=E5=8A=A0=E9=A1=B5=E9=9D=A2?= =?UTF-8?q?=E6=92=AD=E6=94=BE=E9=80=BB=E8=BE=91=E3=80=81=E8=B0=83=E6=95=B4?= =?UTF-8?q?proto=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lan/lan_en.json | 1 + lan/lan_keys.json | 1 + lan/lan_zh.json | 1 + .../handle/impl/udp_talk_accept_handler.dart | 3 + .../handle/impl/udp_talk_hangup_handler.dart | 4 + .../handle/impl/udp_talk_reject_handler.dart | 16 +- .../handle/impl/udp_talk_request_handler.dart | 10 + .../handle/scp_message_base_handle.dart | 5 + lib/talk/startChart/start_chart_manage.dart | 13 +- .../views/talkView/talk_view_logic.dart | 189 +++++++++++++++--- .../views/talkView/talk_view_page.dart | 121 ++++++----- .../views/talkView/talk_view_state.dart | 31 ++- pubspec.yaml | 4 + 13 files changed, 302 insertions(+), 97 deletions(-) diff --git a/lan/lan_en.json b/lan/lan_en.json index e203f2c4..e5aeb2fa 100644 --- a/lan/lan_en.json +++ b/lan/lan_en.json @@ -1011,6 +1011,7 @@ "请在锁设置中开启远程开锁": "Please enable remote unlocking in the lock settings", "接听": "Answer", "截图已保存到相册": "Screenshot saved to album", + "录屏已保存到相册": "Screen recording file saved to album", "添加遥控": "Add remote control", "已连接到锁,请按遥控": "Connected to the lock, please press the remote control", "遥控号": "Remote control number", diff --git a/lan/lan_keys.json b/lan/lan_keys.json index 1656967f..e5047118 100755 --- a/lan/lan_keys.json +++ b/lan/lan_keys.json @@ -1014,6 +1014,7 @@ "请在锁设置中开启远程开锁": "请在锁设置中开启远程开锁", "接听": "接听", "截图已保存到相册": "截图已保存到相册", + "录屏已保存到相册": "录屏已保存到相册", "添加遥控": "添加遥控", "已连接到锁,请按遥控": "已连接到锁,请按遥控", "遥控号": "遥控号", diff --git a/lan/lan_zh.json b/lan/lan_zh.json index 522b21da..ccaae2c0 100755 --- a/lan/lan_zh.json +++ b/lan/lan_zh.json @@ -1013,6 +1013,7 @@ "请在锁设置中开启远程开锁": "请在锁设置中开启远程开锁", "接听": "接听", "截图已保存到相册": "截图已保存到相册", + "录屏已保存到相册": "录屏已保存到相册", "添加遥控": "添加遥控", "已连接到锁,请按遥控": "已连接到锁,请按遥控", "遥控号": "遥控号", diff --git a/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart index f6e932e1..eca0b059 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_accept_handler.dart @@ -39,6 +39,9 @@ class UdpTalkAcceptHandler extends ScpMessageBaseHandle stopRingtone(); // 设置状态为接听成功 talkStatus.setAnsweredSuccessfully(); + // 同意接听之后,停止对讲请求超时监听定时器 + talkeRequestOverTimeTimerManager.receiveMessage(); + talkeRequestOverTimeTimerManager.dispose(); } } diff --git a/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart index f7e39e2a..32c267a9 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_hangup_handler.dart @@ -39,6 +39,10 @@ class UdpTalkHangUpHandler extends ScpMessageBaseHandle startChartManage.stopTalkExpectMessageTimer(); talkStatus.setHangingUpDuring(); stopRingtone(); + + // 拒绝接听之后,停止对讲请求超时监听定时器 + talkeRequestOverTimeTimerManager.receiveMessage(); + talkeRequestOverTimeTimerManager.dispose(); } @override diff --git a/lib/talk/startChart/handle/impl/udp_talk_reject_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_reject_handler.dart index 07dfe4f6..0eb85fa4 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_reject_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_reject_handler.dart @@ -18,16 +18,13 @@ class UdpTalkRejectHandler extends ScpMessageBaseHandle @override void handleReq(ScpMessage scpMessage) { // 收到接听拒绝请求 - startChartManage.sendGenericRespSuccessMessage( - ToPeerId: scpMessage.FromPeerId!, - FromPeerId: scpMessage.ToPeerId!, - PayloadType: scpMessage.PayloadType!, - ); - startChartManage.stopTalkPingMessageTimer(); - startChartManage.stopTalkExpectMessageTimer(); + // 回复成功消息 + replySuccessMessage(scpMessage); + // 停止铃声 stopRingtone(); // 收到接听拒绝回复 talkStatus.setRejected(); + } @override @@ -35,6 +32,11 @@ class UdpTalkRejectHandler extends ScpMessageBaseHandle startChartManage.stopTalkPingMessageTimer(); startChartManage.stopTalkExpectMessageTimer(); stopRingtone(); + + + // 拒绝接听之后,停止对讲请求超时监听定时器 + talkeRequestOverTimeTimerManager.receiveMessage(); + talkeRequestOverTimeTimerManager.dispose(); } @override diff --git a/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart b/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart index 20e649fa..1e3ddb54 100644 --- a/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart +++ b/lib/talk/startChart/handle/impl/udp_talk_request_handler.dart @@ -34,6 +34,16 @@ class UdpTalkRequestHandler extends ScpMessageBaseHandle startChartManage.ToPeerId = scpMessage.FromPeerId!; // 处理收到接听请求后的事件 _talkRequestEvent(talkObjectName: talkReq.callerName); + + // 启动对讲请求超时定时器 + talkeRequestOverTimeTimerManager.startTimer(); + talkeRequestOverTimeTimerManager.setOnTimeout(() { + if (talkStatus.status == TalkStatus.waitingAnswer) { + // 超时未接听,发送挂断请求 + startChartManage.sendTalkRejectMessage(); + Get.back(); + } + }); } @override diff --git a/lib/talk/startChart/handle/scp_message_base_handle.dart b/lib/talk/startChart/handle/scp_message_base_handle.dart index 63e639dd..5a0aabcd 100644 --- a/lib/talk/startChart/handle/scp_message_base_handle.dart +++ b/lib/talk/startChart/handle/scp_message_base_handle.dart @@ -36,6 +36,11 @@ class ScpMessageBaseHandle { final audioManager = AudioPlayerManager(); + // 通话请求超时未处理监听定时器管理 + final talkeRequestOverTimeTimerManager = OverTimeTimerManager( + timeoutInSeconds: 30, + ); + // 通话保持超时监听定时器管理 final talkePingOverTimeTimerManager = OverTimeTimerManager( timeoutInSeconds: 260, diff --git a/lib/talk/startChart/start_chart_manage.dart b/lib/talk/startChart/start_chart_manage.dart index 83485db6..e5087f6d 100644 --- a/lib/talk/startChart/start_chart_manage.dart +++ b/lib/talk/startChart/start_chart_manage.dart @@ -86,7 +86,7 @@ class StartChartManage { final int _maxPayloadSize = 8 * 1024; // 分包大小 // 默认通话的期望数据格式 - TalkExpectReq defaultTalkExpect = TalkExpectReq( + TalkExpectReq _defaultTalkExpect = TalkExpectReq( videoType: [VideoTypeE.IMAGE], audioType: [AudioTypeE.G711], ); @@ -857,7 +857,7 @@ class StartChartManage { (Timer timer) { // 发送期望接受消息 sendTalkExpectMessage( - talkExpect: defaultTalkExpect, + talkExpect: _defaultTalkExpect, ); }, ); @@ -971,9 +971,16 @@ class StartChartManage { talkExpectTimer = null; // 清除定时器引用 } + // 重新发送预期数据 + void reStartTalkExpectMessageTimer() { + stopTalkExpectMessageTimer(); + startTalkExpectTimer(); + } + /// 修改预期接收到的数据 void changeTalkExpectDataType({required TalkExpectReq talkExpect}) { - defaultTalkExpect = talkExpect; + _defaultTalkExpect = talkExpect; + reStartTalkExpectMessageTimer(); } /// 销毁资源 diff --git a/lib/talk/startChart/views/talkView/talk_view_logic.dart b/lib/talk/startChart/views/talkView/talk_view_logic.dart index 264374bb..edd759ea 100644 --- a/lib/talk/startChart/views/talkView/talk_view_logic.dart +++ b/lib/talk/startChart/views/talkView/talk_view_logic.dart @@ -1,34 +1,36 @@ import 'dart:async'; import 'dart:io'; -import 'dart:math'; +import 'dart:ui' as ui; +import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_pcm_sound/flutter_pcm_sound.dart'; -import 'package:flutter_voice_processor/flutter_voice_processor.dart'; +import 'package:flutter_screen_recording/flutter_screen_recording.dart'; +import 'package:gallery_saver/gallery_saver.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/app_settings/app_settings.dart'; -import 'package:star_lock/blue/io_tool/manager_event_bus.dart'; -import 'package:star_lock/talk/call/callTalk.dart'; + import 'package:star_lock/talk/startChart/constant/talk_status.dart'; import 'package:star_lock/talk/startChart/proto/talk_data.pb.dart'; import 'package:star_lock/talk/startChart/proto/talk_data.pbenum.dart'; +import 'package:star_lock/talk/startChart/proto/talk_expect.pb.dart'; import 'package:star_lock/talk/startChart/start_chart_manage.dart'; -import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; + import 'package:star_lock/talk/startChart/views/talkView/talk_view_state.dart'; -import '../../../../talk/call/g711.dart'; -import '../../../../talk/udp/udp_manage.dart'; -import '../../../../talk/udp/udp_senderManage.dart'; import '../../../../tools/baseGetXController.dart'; -import '../../../../tools/eventBusEventManage.dart'; class TalkViewLogic extends BaseGetXController { final TalkViewState state = TalkViewState(); Timer? _syncTimer; int _startTime = 0; - final int bufferSize = 22; // 缓冲区大小(以帧为单位) + final int bufferSize = 20; // 缓冲区大小(以帧为单位) final List frameTimestamps = []; int frameIntervalMs = 45; // 初始帧间隔设置为45毫秒(约22FPS) int minFrameIntervalMs = 30; // 最小帧间隔(约33 FPS) @@ -82,22 +84,27 @@ class TalkViewLogic extends BaseGetXController { void _startListenTalkData() { state.talkDataRepository.talkDataStream.listen((talkData) { final contentType = talkData.contentType; + final currentTimestamp = DateTime.now().millisecondsSinceEpoch; + + /// 如果不是通话中的状态不处理对讲数据 + if (state.startChartTalkStatus.status != TalkStatus.duringCall) { + return; + } + // 判断数据类型,进行分发处理 switch (contentType) { case TalkData_ContentTypeE.G711: - // state.audioBuffer.add(talkData); - if (state.audioBuffer.length < 60) { - // 假设缓冲区大小为60帧 + if (state.audioBuffer.length < bufferSize) { state.audioBuffer.add(talkData); } break; case TalkData_ContentTypeE.Image: - // state.videoBuffer.add(talkData); - // 增加视频缓冲区大小 - if (state.videoBuffer.length < 60) { - // 假设缓冲区大小为60帧 + if (state.videoBuffer.length < bufferSize) { state.videoBuffer.add(talkData); } + + /// 更新网络状态 + updateNetworkStatus(currentTimestamp); break; } }); @@ -136,6 +143,7 @@ class TalkViewLogic extends BaseGetXController { state.listData.value = Uint8List.fromList(talkData.content); } + /// 启动播放 void _startPlayback() { int frameIntervalMs = 45; // 初始帧间隔设置为45毫秒(约22FPS) @@ -154,7 +162,15 @@ class TalkViewLogic extends BaseGetXController { // 播放合适的音频帧 if (state.audioBuffer.isNotEmpty && state.audioBuffer.first.durationMs <= elapsedTime) { - _playAudioData(state.audioBuffer.removeAt(0)); + // 判断音频开关是否打开 + if (state.isOpenVoice.value) { + _playAudioData(state.audioBuffer.removeAt(0)); + } else { + // 如果不播放音频,只从缓冲区中读取数据,但不移除 + // 你可以根据需要调整此处逻辑,例如保留缓冲区的最大长度,防止无限增长 + // 仅移除缓冲区数据但不播放音频,确保音频也是实时更新的 + state.audioBuffer.removeAt(0); + } } // 播放合适的视频帧 @@ -165,10 +181,10 @@ class TalkViewLogic extends BaseGetXController { if (state.videoBuffer.length > 1) { state.videoBuffer.removeAt(0); } else { - // 记录当前时间戳 - frameTimestamps.add(DateTime.now().millisecondsSinceEpoch); - // 计算并更新 FPS - _updateFps(frameTimestamps); + // // 记录当前时间戳 + // frameTimestamps.add(DateTime.now().millisecondsSinceEpoch); + // // 计算并更新 FPS + // _updateFps(frameTimestamps); _playVideoData(state.videoBuffer.removeAt(0)); } } @@ -195,7 +211,15 @@ class TalkViewLogic extends BaseGetXController { // 播放合适的音频帧 if (state.audioBuffer.isNotEmpty && state.audioBuffer.first.durationMs <= elapsedTime) { - _playAudioData(state.audioBuffer.removeAt(0)); + // 判断音频开关是否打开 + if (state.isOpenVoice.value) { + _playAudioData(state.audioBuffer.removeAt(0)); + } else { + // 如果不播放音频,只从缓冲区中读取数据,但不移除 + // 你可以根据需要调整此处逻辑,例如保留缓冲区的最大长度,防止无限增长 + // 仅移除缓冲区数据但不播放音频,确保音频也是实时更新的 + state.audioBuffer.removeAt(0); + } } // 播放合适的视频帧 @@ -206,19 +230,47 @@ class TalkViewLogic extends BaseGetXController { if (state.videoBuffer.length > 1) { state.videoBuffer.removeAt(0); } else { - // 记录当前时间戳 - frameTimestamps.add(DateTime.now().millisecondsSinceEpoch); - // 计算并更新 FPS - _updateFps(frameTimestamps); + // // 记录当前时间戳 + // frameTimestamps.add(DateTime.now().millisecondsSinceEpoch); + // // 计算并更新 FPS + // _updateFps(frameTimestamps); _playVideoData(state.videoBuffer.removeAt(0)); } } }); } + /// 修改网络状态 + void updateNetworkStatus(int currentTimestamp) { + if (state.lastFrameTimestamp.value != 0) { + final frameInterval = currentTimestamp - state.lastFrameTimestamp.value; + if (frameInterval > 500 && frameInterval <= 1000) { + // 判断帧间隔是否在500毫秒到1秒之间 + state.networkStatus.value = NetworkStatus.lagging; + showNetworkStatus("Network is lagging"); + } else if (frameInterval > 1000) { + // 判断帧间隔是否超过1秒 + state.networkStatus.value = NetworkStatus.delayed; + showNetworkStatus("Network is delayed"); + } else { + state.networkStatus.value = NetworkStatus.normal; + state.alertCount.value = 0; // 重置计数器 + EasyLoading.dismiss(); // 网络恢复正常时关闭提示 + } + } + state.lastFrameTimestamp.value = currentTimestamp; + } + + /// 提示网络状态 + void showNetworkStatus(String message) { + if (state.alertCount.value < 3 && !EasyLoading.isShow) { + showToast(message); + state.alertCount++; + } + } + /// 停止播放音频 void _stopPlayG711Data() async { - print('停止播放'); await FlutterPcmSound.pause(); await FlutterPcmSound.stop(); await FlutterPcmSound.clear(); @@ -288,4 +340,85 @@ class TalkViewLogic extends BaseGetXController { // 状态错误,返回页面 Get.back(); } + + /// 更新发送预期数据 + void updateTalkExpect() { + TalkExpectReq talkExpectReq = TalkExpectReq(); + if (state.isOpenVoice.value) { + talkExpectReq = TalkExpectReq( + videoType: [VideoTypeE.IMAGE], + audioType: [AudioTypeE.G711], + ); + } else { + talkExpectReq = TalkExpectReq( + videoType: [VideoTypeE.IMAGE], + audioType: [], + ); + } + + /// 修改发送预期数据 + StartChartManage().changeTalkExpectDataType(talkExpect: talkExpectReq); + state.isOpenVoice.value = !state.isOpenVoice.value; + } + + /// 截图并保存到相册 + Future captureAndSavePng() async { + try { + if (state.globalKey.currentContext == null) { + AppLog.log('截图失败: 未找到当前上下文'); + return; + } + 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); + + if (byteData == null) { + AppLog.log('截图失败: 图像数据为空'); + return; + } + final Uint8List pngBytes = byteData.buffer.asUint8List(); + + // 获取应用程序的文档目录 + final Directory directory = await getApplicationDocumentsDirectory(); + final String imagePath = '${directory.path}/screenshot.png'; + + // 将截图保存为文件 + final File imgFile = File(imagePath); + await imgFile.writeAsBytes(pngBytes); + + // 将截图保存到相册 + await ImageGallerySaver.saveFile(imagePath); + + AppLog.log('截图保存路径: $imagePath'); + showToast('截图已保存到相册'.tr); + } catch (e) { + AppLog.log('截图失败: $e'); + } + } + + /// 开始录屏 + Future startRecording() async { + getPermissionStatus(); + bool started = + await FlutterScreenRecording.startRecordScreenAndAudio("Recording"); + if (started) { + state.isRecording.value = true; + } + } + + /// 停止录屏 + Future stopRecording() async { + String path = await FlutterScreenRecording.stopRecordScreen; + if (path != null) { + state.isRecording.value = false; + // 保存录制的视频到相册 + await GallerySaver.saveVideo(path).then((bool? success) {}); + showToast('录屏已保存到相册'.tr); + } else { + state.isRecording.value = false; + print("Recording failed"); + } + } } diff --git a/lib/talk/startChart/views/talkView/talk_view_page.dart b/lib/talk/startChart/views/talkView/talk_view_page.dart index db49b6ba..879dcb0a 100644 --- a/lib/talk/startChart/views/talkView/talk_view_page.dart +++ b/lib/talk/startChart/views/talkView/talk_view_page.dart @@ -1,10 +1,16 @@ import 'dart:async'; import 'dart:convert'; +import 'dart:io'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; +import 'package:flutter_screen_recording/flutter_screen_recording.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:gallery_saver/gallery_saver.dart'; import 'package:get/get.dart'; +import 'package:image_gallery_saver/image_gallery_saver.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:star_lock/app_settings/app_settings.dart'; import 'package:star_lock/main/lockDetail/realTimePicture/realTimePicture_state.dart'; import 'package:star_lock/talk/call/callTalk.dart'; @@ -43,7 +49,6 @@ class _TalkViewPageState extends State state.animationController.repeat(); //动画开始、结束、向前移动或向后移动时会调用StatusListener state.animationController.addStatusListener((AnimationStatus status) { - // AppLog.log("AnimationStatus:$status"); if (status == AnimationStatus.completed) { state.animationController.reset(); state.animationController.forward(); @@ -70,23 +75,29 @@ class _TalkViewPageState extends State height: ScreenUtil().screenHeight, fit: BoxFit.cover, ) - : Image.memory( - state.listData.value, - gaplessPlayback: true, - width: 1.sw, - height: 1.sh, - fit: BoxFit.cover, - filterQuality: FilterQuality.high, - errorBuilder: ( - BuildContext context, - Object error, - StackTrace? stackTrace, - ) { - return Container(color: Colors.transparent); - }, + : PopScope( + canPop: false, + child: RepaintBoundary( + key: state.globalKey, + child: Image.memory( + state.listData.value, + gaplessPlayback: true, + width: 1.sw, + height: 1.sh, + fit: BoxFit.cover, + filterQuality: FilterQuality.high, + errorBuilder: ( + BuildContext context, + Object error, + StackTrace? stackTrace, + ) { + return Container(color: Colors.transparent); + }, + ), + ), ), ), - Obx(() => state.listData.value.isEmpty + Obx(() => state.talkStatus.value == TalkStatus.answeredSuccessfully ? Positioned( bottom: 300.h, child: Text( @@ -114,17 +125,20 @@ class _TalkViewPageState extends State ), ), ), - Positioned( - top: 100.h, - left: 10.w, - child: Obx( - () => Text( - 'FPS:${state.fps.value}', - style: TextStyle(fontSize: 30.sp, color: Colors.orange,fontWeight: FontWeight.bold), - ), - ), - ), - Obx(() => state.listData.value.isEmpty + // Positioned( + // top: 100.h, + // left: 10.w, + // child: Obx( + // () => Text( + // 'FPS:${state.fps.value}', + // style: TextStyle( + // fontSize: 30.sp, + // color: Colors.orange, + // fontWeight: FontWeight.bold), + // ), + // ), + // ), + Obx(() => state.talkStatus.value == TalkStatus.answeredSuccessfully ? buildRotationTransition() : Container()) ], @@ -137,7 +151,7 @@ class _TalkViewPageState extends State // 打开关闭声音 GestureDetector( onTap: () { - state.isOpenVoice.value = !state.isOpenVoice.value; + logic.updateTalkExpect(); }, child: Container( width: 50.w, @@ -148,16 +162,16 @@ class _TalkViewPageState extends State height: 40.w, image: state.isOpenVoice.value ? const AssetImage( - 'images/main/icon_lockDetail_monitoringCloseVoice.png') + 'images/main/icon_lockDetail_monitoringOpenVoice.png') : const AssetImage( - 'images/main/icon_lockDetail_monitoringOpenVoice.png'))), + 'images/main/icon_lockDetail_monitoringCloseVoice.png'))), ), ), SizedBox(width: 50.w), // 截图 GestureDetector( - onTap: () { - // Get.toNamed(Routers.monitoringRealTimeScreenPage); + onTap: () async { + await logic.captureAndSavePng(); }, child: Container( width: 50.w, @@ -173,32 +187,38 @@ class _TalkViewPageState extends State SizedBox(width: 50.w), // 录制 GestureDetector( - onTap: () { - // Get.toNamed(Routers.monitoringRealTimeScreenPage); + onTap: () async { + if (state.isRecording.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')), + width: 40.w, + height: 40.w, + fit: BoxFit.fill, + image: const AssetImage( + 'images/main/icon_lockDetail_monitoringScreenRecording.png'), + ), ), ), SizedBox(width: 50.w), GestureDetector( - onTap: () { - logic.showToast('功能暂未开放'.tr); - }, - child: Image( - width: 28.w, - height: 28.w, - fit: BoxFit.fill, - image: const AssetImage( - 'images/main/icon_lockDetail_rectangle.png'))) + onTap: () { + logic.showToast('功能暂未开放'.tr); + }, + child: Image( + width: 28.w, + height: 28.w, + fit: BoxFit.fill, + image: const AssetImage('images/main/icon_lockDetail_rectangle.png'), + ), + ), ]); } @@ -212,7 +232,10 @@ class _TalkViewPageState extends State getAnswerBtnImg(), getAnswerBtnName(), Colors.white, - longPress: () async {}, + longPress: () async { + if (state.talkStatus.value == TalkStatus.answeredSuccessfully || + state.talkStatus.value == TalkStatus.duringCall) {} + }, longPressUp: () async {}, onClick: () async { if (state.talkStatus.value == TalkStatus.waitingAnswer) { diff --git a/lib/talk/startChart/views/talkView/talk_view_state.dart b/lib/talk/startChart/views/talkView/talk_view_state.dart index 403ede77..896cbfa4 100644 --- a/lib/talk/startChart/views/talkView/talk_view_state.dart +++ b/lib/talk/startChart/views/talkView/talk_view_state.dart @@ -13,8 +13,15 @@ import 'package:star_lock/talk/startChart/start_chart_talk_status.dart'; import '../../../../tools/storage.dart'; +enum NetworkStatus { + normal, // 0 + lagging, // 1 + delayed, // 2 + packetLoss // 3 +} + class TalkViewState { - RxBool isOpenVoice = false.obs; + int udpSendDataFrameNumber = 0; // 帧序号 // var isSenderAudioData = false.obs;// 是否要发送音频数据 @@ -27,7 +34,7 @@ class TalkViewState { Rx listData = Uint8List(0).obs; //得到的视频流字节数据 RxList listAudioData = [].obs; //得到的音频流字节数据 - + GlobalKey globalKey = GlobalKey(); late final VoiceProcessor? voiceProcessor; late Timer oneMinuteTimeTimer = @@ -40,8 +47,6 @@ class TalkViewState { late Timer openDoorTimer; late AnimationController animationController; - RxDouble fps = 0.0.obs; // 添加 FPS 计数 - late Timer autoBackTimer = Timer(const Duration(seconds: 1), () {}); //发送30秒监视后自动返回 @@ -50,19 +55,25 @@ class TalkViewState { RxInt elapsedSeconds = 0.obs; + + + // 星图对讲相关状态 List audioBuffer = [].obs; List videoBuffer = [].obs; - - RxBool isPlaying = false.obs; // 是否开始播放 - - Rx talkStatus = TalkStatus.none.obs; //星图对讲状态 - // 获取 startChartTalkStatus 的唯一实例 final StartChartTalkStatus startChartTalkStatus = StartChartTalkStatus.instance; - // 通话数据流的单例流数据处理类 final TalkDataRepository talkDataRepository = TalkDataRepository.instance; + RxInt lastFrameTimestamp = 0.obs; // 上一帧的时间戳,用来判断网络环境 + Rx networkStatus = + NetworkStatus.normal.obs; // 网络状态:0-正常 1-网络卡顿 2-网络延迟 3-网络丢包 + RxInt alertCount = 0.obs; // 网络状态提示计数器 + RxInt maxAlertNumber = 3.obs; // 网络状态提示最大提示次数 + RxBool isOpenVoice = true.obs; // 是否打开声音 + RxBool isRecording = true.obs; // 是否录屏中 + RxDouble fps = 0.0.obs; // 添加 FPS 计数 + } diff --git a/pubspec.yaml b/pubspec.yaml index d1426507..0691f5ca 100755 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -256,6 +256,10 @@ dependencies: asn1lib: ^1.0.0 fast_rsa: ^3.6.6 protobuf: ^3.1.0 + #录屏 + flutter_screen_recording: 2.0.16 + #图库保存 + gallery_saver: ^2.3.2