From 42231d60bd3a5fff560702274ddfabdce58901ff Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Mon, 4 Aug 2025 16:07:04 +0800 Subject: [PATCH 01/34] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E9=A6=96=E9=A1=B5?= =?UTF-8?q?=E5=BC=95=E5=AF=BC=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/lottie/intro_foriday.json | 1 + lib/core/widgets/intro_overlay_builder.dart | 36 +++++++++++++++++++++ lib/data/services/novice_guide_service.dart | 4 ++- lib/modules/home/views/home_view.dart | 21 +++++++++--- 4 files changed, 57 insertions(+), 5 deletions(-) create mode 100644 assets/lottie/intro_foriday.json diff --git a/assets/lottie/intro_foriday.json b/assets/lottie/intro_foriday.json new file mode 100644 index 000000000..e030941b4 --- /dev/null +++ b/assets/lottie/intro_foriday.json @@ -0,0 +1 @@ +{"nm":"Comp 1","ddd":0,"h":700,"w":700,"meta":{"g":"@lottiefiles/toolkit-js 0.22.1"},"layers":[{"ty":0,"nm":"Pre-comp 3","sr":1,"st":141.000005743048,"op":231.000009408823,"ip":141.000005743048,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[350,350,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":700,"h":700,"refId":"comp_0","ind":1},{"ty":0,"nm":"Pre-comp 3","sr":1,"st":71.0000028918893,"op":161.000006557664,"ip":71.0000028918893,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[350,350,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":700,"h":700,"refId":"comp_0","ind":2},{"ty":0,"nm":"Pre-comp 3","sr":1,"st":0,"op":90.0000036657751,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[350,350,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":700,"h":700,"refId":"comp_0","ind":3},{"ty":0,"nm":"bubble","sr":1,"st":0,"op":600.000024438501,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[350,350,0],"ix":1},"s":{"a":0,"k":[91,91,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":0,"k":43,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":70,"ix":11}},"ef":[{"ty":0,"mn":"ADBE Gaussian Blur 2","nm":"Gaussian Blur","ix":1,"en":1,"ef":[{"ty":0,"mn":"ADBE Gaussian Blur 2-0001","nm":"Blurriness","ix":1,"v":{"a":0,"k":40.5,"ix":1}},{"ty":7,"mn":"ADBE Gaussian Blur 2-0002","nm":"Blur Dimensions","ix":2,"v":{"a":0,"k":1,"ix":2}},{"ty":7,"mn":"ADBE Gaussian Blur 2-0003","nm":"Repeat Edge Pixels","ix":3,"v":{"a":0,"k":0,"ix":3}}]}],"w":700,"h":700,"refId":"comp_4","ind":4},{"ty":0,"nm":"bubble","sr":1,"st":0,"op":600.000024438501,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[350,350,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":0,"k":226,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":48,"ix":11}},"ef":[{"ty":0,"mn":"ADBE Tint","nm":"Tint","ix":1,"en":1,"ef":[{"ty":2,"mn":"ADBE Tint-0001","nm":"Map Black To","ix":1,"v":{"a":0,"k":[0,0,0,0],"ix":1}},{"ty":2,"mn":"ADBE Tint-0002","nm":"Map White To","ix":2,"v":{"a":0,"k":[1,0,0.7765],"ix":2}},{"ty":0,"mn":"ADBE Tint-0003","nm":"Amount to Tint","ix":3,"v":{"a":0,"k":100,"ix":3}},{"ty":6,"mn":"ADBE Tint-0004","nm":"","ix":4,"v":0}]}],"w":700,"h":700,"refId":"comp_4","ind":5},{"ty":0,"nm":"bubble","sr":1,"st":0,"op":600.000024438501,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[350,350,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":700,"h":700,"refId":"comp_4","ind":6}],"v":"5.5.5","fr":29.9700012207031,"op":231.000009408823,"ip":0,"assets":[{"nm":"","id":"comp_0","layers":[{"ty":0,"nm":"Pre-comp 2","sr":1,"st":13.0000005295009,"op":90.0000036657751,"ip":13.0000005295009,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[350,350,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":0,"k":42,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":55,"ix":11}},"ef":[],"w":700,"h":700,"refId":"comp_1","ind":1},{"ty":0,"nm":"Pre-comp 2","sr":1,"st":0,"op":77.0000031362743,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[350,350,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":700,"h":700,"refId":"comp_1","ind":2}]},{"nm":"","id":"comp_1","layers":[{"ty":0,"nm":"Pre-comp 1","sr":1,"st":9.00000036657752,"op":77.0000031362743,"ip":9.00000036657752,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[350,350,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":0,"k":69,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[{"ty":0,"mn":"ADBE Tint","nm":"Tint","ix":1,"en":1,"ef":[{"ty":2,"mn":"ADBE Tint-0001","nm":"Map Black To","ix":1,"v":{"a":0,"k":[0,0,0,0],"ix":1}},{"ty":2,"mn":"ADBE Tint-0002","nm":"Map White To","ix":2,"v":{"a":0,"k":[1,0.4078,0.6588],"ix":2}},{"ty":0,"mn":"ADBE Tint-0003","nm":"Amount to Tint","ix":3,"v":{"a":0,"k":100,"ix":3}},{"ty":6,"mn":"ADBE Tint-0004","nm":"","ix":4,"v":0}]}],"w":700,"h":700,"refId":"comp_2","ind":1},{"ty":0,"nm":"Pre-comp 1","sr":1,"st":0,"op":68.0000027696968,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[350,350,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":700,"h":700,"refId":"comp_2","ind":2}]},{"nm":"","id":"comp_2","layers":[{"ty":0,"nm":"Shape Layer 1 Comp 1","sr":1,"st":0,"op":47.0000019143492,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[350,350,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":0,"k":275,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":700,"h":700,"refId":"comp_3","ind":1},{"ty":0,"nm":"Shape Layer 1 Comp 1","sr":1,"st":12.00000048877,"op":59.0000024031193,"ip":12.00000048877,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[350,350,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":0,"k":227,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":700,"h":700,"refId":"comp_3","ind":2},{"ty":0,"nm":"Shape Layer 1 Comp 1","sr":1,"st":5.00000020365417,"op":52.0000021180034,"ip":5.00000020365417,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[350,350,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":0,"k":140,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":700,"h":700,"refId":"comp_3","ind":3},{"ty":0,"nm":"Shape Layer 1 Comp 1","sr":1,"st":21.0000008553475,"op":68.0000027696968,"ip":21.0000008553475,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[350,350,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":0,"k":48,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":700,"h":700,"refId":"comp_3","ind":4},{"ty":0,"nm":"Shape Layer 1 Comp 1","sr":1,"st":14.0000005702317,"op":61.0000024845809,"ip":14.0000005702317,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[350,350,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"w":700,"h":700,"refId":"comp_3","ind":5}]},{"nm":"","id":"comp_3","layers":[{"ty":4,"nm":"Shape Layer 1","sr":1,"st":0,"op":47.0000019143492,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-261,141,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0,0,100],"t":0},{"s":[100,100,100],"t":48.0000019550801}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[349,350,0],"t":0,"ti":[19.167,-15.333,0],"to":[-19.167,15.333,0]},{"s":[234,442,0],"t":48.0000019550801}],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100],"t":26},{"s":[0],"t":46.0000018736184}],"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[12,12],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":0,"ix":4},"w":{"a":0,"k":3,"ix":5},"c":{"a":0,"k":[0.9529,0.9608,0.9686],"ix":3}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,0.9529,0.9725],"ix":4},"r":1,"o":{"a":0,"k":100,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-261,141],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1}]},{"nm":"","id":"comp_4","layers":[{"ty":4,"nm":"Shape Layer 1","sr":1,"st":0,"op":600.000024438501,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":0},{"s":[720],"t":231.000009408823}],"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[400,400],"ix":2}},{"ty":"gs","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Stroke","nm":"Gradient Stroke 1","e":{"a":0,"k":[-761.875,495.96],"ix":5},"g":{"p":3,"k":{"a":0,"k":[0,1,1,1,0.496,0.9882352941176471,0.39215686274509803,0.17647058823529413,0.993,1,0.35294117647058826,0.37254901960784315,0,0,0.496,1,0.993,1],"ix":8}},"t":2,"a":{"a":0,"k":0,"ix":7},"h":{"a":0,"k":0,"ix":6},"s":{"a":0,"k":[154.435,-126.834],"ix":4},"lc":1,"lj":1,"ml":4,"o":{"a":0,"k":100,"ix":9},"w":{"a":0,"k":1,"ix":10}},{"ty":"fl","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Fill","nm":"Fill 1","c":{"a":0,"k":[1,0.9529,0.9725],"ix":4},"r":1,"o":{"a":0,"k":0,"ix":5}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[1,-1],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":1},{"ty":4,"nm":"Shape Layer 10","sr":1,"st":0,"op":600.000024438501,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[59.113,-37.893,0],"ix":1},"s":{"a":0,"k":[131.951,131.951,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[351,351,0],"ix":2},"r":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[382],"t":0},{"s":[742],"t":231.000009408823}],"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Shape 1","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,75.084],[-2.326,11.481],[8.484,-20.679],[0,-18.258],[-75.111,0],[-2.638,0.152]],"o":[[0,-12.43],[4.439,-21.907],[-6.526,15.906],[0,75.111],[2.677,0],[-56.982,-4.209]],"v":[[-30.208,-36.759],[-26.642,-72.724],[-61.861,-83.451],[-71.985,-31.87],[64.015,104.13],[71.985,103.892]]},"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":0,"ix":4},"w":{"a":0,"k":3,"ix":5},"c":{"a":0,"k":[0.9529,0.9608,0.9686],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[100,0],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,1,0.9529411764705882,0.9725490196078431,0.487,1,0.9529411764705882,0.9725490196078431,0.998,0.5176470588235295,0,1,0,1,0.487,1,0.998,0],"ix":9}},"t":2,"a":{"a":0,"k":0,"ix":8},"h":{"a":0,"k":0,"ix":7},"s":{"a":0,"k":[0,0],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":2},{"ty":4,"nm":"Shape Layer 4","sr":1,"st":0,"op":600.000024438501,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-128,0,0],"ix":1},"s":{"a":0,"k":[65.625,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[222,350,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":54,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[16,16],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":0,"ix":4},"w":{"a":0,"k":3,"ix":5},"c":{"a":0,"k":[0.9529,0.9608,0.9686],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[100,0],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0.002,1,1,1,0.488,1,1,1,0.998,1,1,1,0.002,1,0.488,0.5,0.998,0],"ix":9}},"t":2,"a":{"a":0,"k":0,"ix":8},"h":{"a":0,"k":0,"ix":7},"s":{"a":0,"k":[0,0],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-128,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":3},{"ty":4,"nm":"Shape Layer 8","sr":1,"st":0,"op":600.000024438501,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"td":1,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[0],"t":0},{"s":[360],"t":59.0000024031193}],"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[400,400],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":0,"ix":4},"w":{"a":0,"k":3,"ix":5},"c":{"a":0,"k":[0.9529,0.9608,0.9686],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[202.396,-0.369],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,1,1,1,0.487,1,1,1,0.998,1,1,1,0,0.44,0.487,0.22,0.998,0],"ix":9}},"t":2,"a":{"a":0,"k":0,"ix":8},"h":{"a":0,"k":0,"ix":7},"s":{"a":0,"k":[0,0],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[1,-1],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":4},{"ty":4,"nm":"Shape Layer 7","sr":1,"st":0,"op":600.000024438501,"ip":0,"hd":false,"ddd":0,"bm":0,"tt":1,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[-118,46,0],"ix":1},"s":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[33.333,100,100],"t":0},{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[100,100,100],"t":29},{"s":[33.333,100,100],"t":59.0000024031193}],"ix":6},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[106,342,0],"t":0,"ti":[-81.667,0,0],"to":[81.667,0,0]},{"s":[596,342,0],"t":59.0000024031193}],"ix":2},"r":{"a":0,"k":38,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":39,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Rectangle 1","ix":1,"cix":2,"np":3,"it":[{"ty":"rc","bm":0,"hd":false,"mn":"ADBE Vector Shape - Rect","nm":"Rectangle Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"r":{"a":0,"k":0,"ix":4},"s":{"a":0,"k":[72,596],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":0,"ix":4},"w":{"a":0,"k":3,"ix":5},"c":{"a":0,"k":[0.9529,0.9608,0.9686],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[-2.662,190],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,1,1,1,0.487,1,1,1,0.998,1,1,1,0,0.44,0.487,0.22,0.998,0],"ix":9}},"t":2,"a":{"a":0,"k":0,"ix":8},"h":{"a":0,"k":0,"ix":7},"s":{"a":0,"k":[1.369,-19],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[-118,46],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":5},{"ty":4,"nm":"Shape Layer 6","sr":1,"st":0,"op":600.000024438501,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[146.132,146.132,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[142,142],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":0,"ix":4},"w":{"a":0,"k":3,"ix":5},"c":{"a":0,"k":[0.9529,0.9608,0.9686],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[60.839,0],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,0.8549019607843137,0.8549019607843137,0.8549019607843137,0.487,0.9254901960784314,0.9254901960784314,0.9254901960784314,0.998,1,1,1,0,0.44,0.487,0.22,0.998,0],"ix":9}},"t":2,"a":{"a":0,"k":0,"ix":8},"h":{"a":0,"k":0,"ix":7},"s":{"a":0,"k":[0,0],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[181.304,181.304],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,-3],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":6},{"ty":4,"nm":"Shape Layer 5","sr":1,"st":0,"op":600.000024438501,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[124.841,124.841,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,458,0],"ix":2},"r":{"a":0,"k":180,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":10,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Shape 2","ix":1,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[0,0],[-36.43,1.19],[0,0],[24.53,-18.36]],"o":[[25.85,-22.17],[0,0],[-32.74,1.05],[0,0]],"v":[[-100.64,-30.635],[-5.11,-67.775],[-5.11,37.125],[-92.55,67.775]]},"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":0,"ix":4},"w":{"a":0,"k":3,"ix":5},"c":{"a":0,"k":[0.9529,0.9608,0.9686],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[0,105.643],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,1,1,1,0.487,1,1,1,0.998,1,1,1,0,1,0.487,0.5,0.998,0],"ix":9}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":0,"k":[-1,-118.622],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]},{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Shape 1","ix":2,"cix":2,"np":3,"it":[{"ty":"sh","bm":0,"hd":false,"mn":"ADBE Vector Shape - Group","nm":"Path 1","ix":1,"d":1,"ks":{"a":0,"k":{"c":true,"i":[[-25.85,-22.17],[0,0],[32.74,1.05],[0,0]],"o":[[0,0],[-24.53,-18.36],[0,0],[36.43,1.19]],"v":[[100.64,-30.635],[92.55,67.775],[5.11,37.125],[5.11,-67.775]]},"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":0,"ix":4},"w":{"a":0,"k":3,"ix":5},"c":{"a":0,"k":[0.9529,0.9608,0.9686],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[0,105.643],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0,1,1,1,0.487,1,1,1,0.998,1,1,1,0,1,0.487,0.5,0.998,0],"ix":9}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":0,"k":[-1,-118.622],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[0,0],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":7},{"ty":4,"nm":"Shape Layer 3","sr":1,"st":0,"op":600.000024438501,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[343,317,0],"ix":2},"r":{"a":0,"k":0,"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[177,116],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":0,"ix":4},"w":{"a":0,"k":3,"ix":5},"c":{"a":0,"k":[0.9529,0.9608,0.9686],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[100,0],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0.002,0.9529411764705882,0.9529411764705882,0.9529411764705882,0.488,0.9764705882352941,0.9764705882352941,0.9764705882352941,0.998,1,1,1,0.002,1,0.488,0.5,0.998,0],"ix":9}},"t":2,"a":{"a":0,"k":0,"ix":8},"h":{"a":0,"k":0,"ix":7},"s":{"a":0,"k":[-10,-146],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[7.5,-104],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":8},{"ty":4,"nm":"Shape Layer 9","sr":1,"st":0,"op":600.000024438501,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[190],"t":0},{"s":[550],"t":230.000009368092}],"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[400,400],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":0,"ix":4},"w":{"a":0,"k":3,"ix":5},"c":{"a":0,"k":[0.9529,0.9608,0.9686],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[29.602,-44.441],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0.002,0.5450980392156862,0.9882352941176471,0.9725490196078431,0.488,0.5686274509803921,0.7294117647058823,0.7176470588235294,0.998,1,0,0,0.002,1,0.488,0.5,0.998,0],"ix":9}},"t":2,"a":{"a":0,"k":0,"ix":8},"h":{"a":0,"k":0,"ix":7},"s":{"a":0,"k":[-131.837,65.407],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[1,-1],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":9},{"ty":4,"nm":"Shape Layer 2","sr":1,"st":0,"op":600.000024438501,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[0,0,0],"ix":1},"s":{"a":0,"k":[100,100,100],"ix":6},"sk":{"a":0,"k":0},"p":{"a":0,"k":[350,350,0],"ix":2},"r":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[190],"t":0},{"s":[1270],"t":230.000009368092}],"ix":10},"sa":{"a":0,"k":0},"o":{"a":0,"k":100,"ix":11}},"ef":[],"shapes":[{"ty":"gr","bm":0,"hd":false,"mn":"ADBE Vector Group","nm":"Ellipse 1","ix":1,"cix":2,"np":3,"it":[{"ty":"el","bm":0,"hd":false,"mn":"ADBE Vector Shape - Ellipse","nm":"Ellipse Path 1","d":1,"p":{"a":0,"k":[0,0],"ix":3},"s":{"a":0,"k":[400,400],"ix":2}},{"ty":"st","bm":0,"hd":false,"mn":"ADBE Vector Graphic - Stroke","nm":"Stroke 1","lc":1,"lj":1,"ml":4,"o":{"a":0,"k":0,"ix":4},"w":{"a":0,"k":3,"ix":5},"c":{"a":0,"k":[0.9529,0.9608,0.9686],"ix":3}},{"ty":"gf","bm":0,"hd":false,"mn":"ADBE Vector Graphic - G-Fill","nm":"Gradient Fill 1","e":{"a":0,"k":[-48.365,-83.495],"ix":6},"g":{"p":3,"k":{"a":0,"k":[0.002,0.9882352941176471,0.7333333333333333,0.984313725490196,0.488,0.5254901960784314,0.9686274509803922,0.9294117647058824,0.998,1,0,0.8470588235294118,0.002,1,0.488,1,0.998,0],"ix":9}},"t":1,"a":{"a":0,"k":0},"h":{"a":0,"k":0},"s":{"a":0,"k":[-134.449,-317.964],"ix":5},"r":1,"o":{"a":0,"k":100,"ix":10}},{"ty":"tr","a":{"a":0,"k":[0,0],"ix":1},"s":{"a":0,"k":[100,100],"ix":3},"sk":{"a":0,"k":0,"ix":4},"p":{"a":0,"k":[1,-1],"ix":2},"r":{"a":0,"k":0,"ix":6},"sa":{"a":0,"k":0,"ix":5},"o":{"a":0,"k":100,"ix":7}}]}],"ind":10}]}]} \ No newline at end of file diff --git a/lib/core/widgets/intro_overlay_builder.dart b/lib/core/widgets/intro_overlay_builder.dart index aab3dae94..5b64fc8c1 100644 --- a/lib/core/widgets/intro_overlay_builder.dart +++ b/lib/core/widgets/intro_overlay_builder.dart @@ -1,8 +1,44 @@ import 'package:flutter/material.dart'; import 'package:flutter_intro/flutter_intro.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; +import 'package:lottie/lottie.dart'; class IntroOverlayBuilder { + static Widget homeBuilder( + StepWidgetParams params, + bool isWakeup, { + Function? onNext, + }) { + return Column( + children: [ + SizedBox( + width: 200.w, + height: 200.w, + child: Lottie.asset('assets/lottie/intro_foriday.json'), + ), + Padding( + padding: EdgeInsets.only(bottom: 20.w), + child: Text( + isWakeup ? '我在呢~' : '对着麦克风说:“小言小言”', + style: TextStyle( + fontSize: 15.sp, + color: Colors.white, + fontWeight: FontWeight.bold, + ), + ), + ), + if (onNext != null) + _button( + '下一步', + () { + params.onNext!(); + onNext(); + }, + ), + ], + ); + } + static Widget aiBuilder( StepWidgetParams params, String text, { diff --git a/lib/data/services/novice_guide_service.dart b/lib/data/services/novice_guide_service.dart index 12faf1a2c..c26e7adc8 100644 --- a/lib/data/services/novice_guide_service.dart +++ b/lib/data/services/novice_guide_service.dart @@ -5,9 +5,11 @@ import 'package:get_storage/get_storage.dart'; class NoviceGuideService extends GetxService { final GetStorage _storage = GetStorage(); - bool isHomeGuide = true; + bool isHomeGuide = false; bool isAIGuide = true; + RxBool isWakeup = false.obs; + int aiStep = 0; List aiStepsList = [ diff --git a/lib/modules/home/views/home_view.dart b/lib/modules/home/views/home_view.dart index b332f5941..5f993a405 100644 --- a/lib/modules/home/views/home_view.dart +++ b/lib/modules/home/views/home_view.dart @@ -114,11 +114,24 @@ class HomeView extends GetView { IntroStepBuilder( group: 'home', order: 2, + getOverlayPosition: ({ + required Offset offset, + required Size screenSize, + required Size size, + }) { + return OverlayPosition( + top: 200.h, + width: screenSize.width, + crossAxisAlignment: CrossAxisAlignment.end, + ); + }, overlayBuilder: (params) { - return IntroOverlayBuilder.aiBuilder( - params, - '对着麦克风说“小言小言”', - onNext: () {}, + return Obx( + () => IntroOverlayBuilder.homeBuilder( + params, + controller.noviceGuideService.isWakeup.value, + onNext: () {}, + ), ); }, builder: (BuildContext context, GlobalKey key) => SizedBox( From ac9e50079009ce4883334b0b1b7aeb37ffd1e16f Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Mon, 4 Aug 2025 18:48:29 +0800 Subject: [PATCH 02/34] =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E9=87=8D=E5=90=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../settings/controllers/settings_controller.dart | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/lib/modules/settings/controllers/settings_controller.dart b/lib/modules/settings/controllers/settings_controller.dart index 7e8e8a090..0f84a5059 100644 --- a/lib/modules/settings/controllers/settings_controller.dart +++ b/lib/modules/settings/controllers/settings_controller.dart @@ -1,9 +1,11 @@ +import 'package:agent_service/agent_service.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:dio/dio.dart'; import 'package:get_storage/get_storage.dart'; import '../../../core/themes/theme_service.dart'; import '../../../data/services/speech_factory.dart'; +import '../../../routes/app_routes.dart'; import '../models/language_model.dart'; import 'package:logger/logger.dart'; import '../../../data/services/ble_manager.dart'; @@ -381,15 +383,21 @@ class SettingsController extends GetxController { } } - void setLanguage(LanguageModel data) { + Future setLanguage(LanguageModel data) async { if (data.value != 'system') { List languageList = data.value.split('_'); Get.updateLocale(Locale(languageList[0], languageList[1])); } else { Get.updateLocale(Get.deviceLocale!); } + print("languageList: ${data.value}"); language.value = data; _storage.write('language', data.value); + // 延迟一下确保语言更新完成,然后重新进入应用 + await Future.delayed(const Duration(milliseconds: 500)); + + // 重新进入应用 - 回到启动页面重新初始化 + Get.offAllNamed(Routes.splash); Get.back(); } From 2de21b03c4786f40d8f5d8088de7f537efdfc70c Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Mon, 4 Aug 2025 18:52:03 +0800 Subject: [PATCH 03/34] =?UTF-8?q?=E7=8E=B0=E5=9C=BA=E5=BD=95=E9=9F=B3?= =?UTF-8?q?=E8=B7=9F=E9=9A=8F=E7=B3=BB=E7=BB=9F=EF=BC=8C=E6=9C=89=E8=80=B3?= =?UTF-8?q?=E6=9C=BA=E8=80=B3=E6=9C=BA=E5=81=9A=E8=BE=93=E5=85=A5=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../meeting/controllers/meeting_record_controller.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/modules/meeting/controllers/meeting_record_controller.dart b/lib/modules/meeting/controllers/meeting_record_controller.dart index 19ada35c7..8cd767ee0 100644 --- a/lib/modules/meeting/controllers/meeting_record_controller.dart +++ b/lib/modules/meeting/controllers/meeting_record_controller.dart @@ -138,7 +138,7 @@ class MeetingRecordController extends GetxController switch (audioType.value) { case 0: fileName.value = "现场录音"; - _audioSourceType = true; + _audioSourceType = false; break; case 1: fileName.value = "音、视频录音"; @@ -271,7 +271,7 @@ class MeetingRecordController extends GetxController void _startHardwareServices() { switch (audioType.value) { case 0: - _bleManager.openEncoder(); + // _bleManager.openEncoder(); break; case 1: _bleManager.openDecoder(); From 58ed6b9e325bb8453851266b8dc582ac15cd7318 Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Mon, 4 Aug 2025 18:59:41 +0800 Subject: [PATCH 04/34] =?UTF-8?q?=E5=8E=BB=E5=A4=84=E5=BD=95=E9=9F=B3?= =?UTF-8?q?=E8=BD=AC=E6=96=87=E6=9C=AC=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/modules/meeting/views/meeting_record_view.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/modules/meeting/views/meeting_record_view.dart b/lib/modules/meeting/views/meeting_record_view.dart index fab9cdf61..4de8a3320 100644 --- a/lib/modules/meeting/views/meeting_record_view.dart +++ b/lib/modules/meeting/views/meeting_record_view.dart @@ -95,7 +95,7 @@ class MeetingRecordView extends GetView { unselectedLabelColor: Colors.grey, tabs: const [ Tab(text: '音频'), - Tab(text: '文本'), + // Tab(text: '文本'), ], ), Expanded( @@ -103,7 +103,7 @@ class MeetingRecordView extends GetView { controller: controller.tabController, children: [ _buildAudioTab(context, isDarkMode), - _buildTextTab(context, isDarkMode), + // _buildTextTab(context, isDarkMode), ], ), ), From 8e45063c2c0ebcefa829aeb96cdbaa015fc16738 Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Mon, 4 Aug 2025 20:05:08 +0800 Subject: [PATCH 05/34] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=BC=95=E5=AF=BC?= =?UTF-8?q?=E9=A1=B5=E8=80=B3=E6=9C=BA=E5=94=A4=E9=86=92=E7=9B=91=E5=90=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data/services/novice_guide_service.dart | 24 +++++++++++++++++++++ lib/modules/home/views/home_view.dart | 8 +++++-- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/lib/data/services/novice_guide_service.dart b/lib/data/services/novice_guide_service.dart index c26e7adc8..ad7b19451 100644 --- a/lib/data/services/novice_guide_service.dart +++ b/lib/data/services/novice_guide_service.dart @@ -1,3 +1,6 @@ +import 'dart:async'; + +import 'package:ble_service/ble_service.dart'; import 'package:flutter_intro/flutter_intro.dart'; import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; @@ -5,6 +8,9 @@ import 'package:get_storage/get_storage.dart'; class NoviceGuideService extends GetxService { final GetStorage _storage = GetStorage(); + final BleService _bleService = BleService(); + StreamSubscription? _statusSubscription; + bool isHomeGuide = false; bool isAIGuide = true; @@ -58,6 +64,12 @@ class NoviceGuideService extends GetxService { // isAIGuide = _storage.read('ai_guide') ?? false; } + @override + void onClose() { + closeWakeupListen(); + super.onClose(); + } + void startHomeGuide() async { if (!isHomeGuide) { await Future.delayed(const Duration(milliseconds: 200)); @@ -75,4 +87,16 @@ class NoviceGuideService extends GetxService { Intro.of(Get.context!).start(group: 'ai'); } } + + void startWakeupListen() { + _statusSubscription = _bleService.onStatusUpdated.listen((event) { + if (event is Map && event['type'] == 'wakeup') { + isWakeup.value = true; + } + }); + } + + void closeWakeupListen() { + _statusSubscription?.cancel(); + } } diff --git a/lib/modules/home/views/home_view.dart b/lib/modules/home/views/home_view.dart index 5f993a405..e1a68c9a1 100644 --- a/lib/modules/home/views/home_view.dart +++ b/lib/modules/home/views/home_view.dart @@ -100,7 +100,9 @@ class HomeView extends GetView { return IntroOverlayBuilder.aiBuilder( params, '欢迎使用Voitrans!首先,让我们唤醒你的专属 AI—— 小言', - onNext: () {}, + onNext: () { + controller.noviceGuideService.startWakeupListen(); + }, ); }, builder: (BuildContext context, GlobalKey key) => SizedBox( @@ -130,7 +132,9 @@ class HomeView extends GetView { () => IntroOverlayBuilder.homeBuilder( params, controller.noviceGuideService.isWakeup.value, - onNext: () {}, + onNext: () { + controller.noviceGuideService.closeWakeupListen(); + }, ), ); }, From df9548289c217e5733a9fa69b2d54c3e9c8bb6fe Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Mon, 4 Aug 2025 20:20:29 +0800 Subject: [PATCH 06/34] =?UTF-8?q?refactor(version=5Fupdate):=20=E9=87=8D?= =?UTF-8?q?=E6=9E=84=E7=89=88=E6=9C=AC=E6=9B=B4=E6=96=B0=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?=E5=B9=B6=E6=B7=BB=E5=8A=A0=E5=86=85=E5=AD=98=E7=BC=93=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 将版本更新服务重构为使用静态变量缓存应用信息,减少重复获取 添加 UserChannel.fromName 方法支持从字符串创建枚举 移除重复的 UserChannel 定义并统一使用 channel_app_model 优化游客登录状态检查逻辑,直接使用缓存值 --- lib/data/models/channel_app_model.dart | 24 +++ lib/data/services/version_update_service.dart | 178 +++++++++--------- .../login/controllers/login_controller.dart | 62 +++--- lib/modules/settings/views/settings_view.dart | 7 - 4 files changed, 135 insertions(+), 136 deletions(-) diff --git a/lib/data/models/channel_app_model.dart b/lib/data/models/channel_app_model.dart index 7fc3dcabf..dac792f28 100644 --- a/lib/data/models/channel_app_model.dart +++ b/lib/data/models/channel_app_model.dart @@ -177,6 +177,30 @@ enum UserChannel { ); } + // 从字符串名称创建枚举 + static UserChannel fromName(String name) { + switch (name.toUpperCase()) { + case 'CHINA_HUAWEI': + return UserChannel.CHINA_HUAWEI; + case 'CHINA_XIAOMI': + return UserChannel.CHINA_XIAOMI; + case 'CHINA_OPPO': + return UserChannel.CHINA_OPPO; + case 'CHINA_VIVO': + return UserChannel.CHINA_VIVO; + case 'CHINA_HONOR': + return UserChannel.CHINA_HONOR; + case 'OFFICIAL': + return UserChannel.OFFICIAL; + case 'GLOBAL_APPLE': + return UserChannel.GLOBAL_APPLE; + case 'GLOBAL_GOOGLE': + return UserChannel.GLOBAL_GOOGLE; + default: + return UserChannel.UNKNOWN; + } + } + // JSON 序列化时返回数字值 int toJson() => value; diff --git a/lib/data/services/version_update_service.dart b/lib/data/services/version_update_service.dart index f624ae7aa..4c408e107 100644 --- a/lib/data/services/version_update_service.dart +++ b/lib/data/services/version_update_service.dart @@ -5,6 +5,7 @@ import 'package:package_info_plus/package_info_plus.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:logger/logger.dart'; import 'package:device_info_plus/device_info_plus.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; import '../models/channel_app_model.dart'; import 'network/api.dart'; import 'dart:math' as math; @@ -13,15 +14,62 @@ import 'dart:math' as math; class VersionUpdateService extends GetxService { static VersionUpdateService get to => Get.find(); - final Logger _logger = Logger(); + // 静态应用信息(内存缓存) + static String? _appVersion = ""; //应用版本号 + static String? _buildNumber = ""; //构建号 + static String? _platform = ""; //平台类型 + static String? _deviceBrand = ""; //设备品牌 + static UserChannel? _channel; //渠道 + static String? _latestVersion = ""; //最新版本号+构建号 + static bool? _hasUpdate = false; //是否有更新 + static bool? _isForceUpdate = false; //是否强制更新 + static String? _updateUrl = ""; //更新链接 + static bool? _isShowTourists = false; //是否显示游客登录 + static DateTime? _lastCheckTime; //上次检查时间 + static const int _cacheIntervalMinutes = 10; // 可配置化 + + // 添加 getter 方法来访问缓存的静态数据 + /// 获取缓存的应用版本 + String get cachedAppVersion => _appVersion ?? ''; + + /// 获取缓存的构建号 + String get cachedBuildNumber => _buildNumber ?? ''; + + /// 获取缓存的平台信息 + String get cachedPlatform => _platform ?? ''; + + /// 获取缓存的设备品牌 + String get cachedDeviceBrand => _deviceBrand ?? ''; + + /// 获取缓存的渠道信息 + UserChannel get cachedChannel => _channel ?? UserChannel.UNKNOWN; + + /// 获取缓存的最新版本 + String get cachedLatestVersion => _latestVersion ?? ''; + + /// 获取缓存的是否有更新 + bool get cachedHasUpdate => _hasUpdate ?? false; + + /// 获取缓存的是否强制更新 + bool get cachedIsForceUpdate => _isForceUpdate ?? false; + + /// 获取缓存的更新地址 + String get cachedUpdateUrl => _updateUrl ?? ''; + + /// 获取缓存的游客登录状态 + bool get cachedShowTourists => _isShowTourists ?? false; - // 游客登录显示状态 - final isShowTourists = false.obs; + /// 获取上次检查时间 + DateTime? get lastCheckTime => _lastCheckTime; + + final Logger _logger = Logger(); /// 获取当前应用版本信息 Future getCurrentVersion() async { try { final packageInfo = await PackageInfo.fromPlatform(); + _appVersion = packageInfo.version; + _buildNumber = packageInfo.buildNumber; return '${packageInfo.version}+${packageInfo.buildNumber}'; } catch (e) { _logger.e('获取当前版本信息失败: $e'); @@ -31,14 +79,6 @@ class VersionUpdateService extends GetxService { /// 获取当前平台信息 Future getCurrentPlatformInfo() async { - if (kIsWeb) { - return const PlatformInfo( - platform: 'web', - storeType: AppStoreType.official, - channel: UserChannel.OFFICIAL, - ); - } - if (Platform.isIOS) { return const PlatformInfo( platform: 'ios', @@ -66,10 +106,14 @@ class VersionUpdateService extends GetxService { final androidInfo = await deviceInfo.androidInfo; final brand = androidInfo.brand.toLowerCase(); - return const PlatformInfo( + _deviceBrand = brand; + String channelFromEnv = dotenv.env['Channel'] ?? 'OFFICIAL'; + _channel = UserChannel.fromName(channelFromEnv); + + return PlatformInfo( platform: 'android', storeType: AppStoreType.official, - channel: UserChannel.OFFICIAL, + channel: _channel ?? UserChannel.OFFICIAL, ); // switch (brand) { @@ -121,10 +165,27 @@ class VersionUpdateService extends GetxService { /// 检查版本更新 Future checkForUpdate() async { + if (_lastCheckTime != null && + DateTime.now().difference(_lastCheckTime!).inMinutes < + _cacheIntervalMinutes) { + return VersionUpdateInfo( + hasUpdate: _hasUpdate ?? false, + currentVersion: _appVersion ?? '1.0.0', + latestVersion: (_latestVersion ?? '1.0.0').split('+')[0], + updateType: _hasUpdate! + ? (_isForceUpdate! ? UpdateType.force : UpdateType.optional) + : UpdateType.none, + ); + } + try { final currentVersion = await getCurrentVersion(); final platformInfo = await getCurrentPlatformInfo(); + _platform = platformInfo.platform; + _channel = platformInfo.channel; + _lastCheckTime = DateTime.now(); + // 调用后端接口获取最新版本信息 final response = await Api.getChannelApp({'channel': platformInfo.channel.value}); @@ -144,9 +205,11 @@ class VersionUpdateService extends GetxService { // 直接从response中获取app信息 final appInfo = AppInfo.fromJson(response['app']); _logger.d('获取到的最新版本信息: ${appInfo.toJson()}'); + _latestVersion = appInfo.version; + _updateUrl = appInfo.address; // 设置游客登录显示状态 - isShowTourists.value = appInfo.tourists; + _isShowTourists = appInfo.tourists; _logger.d('从服务器获取游客登录配置: ${appInfo.tourists}'); // if (channelAppResponse.code != 0 || channelAppResponse.data == null) { @@ -160,18 +223,17 @@ class VersionUpdateService extends GetxService { // } // final appInfo = channelAppResponse.data!.app; - final hasUpdate = _compareVersions(currentVersion, appInfo.version); + _hasUpdate = _compareVersions(currentVersion, appInfo.version); // 判断更新类型:如果主版本号不同则强制更新 UpdateType updateType = UpdateType.none; - if (hasUpdate) { - final isForceUpdate = - _shouldForceUpdate(currentVersion, appInfo.version); - updateType = isForceUpdate ? UpdateType.force : UpdateType.optional; + if (_hasUpdate!) { + _isForceUpdate = _shouldForceUpdate(currentVersion, appInfo.version); + updateType = _isForceUpdate! ? UpdateType.force : UpdateType.optional; } return VersionUpdateInfo( - hasUpdate: hasUpdate, + hasUpdate: _hasUpdate!, currentVersion: currentVersion.split('+')[0], latestVersion: appInfo.version.split('+')[0], appInfo: appInfo, @@ -180,7 +242,7 @@ class VersionUpdateService extends GetxService { } catch (e) { _logger.e('检查版本更新失败: $e'); // 发生错误时,保持默认值 false - isShowTourists.value = false; + _isShowTourists = false; final currentVersion = await getCurrentVersion(); return VersionUpdateInfo( hasUpdate: false, @@ -270,84 +332,16 @@ class VersionUpdateService extends GetxService { return false; } - //final platformInfo = await getCurrentPlatformInfo(); - try { - // switch (platformInfo.storeType) { - // case AppStoreType.appStore: - // return await _openAppStore(); - // case AppStoreType.googlePlay: - // return await _openGooglePlay(); - // case AppStoreType.huawei: - // return await _openHuaweiStore(); - // case AppStoreType.xiaomi: - // return await _openXiaomiStore(); - // case AppStoreType.oppo: - // return await _openOppoStore(); - // case AppStoreType.vivo: - // return await _openVivoStore(); - // case AppStoreType.honor: - // return await _openHonorStore(); - // case AppStoreType.official: - // return await _openOfficialDownload(updateInfo.appInfo!.address!); - // default: - return await _openOfficialDownload(updateInfo.appInfo!.address!); - // } + return await _openDownloadUrl(updateInfo.appInfo!.address!); } catch (e) { _logger.e('执行更新失败: $e'); return false; } } - /// 打开iOS App Store - Future _openAppStore() async { - // 这里需要替换为实际的App Store ID - const appStoreUrl = 'https://apps.apple.com/app/id你的应用ID'; - return await _launchUrl(appStoreUrl); - } - - /// 打开Google Play - Future _openGooglePlay() async { - // 这里需要替换为实际的包名 - const packageName = 'com.yourcompany.deepvoice'; - const playStoreUrl = - 'https://play.google.com/store/apps/details?id=$packageName'; - return await _launchUrl(playStoreUrl); - } - - /// 打开华为应用市场 - Future _openHuaweiStore() async { - const huaweiStoreUrl = 'appmarket://details?id=com.yourcompany.deepvoice'; - return await _launchUrl(huaweiStoreUrl); - } - - /// 打开小米应用商店 - Future _openXiaomiStore() async { - const xiaomiStoreUrl = 'mimarket://details?id=com.yourcompany.deepvoice'; - return await _launchUrl(xiaomiStoreUrl); - } - - /// 打开OPPO软件商店 - Future _openOppoStore() async { - const oppoStoreUrl = - 'oppomarket://details?packagename=com.yourcompany.deepvoice'; - return await _launchUrl(oppoStoreUrl); - } - - /// 打开vivo应用商店 - Future _openVivoStore() async { - const vivoStoreUrl = 'vivomarket://details?id=com.yourcompany.deepvoice'; - return await _launchUrl(vivoStoreUrl); - } - - /// 打开荣耀应用市场 - Future _openHonorStore() async { - const honorStoreUrl = 'honormarket://details?id=com.yourcompany.deepvoice'; - return await _launchUrl(honorStoreUrl); - } - - /// 打开官网下载 - Future _openOfficialDownload(String downloadUrl) async { + /// 打开下载链接 + Future _openDownloadUrl(String downloadUrl) async { return await _launchUrl(downloadUrl); } diff --git a/lib/modules/login/controllers/login_controller.dart b/lib/modules/login/controllers/login_controller.dart index 5b67a8d8a..292ef376d 100644 --- a/lib/modules/login/controllers/login_controller.dart +++ b/lib/modules/login/controllers/login_controller.dart @@ -1,23 +1,23 @@ import 'dart:async'; +import 'dart:io'; -import '../../../data/models/appconfig.dart'; -import '../../../data/models/appconfig_model.dart'; import 'package:get/get.dart'; import 'package:flutter/material.dart'; import 'package:flutter/foundation.dart'; import 'package:logger/web.dart'; -import '../../../routes/app_routes.dart'; +import 'package:get_storage/get_storage.dart'; +import 'package:device_info_plus/device_info_plus.dart'; import 'dart:convert'; import 'facebookLogin.dart'; import 'googleLogin.dart'; import 'weixinLogin.dart'; import 'appleLogin.dart'; +import '../../../routes/app_routes.dart'; +import '../../../data/models/appconfig.dart'; +import '../../../data/models/appconfig_model.dart'; import '../../../data/services/network/api.dart'; import '../../../../data/models/user_Info.dart'; -import 'package:get_storage/get_storage.dart'; -import 'package:device_info_plus/device_info_plus.dart'; -import 'dart:io'; -import 'package:package_info_plus/package_info_plus.dart'; +import '../../../data/models/channel_app_model.dart'; import '../../../data/services/version_update_service.dart'; import '../../../modules/common/widgets/privacy_policy_dialog.dart'; @@ -27,20 +27,20 @@ enum LoginViewState { } // 用户渠道枚举 -enum UserChannel { - UNKNOWN(0), // 未知渠道 - CHINA_HUAWEI(1), // 中国-华为 - CHINA_XIAOMI(2), // 中国-小米 - CHINA_OPPO(3), // 中国-OPPO - CHINA_VIVO(4), // 中国-VIVO - CHINA_HONOR(5), // 中国-荣耀 - OFFICIAL(100), // 官方渠道 - GLOBAL_APPLE(101), // 全球-苹果 - GLOBAL_GOOGLE(102); // 全球-谷歌 - - const UserChannel(this.value); - final int value; -} +// enum UserChannel { +// UNKNOWN(0), // 未知渠道 +// CHINA_HUAWEI(1), // 中国-华为 +// CHINA_XIAOMI(2), // 中国-小米 +// CHINA_OPPO(3), // 中国-OPPO +// CHINA_VIVO(4), // 中国-VIVO +// CHINA_HONOR(5), // 中国-荣耀 +// OFFICIAL(100), // 官方渠道 +// GLOBAL_APPLE(101), // 全球-苹果 +// GLOBAL_GOOGLE(102); // 全球-谷歌 + +// const UserChannel(this.value); +// final int value; +// } class LoginController extends GetxController { static LoginController get to => Get.find(); @@ -212,6 +212,7 @@ class LoginController extends GetxController { String get contactValue => _contactValue.value; String get displayContact => _displayContact.value; List get verificationCode => _verificationCode; + final versionService = VersionUpdateService.to; // 文本控制器 TextEditingController contactController = TextEditingController(); @@ -246,20 +247,9 @@ class LoginController extends GetxController { // 检查游客登录配置 Future _checkGuestLoginConfig() async { try { - final versionService = VersionUpdateService.to; - - // 先检查版本更新,这会同时设置 isShowTourists 的值 - await versionService.checkForUpdate(); + _isGuestLoginVisible.value = versionService.cachedShowTourists; - // 直接使用 VersionUpdateService 中的 isShowTourists 值 - _isGuestLoginVisible.value = versionService.isShowTourists.value; Logger().d('游客登录显示状态: ${_isGuestLoginVisible.value}'); - - // 监听 isShowTourists 的变化 - versionService.isShowTourists.listen((value) { - _isGuestLoginVisible.value = value; - Logger().d('游客登录显示状态更新: $value'); - }); } catch (e) { Logger().e('检查游客登录配置失败: $e'); // 默认不显示游客登录 @@ -301,9 +291,7 @@ class LoginController extends GetxController { // 新增:初始化APP信息(版本号、渠道、国家代码) Future _initAppInfo() async { try { - // 获取APP版本号 - final packageInfo = await PackageInfo.fromPlatform(); - _appVersion = '${packageInfo.version}+${packageInfo.buildNumber}'; + _appVersion = versionService.cachedAppVersion; Logger().d('appVersion: $_appVersion'); // 获取国家代码 @@ -311,7 +299,7 @@ class LoginController extends GetxController { Logger().d('countryCode: $_countryCode'); // 获取用户渠道(根据平台和设备信息确定) - _userChannel = UserChannel.OFFICIAL; + _userChannel = versionService.cachedChannel; Logger().d('userChannel: ${_userChannel.value}'); } catch (e) { Logger().e('获取APP信息失败: $e'); diff --git a/lib/modules/settings/views/settings_view.dart b/lib/modules/settings/views/settings_view.dart index 784c905ed..571c1731c 100644 --- a/lib/modules/settings/views/settings_view.dart +++ b/lib/modules/settings/views/settings_view.dart @@ -16,13 +16,6 @@ import '../../profile/views/unified_avatar.dart'; class SettingsView extends GetView { const SettingsView({Key? key}) : super(key: key); - // 正确获取用户名方式 - // String get userName => User.getname(); - - // String get deviceInfo => User.getdeviceInfo(); - // String get userId => User.getuserId(); - // String get standing => User.getstanding(); - // String get avatar => User.getnavatar(); @override Widget build(BuildContext context) { // 直接使用 Theme.of(context) 获取当前主题 From b4068ca2846283b99dff30e7d7c6dfbed9a9dfa9 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Mon, 4 Aug 2025 21:20:20 +0800 Subject: [PATCH 07/34] =?UTF-8?q?refactor:=20=E7=A7=BB=E9=99=A4=20macOS=20?= =?UTF-8?q?=E5=B9=B3=E5=8F=B0=E7=89=B9=E5=AE=9A=E7=9A=84=E7=AA=97=E5=8F=A3?= =?UTF-8?q?=E5=A4=A7=E5=B0=8F=E8=AE=BE=E7=BD=AE=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 该功能已不再需要,因此清理相关代码以简化主函数 --- lib/main.dart | 24 ------------------------ 1 file changed, 24 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index fd95d2863..6a976e86e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,8 +10,6 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'core/bindings/initial_binding.dart'; import 'routes/app_pages.dart'; import 'routes/app_routes.dart'; -import 'dart:io'; -import 'package:window_size/window_size.dart'; import 'core/translations/app_translations.dart'; import 'core/themes/app_theme.dart'; import 'core/themes/theme_service.dart'; @@ -32,28 +30,6 @@ void main() async { // 初始化登录服务(包含Firebase初始化) LoginService(); - // 设置窗口大小(仅在 macOS 下生效) - if (Platform.isMacOS) { - const double width = 375.0; - const double height = 812.0; - - setWindowTitle('Voitrans'); - setWindowMinSize(const Size(width, height)); - setWindowMaxSize(const Size(width, height)); - - // 居中显示窗口 - final screen = await getCurrentScreen(); - if (screen != null) { - final screenFrame = screen.frame; - final windowFrame = Rect.fromCenter( - center: screenFrame.center, - width: width, - height: height, - ); - setWindowFrame(windowFrame); - } - } - // 运行应用 runApp(const MainApp()); } From 88d43508e4122285180d694c1dc14b93946cc42d Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Tue, 5 Aug 2025 10:46:44 +0800 Subject: [PATCH 08/34] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=94=A4=E9=86=92?= =?UTF-8?q?=E8=80=B3=E6=9C=BA=E5=8A=A8=E7=94=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/core/widgets/intro_overlay_builder.dart | 70 +++++++++++++++++++-- 1 file changed, 65 insertions(+), 5 deletions(-) diff --git a/lib/core/widgets/intro_overlay_builder.dart b/lib/core/widgets/intro_overlay_builder.dart index 5b64fc8c1..bab8412b0 100644 --- a/lib/core/widgets/intro_overlay_builder.dart +++ b/lib/core/widgets/intro_overlay_builder.dart @@ -11,11 +11,13 @@ class IntroOverlayBuilder { }) { return Column( children: [ - SizedBox( - width: 200.w, - height: 200.w, - child: Lottie.asset('assets/lottie/intro_foriday.json'), - ), + isWakeup + ? _AnimatedLottieWidget() + : SizedBox( + width: 200.w, + height: 200.w, + child: Lottie.asset('assets/lottie/intro_foriday.json'), + ), Padding( padding: EdgeInsets.only(bottom: 20.w), child: Text( @@ -106,3 +108,61 @@ class IntroOverlayBuilder { ); } } + +// 新增的动画Widget +class _AnimatedLottieWidget extends StatefulWidget { + @override + _AnimatedLottieWidgetState createState() => _AnimatedLottieWidgetState(); +} + +class _AnimatedLottieWidgetState extends State<_AnimatedLottieWidget> + with SingleTickerProviderStateMixin { + late AnimationController _animationController; + late Animation _scaleAnimation; + + @override + void initState() { + super.initState(); + + // 创建动画控制器,持续时间1.5秒 + _animationController = AnimationController( + duration: const Duration(milliseconds: 1000), + vsync: this, + ); + + // 创建缩放动画,从0.8到1.2再回到1.0 + _scaleAnimation = Tween( + begin: 0.8, + end: 1.2, + ).animate(CurvedAnimation( + parent: _animationController, + curve: Curves.elasticInOut, + )); + + // 启动动画并设置为循环 + _animationController.repeat(reverse: true); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AnimatedBuilder( + animation: _scaleAnimation, + builder: (context, child) { + return Transform.scale( + scale: _scaleAnimation.value, + child: SizedBox( + width: 200.w, + height: 200.w, + child: Lottie.asset('assets/lottie/intro_foriday.json'), + ), + ); + }, + ); + } +} From 5682eb384e56db010e124732a2b7492447354af8 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Tue, 5 Aug 2025 11:57:00 +0800 Subject: [PATCH 09/34] =?UTF-8?q?fix(azure=5Fspeech):=20=E6=81=A2=E5=A4=8D?= =?UTF-8?q?=E9=9F=B3=E9=A2=91=E6=95=B0=E6=8D=AE=E5=9B=9E=E8=B0=83=E5=B9=B6?= =?UTF-8?q?=E6=B3=A8=E9=87=8AASR=E7=9B=B8=E5=85=B3=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 修复AzureAsrHelper中音频数据回调被注释的问题,同时临时注释MeetingRecordController中ASR相关代码以进行调试 --- .../meeting_record_controller.dart | 25 ++++++++++--------- .../azure_speech/AzureAsrHelper.kt | 7 +++--- 2 files changed, 17 insertions(+), 15 deletions(-) diff --git a/lib/modules/meeting/controllers/meeting_record_controller.dart b/lib/modules/meeting/controllers/meeting_record_controller.dart index 8cd767ee0..0d27de8b7 100644 --- a/lib/modules/meeting/controllers/meeting_record_controller.dart +++ b/lib/modules/meeting/controllers/meeting_record_controller.dart @@ -90,7 +90,7 @@ class MeetingRecordController extends GetxController Future _reinitializeAsrService() async { try { // 重新初始化ASR服务 - await _asrService.initialize(); + // await _asrService.initialize(); Logger.info('ASR服务重新初始化成功'); } catch (e) { @@ -163,7 +163,7 @@ class MeetingRecordController extends GetxController _bleManager.closeCodec(); } _asrService.stopRecord(true); - _asrService.stopContinuousRecognition(); + //_asrService.stopContinuousRecognition(); isRecording.value = false; } @@ -196,12 +196,12 @@ class MeetingRecordController extends GetxController /// Toggles visibility of transcribed text content Future toggleTextDisplay() async { - final recognitionStream = await _asrService.recognizeCallback(); - _recognitionSubscription?.cancel(); - _recognitionSubscription = - recognitionStream.listen(_handleRecognitionEvent); + // final recognitionStream = await _asrService.recognizeCallback(); + // _recognitionSubscription?.cancel(); + // _recognitionSubscription = + // recognitionStream.listen(_handleRecognitionEvent); - await _asrService.startContinuousRecognition(_audioSourceType); + //await _asrService.startContinuousRecognition(_audioSourceType); showTextContent.toggle(); } @@ -242,8 +242,8 @@ class MeetingRecordController extends GetxController /// Starts recording session Future _startRecording() async { // Initialize speech recognition - final recognitionStream = - await _asrService.startContinuousRecognition(_audioSourceType); + // final recognitionStream = + // await _asrService.startContinuousRecognition(_audioSourceType); // Prepare storage directory final appDir = await appDocDir; @@ -284,14 +284,14 @@ class MeetingRecordController extends GetxController /// Pauses current recording session Future _pauseRecording() async { - await _asrService.stopContinuousRecognition(); + // await _asrService.stopContinuousRecognition(); await _asrService.pauseRecord(); pauseTimer(); } /// Resumes paused recording session Future _resumeRecording() async { - await _asrService.startContinuousRecognition(_audioSourceType); + //await _asrService.startContinuousRecognition(_audioSourceType); startTimer(); } @@ -303,6 +303,7 @@ class MeetingRecordController extends GetxController intermediateContent.value = event.text; break; case RecognitionEventType.onAudio: + print("音波数据:${event.audio!.length}"); // 添加新音频数据到波形 _processAudioData(event.audio!); break; @@ -461,7 +462,7 @@ class MeetingRecordController extends GetxController } } - await _asrService.stopContinuousRecognition(); + //await _asrService.stopContinuousRecognition(); _resetToDefaultState(); isRecording.value = false; } diff --git a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt index 02d909d4a..ff5ba4ca1 100644 --- a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt +++ b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt @@ -717,7 +717,7 @@ class AzureAsrHelper(private val context: Context) { if (bytesToWrite < data.size) data.copyOf(bytesToWrite) else data try { - //continuousCallback?.onAudio(finalData) + continuousCallback?.onAudio(finalData) pushAudioStream?.write(finalData) recordfile?.saveAudioDataToWav(finalData) } catch (e: Exception) { @@ -922,6 +922,7 @@ class AzureAsrHelper(private val context: Context) { fun onError(error: String) } + /** * 连续识别回调接口 */ @@ -953,9 +954,9 @@ class AzureAsrHelper(private val context: Context) { fun onSessionStopped() /** - * 返回音频 + * 返回音频数据 * - * @param data 识别的音频 + * @param data 音频字节数组 */ fun onAudio(data: ByteArray) From a6b30a14f84717548e4750367e615bcfff3bcaea Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Tue, 5 Aug 2025 12:28:48 +0800 Subject: [PATCH 10/34] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=AE=89=E5=8D=93?= =?UTF-8?q?=E6=98=AF=E5=90=A6=E9=9C=80=E8=A6=81=E5=94=A4=E9=86=92ai?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data/services/novice_guide_service.dart | 2 ++ .../com/yunqiinnovation/ble_service/BleService.kt | 11 ++++++++++- .../yunqiinnovation/ble_service/BleServicePlugin.kt | 7 +++++++ local_plugins/ble_service/lib/ble_service.dart | 11 +++++++++++ 4 files changed, 30 insertions(+), 1 deletion(-) diff --git a/lib/data/services/novice_guide_service.dart b/lib/data/services/novice_guide_service.dart index ad7b19451..d0ef6ccc7 100644 --- a/lib/data/services/novice_guide_service.dart +++ b/lib/data/services/novice_guide_service.dart @@ -89,6 +89,7 @@ class NoviceGuideService extends GetxService { } void startWakeupListen() { + _bleService.setWakeup(false); _statusSubscription = _bleService.onStatusUpdated.listen((event) { if (event is Map && event['type'] == 'wakeup') { isWakeup.value = true; @@ -97,6 +98,7 @@ class NoviceGuideService extends GetxService { } void closeWakeupListen() { + _bleService.setWakeup(true); _statusSubscription?.cancel(); } } diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt index 0426e6409..216f8a136 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleService.kt @@ -87,6 +87,9 @@ object BleService { // 是否是ota模式 var isEnterOta = false + // 是否需要唤醒 + var isWakeup = true + // 是否回复指令 var isReply = true @@ -1515,7 +1518,13 @@ object BleService { private fun notifyWakeupSignalReceived() { for (callback in callbacks) { try { - callback.onWakeupSignalReceived() + if (isWakeup) { + callback.onWakeupSignalReceived() + } else { + if (callback::class.java.simpleName == "BleServicePlugin") { + callback.onWakeupSignalReceived() + } + } } catch (e: Exception) { Log.e(TAG, "分发唤醒信号回调异常", e) } diff --git a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt index f8f7b3db9..d18f07de6 100644 --- a/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt +++ b/local_plugins/ble_service/android/src/main/kotlin/com/yunqiinnovation/ble_service/BleServicePlugin.kt @@ -278,6 +278,13 @@ class BleServicePlugin : FlutterPlugin, MethodCallHandler, ActivityAware, val connected = BleService.isConnected() result.success(connected) } + "setWakeup" -> { + val wakeup = call.argument("wakeup") + if (wakeup != null) { + BleService.isWakeup = wakeup + } + result.success("") + } "registerBackgroundScan" -> { bleCompanionManager.registerBackgroundScan() result.success(true) diff --git a/local_plugins/ble_service/lib/ble_service.dart b/local_plugins/ble_service/lib/ble_service.dart index 3cfb0954d..7e5a21ffd 100644 --- a/local_plugins/ble_service/lib/ble_service.dart +++ b/local_plugins/ble_service/lib/ble_service.dart @@ -272,6 +272,17 @@ class BleService { } } + /// 设置是否是否需要唤醒 + Future setWakeup(bool wakeup) async { + try { + await _methodChannel.invokeMethod('setWakeup', { + 'wakeup': wakeup, + }); + } catch (e) { + print('设置是否是否需要唤醒失败: $e'); + } + } + /// 断开连接 Future disconnect() async { try { From f5f2e5b551686f249e9759d9be2893f9d5faf5cc Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Tue, 5 Aug 2025 14:45:14 +0800 Subject: [PATCH 11/34] =?UTF-8?q?feat(=E8=AF=AD=E9=9F=B3=E8=AF=86=E5=88=AB?= =?UTF-8?q?):=20=E6=B7=BB=E5=8A=A0=E8=AE=BE=E7=BD=AE=E6=94=AF=E6=8C=81?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E5=8A=9F=E8=83=BD=E5=B9=B6=E6=94=B9=E8=BF=9B?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E5=A4=84=E7=90=86=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加AgentService.setSupportedLanguages方法用于设置语音识别语言 改进BleManager和SettingsController中的语言处理逻辑,支持从存储读取用户设置 添加语言映射功能,通过LanguageManager处理语言代码转换 --- lib/data/services/ble_manager.dart | 53 ++++++++++----- .../controllers/settings_controller.dart | 65 +++++++++++++++++++ .../agent_service/lib/agent_service.dart | 15 +++++ 3 files changed, 116 insertions(+), 17 deletions(-) diff --git a/lib/data/services/ble_manager.dart b/lib/data/services/ble_manager.dart index ae0bf3ea5..f181dcc77 100644 --- a/lib/data/services/ble_manager.dart +++ b/lib/data/services/ble_manager.dart @@ -9,6 +9,7 @@ import 'package:get_storage/get_storage.dart'; import 'dart:convert'; import 'package:agent_service/agent_service.dart'; import 'bluetooth_manager.dart'; +import 'language_manager.dart'; // 重新导出BLE连接状态常量,让外部可以通过BleManager访问 export 'package:ble_service/ble_service.dart' show BleConnectionState; @@ -556,26 +557,44 @@ class BleManager extends GetxService { // 根据APP语言动态配置语音识别语言 List supportedLanguages = []; try { - // 使用 GetX 获取 APP 当前语言环境 (Locale) - final appLocale = Get.locale; - - if (appLocale != null) { - final lang = appLocale.languageCode; // e.g., 'en' - final country = appLocale.countryCode; // e.g., 'US' - - // 构造 Azure SDK 需要的格式 (e.g., 'en-US') - if (country != null && country.isNotEmpty) { - supportedLanguages.add('$lang-$country'); - } else { - supportedLanguages.add(lang); // fallback only language code + final storage = GetStorage(); + final languageManager = Get.find(); + + // 从存储中读取用户设置的语言 + String? savedLanguage = storage.read('language'); + + if (savedLanguage != null && savedLanguage != 'system') { + // 用户设置了特定语言 + final parts = savedLanguage.split('_'); + if (parts.length == 2) { + final asrCode = languageManager.getAsrCodeByShortCode(parts[0]); + if (asrCode != null) { + supportedLanguages.add(asrCode); + } else { + // 直接使用语言-国家格式 + supportedLanguages.add('${parts[0]}-${parts[1]}'); + } + } + } else { + // 跟随系统语言或未设置 + final appLocale = Get.locale; + if (appLocale != null) { + final lang = appLocale.languageCode; + final country = appLocale.countryCode; + + // 尝试从语言管理器获取对应的ASR代码 + final asrCode = languageManager.getAsrCodeByShortCode(lang); + if (asrCode != null) { + supportedLanguages.add(asrCode); + } else if (country != null && country.isNotEmpty) { + // 构造 Azure SDK 需要的格式 (e.g., 'en-US') + supportedLanguages.add('$lang-$country'); + } else { + supportedLanguages.add('en-US'); // 默认英语 + } } } - // 添加 en-US 作为备选 - // if (!supportedLanguages.contains('en-US')) { - // supportedLanguages.add('en-US'); - // } - // 最终兜底 if (supportedLanguages.isEmpty) { supportedLanguages.add('en-US'); diff --git a/lib/modules/settings/controllers/settings_controller.dart b/lib/modules/settings/controllers/settings_controller.dart index 0f84a5059..4553f3d1d 100644 --- a/lib/modules/settings/controllers/settings_controller.dart +++ b/lib/modules/settings/controllers/settings_controller.dart @@ -9,6 +9,7 @@ import '../../../routes/app_routes.dart'; import '../models/language_model.dart'; import 'package:logger/logger.dart'; import '../../../data/services/ble_manager.dart'; +import '../../../data/services/language_manager.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:permission_handler/permission_handler.dart'; @@ -393,6 +394,10 @@ class SettingsController extends GetxController { print("languageList: ${data.value}"); language.value = data; _storage.write('language', data.value); + + // 更新agent服务的语音识别语言设置 + await _updateAgentLanguageSettings(data); + // 延迟一下确保语言更新完成,然后重新进入应用 await Future.delayed(const Duration(milliseconds: 500)); @@ -401,6 +406,66 @@ class SettingsController extends GetxController { Get.back(); } + /// 更新agent服务的语音识别语言设置 + Future _updateAgentLanguageSettings(LanguageModel languageModel) async { + try { + final languageManager = Get.find(); + List supportedLanguages = []; + + // 根据选择的语言设置对应的ASR语言代码 + if (languageModel.value == 'system') { + // 跟随系统语言 + final deviceLocale = Get.deviceLocale; + if (deviceLocale != null) { + final lang = deviceLocale.languageCode; + final country = deviceLocale.countryCode; + if (country != null && country.isNotEmpty) { + supportedLanguages.add('$lang-$country'); + } else { + // 根据语言代码映射到对应的ASR代码 + final asrCode = languageManager.getAsrCodeByShortCode(lang); + if (asrCode != null) { + supportedLanguages.add(asrCode); + } else { + supportedLanguages.add('en-US'); // 默认英语 + } + } + } else { + supportedLanguages.add('en-US'); // 默认英语 + } + } else { + // 根据选择的语言值映射到ASR代码 + final parts = languageModel.value.split('_'); + if (parts.length == 2) { + final asrCode = languageManager.getAsrCodeByShortCode(parts[0]); + if (asrCode != null) { + supportedLanguages.add(asrCode); + } else { + // 直接使用语言-国家格式 + supportedLanguages.add('${parts[0]}-${parts[1]}'); + } + } + } + + // 确保至少有一个语言 + if (supportedLanguages.isEmpty) { + supportedLanguages.add('en-US'); + } + + Logger().i('更新agent服务语音识别语言: ${supportedLanguages.join(', ')}'); + + // 调用agent服务更新语言设置 + final success = await AgentService.setSupportedLanguages(supportedLanguages); + if (success) { + Logger().i('agent服务语言设置更新成功'); + } else { + Logger().w('agent服务语言设置更新失败'); + } + } catch (e) { + Logger().e('更新agent服务语言设置时出错: $e'); + } + } + // 加载当前语音服务类型 void _loadCurrentSpeechServiceType() { try { diff --git a/local_plugins/agent_service/lib/agent_service.dart b/local_plugins/agent_service/lib/agent_service.dart index 5871bd8fe..e3e2eb1c4 100644 --- a/local_plugins/agent_service/lib/agent_service.dart +++ b/local_plugins/agent_service/lib/agent_service.dart @@ -402,6 +402,21 @@ class AgentService { } } + /// 设置支持的语音识别语言 + /// + /// [languages] 支持的语言列表,如 ["zh-CN", "en-US"] + /// 返回是否设置成功 + static Future setSupportedLanguages(List languages) async { + try { + final bool result = await _channel.invokeMethod('setSupportedLanguages', { + 'languages': languages, + }); + return result; + } on PlatformException catch (e) { + throw AgentServiceException(e.code, e.message ?? '设置支持语言失败', e.details); + } + } + /// 释放资源 static Future dispose() async { try { From 84b91cccb7db3639d60ebadc2c95f99bcfa9af21 Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Tue, 5 Aug 2025 19:48:47 +0800 Subject: [PATCH 12/34] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dios=E4=B8=8D=E8=87=AA?= =?UTF-8?q?=E5=8A=A8=E8=AF=86=E5=88=AB=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../azure_speech/Sources/azure_speech/AzureAsrHelper.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift index 3106c1236..92b6b40c0 100644 --- a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift +++ b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift @@ -98,11 +98,11 @@ public class AzureAsrHelper: NSObject { } // 创建语音配置 - speechConfig = try SPXSpeechConfiguration(subscription: subscriptionKey, region: region) + speechConfig = try SPXSpeechConfiguration(subscription: subscriptionKey, region: region) // 针对翻译场景的性能优化 // optimizeForTranslation() if isAutoDetectLanguage { - speechConfig?.setPropertyTo("Continuous", byName: "SpeechServiceConnection_LanguageIdMode") + speechConfig?.setPropertyTo("Continuous", by: SPXPropertyId.speechServiceConnectionLanguageIdMode) // 不在这里设置语言,让SPXAutoDetectSourceLanguageConfiguration处理 os_log("启用语言检测模式,支持语言: %{public}@", log: log, type: .info, supportedLanguages.joined(separator: ",")) } else { From dc13abaeca9513b24f5b82be32b51f6a4a577303 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Wed, 6 Aug 2025 08:29:09 +0800 Subject: [PATCH 13/34] =?UTF-8?q?refactor(ui):=20=E9=87=8D=E6=9E=84?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E6=B0=94=E6=B3=A1=E5=92=8C=E8=BF=9B=E5=BA=A6?= =?UTF-8?q?=E6=8C=89=E9=92=AE=E7=BB=84=E4=BB=B6=E6=A0=B7=E5=BC=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 统一消息气泡的内边距和圆角样式 - 将circular_progress_button移动到widgets目录 - 优化音乐播放列表视图的代码格式 - 移除平台特定的隐私设置条件判断 --- lib/modules/agent/views/message_bubble.dart | 26 ++++------ lib/modules/agent/views/music_card.dart | 2 +- .../circular_progress_button.dart | 0 .../music/views/music_playlist_view.dart | 13 +++-- lib/modules/settings/views/settings_view.dart | 51 +++++++++---------- 5 files changed, 43 insertions(+), 49 deletions(-) rename lib/modules/agent/{views => widgets}/circular_progress_button.dart (100%) diff --git a/lib/modules/agent/views/message_bubble.dart b/lib/modules/agent/views/message_bubble.dart index b8903cc5f..6099dc1f0 100644 --- a/lib/modules/agent/views/message_bubble.dart +++ b/lib/modules/agent/views/message_bubble.dart @@ -46,28 +46,20 @@ class MessageBubble extends GetView { isUser ? CrossAxisAlignment.end : CrossAxisAlignment.start, children: [ Container( - padding: hasImage - ? (message == '[图片]' - ? EdgeInsets.zero - : isUser - ? const EdgeInsets.fromLTRB( - 12, 12, 16, 12) // 用户消息右侧多一点内边距 - : const EdgeInsets.fromLTRB( - 16, 12, 12, 12)) // AI消息左侧多一点内边距 - : isUser - ? const EdgeInsets.fromLTRB( - 12, 12, 16, 12) // 用户消息右侧多一点内边距 - : const EdgeInsets.fromLTRB( - 16, 12, 12, 12), // AI消息左侧多一点内边距 + padding: (hasImage && message == '[图片]') + ? EdgeInsets.zero + : const EdgeInsets.all(12), decoration: BoxDecoration( color: _getBubbleColor(context), - borderRadius: BorderRadius.circular(18).copyWith( + borderRadius: BorderRadius.only( topLeft: isUser ? const Radius.circular(18) : const Radius.circular(4), - topRight: !isUser - ? const Radius.circular(18) - : const Radius.circular(4), + topRight: isUser + ? const Radius.circular(4) + : const Radius.circular(18), + bottomLeft: const Radius.circular(18), + bottomRight: const Radius.circular(18), ), boxShadow: [ BoxShadow( diff --git a/lib/modules/agent/views/music_card.dart b/lib/modules/agent/views/music_card.dart index 4da822412..1639994d8 100644 --- a/lib/modules/agent/views/music_card.dart +++ b/lib/modules/agent/views/music_card.dart @@ -3,7 +3,7 @@ import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:get/get.dart'; import '../../../routes/app_routes.dart'; import '../controllers/agent_controller.dart'; -import 'circular_progress_button.dart'; +import '../widgets/circular_progress_button.dart'; /// 构建音乐项目 Widget buildMusicItem(BuildContext context, int id, String name, String url, diff --git a/lib/modules/agent/views/circular_progress_button.dart b/lib/modules/agent/widgets/circular_progress_button.dart similarity index 100% rename from lib/modules/agent/views/circular_progress_button.dart rename to lib/modules/agent/widgets/circular_progress_button.dart diff --git a/lib/modules/music/views/music_playlist_view.dart b/lib/modules/music/views/music_playlist_view.dart index 29f2c520b..d549252fe 100644 --- a/lib/modules/music/views/music_playlist_view.dart +++ b/lib/modules/music/views/music_playlist_view.dart @@ -3,7 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_screenutil/flutter_screenutil.dart'; import '../../../core/utils/logger.dart'; import '../../agent/controllers/agent_controller.dart'; -import '../../agent/views/circular_progress_button.dart'; +import '../../agent/widgets/circular_progress_button.dart'; class MusicPlaylistView extends StatelessWidget { const MusicPlaylistView({Key? key}) : super(key: key); @@ -115,7 +115,9 @@ class MusicPlaylistView extends StatelessWidget { ); } - final title = currentSong['name'] ?? currentSong['title'] ?? 'unknownSong'.tr; // 未知歌曲 + final title = currentSong['name'] ?? + currentSong['title'] ?? + 'unknownSong'.tr; // 未知歌曲 final artist = currentSong['singer'] ?? currentSong['sgener'] ?? currentSong['artist'] ?? @@ -192,7 +194,8 @@ class MusicPlaylistView extends StatelessWidget { final id = item["id"] ?? 0; final name = item["name"] ?? 'unknownSong'.tr; // 未知歌曲 final url = item["url"] ?? ""; - final singer = item["singer"] ?? item["sgener"] ?? 'unknownArtist'.tr; // 未知歌手 + final singer = + item["singer"] ?? item["sgener"] ?? 'unknownArtist'.tr; // 未知歌手 final image = item["image"] ?? ""; return _buildMusicItem( @@ -628,8 +631,8 @@ class MusicPlaylistView extends StatelessWidget { } // 播放选中的歌曲 - Future _playSelectedSong(AgentController controller, Map song, - List> playlist) async { + Future _playSelectedSong(AgentController controller, + Map song, List> playlist) async { // 防止重复点击导致的状态混乱 if (controller.isLoadingMusic.value) { return; diff --git a/lib/modules/settings/views/settings_view.dart b/lib/modules/settings/views/settings_view.dart index 571c1731c..8c12701a1 100644 --- a/lib/modules/settings/views/settings_view.dart +++ b/lib/modules/settings/views/settings_view.dart @@ -418,32 +418,31 @@ class SettingsView extends GetView { }, isDarkMode: isDarkMode, ), - if (Platform.isAndroid) ...[ - Divider( - height: 1, - color: isDarkMode - ? Colors.white.withOpacity(0.1) - : Colors.grey[200]), + // if (Platform.isAndroid) ...[ + Divider( + height: 1, + color: isDarkMode + ? Colors.white.withOpacity(0.1) + : Colors.grey[200]), - // 隐私设置 - _buildSimpleNavigationSetting( - title: 'privacySettings'.tr, // 隐私设置 - subtitle: 'privacySettingsInfo'.tr, // 管理数据收集和权限 - icon: Icons.shield_outlined, - iconBgColor: isDarkMode - ? Colors.purple[900]!.withOpacity(0.3) - : Colors.purple[100]!, - iconColor: isDarkMode - ? Colors.purple[300]! - : Colors.purple[600]!, - titleColor: isDarkMode ? Colors.white : null, - subtitleColor: isDarkMode ? Colors.white70 : null, - onTap: () { - AppSettings.openAppSettings(); - }, - isDarkMode: isDarkMode, - ) - ], + // 隐私设置 + _buildSimpleNavigationSetting( + title: 'privacySettings'.tr, // 隐私设置 + subtitle: 'privacySettingsInfo'.tr, // 管理数据收集和权限 + icon: Icons.shield_outlined, + iconBgColor: isDarkMode + ? Colors.purple[900]!.withOpacity(0.3) + : Colors.purple[100]!, + iconColor: + isDarkMode ? Colors.purple[300]! : Colors.purple[600]!, + titleColor: isDarkMode ? Colors.white : null, + subtitleColor: isDarkMode ? Colors.white70 : null, + onTap: () { + AppSettings.openAppSettings(); + }, + isDarkMode: isDarkMode, + ), + // ], Divider( height: 1, @@ -599,7 +598,7 @@ class SettingsView extends GetView { ? Colors.white.withOpacity(0.1) : Colors.grey[200]), - // 添加权限管理设置项 + // 权限管理设置项 _buildSimpleNavigationSetting( title: 'permissionManagement'.tr, // 权限管理 subtitle: 'permissionManagementInfo'.tr, // 管理应用权限设置 From aae0eed29b26ec074e65170b89e53e85ad9f3d40 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Wed, 6 Aug 2025 17:18:33 +0800 Subject: [PATCH 14/34] =?UTF-8?q?Revert=20"fix(azure=5Fspeech):=20?= =?UTF-8?q?=E6=81=A2=E5=A4=8D=E9=9F=B3=E9=A2=91=E6=95=B0=E6=8D=AE=E5=9B=9E?= =?UTF-8?q?=E8=B0=83=E5=B9=B6=E6=B3=A8=E9=87=8AASR=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E4=BB=A3=E7=A0=81"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This reverts commit 09b0dc3d232e295367130c06129dd34852e6ddec. --- .../meeting_record_controller.dart | 25 +++++++++---------- .../azure_speech/AzureAsrHelper.kt | 7 +++--- 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/lib/modules/meeting/controllers/meeting_record_controller.dart b/lib/modules/meeting/controllers/meeting_record_controller.dart index 0d27de8b7..8cd767ee0 100644 --- a/lib/modules/meeting/controllers/meeting_record_controller.dart +++ b/lib/modules/meeting/controllers/meeting_record_controller.dart @@ -90,7 +90,7 @@ class MeetingRecordController extends GetxController Future _reinitializeAsrService() async { try { // 重新初始化ASR服务 - // await _asrService.initialize(); + await _asrService.initialize(); Logger.info('ASR服务重新初始化成功'); } catch (e) { @@ -163,7 +163,7 @@ class MeetingRecordController extends GetxController _bleManager.closeCodec(); } _asrService.stopRecord(true); - //_asrService.stopContinuousRecognition(); + _asrService.stopContinuousRecognition(); isRecording.value = false; } @@ -196,12 +196,12 @@ class MeetingRecordController extends GetxController /// Toggles visibility of transcribed text content Future toggleTextDisplay() async { - // final recognitionStream = await _asrService.recognizeCallback(); - // _recognitionSubscription?.cancel(); - // _recognitionSubscription = - // recognitionStream.listen(_handleRecognitionEvent); + final recognitionStream = await _asrService.recognizeCallback(); + _recognitionSubscription?.cancel(); + _recognitionSubscription = + recognitionStream.listen(_handleRecognitionEvent); - //await _asrService.startContinuousRecognition(_audioSourceType); + await _asrService.startContinuousRecognition(_audioSourceType); showTextContent.toggle(); } @@ -242,8 +242,8 @@ class MeetingRecordController extends GetxController /// Starts recording session Future _startRecording() async { // Initialize speech recognition - // final recognitionStream = - // await _asrService.startContinuousRecognition(_audioSourceType); + final recognitionStream = + await _asrService.startContinuousRecognition(_audioSourceType); // Prepare storage directory final appDir = await appDocDir; @@ -284,14 +284,14 @@ class MeetingRecordController extends GetxController /// Pauses current recording session Future _pauseRecording() async { - // await _asrService.stopContinuousRecognition(); + await _asrService.stopContinuousRecognition(); await _asrService.pauseRecord(); pauseTimer(); } /// Resumes paused recording session Future _resumeRecording() async { - //await _asrService.startContinuousRecognition(_audioSourceType); + await _asrService.startContinuousRecognition(_audioSourceType); startTimer(); } @@ -303,7 +303,6 @@ class MeetingRecordController extends GetxController intermediateContent.value = event.text; break; case RecognitionEventType.onAudio: - print("音波数据:${event.audio!.length}"); // 添加新音频数据到波形 _processAudioData(event.audio!); break; @@ -462,7 +461,7 @@ class MeetingRecordController extends GetxController } } - //await _asrService.stopContinuousRecognition(); + await _asrService.stopContinuousRecognition(); _resetToDefaultState(); isRecording.value = false; } diff --git a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt index ff5ba4ca1..02d909d4a 100644 --- a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt +++ b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt @@ -717,7 +717,7 @@ class AzureAsrHelper(private val context: Context) { if (bytesToWrite < data.size) data.copyOf(bytesToWrite) else data try { - continuousCallback?.onAudio(finalData) + //continuousCallback?.onAudio(finalData) pushAudioStream?.write(finalData) recordfile?.saveAudioDataToWav(finalData) } catch (e: Exception) { @@ -922,7 +922,6 @@ class AzureAsrHelper(private val context: Context) { fun onError(error: String) } - /** * 连续识别回调接口 */ @@ -954,9 +953,9 @@ class AzureAsrHelper(private val context: Context) { fun onSessionStopped() /** - * 返回音频数据 + * 返回音频 * - * @param data 音频字节数组 + * @param data 识别的音频 */ fun onAudio(data: ByteArray) From 1c6f5655aa09b3ac7e56399b683a77035607e336 Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Wed, 6 Aug 2025 18:36:40 +0800 Subject: [PATCH 15/34] =?UTF-8?q?=E8=AE=BE=E7=BD=AEai=E8=AF=AD=E8=A8=80?= =?UTF-8?q?=E5=9B=9E=E5=A4=8D=E5=A4=84=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agent_service/AgentService.kt | 8 ++++--- .../agent_service/AgentServiceImpl.swift | 19 ++++++++++++--- .../Sources/chat_api/ChatApiService.swift | 24 +++++++++---------- .../chat_api/Sources/chat_api/MCPClient.swift | 14 +++++------ .../open_ai_service/MCPClient.kt | 2 +- .../ios/Classes/MCPClient.swift | 2 +- .../Sources/open_ai_service/MCPClient.swift | 2 +- 7 files changed, 43 insertions(+), 28 deletions(-) diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt index a1921b405..56c8384a4 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt @@ -81,6 +81,7 @@ object AgentService : CoroutineScope { // 服务端连接 private var serverurl = "" private var usertoken = "" + private var language = "" // 历史聊天消息缓存 private val historyMessages = JSONArray() @@ -197,8 +198,8 @@ object AgentService : CoroutineScope { //获取语音识别的语种 val supportedLanguagesConfig = config["supportedLanguages"] as? List val supportedLanguages = supportedLanguagesConfig?.toTypedArray() - ?: arrayOf("zh-CN", "en-US") // 默认支持多语言 - + ?: arrayOf("zh-CN") // 默认支持多语言 + language = supportedLanguages[0] val asrInitSuccess = initialize( subscriptionKey = config["azureSpeechKey"]?.toString() ?: "", region = config["azureSpeechRegion"]?.toString() ?: "", @@ -825,7 +826,8 @@ object AgentService : CoroutineScope { // 先添加系统提示词 if (systemPrompt.isNotEmpty()) { - val mapdata = UserPortrait.getAll() + val mapdata = UserPortrait.getAll().toMutableMap() + mapdata["language"] = language // Log.d(TAG, "liwei--------- 用户画像: ${mapdata}") val _systemPrompt = Utils.fillTemplate(systemPrompt, mapdata) messagesWithSystemPrompt.put(createSystemMessage(_systemPrompt)) diff --git a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift index b884a0c71..bc90164b2 100644 --- a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift +++ b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift @@ -59,6 +59,7 @@ class AgentServiceImpl: NSObject { internal var isAiStreaming: Bool = false var currsessionId: String = "" + var language: String = "" private var idleTimer: DispatchSourceTimer? private let maxIdleSeconds: TimeInterval = 10 @@ -158,6 +159,9 @@ class AgentServiceImpl: NSObject { if let openaivisionModel = config["openaivisionModel"] as? String { self.visionModel = openaivisionModel } + if let supportedLanguages = config["supportedLanguages"] as? [String] { + self.language = supportedLanguages[0] + } if let customSystemPrompt = config["systemPrompt"] as? String, !customSystemPrompt.isEmpty { self.systemPrompt = customSystemPrompt } else { @@ -208,7 +212,7 @@ class AgentServiceImpl: NSObject { azureTtsHelper?.addListener(self) // 获取支持的语言列表,支持从配置中读取 - let supportedLanguages = config["supportedLanguages"] as? [String] ?? ["zh-CN", "en-US"] + let supportedLanguages = config["supportedLanguages"] as? [String] ?? ["zh-CN"] guard let asrSuccess = azureAsrHelper?.initialize( subscriptionKey: azureSpeechKey, @@ -476,9 +480,16 @@ audioStream.saveAudioDataTo(data: audioData) var messages: [[String: Any]] = [] if !systemPrompt.isEmpty { - - let info: [String: String] = UserPortrait.shared.getAll() + var info = UserPortrait.shared.getAll() + info["language"] = self.language + + // 添加调试日志 + print("系统提示词模板: \(systemPrompt)") + print("用户画像数据: \(info)") + let _systemPrompt = fillTemplate(systemPrompt, with: info) + print("替换后的系统提示词: \(_systemPrompt)") + messages.append(chatApiService.createSystemMessage(content: _systemPrompt)) os_log("添加系统提示消息", log: logger, type: .info) } @@ -897,6 +908,8 @@ private func jsonToString(_ json: [String: Any]) -> String? { os_log("语言列表为空,使用默认语言", log: logger, type: .info) return false } + + self.language = languages[0] // 如果正在识别,需要先停止 let wasRecognizing = isRecognizing diff --git a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift index 689117876..267f4ffff 100644 --- a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift +++ b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift @@ -68,18 +68,18 @@ public class ChatApiService: NSObject { /// 设置流式回调 public func setStreamCallback(_ sessionId: String, _ callback: StreamCallback) { - print("[ChatApiService] 设置回调 for sessionId: \(sessionId)") - print("[ChatApiService] 回调对象: \(callback)") + // print("[ChatApiService] 设置回调 for sessionId: \(sessionId)") + // print("[ChatApiService] 回调对象: \(callback)") self.sessionCallbacks[sessionId] = callback print("[ChatApiService] 回调设置完成,当前回调数量: \(sessionCallbacks.count)") } public func getSessionCallback(_ sessionId: String) -> StreamCallback? { let callback = self.sessionCallbacks[sessionId] - print("[ChatApiService] getSessionCallback for sessionId: \(sessionId)") - print("[ChatApiService] sessionCallbacks count: \(sessionCallbacks.count)") - print("[ChatApiService] sessionCallbacks keys: \(Array(sessionCallbacks.keys))") - print("[ChatApiService] callback exists: \(callback != nil)") + // print("[ChatApiService] getSessionCallback for sessionId: \(sessionId)") + // print("[ChatApiService] sessionCallbacks count: \(sessionCallbacks.count)") + // print("[ChatApiService] sessionCallbacks keys: \(Array(sessionCallbacks.keys))") + // print("[ChatApiService] callback exists: \(callback != nil)") return callback } //移除回调函数 @@ -311,7 +311,7 @@ public func sendMessageStream(_ sessionId: String, messages: [[String: Any]]) { tools: chatTools ) - print("[ChatApiService] 发起AI问答 问题:\(messages[0]) 工具数量: \(chatTools.count)") + // print("[ChatApiService] 发起AI问答 问题:\(messages[0]) 工具数量: \(chatTools.count)") } else { query = ChatQuery( messages: chatMessages, @@ -335,14 +335,14 @@ public func sendMessageStream(_ sessionId: String, messages: [[String: Any]]) { // 处理文本内容 if let content = delta.content { replyContext += content - print("[ChatApiService] 收到内容: \(content)") - print("[ChatApiService] 当前sessionId: \(sessionId)") + // print("[ChatApiService] 收到内容: \(content)") + // print("[ChatApiService] 当前sessionId: \(sessionId)") let callback = self.getSessionCallback(sessionId) if let callback = callback { - print("[ChatApiService] 找到回调,准备调用 onToken") + // print("[ChatApiService] 找到回调,准备调用 onToken") callback.onToken(sessionId, content) - print("[ChatApiService] onToken 调用完成") + // print("[ChatApiService] onToken 调用完成") } else { print("[ChatApiService] 警告:未找到 sessionId \(sessionId) 的回调") } @@ -932,7 +932,7 @@ private func convertMapToJsonObject(_ map: [String: Any]) -> [String: Any] { // 创建工具参数 let toolParam = ChatQuery.ChatCompletionToolParam(function: functionDefinition) - print("解析工具 \(name):\(schema)") + // print("解析工具 \(name):\(schema)") chatTools.append(toolParam) } diff --git a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift index b65a7aaa4..7e497bfbb 100644 --- a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift +++ b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift @@ -479,20 +479,20 @@ public class MCPClient { // 添加连接成功后的工具列表汇总输出 if connectedCount > 0 { print("[MCPClient] 🎉 MCP 连接完成,共连接 \(connectedCount) 个服务器") - print("[MCPClient] 📋 所有可用工具汇总:") + // print("[MCPClient] 📋 所有可用工具汇总:") let allTools = getToolMaps() for (index, toolMap) in allTools.enumerated() { if let function = toolMap["function"] as? [String: Any], let name = function["name"] as? String, let description = function["description"] as? String { - print("[MCPClient] \(index + 1). \(name) - \(description)") + // print("[MCPClient] \(index + 1). \(name) - \(description)") } } - print("[MCPClient] 📊 工具总数: \(allTools.count)") - print("[MCPClient] 🔧 本地函数: \(localFunctionDefs.count) 个") - print("[MCPClient] 🌐 远程工具: \(allTools.count - localFunctionDefs.count) 个") + // print("[MCPClient] 📊 工具总数: \(allTools.count)") + // print("[MCPClient] 🔧 本地函数: \(localFunctionDefs.count) 个") + // print("[MCPClient] 🌐 远程工具: \(allTools.count - localFunctionDefs.count) 个") } return connectedCount > 0 @@ -508,7 +508,7 @@ public class MCPClient { parameters: Any, handler: FunctionHandler ) -> Bool { - print("[MCPClient] 注册本地函数: \(name)") + // print("[MCPClient] 注册本地函数: \(name)") localFunctions[name] = handler let functionDef: [String: Any] = [ @@ -544,7 +544,7 @@ public class MCPClient { for client in subClients.values { let tools = client.getToolMaps() - print("[MCPClient] 读取工具列表: \(tools)") + // print("[MCPClient] 读取工具列表: \(tools)") allToolMaps.append(contentsOf: tools) } diff --git a/local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/MCPClient.kt b/local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/MCPClient.kt index b15378c46..370e16a5b 100644 --- a/local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/MCPClient.kt +++ b/local_plugins/open_ai_service/android/src/main/kotlin/com/yunqiinnovation/open_ai_service/MCPClient.kt @@ -768,7 +768,7 @@ class MCPClient(private val context: Context? = null) : AutoCloseable { // 保存函数定义 localFunctionDefs[name] = ObjectMapper().writeValueAsString(functionDef) - Log.d(TAG, "注册本地函数成功: $name") + // Log.d(TAG, "注册本地函数成功: $name") return true } catch (e: Exception) { Log.e(TAG, "注册本地函数失败: ${e.message}", e) diff --git a/local_plugins/open_ai_service/ios/Classes/MCPClient.swift b/local_plugins/open_ai_service/ios/Classes/MCPClient.swift index c865ff079..121653f02 100644 --- a/local_plugins/open_ai_service/ios/Classes/MCPClient.swift +++ b/local_plugins/open_ai_service/ios/Classes/MCPClient.swift @@ -312,7 +312,7 @@ public class MCPClient { let jsonString = String(data: jsonData, encoding: .utf8) ?? "" localFunctionDefs[name] = jsonString - print("\(tag) 成功注册本地函数: \(name)") + // print("\(tag) 成功注册本地函数: \(name)") return true } catch { print("\(tag) 注册本地函数失败: \(error.localizedDescription)") diff --git a/local_plugins/open_ai_service/ios/open_ai_service/Sources/open_ai_service/MCPClient.swift b/local_plugins/open_ai_service/ios/open_ai_service/Sources/open_ai_service/MCPClient.swift index c865ff079..121653f02 100644 --- a/local_plugins/open_ai_service/ios/open_ai_service/Sources/open_ai_service/MCPClient.swift +++ b/local_plugins/open_ai_service/ios/open_ai_service/Sources/open_ai_service/MCPClient.swift @@ -312,7 +312,7 @@ public class MCPClient { let jsonString = String(data: jsonData, encoding: .utf8) ?? "" localFunctionDefs[name] = jsonString - print("\(tag) 成功注册本地函数: \(name)") + // print("\(tag) 成功注册本地函数: \(name)") return true } catch { print("\(tag) 注册本地函数失败: \(error.localizedDescription)") From ad57e972ab62f41ec1a35e7cc11c94f680cf560c Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Wed, 6 Aug 2025 18:38:10 +0800 Subject: [PATCH 16/34] =?UTF-8?q?=E5=A4=84=E7=90=86android=E7=9A=84?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E5=88=87=E6=8D=A2ai=E9=80=82=E9=85=8D?= =?UTF-8?q?=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/com/yunqiinnovation/agent_service/AgentService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt index 56c8384a4..425d54b01 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt @@ -1129,7 +1129,7 @@ object AgentService : CoroutineScope { sendEvent("error", mapOf("code" to "EMPTY_LANGUAGES", "message" to "语言列表不能为空")) return false } - + language = languages[0] return try { Log.i(TAG, "设置支持的语言: ${languages.joinToString(", ")}") From 1bc1f23d334bd38ca21734b5c6c84ebd800e980d Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Wed, 6 Aug 2025 19:20:11 +0800 Subject: [PATCH 17/34] =?UTF-8?q?feat(=E8=AF=AD=E9=9F=B3=E6=9C=8D=E5=8A=A1?= =?UTF-8?q?):=20=E5=9C=A8=E8=AE=BE=E7=BD=AE=E8=AF=AD=E9=9F=B3=E8=AF=86?= =?UTF-8?q?=E5=88=AB=E8=AF=AD=E8=A8=80=E6=97=B6=E5=90=8C=E6=AD=A5=E6=9B=B4?= =?UTF-8?q?=E6=96=B0TTS=E8=AF=AD=E8=A8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加TTS语言同步功能,当设置ASR支持的语言时自动更新TTS语音角色 完善语言映射表,支持多种语言的语音角色配置 --- .../agent_service/AgentService.kt | 122 ++++++++++++- .../agent_service/AgentServiceImpl.swift | 169 ++++++++++++++++-- 2 files changed, 269 insertions(+), 22 deletions(-) diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt index a1921b405..6a72831c8 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt @@ -1113,7 +1113,7 @@ object AgentService : CoroutineScope { } /** - * 设置支持的语音识别语言 + * 设置支持的语音识别语言,同时更新TTS语言设置 */ fun setSupportedLanguages(languages: List): Boolean { if (!isInitialized) { @@ -1132,28 +1132,35 @@ object AgentService : CoroutineScope { Log.i(TAG, "设置支持的语言: ${languages.joinToString(", ")}") // 重新初始化ASR服务以应用新的语言设置 - val success = azureAsrHelper?.initialize( + val asrSuccess = azureAsrHelper?.initialize( subscriptionKey = this.azureSpeechKey, region = this.azureSpeechRegion, supportedLanguages = languages.toTypedArray(), audioSourceType = audioSourceType ) ?: false - if (success) { - Log.i(TAG, "语言设置更新成功") + // 同时更新TTS语言设置 + val ttsSuccess = updateTtsLanguage(languages.first()) // 使用第一个语言作为TTS语言 + + val overallSuccess = asrSuccess && ttsSuccess + + if (overallSuccess) { + Log.i(TAG, "ASR和TTS语言设置更新成功") sendEvent("languages_updated", mapOf( "languages" to languages, - "status" to "success" + "status" to "success", + "asr_success" to asrSuccess, + "tts_success" to ttsSuccess )) } else { - Log.e(TAG, "语言设置更新失败") + Log.e(TAG, "语言设置更新失败 - ASR: $asrSuccess, TTS: $ttsSuccess") sendEvent("error", mapOf( "code" to "LANGUAGE_UPDATE_FAILED", - "message" to "语言设置更新失败" + "message" to "语言设置更新失败 - ASR: $asrSuccess, TTS: $ttsSuccess" )) } - success + overallSuccess } catch (e: Exception) { Log.e(TAG, "设置支持语言异常: ${e.message}") sendEvent("error", mapOf( @@ -1164,6 +1171,105 @@ object AgentService : CoroutineScope { } } + /** + * 更新TTS语言设置 + * + * @param languageCode ASR语言代码,如 "zh-CN", "en-US" + * @return 是否更新成功 + */ + private fun updateTtsLanguage(languageCode: String): Boolean { + return try { + Log.i(TAG, "更新TTS语言设置: $languageCode") + + // 根据ASR语言代码映射到对应的TTS语音角色 + val voiceName = getTtsVoiceNameByAsrCode(languageCode) + + if (voiceName.isNotEmpty()) { + // 设置TTS语音角色 + val success = ttsService?.setVoice(voiceName) ?: false + + if (success) { + Log.i(TAG, "TTS语音角色设置成功: $voiceName") + } else { + Log.w(TAG, "TTS语音角色设置失败: $voiceName") + } + + success + } else { + Log.w(TAG, "未找到语言代码 $languageCode 对应的TTS语音角色") + false + } + } catch (e: Exception) { + Log.e(TAG, "更新TTS语言设置异常: ${e.message}") + false + } + } + + /** + * 根据ASR语言代码获取对应的TTS语音角色名称 + * 与LanguageManager中的映射保持一致 + * + * @param asrCode ASR语言代码,如 "zh-CN", "en-US" + * @return TTS语音角色名称 + */ + private fun getTtsVoiceNameByAsrCode(asrCode: String): String { + return when (asrCode) { + // 中文相关 + "zh-CN" -> "zh-CN-XiaoxiaoNeural" + "zh-TW" -> "zh-TW-HsiaoChenNeural" + "zh-HK" -> "zh-HK-HiuMaanNeural" + + // 英语相关 + "en-US" -> "en-US-AriaNeural" + + // 亚洲语言 + "ja-JP" -> "ja-JP-NanamiNeural" + "ko-KR" -> "ko-KR-SunHiNeural" + "th-TH" -> "th-TH-PremwadeeNeural" + "vi-VN" -> "vi-VN-HoaiMyNeural" + "id-ID" -> "id-ID-GadisNeural" + "hi-IN" -> "hi-IN-SwaraNeural" + "ms-MY" -> "ms-MY-YasminNeural" + "bn-IN" -> "bn-IN-TanishaaNeural" + "ta-IN" -> "ta-IN-PallaviNeural" + "te-IN" -> "te-IN-ShrutiNeural" + "mr-IN" -> "mr-IN-AarohiNeural" + "ur-IN" -> "ur-IN-GulNeural" + "tr-TR" -> "tr-TR-EmelNeural" + "fa-IR" -> "fa-IR-DilaraNeural" + + // 欧洲语言 + "fr-FR" -> "fr-FR-DeniseNeural" + "es-ES" -> "es-ES-ElviraNeural" + "pt-BR" -> "pt-BR-FranciscaNeural" + "it-IT" -> "it-IT-ElsaNeural" + "de-DE" -> "de-DE-KatjaNeural" + "ru-RU" -> "ru-RU-SvetlanaNeural" + "pl-PL" -> "pl-PL-AgnieszkaNeural" + "nl-NL" -> "nl-NL-ColetteNeural" + "sv-SE" -> "sv-SE-SofieNeural" + "cs-CZ" -> "cs-CZ-VlastaNeural" + "el-GR" -> "el-GR-AthinaNeural" + "ro-RO" -> "ro-RO-AlinaNeural" + "hu-HU" -> "hu-HU-NoemiNeural" + "uk-UA" -> "uk-UA-PolinaNeural" + "da-DK" -> "da-DK-ChristelNeural" + "fi-FI" -> "fi-FI-NooraNeural" + "nb-NO" -> "nb-NO-IselinNeural" + "hr-HR" -> "hr-HR-GabrijelaNeural" + "ca-ES" -> "ca-ES-JoanaNeural" + + // 阿拉伯语和非洲语言 + "ar-EG" -> "ar-EG-SalmaNeural" + "sw-KE" -> "sw-KE-ZuriNeural" + + else -> { + Log.w(TAG, "未支持的语言代码: $asrCode,使用默认英语语音") + "en-US-AriaNeural" + } + } + } + /** * 保存聊天消息 */ diff --git a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift index b884a0c71..5362f880f 100644 --- a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift +++ b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift @@ -879,53 +879,194 @@ private func jsonToString(_ json: [String: Any]) -> String? { } /** - * 设置支持的语音识别语言 - * + * 设置支持的语音识别语言,同时更新TTS语言设置 + * * @param languages 支持的语言列表,如 ["zh-CN", "en-US"] * @return 是否设置成功 */ func setSupportedLanguages(_ languages: [String]) -> Bool { os_log("设置支持的语言: %{public}@", log: logger, type: .info, languages.joined(separator: ", ")) - + if !isInitialized { os_log("服务未初始化,无法设置语言", log: logger, type: .error) sendError("服务未初始化", code: "NOT_INITIALIZED") return false } - + if languages.isEmpty { os_log("语言列表为空,使用默认语言", log: logger, type: .info) return false } - + // 如果正在识别,需要先停止 let wasRecognizing = isRecognizing if wasRecognizing { _ = stopRecognition() } - + // 重新初始化ASR引擎,使用新的语言设置 - guard let success = azureAsrHelper?.initialize( + guard let asrSuccess = azureAsrHelper?.initialize( subscriptionKey: azureSpeechKey, region: azureSpeechRegion, supportedLanguages: languages, audioSourceType: .microphone - ), success else { + ), asrSuccess else { os_log("重新初始化ASR引擎失败", log: logger, type: .error) sendError("重新初始化ASR引擎失败", code: "ASR_INIT_ERROR") return false } - - os_log("成功设置支持的语言: %{public}@", log: logger, type: .info, languages.joined(separator: ", ")) - + + // 同时更新TTS语言设置 + let ttsSuccess = updateTtsLanguage(languages.first ?? "zh-CN") + + let overallSuccess = asrSuccess && ttsSuccess + + if overallSuccess { + os_log("ASR和TTS语言设置更新成功: %{public}@", log: logger, type: .info, languages.joined(separator: ", ")) + } else { + os_log("语言设置更新失败 - ASR: %{public}@, TTS: %{public}@", log: logger, type: .error, String(asrSuccess), String(ttsSuccess)) + } + // 如果之前正在识别,重新开始 if wasRecognizing { _ = startRecognition() } - - return true + + return overallSuccess } - + + /** + * 更新TTS语言设置 + * + * @param languageCode ASR语言代码,如 "zh-CN", "en-US" + * @return 是否更新成功 + */ + private func updateTtsLanguage(_ languageCode: String) -> Bool { + os_log("更新TTS语言设置: %{public}@", log: logger, type: .info, languageCode) + + // 根据ASR语言代码映射到对应的TTS语音角色 + let voiceName = getTtsVoiceNameByAsrCode(languageCode) + + if !voiceName.isEmpty { + // 设置TTS语音角色 + let success = azureTtsHelper?.setVoice(voiceName) ?? false + + if success { + os_log("TTS语音角色设置成功: %{public}@", log: logger, type: .info, voiceName) + } else { + os_log("TTS语音角色设置失败: %{public}@", log: logger, type: .error, voiceName) + } + + return success + } else { + os_log("未找到语言代码 %{public}@ 对应的TTS语音角色", log: logger, type: .error, languageCode) + return false + } + } + + /** + * 根据ASR语言代码获取对应的TTS语音角色名称 + * 与LanguageManager中的映射保持一致 + * + * @param asrCode ASR语言代码,如 "zh-CN", "en-US" + * @return TTS语音角色名称 + */ + private func getTtsVoiceNameByAsrCode(_ asrCode: String) -> String { + switch asrCode { + // 中文相关 + case "zh-CN": + return "zh-CN-XiaoxiaoNeural" + case "zh-TW": + return "zh-TW-HsiaoChenNeural" + case "zh-HK": + return "zh-HK-HiuMaanNeural" + + // 英语相关 + case "en-US": + return "en-US-AriaNeural" + + // 亚洲语言 + case "ja-JP": + return "ja-JP-NanamiNeural" + case "ko-KR": + return "ko-KR-SunHiNeural" + case "th-TH": + return "th-TH-PremwadeeNeural" + case "vi-VN": + return "vi-VN-HoaiMyNeural" + case "id-ID": + return "id-ID-GadisNeural" + case "hi-IN": + return "hi-IN-SwaraNeural" + case "ms-MY": + return "ms-MY-YasminNeural" + case "bn-IN": + return "bn-IN-TanishaaNeural" + case "ta-IN": + return "ta-IN-PallaviNeural" + case "te-IN": + return "te-IN-ShrutiNeural" + case "mr-IN": + return "mr-IN-AarohiNeural" + case "ur-IN": + return "ur-IN-GulNeural" + case "tr-TR": + return "tr-TR-EmelNeural" + case "fa-IR": + return "fa-IR-DilaraNeural" + + // 欧洲语言 + case "fr-FR": + return "fr-FR-DeniseNeural" + case "es-ES": + return "es-ES-ElviraNeural" + case "pt-BR": + return "pt-BR-FranciscaNeural" + case "it-IT": + return "it-IT-ElsaNeural" + case "de-DE": + return "de-DE-KatjaNeural" + case "ru-RU": + return "ru-RU-SvetlanaNeural" + case "pl-PL": + return "pl-PL-AgnieszkaNeural" + case "nl-NL": + return "nl-NL-ColetteNeural" + case "sv-SE": + return "sv-SE-SofieNeural" + case "cs-CZ": + return "cs-CZ-VlastaNeural" + case "el-GR": + return "el-GR-AthinaNeural" + case "ro-RO": + return "ro-RO-AlinaNeural" + case "hu-HU": + return "hu-HU-NoemiNeural" + case "uk-UA": + return "uk-UA-PolinaNeural" + case "da-DK": + return "da-DK-ChristelNeural" + case "fi-FI": + return "fi-FI-NooraNeural" + case "nb-NO": + return "nb-NO-IselinNeural" + case "hr-HR": + return "hr-HR-GabrijelaNeural" + case "ca-ES": + return "ca-ES-JoanaNeural" + + // 阿拉伯语和非洲语言 + case "ar-EG": + return "ar-EG-SalmaNeural" + case "sw-KE": + return "sw-KE-ZuriNeural" + + default: + os_log("未支持的语言代码: %{public}@,使用默认英语语音", log: logger, type: .error, asrCode) + return "en-US-AriaNeural" + } + } + func dispose() -> Bool { print("ai释放资源") if isRecognizing { From d33330eda0c46372a233e07e4144ff816bbe3841 Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Wed, 6 Aug 2025 20:18:53 +0800 Subject: [PATCH 18/34] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dai=E5=A3=B0=E9=9F=B3?= =?UTF-8?q?=E6=97=B6=E5=A4=A7=E6=97=B6=E5=B0=8F=E7=9A=84=E9=97=AE=E9=A2=98?= =?UTF-8?q?=EF=BC=8C=E5=8A=A0=E5=85=A5=E8=AF=86=E5=88=AB=E8=B6=85=E6=97=B6?= =?UTF-8?q?=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agent/controllers/agent_controller.dart | 20 ++++++++--- .../controllers/translation_controller.dart | 9 ++++- .../agent_service/AgentService.kt | 10 +++--- .../agent_service/AgentServiceImpl.swift | 22 +++++++++--- .../agent_service/AgentServicePlugin.swift | 9 ++++- .../agent_service/lib/agent_service.dart | 29 +++++++++++++-- .../azure_speech/AzureAsrHelper.kt | 2 +- .../Sources/azure_speech/AzureAsrHelper.swift | 36 ++++++++++++------- .../azure_speech/AzureSpeechPlugin.swift | 15 ++++++-- 9 files changed, 117 insertions(+), 35 deletions(-) diff --git a/lib/modules/agent/controllers/agent_controller.dart b/lib/modules/agent/controllers/agent_controller.dart index 5330abd4c..8debed827 100644 --- a/lib/modules/agent/controllers/agent_controller.dart +++ b/lib/modules/agent/controllers/agent_controller.dart @@ -3,6 +3,8 @@ import 'dart:convert'; import 'dart:ffi'; import 'dart:io'; import 'package:amap_service/amap_service.dart'; +import '../../../data/services/ble_manager.dart'; +import '../../../data/services/bluetooth_manager.dart'; import '../../../data/services/novice_guide_service.dart'; import '/core/utils/upload_oss.dart'; import '/data/models/user_Info.dart'; @@ -41,7 +43,10 @@ class AgentController extends GetxController { final ScrollController scrollController = ScrollController(); final noviceGuideService = Get.find(); - + // 确保服务已初始化 + // ========== 依赖服务 ========== + final BluetoothManager _bluetoothManager = Get.find(); + final BleManager _bleManager = Get.find(); //音乐播放管理 final _musiceManager = Get.find(); @@ -558,16 +563,16 @@ class AgentController extends GetxController { break; case AgentServiceEventType.ttsStarted: - // isSpeaking.value = true; - // Logger.i(TAG, 'TTS开始播放'); + isSpeaking.value = true; + print('TTS开始播放'); break; case AgentServiceEventType.ttsCompleted: case AgentServiceEventType.ttsStopped: case AgentServiceEventType.ttsCanceled: - // isSpeaking.value = false; + isSpeaking.value = false; - // Logger.i(TAG, 'TTS停止播放, $event.type'); + Logger.i(TAG, 'TTS停止播放, $event.type'); break; case AgentServiceEventType.playbackStarted: @@ -1035,6 +1040,11 @@ class AgentController extends GetxController { try { await AgentService.recognizeCallback(); + if (_bluetoothManager.currentDeviceRx.value != null) { + await AgentService.restoreOriginalAudioState(); + } else { + await AgentService.disableBluetoothAudio(); + } } catch (e) { Logger.e(TAG, '启动识别器失败: $e'); } diff --git a/lib/modules/translation/controllers/translation_controller.dart b/lib/modules/translation/controllers/translation_controller.dart index 94cf4b25c..ae649a20e 100644 --- a/lib/modules/translation/controllers/translation_controller.dart +++ b/lib/modules/translation/controllers/translation_controller.dart @@ -9,6 +9,7 @@ import 'package:get_storage/get_storage.dart'; import 'package:intl/intl.dart'; import 'package:path_provider/path_provider.dart'; import 'package:permission_handler/permission_handler.dart'; +import '../../../data/services/bluetooth_manager.dart'; import '../../../data/services/music_manager.dart'; import '../../../data/services/volcano_translation_service.dart'; import '../../../data/services/tts_service.dart'; @@ -30,6 +31,8 @@ class TranslationController extends GetxController { static const String TAG = 'TranslationController'; // 服务 + final BluetoothManager _bluetoothManager = Get.find(); + final BleManager _bleManager = Get.find(); final AsrService _asrService = Get.find(); final VolcanoTranslationService _translationService = Get.find(); @@ -483,7 +486,11 @@ class TranslationController extends GetxController { if (currentMode.value == 'faceToFace') { _asrService.stopContinuousRecognition(); } else { - await _ttsService.setAudioOutputDevice(0); + if (_bluetoothManager.currentDeviceRx.value != null) { + restoreOriginalAudioState(); + } else { + disableBluetoothAudio(); + } await _asrService.startContinuousRecognition(_audioSourceType); // 开始ASR活跃时长计时 _startAsrActiveTracking(); diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt index 76f1fc5dc..cd5762b9d 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt @@ -420,7 +420,7 @@ object AgentService : CoroutineScope { try { delay(maxIdleSeconds * 1000L) - if (isActive && isRecognitionActive && !hasSpeechDetected && !isTtsSpeaking && !isAiStreaming) { + if (isActive && isRecognitionActive && !isTtsSpeaking && !isAiStreaming) { stopRecognition() sendEvent( "auto_stop", @@ -522,12 +522,12 @@ object AgentService : CoroutineScope { override fun onRecognizing(recognizing: String, detectedLanguage: String) { if (recognizing.isNotEmpty()) { // 检测到语音,更新状态 - val previousHasSpeech = hasSpeechDetected - _hasSpeechDetected.set(true) + val previousHasSpeech = hasSpeechDetected + _hasSpeechDetected.set(true) if (!previousHasSpeech) { - restartIdleCheck() - } + restartIdleCheck() + } sendEvent( "recognizing", mapOf( diff --git a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift index e0e0ece65..65fe56f08 100644 --- a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift +++ b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift @@ -291,8 +291,8 @@ class AgentServiceImpl: NSObject { // 使用更安全的方式检查self是否存在 DispatchQueue.main.async { [weak self] in guard let self = self else { return } - - if self.isRecognizing && !self.hasSpeechDetected && !self.isSpeaking && !self.isAiStreaming { + + if self.isRecognizing && !self.isSpeaking && !self.isAiStreaming { self.stopRecognition() self.sendEvent(name: "auto_stop", data: [ "reason": "idle_timeout", @@ -349,8 +349,14 @@ print("ai启动语音=\(audioSourceType)=\(useBle)") return true } - func recognizeCallback(useBle: Bool = false) -> Bool { - + func recognizeCallback() -> Bool { + print("重新设置识别回调=") + guard let asrsetupSuccess = azureAsrHelper?.setupEventListeners( + callback: self + ), asrsetupSuccess else { + sendError("初始化语音识别服务失败", code: "ASR_INIT_ERROR") + return false + } return true } @@ -365,7 +371,12 @@ print("ai启动语音=\(audioSourceType)=\(useBle)") return true } - + func disableBluetoothAudio() { + azureAsrHelper?.disableBluetoothAudio() + } + func restoreOriginalAudioState() { + azureAsrHelper?.restoreOriginalAudioState() + } func pushAudioData(_ audioData: Data) -> Bool { if !isRecognizing || !isInitialized { return false @@ -1572,6 +1583,7 @@ extension AgentServiceImpl: AzureAsrHelper.ContinuousRecognizeCallback { hasSpeechDetected = true if !previousHasSpeech { + print("onRecognizing restartIdleCheck") restartIdleCheck() } diff --git a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServicePlugin.swift b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServicePlugin.swift index 0c0ef5703..fb8baa1f8 100644 --- a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServicePlugin.swift +++ b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServicePlugin.swift @@ -51,7 +51,14 @@ public class AgentServicePlugin: NSObject, FlutterPlugin { case "stopConversation": impl.stopRecognition() result(true) - + + case "disableBluetoothAudio": + impl.disableBluetoothAudio(); + return + + case "restoreOriginalAudioState": + impl.restoreOriginalAudioState(); + return case "processTextInput": guard let arguments = call.arguments as? [String: Any], let text = arguments["text"] as? String else { diff --git a/local_plugins/agent_service/lib/agent_service.dart b/local_plugins/agent_service/lib/agent_service.dart index e3e2eb1c4..f8294ef2a 100644 --- a/local_plugins/agent_service/lib/agent_service.dart +++ b/local_plugins/agent_service/lib/agent_service.dart @@ -261,6 +261,33 @@ class AgentService { } } + /// 恢复原始音频状态 + /// + /// 恢复原始音频状态 + /// 返回是否成功恢复原始音频状态 + static Future restoreOriginalAudioState() async { + try { + final bool result = + await _channel.invokeMethod('restoreOriginalAudioState'); + return result; + } on PlatformException catch (e) { + throw AgentServiceException(e.code, e.message ?? '恢复原始音频状态失败', e.details); + } + } + + /// 禁用蓝牙音频 + /// + /// 禁用蓝牙音频 + /// 返回是否成功禁用蓝牙音频 + static Future disableBluetoothAudio() async { + try { + final bool result = await _channel.invokeMethod('disableBluetoothAudio'); + return result; + } on PlatformException catch (e) { + throw AgentServiceException(e.code, e.message ?? '禁用蓝牙音频失败', e.details); + } + } + /// 停止对话 /// /// 停止语音识别 @@ -332,8 +359,6 @@ class AgentService { } } - - /// 停止语音合成 static Future stopAiSteam() async { try { diff --git a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt index 02d909d4a..0d00c776b 100644 --- a/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt +++ b/local_plugins/azure_speech/android/src/main/kotlin/com/yunqiinnovation/azure_speech/AzureAsrHelper.kt @@ -745,7 +745,7 @@ class AzureAsrHelper(private val context: Context) { */ fun saveAudioDataTo(buffer: ByteArray) { - if (audioSourceType == AudioSourceType.MICROPHONE) return + if (audioSourceType == AudioSourceType.MICROPHONE||!isContinuousRecognitionActive) return // 放入队列,由写线程写入 writeQueue.offer(buffer.copyOf()) diff --git a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift index 92b6b40c0..40846707f 100644 --- a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift +++ b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureAsrHelper.swift @@ -131,7 +131,7 @@ public class AzureAsrHelper: NSObject { */ private func preInitializeAudioComponents() { if audioStream == nil { - audioStream = AudioStream() + audioStream = AudioStream(parentHelper: self) audioStream?.initAudioRecord() audioStream?.onAudioData = { [weak self] data in self?.recordfile?.saveAudioDataToWav(data) @@ -354,7 +354,7 @@ public class AzureAsrHelper: NSObject { do { // 【优化】检查是否已经初始化,避免重复创建 if audioStream == nil { - audioStream = AudioStream() + audioStream = AudioStream(parentHelper: self) audioStream?.initAudioRecord() audioStream?.onAudioData = { [weak self] data in self?.recordfile?.saveAudioDataToWav(data) @@ -548,6 +548,7 @@ public class AzureAsrHelper: NSObject { public func disableBluetoothAudio() { do { + print("禁用蓝牙音频功能,切换回正常音频模式") audioStream?.setAudioOutputRoute(.speaker) } catch { print("disableBluetoothAudio") @@ -559,6 +560,7 @@ public class AzureAsrHelper: NSObject { public func restoreOriginalAudioState() { do { + print("恢复原始音频设备状态(通常是重新启用蓝牙)") audioStream?.setAudioOutputRoute(.receiver) } catch { print("restoreOriginalAudioState") @@ -632,9 +634,18 @@ public class AzureAsrHelper: NSObject { private var isRunning = false public var isWriting = false private let bufferSize: Int = 4096 - private var writeThread: DispatchQueue? - private var currentRoute: AudioOutputRoute? - public var onAudioData: ((Data) -> Void)? + private var writeThread: DispatchQueue? + private var currentRoute: AudioOutputRoute? + public var onAudioData: ((Data) -> Void)? + + // 添加对外部类的弱引用 + private weak var parentHelper: AzureAsrHelper? + + // 添加初始化方法,接收外部类引用 + init(parentHelper: AzureAsrHelper) { + self.parentHelper = parentHelper + super.init() + } /** * 初始化 */ @@ -654,7 +665,7 @@ public class AzureAsrHelper: NSObject { // 创建新的写线程 writeThread = DispatchQueue(label: "audio.stream.writer") startWriteThread() - self.setAudioOutputRoute(.receiver) + //self.setAudioOutputRoute(.receiver) } /// 获取最佳音频格式 (iOS 通常支持标准采样率) @@ -717,7 +728,8 @@ public class AzureAsrHelper: NSObject { * @param data 音频数据字节数组 */ public func saveAudioDataTo(data: Data) { - if audioSourceType != .external { + // 通过父类引用调用方法 + if audioSourceType != .external || !(parentHelper?.isContinuousRecognitionActive() ?? false) { return } // print("外部data=\(data)") @@ -879,8 +891,7 @@ private func runMicrophoneCapture() { case bluetooth } public func setAudioOutputRoute(_ route: AudioOutputRoute) { - guard currentRoute != route else { return } - print("setAudioOutputRoute") + do { // if self.isWriting { @@ -890,14 +901,15 @@ private func runMicrophoneCapture() { try audioSession.setActive(false) } - + print("setAudioOutputRoutecurrent,route=\(route)") switch route { case .speaker: + print("setAudioOutputRoutecurrent,speaker") // 使用扬声器时必须用videoChat模式 try audioSession.setCategory( .playAndRecord, mode: .videoChat, - options: [.allowBluetooth, .mixWithOthers, .defaultToSpeaker] + options: [.defaultToSpeaker] ) try audioSession.overrideOutputAudioPort(.speaker) @@ -906,7 +918,7 @@ private func runMicrophoneCapture() { try audioSession.setCategory( .playAndRecord, mode: .voiceChat, - options: [.allowBluetooth, .mixWithOthers, .defaultToSpeaker] + options: [.allowBluetooth] ) try audioSession.overrideOutputAudioPort(.none) diff --git a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift index f07e56855..84c96f361 100644 --- a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift +++ b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift @@ -184,6 +184,15 @@ import os.log callback: currentAsrCallback! ) result(success) + + case "disableBluetoothAudio": + azureAsrHelper.disableBluetoothAudio(); + return + + case "restoreOriginalAudioState": + azureAsrHelper.restoreOriginalAudioState(); + return + case "stopContinuousRecognition": let success = azureAsrHelper.stopContinuousRecognition() @@ -224,7 +233,7 @@ import os.log return } do { - print("音频文件名称为: \(filePath)") + print("音频文件名称为: (filePath)") try azureAsrHelper.renameFile(filePath: filePath, newName: newName) result(true) } catch { @@ -239,7 +248,7 @@ import os.log return } do { - print("音频文件名称为: \(sourcePath)") + print("音频文件名称为: (sourcePath)") try azureAsrHelper.moveFile(sourcePath: sourcePath, destPath: destPath) result(true) } catch { @@ -253,7 +262,7 @@ import os.log return } do { - print("音频文件名称为: \(filePath)") + print("音频文件名称为: (filePath)") try azureAsrHelper.enableRecord(filePath: filePath) result(true) } catch { From fcce6427ff18ac8405e85399fd2f1cfb64fd3cc7 Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Wed, 6 Aug 2025 20:59:04 +0800 Subject: [PATCH 19/34] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift index 7e497bfbb..35a9df702 100644 --- a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift +++ b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/MCPClient.swift @@ -486,7 +486,7 @@ public class MCPClient { if let function = toolMap["function"] as? [String: Any], let name = function["name"] as? String, let description = function["description"] as? String { - // print("[MCPClient] \(index + 1). \(name) - \(description)") + print("[MCPClient] \(index + 1). \(name) - \(description)") } } From 29e77ac606a79ca9b617548c3f6d2663757845cd Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Thu, 7 Aug 2025 11:49:40 +0800 Subject: [PATCH 20/34] =?UTF-8?q?=E4=BC=98=E5=8C=96=E5=9D=90=E6=A0=87?= =?UTF-8?q?=E8=AF=BB=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/azure_speech/AzureTtsHelper.swift | 2 +- .../Sources/chat_api/SystemFunctionHandler.swift | 11 ++++------- .../Sources/location_service/LocationData.swift | 4 +--- .../Sources/location_service/LocationService.swift | 12 +++++------- pubspec.yaml | 2 +- 5 files changed, 12 insertions(+), 19 deletions(-) diff --git a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureTtsHelper.swift b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureTtsHelper.swift index 3d6c25e73..86ac66bb9 100644 --- a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureTtsHelper.swift +++ b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureTtsHelper.swift @@ -166,7 +166,7 @@ public class AzureTtsHelper: NSObject, ITtsService { let currentText = streamBuffer // 标点符号列表 (与Android一致) - let punctuationMarks: [Character] = [".", "。", "!", "!", "?", "?", ";", ";", ",", ",", ":", ":", "\n"] + let punctuationMarks: [Character] = [".", "。", "!", "!","*", "?", "?", ";", ";", ",", ",", ":", ":", "\n"] // 从后往前查找最后一个标点符号 var lastPunctuationIndex = -1 diff --git a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift index bcacf4eb5..e55b3c15f 100644 --- a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift +++ b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift @@ -462,8 +462,7 @@ private class GetCurrentLocationHandler: FunctionHandler { guard let latStr = locationInfo["user_lat"] as? String, let lngStr = locationInfo["user_lng"] as? String, let latitude = Double(latStr), - let longitude = Double(lngStr), - let address = locationInfo["user_location"] as? String else { + let longitude = Double(lngStr) else { print("[GetCurrentLocationHandler] 位置数据格式错误") continuation.resume(returning: self.createErrorResponse(message: "位置数据格式错误,请重试")) return @@ -471,8 +470,8 @@ private class GetCurrentLocationHandler: FunctionHandler { print("[GetCurrentLocationHandler] 位置获取成功: lat=\(latitude), lng=\(longitude)") - let message = "当前位置:\(address)(纬度 \(latitude),经度 \(longitude))" - let successResponse = self.createSuccessResponse(message: message, latitude: latitude, longitude: longitude, address: address) + // let message = "当前位置:\(address)(纬度 \(latitude),经度 \(longitude))" + let successResponse = self.createSuccessResponse(latitude: latitude, longitude: longitude) print("[GetCurrentLocationHandler] 返回位置结果") continuation.resume(returning: successResponse) @@ -486,14 +485,12 @@ private class GetCurrentLocationHandler: FunctionHandler { } } - private func createSuccessResponse(message: String, latitude: Double, longitude: Double, address: String) -> String { + private func createSuccessResponse(latitude: Double, longitude: Double) -> String { let result: [String: Any] = [ "success": true, - "message": message, "data": [ "latitude": latitude, "longitude": longitude, - "address": address, "timestamp": Date().timeIntervalSince1970 * 1000 ] ] diff --git a/local_plugins/location_service/ios/location_service/Sources/location_service/LocationData.swift b/local_plugins/location_service/ios/location_service/Sources/location_service/LocationData.swift index af7f9827c..4701388b1 100644 --- a/local_plugins/location_service/ios/location_service/Sources/location_service/LocationData.swift +++ b/local_plugins/location_service/ios/location_service/Sources/location_service/LocationData.swift @@ -4,13 +4,11 @@ import Foundation public struct LocationData: Codable { public let latitude: Double public let longitude: Double - public let address: String public let timestamp: TimeInterval - public init(latitude: Double, longitude: Double, address: String, timestamp: TimeInterval) { + public init(latitude: Double, longitude: Double,timestamp: TimeInterval) { self.latitude = latitude self.longitude = longitude - self.address = address self.timestamp = timestamp } } diff --git a/local_plugins/location_service/ios/location_service/Sources/location_service/LocationService.swift b/local_plugins/location_service/ios/location_service/Sources/location_service/LocationService.swift index 2312b1fc4..bc682d13e 100644 --- a/local_plugins/location_service/ios/location_service/Sources/location_service/LocationService.swift +++ b/local_plugins/location_service/ios/location_service/Sources/location_service/LocationService.swift @@ -190,11 +190,11 @@ public class LocationService: NSObject { } // 使用缓存的状态信息 - print("[LocationService] 开始位置请求") - print("[LocationService] 缓存的权限状态: \(self.authorizationStatus.rawValue)") - print("[LocationService] 缓存的位置服务状态: \(self.isLocationServicesEnabled)") - print("[LocationService] 当前线程: \(Thread.current)") - print("[LocationService] 是否主线程: \(Thread.isMainThread)") + // print("[LocationService] 开始位置请求") + // print("[LocationService] 缓存的权限状态: \(self.authorizationStatus.rawValue)") + // print("[LocationService] 缓存的位置服务状态: \(self.isLocationServicesEnabled)") + // print("[LocationService] 当前线程: \(Thread.current)") + // print("[LocationService] 是否主线程: \(Thread.isMainThread)") // 验证权限状态 if !self.checkLocationPermission() { @@ -238,7 +238,6 @@ public class LocationService: NSObject { let locationData = LocationData( latitude: latitude, longitude: longitude, - address: "", timestamp: Date().timeIntervalSince1970 * 1000 ) completion(.success(locationData)) @@ -273,7 +272,6 @@ public class LocationService: NSObject { return [ "user_lat": String(locationData.latitude), "user_lng": String(locationData.longitude), - "user_location": locationData.address, "timestamp": locationData.timestamp ] } diff --git a/pubspec.yaml b/pubspec.yaml index d59266ff8..ed31e55ad 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,7 +55,7 @@ dependencies: uuid: ^4.3.3 record: ^5.2.1 share_plus: ^11.0.0 - image_picker: ^1.1.2 + image_picker: ^1.1. audio_waveforms: ^1.3.0 font_awesome_flutter: ^10.8.0 lottie: ^3.3.1 From 96d82ee31777aa0778e46360a3faea351ec6d60e Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Thu, 7 Aug 2025 14:24:56 +0800 Subject: [PATCH 21/34] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E7=BB=8F=E7=BA=AC?= =?UTF-8?q?=E5=BA=A6=E6=95=B0=E6=8D=AE=E9=80=82=E9=85=8D=E9=AB=98=E5=BE=B7?= =?UTF-8?q?=E7=94=A8=E7=9A=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/location_service/LocationData.swift | 2 ++ 1 file changed, 2 insertions(+) diff --git a/local_plugins/location_service/ios/location_service/Sources/location_service/LocationData.swift b/local_plugins/location_service/ios/location_service/Sources/location_service/LocationData.swift index 4701388b1..5facd02e0 100644 --- a/local_plugins/location_service/ios/location_service/Sources/location_service/LocationData.swift +++ b/local_plugins/location_service/ios/location_service/Sources/location_service/LocationData.swift @@ -5,10 +5,12 @@ public struct LocationData: Codable { public let latitude: Double public let longitude: Double public let timestamp: TimeInterval + public let latitudeandlongitude: String public init(latitude: Double, longitude: Double,timestamp: TimeInterval) { self.latitude = latitude self.longitude = longitude self.timestamp = timestamp + self.latitudeandlongitude = "\(latitude),\(longitude)" } } From 3339fb84b68c1a7784dde1b07d2844d4ce82afc3 Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Thu, 7 Aug 2025 17:13:51 +0800 Subject: [PATCH 22/34] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E9=80=80=E5=87=BA?= =?UTF-8?q?=E4=B8=8D=E6=89=93=E6=96=AD=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/modules/translation/controllers/translation_controller.dart | 2 +- .../azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift | 2 +- pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/modules/translation/controllers/translation_controller.dart b/lib/modules/translation/controllers/translation_controller.dart index ae649a20e..d3b3c1631 100644 --- a/lib/modules/translation/controllers/translation_controller.dart +++ b/lib/modules/translation/controllers/translation_controller.dart @@ -367,7 +367,7 @@ class TranslationController extends GetxController { @override void onClose() { - stopRecognition(); + stopAll(); _recognitionSubscription?.cancel(); _recognitionSubscription = null; _voiceInteractionSubscription?.cancel(); diff --git a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift index 84c96f361..5955ac7c8 100644 --- a/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift +++ b/local_plugins/azure_speech/ios/azure_speech/Sources/azure_speech/AzureSpeechPlugin.swift @@ -359,7 +359,7 @@ import os.log let success = azureTtsHelper.flushStream() result(success) - case "stop": + case "stopSpeaking": let success = azureTtsHelper.stop() result(success) diff --git a/pubspec.yaml b/pubspec.yaml index ed31e55ad..d59266ff8 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,7 +55,7 @@ dependencies: uuid: ^4.3.3 record: ^5.2.1 share_plus: ^11.0.0 - image_picker: ^1.1. + image_picker: ^1.1.2 audio_waveforms: ^1.3.0 font_awesome_flutter: ^10.8.0 lottie: ^3.3.1 From b157782b7c8bdd7537d8aa6d2b10fc74df464bcf Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Thu, 7 Aug 2025 18:32:20 +0800 Subject: [PATCH 23/34] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E8=8B=B9=E6=9E=9C?= =?UTF-8?q?=E7=9A=84=E5=9B=BE=E7=89=87=E8=AF=86=E5=88=AB=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E8=A1=A5=E5=85=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sources/chat_api/ChatApiService.swift | 66 +++++++++++++++---- 1 file changed, 55 insertions(+), 11 deletions(-) diff --git a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift index 267f4ffff..524feda90 100644 --- a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift +++ b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/ChatApiService.swift @@ -311,7 +311,7 @@ public func sendMessageStream(_ sessionId: String, messages: [[String: Any]]) { tools: chatTools ) - // print("[ChatApiService] 发起AI问答 问题:\(messages[0]) 工具数量: \(chatTools.count)") + print("[ChatApiService] 发起AI问答 问题:\(messages) 工具数量: \(chatTools.count)") } else { query = ChatQuery( messages: chatMessages, @@ -761,7 +761,44 @@ private func convertMapToJsonObject(_ map: [String: Any]) -> [String: Any] { } return false } - + // 解析并构建正确的图片消息 + func parseImageData(_ data: [String: Any]) -> ChatQuery.ChatCompletionMessageParam? { + // 1. 校验基础字段 + guard let type = data["type"] as? String, type == "image_url", + let imageUrlDict = data["image_url"] as? [String: Any], + let imageUrlString = imageUrlDict["url"] as? String else { + print("数据结构错误:缺少type或image_url.url") + return nil + } + + // 2. 补充默认的detail(原结构体必须,默认auto) + let detailString = imageUrlDict["detail"] as? String ?? "auto" + let imageDetail: ChatQuery.ChatCompletionMessageParam.UserMessageParam.Content.VisionContent.ChatCompletionContentPartImageParam.ImageURL.Detail + switch detailString.lowercased() { + case "low": + imageDetail = .low + case "high": + imageDetail = .high + default: + imageDetail = .auto + } + + // 3. 逐层构建结构体(按原结构体要求映射) + let imageUrlModel = ChatQuery.ChatCompletionMessageParam.UserMessageParam.Content.VisionContent.ChatCompletionContentPartImageParam.ImageURL( + url: imageUrlString, + detail: imageDetail + ) + + let imageContentPart = ChatQuery.ChatCompletionMessageParam.UserMessageParam.Content.VisionContent.ChatCompletionContentPartImageParam( + imageUrl: imageUrlModel + ) + + let visionContent = ChatQuery.ChatCompletionMessageParam.UserMessageParam.Content.VisionContent.chatCompletionContentPartImageParam(imageContentPart) + let userContent = ChatQuery.ChatCompletionMessageParam.UserMessageParam.Content.vision([visionContent]) + let userMessageParam = ChatQuery.ChatCompletionMessageParam.UserMessageParam(content: userContent) + + return .user(userMessageParam) + } /// 转换为OpenAI Message格式 private func convertToChatMessages(_ messages: [[String: Any]]) throws -> [ChatQuery.ChatCompletionMessageParam] { var chatMessages: [ChatQuery.ChatCompletionMessageParam] = [] @@ -851,17 +888,24 @@ private func convertMapToJsonObject(_ map: [String: Any]) -> [String: Any] { var textContent = "" for item in contentArray { - if let type = item["type"] as? String, type == "text", - let text = item["text"] as? String { - textContent += text + if let type = item["type"] as? String { + switch type { + case "text": + if let text = item["text"] as? String { + if let chatMessage = ChatQuery.ChatCompletionMessageParam(role: messageRole, content: text) { + chatMessages.append(chatMessage) + } + } + case "image_url": + if let imageMessage = parseImageData(item) { + chatMessages.append(imageMessage) + print("图片消息解析成功") + } + default: + break + } } } - - let finalContent = textContent.isEmpty ? "多媒体内容" : textContent - - if let chatMessage = ChatQuery.ChatCompletionMessageParam(role: messageRole, content: finalContent) { - chatMessages.append(chatMessage) - } } } From 5c5f5909ab5e69fb60c075b7693b7b3e790aa476 Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Thu, 7 Aug 2025 21:33:02 +0800 Subject: [PATCH 24/34] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E8=81=8A=E5=A4=A9?= =?UTF-8?q?=E5=9B=BE=E7=89=87=E6=98=BE=E7=A4=BA=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data/models/message_model.dart | 4 ++ .../agent/controllers/agent_controller.dart | 41 +++++++++++++------ lib/modules/agent/views/message_bubble.dart | 4 +- .../agent_service/AgentService.kt | 4 +- .../agent_service/AgentServiceImpl.swift | 17 +++++--- .../chat_storage/ChatStorageHelper.swift | 6 +-- 6 files changed, 50 insertions(+), 26 deletions(-) diff --git a/lib/data/models/message_model.dart b/lib/data/models/message_model.dart index aef99cbc9..09744a939 100644 --- a/lib/data/models/message_model.dart +++ b/lib/data/models/message_model.dart @@ -1,3 +1,7 @@ +import 'dart:io'; + +import 'package:path_provider/path_provider.dart'; + class Message { final bool isUser; //标识信息是否来自用户还是AI final String text; //信息文本内容 diff --git a/lib/modules/agent/controllers/agent_controller.dart b/lib/modules/agent/controllers/agent_controller.dart index 8debed827..a6a5730f7 100644 --- a/lib/modules/agent/controllers/agent_controller.dart +++ b/lib/modules/agent/controllers/agent_controller.dart @@ -126,7 +126,7 @@ class AgentController extends GetxController { String _currentResponseId = ''; final TAG = 'AgentController'; - + var AppDir = "".obs; // 会话ID final String sessionId = 'default_agent'; @@ -153,6 +153,7 @@ class AgentController extends GetxController { @override void onInit() { super.onInit(); + _initAppDir(); _loadTtsPreference(); //加载TTS偏好设置 _setupScrollListener(); //设置滚动监听器 _loadChatHistory(); //加载聊天历史记录 @@ -164,6 +165,10 @@ class AgentController extends GetxController { startVoiceInput(); //开启语音输入 } + Future _initAppDir() async { + AppDir.value = (await getApplicationDocumentsDirectory()).path; + } + // 初始化音乐播放状态 Future _initMusicStatus() async { try { @@ -291,7 +296,7 @@ class AgentController extends GetxController { page: 1, //从第一页开始 pageSize: pageSize, //每页20条记录 ); - + Logger.i(TAG, '加载聊天历史记录: $result'); if (result.messages.isNotEmpty) { // 转换并添加消息到列表 final historyMessages = _convertChatMessagesToMessages(result.messages); @@ -303,7 +308,8 @@ class AgentController extends GetxController { // 更新分页状态 currentPage.value = 1; hasMoreMessages.value = result.totalPages > 1; - + print( + 'liwei-------------读取历史记录 ${AppDir.value}${historyMessages[0].imagePath}'); Logger.i(TAG, '成功加载 ${historyMessages.length} 条聊天记录,总页数: ${result.totalPages}'); _logMessageDetails(historyMessages); @@ -421,7 +427,7 @@ class AgentController extends GetxController { 'meta: ${msg.meta != null ? "有" : "无"}' '}'); } - Logger.d(TAG, '===== 历史消息详情结束 ====='); + Logger.d(TAG, '===== 历史消息详情结束 ==lishi==='); } // 设置滚动监听器 @@ -903,10 +909,14 @@ class AgentController extends GetxController { } // 复制图片到应用永久存储目录 - final permanentImagePath = await _copyImageToPermanentStorage(imagePath); + // 复制图片到应用永久存储目录 + final imagePathResult = await _copyImageToPermanentStorage(imagePath); + final permanentImagePath = imagePathResult['fullPath']!; + final relativeImagePath = imagePathResult['relativePath']!; var ImageUrl = await UploadOss.upload( - filepath: imagePath, rootDir: 'User/${User.instance.uid}/ChatImages'); - Logger.i(TAG, '图片上传: $imagePath URL: $ImageUrl'); + filepath: permanentImagePath, + rootDir: 'User/${User.instance.uid}/ChatImages'); + Logger.i(TAG, '图片上传: $permanentImagePath URL: $ImageUrl'); // 显示的文本,如果没有提供则使用默认值 final displayText = text?.isNotEmpty == true ? text! : '[图片]'; @@ -915,7 +925,7 @@ class AgentController extends GetxController { isUser: true, text: displayText, hasImage: true, - imagePath: permanentImagePath, + imagePath: relativeImagePath, ); messages.add(message); @@ -935,7 +945,7 @@ class AgentController extends GetxController { // 调用Agent Service处理图片,考虑TTS状态 await AgentService.processImageInput( - permanentImagePath, + relativeImagePath, ImageUrl, text: text ?? '', speakResponse: isTtsEnabled.value, @@ -948,12 +958,16 @@ class AgentController extends GetxController { } // 将图片复制到应用的永久存储目录 - Future _copyImageToPermanentStorage(String sourcePath) async { + Future> _copyImageToPermanentStorage( + String sourcePath) async { try { final File sourceFile = File(sourcePath); if (!await sourceFile.exists()) { Logger.e(TAG, '源图片文件不存在: $sourcePath'); - return sourcePath; // 如果源文件不存在,返回原路径 + return { + 'fullPath': sourcePath, + 'relativePath': sourcePath + }; // 如果源文件不存在,返回原路径 } // 创建应用文档目录下的chatImages文件夹 @@ -967,15 +981,16 @@ class AgentController extends GetxController { final fileName = '${DateTime.now().millisecondsSinceEpoch}_${sourcePath.split('/').last}'; final targetPath = '${imagesDir.path}/$fileName'; + final relativePath = '/chatImages/$fileName'; // 复制文件 await sourceFile.copy(targetPath); Logger.i(TAG, '图片已复制到永久存储: $targetPath'); - return targetPath; + return {'fullPath': targetPath, 'relativePath': relativePath}; } catch (e) { Logger.e(TAG, '复制图片到永久存储失败: $e'); - return sourcePath; // 出错时返回原路径 + return {'fullPath': sourcePath, 'relativePath': sourcePath}; // 出错时返回原路径 } } diff --git a/lib/modules/agent/views/message_bubble.dart b/lib/modules/agent/views/message_bubble.dart index 6099dc1f0..660f8c7d4 100644 --- a/lib/modules/agent/views/message_bubble.dart +++ b/lib/modules/agent/views/message_bubble.dart @@ -169,7 +169,7 @@ class MessageBubble extends GetView { child: ClipRRect( borderRadius: BorderRadius.circular(12), child: Image.file( - File(imagePath!), + File('${controller.AppDir.value}$imagePath'), width: 200, height: 200, fit: BoxFit.cover, @@ -202,7 +202,7 @@ class MessageBubble extends GetView { child: ClipRRect( borderRadius: BorderRadius.circular(12), child: Image.file( - File(imagePath!), + File('${controller.AppDir.value}$imagePath'), width: 200, height: 200, fit: BoxFit.cover, diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt index cd5762b9d..d084b19fc 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt @@ -526,8 +526,8 @@ object AgentService : CoroutineScope { _hasSpeechDetected.set(true) if (!previousHasSpeech) { - restartIdleCheck() - } + restartIdleCheck() + } sendEvent( "recognizing", mapOf( diff --git a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift index 65fe56f08..b6a154a9f 100644 --- a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift +++ b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift @@ -469,8 +469,8 @@ audioStream.saveAudioDataTo(data: audioData) processWithChatApiServiceInternal(userMessage: userMessage, displayText: text, speakResponse: speakResponse) } - private func processWithChatApiServiceInternal(userMessage: [String: Any], displayText: String, speakResponse: Bool = true, hasImage: Bool = false) { - os_log("开始处理ChatAPI请求: displayText=%{public}@, speakResponse=%{public}@, hasImage=%{public}@", + private func processWithChatApiServiceInternal(userMessage: [String: Any], displayText: String, speakResponse: Bool = true, hasImage: Bool = false,imagePath:String = "") { + os_log("开始处理ChatAPI请求: displayText=%{public}@, speakResponse=%{public}@, hasImage=%{public}@", log: logger, type: .info, displayText, speakResponse ? "是" : "否", hasImage ? "是" : "否") guard let chatApiService = chatApiService else { @@ -517,7 +517,8 @@ audioStream.saveAudioDataTo(data: audioData) agentService: self, speakResponse: speakResponse, displayText: displayText, - hasImage: hasImage + hasImage: hasImage, + imagePath: imagePath ) currsessionId = UUID().uuidString os_log("设置回调并调用sendMessageStream", log: logger, type: .info) @@ -574,7 +575,8 @@ audioStream.saveAudioDataTo(data: audioData) userMessage: userMessage, displayText: displayText, speakResponse: speakResponse, - hasImage: true + hasImage: true, + imagePath: imagePath ) } } @@ -1132,19 +1134,22 @@ class ChatApiStreamCallback: StreamCallback { private let speakResponse: Bool private let displayText: String private let hasImage: Bool + private let imagePath: String private var responseBuilder = "" private var metadata = "" private var userMetadata:[String: Any] = [:] private var reply = true; //是否回复 private var broadcast = true //是否播发 - init(agentService: AgentServiceImpl, speakResponse: Bool, displayText: String, hasImage: Bool) { + init(agentService: AgentServiceImpl, speakResponse: Bool, displayText: String, hasImage: Bool,imagePath:String) { self.agentService = agentService self.speakResponse = speakResponse self.displayText = displayText self.hasImage = hasImage + self.imagePath = imagePath + self.userMetadata["hasImage"] = hasImage + self.userMetadata["imagePath"] = imagePath - } func onUsage(_ sessionid: String,_ prompt_tokens: Int?,_ completion_tokens: Int?,_ total_tokens: Int?){ diff --git a/local_plugins/chat_storage/ios/chat_storage/Sources/chat_storage/ChatStorageHelper.swift b/local_plugins/chat_storage/ios/chat_storage/Sources/chat_storage/ChatStorageHelper.swift index eb3e38433..96fa34a8e 100644 --- a/local_plugins/chat_storage/ios/chat_storage/Sources/chat_storage/ChatStorageHelper.swift +++ b/local_plugins/chat_storage/ios/chat_storage/Sources/chat_storage/ChatStorageHelper.swift @@ -186,7 +186,7 @@ public class ChatStorageHelper { "totalCount": totalCount, "totalPages": totalPages ] - + os_log("getMessages: 读取历史记录: %{public}@", log: logger, type: .error, messagesArray) do { let jsonData = try JSONSerialization.data(withJSONObject: result, options: []) if let jsonString = String(data: jsonData, encoding: .utf8) { @@ -199,7 +199,7 @@ public class ChatStorageHelper { let errmsg = String(cString: sqlite3_errmsg(db)!) os_log("getMessages: SQL准备失败: %{public}@", log: logger, type: .error, errmsg) } - + // 返回空结果,但保持格式一致 let emptyResult: [String: Any] = [ "messages": [], @@ -370,4 +370,4 @@ public class ChatStorageHelper { return messages } -} \ No newline at end of file +} From a4c807f084d2e073191c5611bb49ff830df83124 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Fri, 8 Aug 2025 10:56:41 +0800 Subject: [PATCH 25/34] =?UTF-8?q?-=20=E5=9C=A8TranslationView=E4=B8=AD?= =?UTF-8?q?=E8=B0=83=E6=95=B4=E8=AF=AD=E8=A8=80=E9=80=89=E6=8B=A9=E5=AF=B9?= =?UTF-8?q?=E8=AF=9D=E6=A1=86=E7=9A=84=E5=AF=B9=E9=BD=90=E6=96=B9=E5=BC=8F?= =?UTF-8?q?=E5=92=8C=E5=AE=BD=E5=BA=A6=20-=20=E4=BF=AE=E6=94=B9SnackBar?= =?UTF-8?q?=E7=9A=84=E6=98=BE=E7=A4=BA=E6=97=B6=E9=95=BF=20-=20=E5=9C=A8Ho?= =?UTF-8?q?meView=E4=B8=AD=E6=B7=BB=E5=8A=A0=E8=AE=BE=E5=A4=87=E7=AE=A1?= =?UTF-8?q?=E7=90=86=E6=8C=89=E9=92=AE=E5=B9=B6=E4=BC=98=E5=8C=96=E7=94=B5?= =?UTF-8?q?=E6=B1=A0=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/modules/home/views/home_view.dart | 103 +++++++++++++----- .../controllers/settings_controller.dart | 2 - .../translation/views/translation_view.dart | 22 ++-- .../agent_service/lib/agent_service.dart | 3 +- 4 files changed, 87 insertions(+), 43 deletions(-) diff --git a/lib/modules/home/views/home_view.dart b/lib/modules/home/views/home_view.dart index e1a68c9a1..e0350d376 100644 --- a/lib/modules/home/views/home_view.dart +++ b/lib/modules/home/views/home_view.dart @@ -271,7 +271,7 @@ class HomeView extends GetView { ), ], ), - padding: EdgeInsets.all(16.w), + padding: EdgeInsets.fromLTRB(16.w, 16.h, 16.w, 16.h), child: Column( children: [ // 状态标题行 @@ -294,7 +294,7 @@ class HomeView extends GetView { if (!controller.isFullyConnectedRx.value) { return _buildConnectButton(); } else { - return const SizedBox(); + return _buildDeviceButton(isDarkMode); } }), ], @@ -417,12 +417,42 @@ class HomeView extends GetView { ), SizedBox(height: 12.h), _buildBatteryInfoGrid(isDarkMode), - // SizedBox(height: 16.h), - // _buildDeviceDetailsCard(isDarkMode), ], ); } + // 设备管理按钮 + Widget _buildDeviceButton(bool isDarkMode) { + return ElevatedButton( + onPressed: () { + Get.toNamed(Routes.devices); // 跳转到设备管理页面 + }, + style: ElevatedButton.styleFrom( + backgroundColor: Colors.blue, + foregroundColor: Colors.white, + minimumSize: Size(double.infinity, 44.h), + elevation: 2, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(10.r), + ), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon(Icons.device_hub, size: 16.sp), // 设备图标 + SizedBox(width: 8.w), + Text( + 'deviceManagement'.tr, + style: TextStyle( + fontSize: 14.sp, + fontWeight: FontWeight.w500, + ), + ), + ], + ), + ); + } + // 电池信息网格布局 Widget _buildBatteryInfoGrid(bool isDarkMode) { return Obx(() { @@ -522,6 +552,8 @@ class HomeView extends GetView { batteryColor = Colors.red[600]!; } + bool isBatteryZero = batteryLevel == 0; + return Expanded( child: Padding( padding: EdgeInsets.symmetric(vertical: 6.h), @@ -540,32 +572,45 @@ class HomeView extends GetView { ), SizedBox(height: 4.h), - // 电池指示器 - Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - batteryIcon, - size: 12.sp, - color: batteryColor, - ), - SizedBox(width: 2.w), - Text( - '$batteryLevel%', - style: TextStyle( - fontSize: 11.sp, - fontWeight: FontWeight.w500, - color: batteryColor, - ), - ), - if (isCharging) - Icon( - Icons.bolt, - size: 10.sp, - color: Colors.green[600], + // 电池指示器 - 当电量为0时,显示横线 + isBatteryZero + ? Container( + width: 20.w, // 横线宽度 + height: 12.sp, // 设置与图标相同的高度,确保对齐 + alignment: Alignment.center, // 居中对齐 + child: Container( + width: 10.w, + height: 1.5.h, // 实际横线高度 + color: isDarkMode + ? Colors.grey[600] + : Colors.grey[400], // 横线颜色 + ), + ) + : Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Icon( + batteryIcon, + size: 12.sp, + color: batteryColor, + ), + SizedBox(width: 2.w), + Text( + '$batteryLevel%', + style: TextStyle( + fontSize: 11.sp, + fontWeight: FontWeight.w500, + color: batteryColor, + ), + ), + if (isCharging) + Icon( + Icons.bolt, + size: 10.sp, + color: Colors.green[600], + ), + ], ), - ], - ), ], ), ), diff --git a/lib/modules/settings/controllers/settings_controller.dart b/lib/modules/settings/controllers/settings_controller.dart index 4553f3d1d..4dcecf302 100644 --- a/lib/modules/settings/controllers/settings_controller.dart +++ b/lib/modules/settings/controllers/settings_controller.dart @@ -247,11 +247,9 @@ class SettingsController extends GetxController { final PackageInfo packageInfo = await PackageInfo.fromPlatform(); appVersion.value = packageInfo.version; buildNumber.value = packageInfo.buildNumber; - print('版本信息: ${packageInfo.version}+${packageInfo.buildNumber}'); } catch (e) { appVersion.value = '未知'; buildNumber.value = '0'; - print('获取版本信息失败: $e'); } } diff --git a/lib/modules/translation/views/translation_view.dart b/lib/modules/translation/views/translation_view.dart index d69278376..a7c21d007 100644 --- a/lib/modules/translation/views/translation_view.dart +++ b/lib/modules/translation/views/translation_view.dart @@ -662,13 +662,15 @@ class TranslationView extends GetView { // 显示语言选择对话框 void _showLanguageSelectionDialog(bool isSource, bool isDarkMode) { - Get.dialog( - Dialog( - backgroundColor: isDarkMode ? Colors.grey[900] : Colors.white, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16.r), // 统一圆角尺寸为16 - ), - insetPadding: EdgeInsets.symmetric(horizontal: 20.w), + Get.dialog(Dialog( + backgroundColor: isDarkMode ? Colors.grey[900] : Colors.white, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16.r), // 统一圆角尺寸为16 + ), + alignment: isSource ? Alignment.centerLeft : Alignment.centerRight, + insetPadding: EdgeInsets.symmetric(horizontal: 10.w), + child: SizedBox( + width: 0.4.sw, child: Padding( // 使用Padding替代Container的padding padding: EdgeInsets.all(12.w), // 统一内边距 @@ -807,7 +809,7 @@ class TranslationView extends GetView { ), ), ), - ); + )); } // 构建加载更多指示器 @@ -1035,7 +1037,7 @@ class TranslationView extends GetView { snackPosition: SnackPosition.BOTTOM, backgroundColor: Get.isDarkMode ? Colors.grey[800] : Colors.grey[200], colorText: Get.isDarkMode ? Colors.white : Colors.black, - duration: const Duration(seconds: 2), + duration: const Duration(milliseconds: 800), ); } catch (e) { // 显示删除失败提示 @@ -1045,7 +1047,7 @@ class TranslationView extends GetView { snackPosition: SnackPosition.BOTTOM, backgroundColor: Colors.red[400], colorText: Colors.white, - duration: const Duration(seconds: 3), + duration: const Duration(milliseconds: 1500), ); } } diff --git a/local_plugins/agent_service/lib/agent_service.dart b/local_plugins/agent_service/lib/agent_service.dart index e3e2eb1c4..6b80b9867 100644 --- a/local_plugins/agent_service/lib/agent_service.dart +++ b/local_plugins/agent_service/lib/agent_service.dart @@ -332,8 +332,6 @@ class AgentService { } } - - /// 停止语音合成 static Future stopAiSteam() async { try { @@ -385,6 +383,7 @@ class AgentService { /// /// [imagePath] 图片文件路径 /// [text] 可选的文本描述或问题 + /// [imageUrl] 图片url /// [speakResponse] 是否朗读响应 /// 返回是否成功处理图片 static Future processImageInput(String imagePath, String imageUrl, From cde830ce5bf5bf58d91dd540e80af4ed00d07480 Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Fri, 8 Aug 2025 11:10:11 +0800 Subject: [PATCH 26/34] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=A4=9A=E5=AA=92?= =?UTF-8?q?=E4=BD=93=E5=9B=BE=E7=89=87=E7=BB=9F=E8=AE=A1token=E6=8A=A5?= =?UTF-8?q?=E9=94=99=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/yunqiinnovation/chat_api/Utils.kt | 46 +++++++++++++++---- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/Utils.kt b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/Utils.kt index 77a3dffde..d303ae3b4 100644 --- a/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/Utils.kt +++ b/local_plugins/chat_api/android/src/main/kotlin/com/yunqiinnovation/chat_api/Utils.kt @@ -9,6 +9,8 @@ import android.provider.Settings import android.util.Log import androidx.core.content.ContextCompat import com.aallam.openai.api.chat.ChatMessage +import com.aallam.openai.api.chat.TextContent +import com.aallam.openai.api.chat.ListContent import io.ktor.client.* import io.ktor.client.engine.okhttp.* import io.ktor.client.plugins.sse.* @@ -169,16 +171,42 @@ fun roughTokenCount(text: String): Int { /** * 估算消息列表的总 token 数量(近似) - * @param messages List of message map: role: user/assistant, content: string + * @param messages List of message map: role: user/assistant, content: string or array * @return 估算 token 数 */ fun estimatePromptTokens(messages: List): Int { - var tokens = 0 - for (msg in messages) { - tokens += 4 // 每条消息结构开销 - tokens += roughTokenCount(msg.role.role.lowercase()) // ChatRole 枚举转小写字符串 - tokens += roughTokenCount(msg.content ?: "") + var totalText = "" + for (message in messages) { + when (val content = message.messageContent) { + is TextContent? -> { + totalText += content?.content + " " + } + is ListContent -> { + // 处理多模态内容数组 + content.content.forEach { item -> + if (item is Map<*, *>) { + val type = item["type"] as? String + if (type == "text") { + val text = item["text"] as? String ?: "" + totalText += text + " " + }else if(type == "image_url"){ + val text = item["image_url"] as? String ?: "" + totalText += text + " " + } + } + } + } + } } - tokens += 2 // 结尾 assistant 回复结构开销 - return tokens -} \ No newline at end of file + return roughTokenCount(totalText) +} +//fun estimatePromptTokens(messages: List): Int { +// var tokens = 0 +// for (msg in messages) { +// tokens += 4 // 每条消息结构开销 +// tokens += roughTokenCount(msg.role.role.lowercase()) // ChatRole 枚举转小写字符串 +// tokens += roughTokenCount(msg.content ?: "") +// } +// tokens += 2 // 结尾 assistant 回复结构开销 +// return tokens +//} \ No newline at end of file From 956bd2525c6d3628dc91b3f20b983b02b22b0c8a Mon Sep 17 00:00:00 2001 From: tanlongsheng <252620078@qq.com> Date: Fri, 8 Aug 2025 15:26:32 +0800 Subject: [PATCH 27/34] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E5=BC=95=E5=AF=BC?= =?UTF-8?q?=E9=A1=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/data/services/novice_guide_service.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/data/services/novice_guide_service.dart b/lib/data/services/novice_guide_service.dart index d0ef6ccc7..d32be2368 100644 --- a/lib/data/services/novice_guide_service.dart +++ b/lib/data/services/novice_guide_service.dart @@ -11,7 +11,7 @@ class NoviceGuideService extends GetxService { final BleService _bleService = BleService(); StreamSubscription? _statusSubscription; - bool isHomeGuide = false; + bool isHomeGuide = true; bool isAIGuide = true; RxBool isWakeup = false.obs; From 3f5591f1cfe03c0e93dd553637c5b80aab29e0e4 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Fri, 8 Aug 2025 15:29:01 +0800 Subject: [PATCH 28/34] =?UTF-8?q?refactor(settings):=20=E7=A7=BB=E9=99=A4?= =?UTF-8?q?=E6=B3=A8=E9=87=8A=E6=8E=89=E7=9A=84=E9=9A=90=E7=A7=81=E8=AE=BE?= =?UTF-8?q?=E7=BD=AE=E5=92=8C=E5=88=86=E9=9A=94=E7=BA=BF=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 清理不再使用的隐私设置相关代码,保持代码整洁 --- lib/modules/settings/views/settings_view.dart | 44 +++++++++---------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/lib/modules/settings/views/settings_view.dart b/lib/modules/settings/views/settings_view.dart index 8c12701a1..b30e3e8fd 100644 --- a/lib/modules/settings/views/settings_view.dart +++ b/lib/modules/settings/views/settings_view.dart @@ -419,29 +419,29 @@ class SettingsView extends GetView { isDarkMode: isDarkMode, ), // if (Platform.isAndroid) ...[ - Divider( - height: 1, - color: isDarkMode - ? Colors.white.withOpacity(0.1) - : Colors.grey[200]), + // Divider( + // height: 1, + // color: isDarkMode + // ? Colors.white.withOpacity(0.1) + // : Colors.grey[200]), - // 隐私设置 - _buildSimpleNavigationSetting( - title: 'privacySettings'.tr, // 隐私设置 - subtitle: 'privacySettingsInfo'.tr, // 管理数据收集和权限 - icon: Icons.shield_outlined, - iconBgColor: isDarkMode - ? Colors.purple[900]!.withOpacity(0.3) - : Colors.purple[100]!, - iconColor: - isDarkMode ? Colors.purple[300]! : Colors.purple[600]!, - titleColor: isDarkMode ? Colors.white : null, - subtitleColor: isDarkMode ? Colors.white70 : null, - onTap: () { - AppSettings.openAppSettings(); - }, - isDarkMode: isDarkMode, - ), + // // 隐私设置 + // _buildSimpleNavigationSetting( + // title: 'privacySettings'.tr, // 隐私设置 + // subtitle: 'privacySettingsInfo'.tr, // 管理数据收集和权限 + // icon: Icons.shield_outlined, + // iconBgColor: isDarkMode + // ? Colors.purple[900]!.withOpacity(0.3) + // : Colors.purple[100]!, + // iconColor: + // isDarkMode ? Colors.purple[300]! : Colors.purple[600]!, + // titleColor: isDarkMode ? Colors.white : null, + // subtitleColor: isDarkMode ? Colors.white70 : null, + // onTap: () { + // AppSettings.openAppSettings(); + // }, + // isDarkMode: isDarkMode, + // ), // ], Divider( From 69fce2898fb8548fc706910371a58f357a4b0be5 Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Fri, 8 Aug 2025 15:44:36 +0800 Subject: [PATCH 29/34] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E7=89=88=E6=9C=AC?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index d59266ff8..17c0f91a2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,7 +1,7 @@ name: voitrans description: "Voitrans - AI Voice Assistant." publish_to: "none" -version: 1.0.4+20 +version: 1.0.6+20 environment: sdk: ">=3.3.0 <4.0.0" From 6e855d6d7e0d7a4dc7707cc0fad63b1c30306fa3 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Fri, 8 Aug 2025 15:48:14 +0800 Subject: [PATCH 30/34] =?UTF-8?q?feat(profile):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E6=B8=85=E9=99=A4=E7=BC=93=E5=AD=98=E5=8A=9F=E8=83=BD=E5=8F=8A?= =?UTF-8?q?=E5=A4=9A=E8=AF=AD=E8=A8=80=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在个人资料页面添加清除缓存的功能选项,包括确认对话框和相应的多语言翻译 --- lib/core/translations/language/en_us.dart | 2 + lib/core/translations/language/zh_cn.dart | 2 + lib/modules/profile/views/profile_view.dart | 132 ++++++++++---------- 3 files changed, 70 insertions(+), 66 deletions(-) diff --git a/lib/core/translations/language/en_us.dart b/lib/core/translations/language/en_us.dart index 51d9bd764..7dca392ef 100644 --- a/lib/core/translations/language/en_us.dart +++ b/lib/core/translations/language/en_us.dart @@ -436,6 +436,8 @@ const Map enUS = { 'deleteAccount': 'Delete Account', 'deleteAccountTips': 'Are you sure you want to permanently delete your account? This action cannot be undone.', + 'clean': 'Clear Cache', // 清除缓存 + 'cleanTips': 'Are you sure you want to clear personal cache?', // 确定要清除个人缓存吗? //Profile 错误信息 'networkConnectionFailed': diff --git a/lib/core/translations/language/zh_cn.dart b/lib/core/translations/language/zh_cn.dart index 6bd1701d6..d73546416 100644 --- a/lib/core/translations/language/zh_cn.dart +++ b/lib/core/translations/language/zh_cn.dart @@ -411,6 +411,8 @@ const Map zhCN = { "logoutTips": "确定要退出当前账号吗?", 'deleteAccount': '注销账号', 'deleteAccountTips': '确定要永久删除您的账号吗?此操作不可撤销', + 'clean': '清除缓存', + 'cleanTips': '确定要清除个人缓存吗?', // Profile 错误信息 'networkConnectionFailed': '网络连接失败,请检查网络设置后重试', diff --git a/lib/modules/profile/views/profile_view.dart b/lib/modules/profile/views/profile_view.dart index 328d7c19a..4dbf73dd3 100644 --- a/lib/modules/profile/views/profile_view.dart +++ b/lib/modules/profile/views/profile_view.dart @@ -740,72 +740,72 @@ class EditProfileView extends GetView { }, isDarkMode: isDarkMode, ), - // Divider( - // height: 1, - // color: isDarkMode - // ? Colors.white.withOpacity(0.1) - // : Colors.grey[200]), - // // 清理缓存 - // _buildSimpleNavigationSetting( - // title: 'clean'.tr, // 清理缓存 - // icon: Icons.logout, - // iconBgColor: isDarkMode - // ? Colors.red[900]!.withOpacity(0.3) - // : Colors.red[50]!, - // iconColor: isDarkMode ? Colors.red[300]! : Colors.red[600]!, - // titleColor: - // isDarkMode ? Colors.red[300]! : Colors.red[600]!, - // onTap: () { - // // 退出登录确认对话框 - // Get.dialog( - // AlertDialog( - // backgroundColor: - // isDarkMode ? Colors.grey[900] : Colors.white, - // title: Text( - // 'clean'.tr, // 清理缓存 - // style: TextStyle( - // color: isDarkMode ? Colors.white : Colors.black87, - // ), - // ), - // content: Text( - // 'cleanTips'.tr, // 确定要退出当前账号吗? - // style: TextStyle( - // color: - // isDarkMode ? Colors.white70 : Colors.black54, - // ), - // ), - // actions: [ - // TextButton( - // onPressed: () => Get.back(), - // child: Text( - // 'cancel'.tr, // 取消 - // style: TextStyle( - // color: isDarkMode - // ? Colors.white70 - // : Colors.black54, - // ), - // ), - // ), - // TextButton( - // onPressed: () { - // Get.back(); - // controller.clean(); - // }, - // child: Text( - // 'confirm'.tr, // 确定 - // style: TextStyle( - // color: isDarkMode - // ? Colors.red[300] - // : Colors.red[600], - // ), - // ), - // ), - // ], - // ), - // ); - // }, - // isDarkMode: isDarkMode, - // ), + Divider( + height: 1, + color: isDarkMode + ? Colors.white.withOpacity(0.1) + : Colors.grey[200]), + // 清理缓存 + _buildSimpleNavigationSetting( + title: 'clean'.tr, // 清理缓存 + icon: Icons.cleaning_services, + iconBgColor: isDarkMode + ? Colors.red[900]!.withOpacity(0.3) + : Colors.red[50]!, + iconColor: isDarkMode ? Colors.red[300]! : Colors.red[600]!, + titleColor: + isDarkMode ? Colors.red[300]! : Colors.red[600]!, + onTap: () { + // 退出登录确认对话框 + Get.dialog( + AlertDialog( + backgroundColor: + isDarkMode ? Colors.grey[900] : Colors.white, + title: Text( + 'clean'.tr, // 清除缓存 + style: TextStyle( + color: isDarkMode ? Colors.white : Colors.black87, + ), + ), + content: Text( + 'cleanTips'.tr, // 确定要清除个人缓存吗? + style: TextStyle( + color: + isDarkMode ? Colors.white70 : Colors.black54, + ), + ), + actions: [ + TextButton( + onPressed: () => Get.back(), + child: Text( + 'cancel'.tr, // 取消 + style: TextStyle( + color: isDarkMode + ? Colors.white70 + : Colors.black54, + ), + ), + ), + TextButton( + onPressed: () { + Get.back(); + controller.clean(); + }, + child: Text( + 'confirm'.tr, // 确定 + style: TextStyle( + color: isDarkMode + ? Colors.red[300] + : Colors.red[600], + ), + ), + ), + ], + ), + ); + }, + isDarkMode: isDarkMode, + ), Divider( height: 1, color: isDarkMode From 0f527392a78767383c1856523822074f6daa15f4 Mon Sep 17 00:00:00 2001 From: lxm <1274844347@qq.com> Date: Fri, 8 Aug 2025 17:40:09 +0800 Subject: [PATCH 31/34] =?UTF-8?q?refactor(ota=5Fupgrade):=20=E4=BC=98?= =?UTF-8?q?=E5=8C=96=E7=89=88=E6=9C=AC=E6=AF=94=E8=BE=83=E9=80=BB=E8=BE=91?= =?UTF-8?q?=E4=BB=A5=E6=94=AF=E6=8C=81=E5=8D=87=E7=BA=A7=E5=92=8C=E9=99=8D?= =?UTF-8?q?=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 改进版本比较方法,支持版本号长度不一致的情况,并允许检测升级和降级。同时将变量名从isUpdate改为hasUpdate以提高可读性,并添加flutter_screenutil的宽度单位 --- .../controllers/ota_upgrade_controller.dart | 38 +++++++++++++------ 1 file changed, 26 insertions(+), 12 deletions(-) diff --git a/lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart b/lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart index 7adfc296b..91e28e89d 100644 --- a/lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart +++ b/lib/modules/ota_upgrade/controllers/ota_upgrade_controller.dart @@ -1,4 +1,5 @@ import 'package:classic_bluetooth/classic_bluetooth.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; import 'package:voitrans/data/models/appconfig_model.dart'; import '/routes/app_routes.dart'; import 'package:logger/web.dart'; @@ -214,10 +215,9 @@ class OtaUpgradeController extends GetxController { updatepackageaddress = product!.updatepackageaddress!; // 版本检查 - bool isUpdate = _isNewVersionAvailable(product.version!); + bool hasUpdate = _isNewVersionAvailable(product.version!); - - isUpdateAvailable.value = isUpdate; + isUpdateAvailable.value = hasUpdate; } catch (e) { // 统一错误处理 errorMessage.value = e.toString(); @@ -238,22 +238,36 @@ class OtaUpgradeController extends GetxController { } } - // 版本比较方法优化 - bool _isNewVersionAvailable(String latest) { + // 版本比较方法优化 - 支持升级和降级 + bool _isNewVersionAvailable(String latestVersion) { try { List currentParts = currentVersion.value .split('.') .map((e) => int.parse(e.trim())) .toList(); List latestParts = - latest.split('.').map((e) => int.parse(e.trim())).toList(); + latestVersion.split('.').map((e) => int.parse(e.trim())).toList(); + + // 确保两个版本号长度一致,短的用0补齐 + int maxLength = currentParts.length > latestParts.length + ? currentParts.length + : latestParts.length; - for (int i = 0; i < latestParts.length; i++) { - int current = (i < currentParts.length) ? currentParts[i] : 0; - if (latestParts[i] > current) return true; - if (latestParts[i] < current) return false; + while (currentParts.length < maxLength) { + currentParts.add(0); } - return false; + while (latestParts.length < maxLength) { + latestParts.add(0); + } + + // 比较每一段,只要有不同就返回true(支持升级和降级) + for (int i = 0; i < maxLength; i++) { + if (latestParts[i] != currentParts[i]) { + return true; // 版本不同,允许更新(升级或降级) + } + } + + return false; // 版本完全相同,无需更新 } catch (e) { Logger().d('版本比较错误: $e'); return false; @@ -654,7 +668,7 @@ class OtaUpgradeController extends GetxController { color: Colors.green[600], size: 28, ), - SizedBox(width: 12), + SizedBox(width: 12.w), Text( '升级成功', style: TextStyle( From 1452b393abad6ad8c2648685c3f72a44de331d15 Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Fri, 8 Aug 2025 18:48:05 +0800 Subject: [PATCH 32/34] =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E7=95=8C=E9=9D=A2?= =?UTF-8?q?=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/modules/settings/views/settings_view.dart | 49 +++++++++---------- 1 file changed, 24 insertions(+), 25 deletions(-) diff --git a/lib/modules/settings/views/settings_view.dart b/lib/modules/settings/views/settings_view.dart index b30e3e8fd..7e2c18d9a 100644 --- a/lib/modules/settings/views/settings_view.dart +++ b/lib/modules/settings/views/settings_view.dart @@ -591,32 +591,31 @@ class SettingsView extends GetView { onTap: () => Get.toNamed(Routes.OTA_UPGRADE), isDarkMode: isDarkMode, ), - if (Platform.isAndroid) ...[ - Divider( - height: 1, - color: isDarkMode - ? Colors.white.withOpacity(0.1) - : Colors.grey[200]), + // if (Platform.isAndroid) ...[ + Divider( + height: 1, + color: isDarkMode + ? Colors.white.withOpacity(0.1) + : Colors.grey[200]), - // 权限管理设置项 - _buildSimpleNavigationSetting( - title: 'permissionManagement'.tr, // 权限管理 - subtitle: 'permissionManagementInfo'.tr, // 管理应用权限设置 - icon: Icons.security, - iconBgColor: isDarkMode - ? Colors.orange[900]!.withOpacity(0.3) - : Colors.orange[100]!, - iconColor: isDarkMode - ? Colors.orange[300]! - : Colors.orange[600]!, - titleColor: isDarkMode ? Colors.white : null, - subtitleColor: isDarkMode ? Colors.white70 : null, - onTap: () { - Get.toNamed(Routes.permissions); - }, - isDarkMode: isDarkMode, - ), - ] + // 权限管理设置项 + _buildSimpleNavigationSetting( + title: 'permissionManagement'.tr, // 权限管理 + subtitle: 'permissionManagementInfo'.tr, // 管理应用权限设置 + icon: Icons.security, + iconBgColor: isDarkMode + ? Colors.orange[900]!.withOpacity(0.3) + : Colors.orange[100]!, + iconColor: + isDarkMode ? Colors.orange[300]! : Colors.orange[600]!, + titleColor: isDarkMode ? Colors.white : null, + subtitleColor: isDarkMode ? Colors.white70 : null, + onTap: () { + Get.toNamed(Routes.permissions); + }, + isDarkMode: isDarkMode, + ), + // ] ], ), ), From 2c95bf961ae4d5e107b4ca069491c6eb2cb07333 Mon Sep 17 00:00:00 2001 From: fdp <1286779656@qq.com> Date: Fri, 8 Aug 2025 20:13:46 +0800 Subject: [PATCH 33/34] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=88=87=E6=8D=A2?= =?UTF-8?q?=E8=AF=AD=E8=A8=80=E5=94=A4=E9=86=92=E4=B8=8D=E4=BA=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../agent_service/AgentService.kt | 30 +++++++++++++++---- .../agent_service/AgentServiceImpl.swift | 15 ++++++---- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt index d084b19fc..13594ed36 100644 --- a/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt +++ b/local_plugins/agent_service/android/src/main/kotlin/com/yunqiinnovation/agent_service/AgentService.kt @@ -1134,13 +1134,31 @@ object AgentService : CoroutineScope { Log.i(TAG, "设置支持的语言: ${languages.joinToString(", ")}") // 重新初始化ASR服务以应用新的语言设置 - val asrSuccess = azureAsrHelper?.initialize( - subscriptionKey = this.azureSpeechKey, - region = this.azureSpeechRegion, - supportedLanguages = languages.toTypedArray(), - audioSourceType = audioSourceType - ) ?: false + val asrSuccess = AzureAsrHelper(context).apply { + // 使用协程处理异步初始化 + CoroutineScope(Dispatchers.IO).launch { + //获取语音识别的语种 + val supportedLanguagesConfig = config["supportedLanguages"] as? List + val supportedLanguages = supportedLanguagesConfig?.toTypedArray() + ?: arrayOf("zh-CN") // 默认支持多语言 + language = supportedLanguages[0] + val asrInitSuccess = initialize( + subscriptionKey = config["azureSpeechKey"]?.toString() ?: "", + region = config["azureSpeechRegion"]?.toString() ?: "", + supportedLanguages = supportedLanguages, + audioSourceType = if (isExternalActive) AzureAsrHelper.AudioSourceType.EXTERNAL + else AzureAsrHelper.AudioSourceType.MICROPHONE + ) + if (asrInitSuccess) { + withContext(Dispatchers.Main) { + recognizeCallback() + } + } else { + Log.w(TAG, "Azure ASR初始化失败") + } + } + } // 同时更新TTS语言设置 val ttsSuccess = updateTtsLanguage(languages.first()) // 使用第一个语言作为TTS语言 diff --git a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift index b6a154a9f..e5ce57159 100644 --- a/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift +++ b/local_plugins/agent_service/ios/agent_service/Sources/agent_service/AgentServiceImpl.swift @@ -932,16 +932,21 @@ private func jsonToString(_ json: [String: Any]) -> String? { // 重新初始化ASR引擎,使用新的语言设置 guard let asrSuccess = azureAsrHelper?.initialize( - subscriptionKey: azureSpeechKey, - region: azureSpeechRegion, + subscriptionKey: azureSpeechKey, + region: azureSpeechRegion, supportedLanguages: languages, audioSourceType: .microphone ), asrSuccess else { - os_log("重新初始化ASR引擎失败", log: logger, type: .error) - sendError("重新初始化ASR引擎失败", code: "ASR_INIT_ERROR") + sendError("初始化语音识别服务失败", code: "ASR_INIT_ERROR") return false } - + guard let asrsetupSuccess = azureAsrHelper?.setupEventListeners( + callback: self + ), asrsetupSuccess else { + sendError("初始化语音识别服务失败", code: "ASR_INIT_ERROR") + return false + } + // 同时更新TTS语言设置 let ttsSuccess = updateTtsLanguage(languages.first ?? "zh-CN") From 305c202f419f58eb54139cdfb272b22d2a9cd729 Mon Sep 17 00:00:00 2001 From: liwei1dao Date: Fri, 8 Aug 2025 20:14:02 +0800 Subject: [PATCH 34/34] =?UTF-8?q?=E4=B8=8A=E4=BC=A0=20=E7=94=A8=E6=88=B7?= =?UTF-8?q?=E7=94=BB=E5=83=8F=E4=BB=A3=E7=A0=81=E6=8E=A5=E5=8F=A3=E9=94=99?= =?UTF-8?q?=E8=AF=AF=E5=AF=BC=E8=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../chat_api/SystemFunctionHandler.swift | 14 +++++----- .../location_service/LocationService.swift | 26 +++++++++++++------ 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift index e55b3c15f..c6a35e433 100644 --- a/local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift +++ b/local_plugins/chat_api/ios/chat_api/Sources/chat_api/SystemFunctionHandler.swift @@ -419,7 +419,7 @@ public class SystemFunctionHandler: NSObject { "required": ["key", "value"] ] - let handler = SetUserProfileFieldHandler() + let handler: SetUserProfileFieldHandler = SetUserProfileFieldHandler() _ = client.registerLocalFunction( name: "set_user_profile_field", description: "设置或更新用户画像中的某一个字段", @@ -844,21 +844,19 @@ private class OpenRecorderHandler: FunctionHandler { private class SetUserProfileFieldHandler: FunctionHandler { func handle(arguments: [String: Any]) async throws -> String { guard let key = arguments["key"] as? String, - let value = arguments["value"] as? String else { - throw ChatApiException("缺少必要参数:key 和 value") + let value = arguments["value"] as? String else { + return "设置成功" } - // 验证参数 if key.isEmpty { - throw ChatApiException("画像字段名不能为空") + return "设置成功" } if value.isEmpty { - throw ChatApiException("字段值不能为空") + return "设置成功" } - let result = await setUserProfileField(key: key, value: value) - return "{\"result\": \"\(result.message)\", \"key\": \"\(key)\", \"value\": \"\(value)\"}\"}" + return "设置成功" } private func setUserProfileField(key: String, value: String) async -> Result { diff --git a/local_plugins/location_service/ios/location_service/Sources/location_service/LocationService.swift b/local_plugins/location_service/ios/location_service/Sources/location_service/LocationService.swift index bc682d13e..906789434 100644 --- a/local_plugins/location_service/ios/location_service/Sources/location_service/LocationService.swift +++ b/local_plugins/location_service/ios/location_service/Sources/location_service/LocationService.swift @@ -27,7 +27,6 @@ public class LocationService: NSObject { super.init() setupLocationManager() } - private func setupLocationManager() { DispatchQueue.main.async { [weak self] in self?.locationManager = CLLocationManager() @@ -35,15 +34,26 @@ public class LocationService: NSObject { self?.locationManager?.desiredAccuracy = kCLLocationAccuracyBest self?.locationManager?.distanceFilter = 10 - // 初始化时获取当前状态 - self?.authorizationStatus = CLLocationManager.authorizationStatus() - self?.isLocationServicesEnabled = CLLocationManager.locationServicesEnabled() - - print("[LocationService] LocationManager 初始化完成") - print("[LocationService] 初始权限状态: \(self?.authorizationStatus.rawValue ?? -1)") - print("[LocationService] 位置服务启用: \(self?.isLocationServicesEnabled ?? false)") + // 不需要手动获取状态,delegate 方法会自动被调用 + print("[LocationService] LocationManager 初始化完成,等待权限状态回调") } } + // private func setupLocationManager() { + // DispatchQueue.main.async { [weak self] in + // self?.locationManager = CLLocationManager() + // self?.locationManager?.delegate = self + // self?.locationManager?.desiredAccuracy = kCLLocationAccuracyBest + // self?.locationManager?.distanceFilter = 10 + + // // 初始化时获取当前状态 + // self?.authorizationStatus = CLLocationManager.authorizationStatus() + // self?.isLocationServicesEnabled = CLLocationManager.locationServicesEnabled() + + // print("[LocationService] LocationManager 初始化完成") + // print("[LocationService] 初始权限状态: \(self?.authorizationStatus.rawValue ?? -1)") + // print("[LocationService] 位置服务启用: \(self?.isLocationServicesEnabled ?? false)") + // } + // } /// 获取当前位置 - 主要接口 public func getCurrentLocation(completion: @escaping (Result<[String: Any], Error>) -> Void) {