From: Anuj Solanki Date: Sun, 22 Sep 2024 11:37:57 +0000 (+0530) Subject: Integrate voice assistant into flutter-ics-homescreen X-Git-Url: https://gerrit.automotivelinux.org/gerrit/gitweb?a=commitdiff_plain;h=b363cc883dfb7bc8929fc65dc13168d4a698f53f;p=apps%2Fflutter-ics-homescreen.git Integrate voice assistant into flutter-ics-homescreen - Implemented client to connect with agl-service-voiceagent for command execution, wake word detection. - ⁠Added a settings tile on the settings page for configuring voice assistant settings. - Included toggle buttons for wake word mode, online mode, popup and speech-to-text model in the voice assistant settings. - Added a button on the homepage to start the voice assistant. Bug-AGL: SPEC-5200 Change-Id: I932e89a282ecfe139153be511d32cc10a5c31c55 Signed-off-by: Anuj Solanki --- diff --git a/animations/LoadingAnimation.json b/animations/LoadingAnimation.json new file mode 100644 index 0000000..b8a7d49 --- /dev/null +++ b/animations/LoadingAnimation.json @@ -0,0 +1 @@ +{"nm":"LoadingAnimation","ddd":0,"h":40,"w":277,"meta":{"g":"LottieFiles Figma v67"},"layers":[{"ty":0,"nm":"voiceAnimation","sr":1,"st":0,"op":79.18,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[138.5,20]},"s":{"a":0,"k":[100,100]},"sk":{"a":0,"k":0},"p":{"a":0,"k":[138.5,20]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":277,"h":40,"refId":"1","ind":1}],"v":"5.7.0","fr":60,"op":78.18,"ip":0,"assets":[{"nm":"[Asset] voiceAnimation","id":"1","layers":[{"ty":0,"nm":"Frame 3","sr":1,"st":0,"op":79.18,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[15,15],"t":78},{"s":[15,15],"t":84}]},"s":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":78},{"s":[100,100],"t":84}]},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[203.5,20],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[203.5,20],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[203.5,20],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[203.5,20],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[203.5,20],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[203.5,20],"t":78},{"s":[203.5,20],"t":84}]},"r":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":78},{"s":[0],"t":84}]},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":78},{"s":[100],"t":84}]}},"w":277,"h":40,"refId":"2","ind":1},{"ty":0,"nm":"Frame 2","sr":1,"st":0,"op":79.18,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[15,15],"t":78},{"s":[15,15],"t":84}]},"s":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":78},{"s":[100,100],"t":84}]},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[143.5,20],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[143.5,20],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[143.5,20],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[143.5,20],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[143.5,20],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[143.5,20],"t":78},{"s":[143.5,20],"t":84}]},"r":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":78},{"s":[0],"t":84}]},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":78},{"s":[100],"t":84}]}},"w":277,"h":40,"refId":"3","ind":2},{"ty":0,"nm":"Frame 1","sr":1,"st":0,"op":79.18,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[15,15],"t":78},{"s":[15,15],"t":84}]},"s":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":78},{"s":[100,100],"t":84}]},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[83.5,20],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[83.5,20],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[83.5,20],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[83.5,20],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[83.5,20],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[83.5,20],"t":78},{"s":[83.5,20],"t":84}]},"r":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":78},{"s":[0],"t":84}]},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":78},{"s":[100],"t":84}]}},"w":277,"h":40,"refId":"4","ind":3},{"ty":4,"nm":"voiceAnimation Bg","sr":1,"st":0,"op":79.18,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[138.5,20],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[138.5,20],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[138.5,20],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[138.5,20],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[138.5,20],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[138.5,20],"t":78},{"s":[138.5,20],"t":84}]},"s":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":78},{"s":[100,100],"t":84}]},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[138.5,20],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[138.5,20],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[138.5,20],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[138.5,20],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[138.5,20],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[138.5,20],"t":78},{"s":[138.5,20],"t":84}]},"r":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":78},{"s":[0],"t":84}]},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":78},{"s":[100],"t":84}]}},"shapes":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[277,0],[277,40],[0,40],[0,0]]}],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[277,0],[277,40],[0,40],[0,0]]}],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[277,0],[277,40],[0,40],[0,0]]}],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[277,0],[277,40],[0,40],[0,0]]}],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[277,0],[277,40],[0,40],[0,0]]}],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[277,0],[277,40],[0,40],[0,0]]}],"t":78},{"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[277,0],[277,40],[0,40],[0,0]]}],"t":84}]}}],"ind":4}]},{"nm":"[Asset] Frame 3","id":"2","layers":[{"ty":4,"nm":"circle3","sr":1,"st":0,"op":79.18,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[10,10],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[10,10],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[10,10],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[10,10],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[10,10],"t":78},{"s":[10,10],"t":84}]},"s":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":78},{"s":[100,100],"t":84}]},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15.5,15],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15.5,15],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15.5,15],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15.5,15],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15.5,15],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[15.5,15],"t":78},{"s":[15.5,15],"t":84}]},"r":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":78},{"s":[0],"t":84}]},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":78},{"s":[100],"t":84}]}},"ef":[{"ty":25,"nm":"","en":1,"ef":[{"ty":2,"nm":"color","v":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.7687,0.1138,1],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.7687,0.1138,1],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.7687,0.1138,1],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.7687,0.1138,1],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.7687,0.1138,1],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0.7687,0.1138,1],"t":78},{"s":[0.7687,0.1138,1],"t":84}]}},{"ty":0,"nm":"opacity","v":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[256],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[256],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[256],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[256],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[256],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[256],"t":78},{"s":[256],"t":84}]}},{"ty":1,"nm":"angle","v":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[90],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[90],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[90],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[90],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[90],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[90],"t":78},{"s":[90],"t":84}]}},{"ty":0,"nm":"distance","v":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":78},{"s":[0],"t":84}]}},{"ty":0,"nm":"blur","v":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[8],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[8],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[8],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[8],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[8],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[8],"t":78},{"s":[8],"t":84}]}}]}],"shapes":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,-5.52],[0,0],[5.52,0],[0,0],[0,5.52],[0,0],[-5.52,0],[0,0]],"o":[[0,0],[0,5.52],[0,0],[-5.52,0],[0,0],[0,-5.52],[0,0],[5.52,0]],"v":[[20,10],[20,10],[10,20],[10,20],[0,10],[0,10],[10,0],[10,0]]}],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,-5.52],[0,0],[5.52,0],[0,0],[0,5.52],[0,0],[-5.52,0],[0,0]],"o":[[0,0],[0,5.52],[0,0],[-5.52,0],[0,0],[0,-5.52],[0,0],[5.52,0]],"v":[[20,10],[20,10],[10,20],[10,20],[0,10],[0,10],[10,0],[10,0]]}],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,-5.52],[0,0],[5.52,0],[0,0],[0,5.52],[0,0],[-5.52,0],[0,0]],"o":[[0,0],[0,5.52],[0,0],[-5.52,0],[0,0],[0,-5.52],[0,0],[5.52,0]],"v":[[20,10],[20,10],[10,20],[10,20],[0,10],[0,10],[10,0],[10,0]]}],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,-5.52],[0,0],[5.52,0],[0,0],[0,5.52],[0,0],[-5.52,0],[0,0]],"o":[[0,0],[0,5.52],[0,0],[-5.52,0],[0,0],[0,-5.52],[0,0],[5.52,0]],"v":[[20,10],[20,10],[10,20],[10,20],[0,10],[0,10],[10,0],[10,0]]}],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,-8.28],[0,0],[8.28,0],[0,0],[0,8.28],[0,0],[-8.28,0],[0,0]],"o":[[0,0],[0,8.28],[0,0],[-8.28,0],[0,0],[0,-8.28],[0,0],[8.28,0]],"v":[[30,15],[30,15],[15,30],[15,30],[0,15],[0,15],[15,0],[15,0]]}],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,-5.52],[0,0],[5.52,0],[0,0],[0,5.52],[0,0],[-5.52,0],[0,0]],"o":[[0,0],[0,5.52],[0,0],[-5.52,0],[0,0],[0,-5.52],[0,0],[5.52,0]],"v":[[20,10],[20,10],[10,20],[10,20],[0,10],[0,10],[10,0],[10,0]]}],"t":78},{"s":[{"c":true,"i":[[0,-5.52],[0,0],[5.52,0],[0,0],[0,5.52],[0,0],[-5.52,0],[0,0]],"o":[[0,0],[0,5.52],[0,0],[-5.52,0],[0,0],[0,-5.52],[0,0],[5.52,0]],"v":[[20,10],[20,10],[10,20],[10,20],[0,10],[0,10],[10,0],[10,0]]}],"t":84}]}},{"ty":"fl","bm":0,"hd":false,"nm":"","c":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.7693,0.1125,1],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.7693,0.1125,1],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.7693,0.1125,1],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.7693,0.1125,1],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.7693,0.1125,1],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0.7693,0.1125,1],"t":78},{"s":[0.7693,0.1125,1],"t":84}]},"r":1,"o":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":78},{"s":[100],"t":84}]}}],"ind":1},{"ty":4,"nm":"Frame 3 Bg","sr":1,"st":0,"op":79.18,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[15,15],"t":78},{"s":[15,15],"t":84}]},"s":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":78},{"s":[100,100],"t":84}]},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[15,15],"t":78},{"s":[15,15],"t":84}]},"r":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":78},{"s":[0],"t":84}]},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":78},{"s":[100],"t":84}]}},"shapes":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":78},{"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":84}]}}],"ind":2}]},{"nm":"[Asset] Frame 2","id":"3","layers":[{"ty":4,"nm":"circle2","sr":1,"st":0,"op":79.18,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[10,10],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[10,10],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[10,10],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[10,10],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[10,10],"t":78},{"s":[10,10],"t":84}]},"s":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":78},{"s":[100,100],"t":84}]},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15.5,15],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15.5,15],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15.5,15],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15.5,15],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15.5,15],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[15.5,15],"t":78},{"s":[15.5,15],"t":84}]},"r":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":78},{"s":[0],"t":84}]},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":78},{"s":[100],"t":84}]}},"ef":[{"ty":25,"nm":"","en":1,"ef":[{"ty":2,"nm":"color","v":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.2314,0.3569,1],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.2314,0.3569,1],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.2314,0.3569,1],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.2314,0.3569,1],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.2314,0.3569,1],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0.2314,0.3569,1],"t":78},{"s":[0.2314,0.3569,1],"t":84}]}},{"ty":0,"nm":"opacity","v":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[256],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[256],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[256],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[256],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[256],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[256],"t":78},{"s":[256],"t":84}]}},{"ty":1,"nm":"angle","v":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[90],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[90],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[90],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[90],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[90],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[90],"t":78},{"s":[90],"t":84}]}},{"ty":0,"nm":"distance","v":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":78},{"s":[0],"t":84}]}},{"ty":0,"nm":"blur","v":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[8],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[8],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[8],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[8],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[8],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[8],"t":78},{"s":[8],"t":84}]}}]}],"shapes":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,-5.52],[0,0],[5.52,0],[0,0],[0,5.52],[0,0],[-5.52,0],[0,0]],"o":[[0,0],[0,5.52],[0,0],[-5.52,0],[0,0],[0,-5.52],[0,0],[5.52,0]],"v":[[20,10],[20,10],[10,20],[10,20],[0,10],[0,10],[10,0],[10,0]]}],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,-5.52],[0,0],[5.52,0],[0,0],[0,5.52],[0,0],[-5.52,0],[0,0]],"o":[[0,0],[0,5.52],[0,0],[-5.52,0],[0,0],[0,-5.52],[0,0],[5.52,0]],"v":[[20,10],[20,10],[10,20],[10,20],[0,10],[0,10],[10,0],[10,0]]}],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,-5.52],[0,0],[5.52,0],[0,0],[0,5.52],[0,0],[-5.52,0],[0,0]],"o":[[0,0],[0,5.52],[0,0],[-5.52,0],[0,0],[0,-5.52],[0,0],[5.52,0]],"v":[[20,10],[20,10],[10,20],[10,20],[0,10],[0,10],[10,0],[10,0]]}],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,-8.28],[0,0],[8.28,0],[0,0],[0,8.28],[0,0],[-8.28,0],[0,0]],"o":[[0,0],[0,8.28],[0,0],[-8.28,0],[0,0],[0,-8.28],[0,0],[8.28,0]],"v":[[30,15],[30,15],[15,30],[15,30],[0,15],[0,15],[15,0],[15,0]]}],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,-5.52],[0,0],[5.52,0],[0,0],[0,5.52],[0,0],[-5.52,0],[0,0]],"o":[[0,0],[0,5.52],[0,0],[-5.52,0],[0,0],[0,-5.52],[0,0],[5.52,0]],"v":[[20,10],[20,10],[10,20],[10,20],[0,10],[0,10],[10,0],[10,0]]}],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,-5.52],[0,0],[5.52,0],[0,0],[0,5.52],[0,0],[-5.52,0],[0,0]],"o":[[0,0],[0,5.52],[0,0],[-5.52,0],[0,0],[0,-5.52],[0,0],[5.52,0]],"v":[[20,10],[20,10],[10,20],[10,20],[0,10],[0,10],[10,0],[10,0]]}],"t":78},{"s":[{"c":true,"i":[[0,-5.52],[0,0],[5.52,0],[0,0],[0,5.52],[0,0],[-5.52,0],[0,0]],"o":[[0,0],[0,5.52],[0,0],[-5.52,0],[0,0],[0,-5.52],[0,0],[5.52,0]],"v":[[20,10],[20,10],[10,20],[10,20],[0,10],[0,10],[10,0],[10,0]]}],"t":84}]}},{"ty":"fl","bm":0,"hd":false,"nm":"","c":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.2334,0.356,1],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.2334,0.356,1],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.2334,0.356,1],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.2334,0.356,1],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.2334,0.356,1],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0.2334,0.356,1],"t":78},{"s":[0.2334,0.356,1],"t":84}]},"r":1,"o":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":78},{"s":[100],"t":84}]}}],"ind":1},{"ty":4,"nm":"Frame 2 Bg","sr":1,"st":0,"op":79.18,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[15,15],"t":78},{"s":[15,15],"t":84}]},"s":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":78},{"s":[100,100],"t":84}]},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[15,15],"t":78},{"s":[15,15],"t":84}]},"r":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":78},{"s":[0],"t":84}]},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":78},{"s":[100],"t":84}]}},"shapes":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":78},{"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":84}]}}],"ind":2}]},{"nm":"[Asset] Frame 1","id":"4","layers":[{"ty":4,"nm":"circle1","sr":1,"st":0,"op":79.18,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[9,9],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[9,9],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[10,10],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[10,10],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[9,9],"t":78},{"s":[9,9],"t":84}]},"s":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":78},{"s":[100,100],"t":84}]},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15.5,15],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15.5,15],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15.5,15],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15.5,15],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15.5,15],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[15.5,15],"t":78},{"s":[15.5,15],"t":84}]},"r":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":78},{"s":[0],"t":84}]},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":78},{"s":[100],"t":84}]}},"ef":[{"ty":25,"nm":"","en":1,"ef":[{"ty":2,"nm":"color","v":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.0236,0.9412,1],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.0236,0.9412,1],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.0236,0.9412,1],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.0236,0.9412,1],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.0236,0.9412,1],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0.0236,0.9412,1],"t":78},{"s":[0.0236,0.9412,1],"t":84}]}},{"ty":0,"nm":"opacity","v":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[256],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[256],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[256],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[256],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[256],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[256],"t":78},{"s":[256],"t":84}]}},{"ty":1,"nm":"angle","v":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[90],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[90],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[90],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[90],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[90],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[90],"t":78},{"s":[90],"t":84}]}},{"ty":0,"nm":"distance","v":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":78},{"s":[0],"t":84}]}},{"ty":0,"nm":"blur","v":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[8],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[8],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[8],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[8],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[8],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[8],"t":78},{"s":[8],"t":84}]}}]}],"shapes":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,-4.97],[0,0],[4.97,0],[0,0],[0,4.97],[0,0],[-4.97,0],[0,0]],"o":[[0,0],[0,4.97],[0,0],[-4.97,0],[0,0],[0,-4.97],[0,0],[4.97,0]],"v":[[18,9],[18,9],[9,18],[9,18],[0,9],[0,9],[9,0],[9,0]]}],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,-4.97],[0,0],[4.97,0],[0,0],[0,4.97],[0,0],[-4.97,0],[0,0]],"o":[[0,0],[0,4.97],[0,0],[-4.97,0],[0,0],[0,-4.97],[0,0],[4.97,0]],"v":[[18,9],[18,9],[9,18],[9,18],[0,9],[0,9],[9,0],[9,0]]}],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,-8.28],[0,0],[8.28,0],[0,0],[0,8.28],[0,0],[-8.28,0],[0,0]],"o":[[0,0],[0,8.28],[0,0],[-8.28,0],[0,0],[0,-8.28],[0,0],[8.28,0]],"v":[[30,15],[30,15],[15,30],[15,30],[0,15],[0,15],[15,0],[15,0]]}],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,-5.52],[0,0],[5.52,0],[0,0],[0,5.52],[0,0],[-5.52,0],[0,0]],"o":[[0,0],[0,5.52],[0,0],[-5.52,0],[0,0],[0,-5.52],[0,0],[5.52,0]],"v":[[20,10],[20,10],[10,20],[10,20],[0,10],[0,10],[10,0],[10,0]]}],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,-5.52],[0,0],[5.52,0],[0,0],[0,5.52],[0,0],[-5.52,0],[0,0]],"o":[[0,0],[0,5.52],[0,0],[-5.52,0],[0,0],[0,-5.52],[0,0],[5.52,0]],"v":[[20,10],[20,10],[10,20],[10,20],[0,10],[0,10],[10,0],[10,0]]}],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,-4.97],[0,0],[4.97,0],[0,0],[0,4.97],[0,0],[-4.97,0],[0,0]],"o":[[0,0],[0,4.97],[0,0],[-4.97,0],[0,0],[0,-4.97],[0,0],[4.97,0]],"v":[[18,9],[18,9],[9,18],[9,18],[0,9],[0,9],[9,0],[9,0]]}],"t":78},{"s":[{"c":true,"i":[[0,-4.97],[0,0],[4.97,0],[0,0],[0,4.97],[0,0],[-4.97,0],[0,0]],"o":[[0,0],[0,4.97],[0,0],[-4.97,0],[0,0],[0,-4.97],[0,0],[4.97,0]],"v":[[18,9],[18,9],[9,18],[9,18],[0,9],[0,9],[9,0],[9,0]]}],"t":84}]}},{"ty":"fl","bm":0,"hd":false,"nm":"","c":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.0251,0.9415,1],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.0251,0.9415,1],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.0251,0.9415,1],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.0251,0.9415,1],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0.0251,0.9415,1],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0.0251,0.9415,1],"t":78},{"s":[0.0251,0.9415,1],"t":84}]},"r":1,"o":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":78},{"s":[100],"t":84}]}}],"ind":1},{"ty":4,"nm":"Frame 1 Bg","sr":1,"st":0,"op":79.18,"ip":0,"hd":false,"ddd":0,"bm":0,"hasMask":false,"ao":0,"ks":{"a":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[15,15],"t":78},{"s":[15,15],"t":84}]},"s":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100,100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100,100],"t":78},{"s":[100,100],"t":84}]},"sk":{"a":0,"k":0},"p":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[15,15],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[15,15],"t":78},{"s":[15,15],"t":84}]},"r":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[0],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[0],"t":78},{"s":[0],"t":84}]},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[100],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[100],"t":78},{"s":[100],"t":84}]}},"shapes":[{"ty":"sh","bm":0,"hd":false,"nm":"","d":1,"ks":{"a":1,"k":[{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":0},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":6},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":24},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":42},{"o":{"x":0.33,"y":1},"i":{"x":0.68,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":60},{"o":{"x":0,"y":0},"i":{"x":1,"y":1},"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":78},{"s":[{"c":true,"i":[[0,0],[0,0],[0,0],[0,0]],"o":[[0,0],[0,0],[0,0],[0,0]],"v":[[30,0],[30,30],[0,30],[0,0]]}],"t":84}]}}],"ind":2}]}]} diff --git a/assets/VoiceAssistantActive.svg b/assets/VoiceAssistantActive.svg new file mode 100644 index 0000000..3589294 --- /dev/null +++ b/assets/VoiceAssistantActive.svg @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/VoiceAssistantBottomSheet.svg b/assets/VoiceAssistantBottomSheet.svg new file mode 100644 index 0000000..cde9fbc --- /dev/null +++ b/assets/VoiceAssistantBottomSheet.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/assets/VoiceAssistantBottomSheetBg.png b/assets/VoiceAssistantBottomSheetBg.png new file mode 100644 index 0000000..059864a Binary files /dev/null and b/assets/VoiceAssistantBottomSheetBg.png differ diff --git a/assets/VoiceAssistantEnabled.svg b/assets/VoiceAssistantEnabled.svg new file mode 100644 index 0000000..6b5a83d --- /dev/null +++ b/assets/VoiceAssistantEnabled.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/VoiceAssistantPressed.svg b/assets/VoiceAssistantPressed.svg new file mode 100644 index 0000000..7ac4e33 --- /dev/null +++ b/assets/VoiceAssistantPressed.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/VoiceControlButton.svg b/assets/VoiceControlButton.svg new file mode 100644 index 0000000..bbd2757 --- /dev/null +++ b/assets/VoiceControlButton.svg @@ -0,0 +1,62 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist index 9625e10..7c56964 100644 --- a/ios/Flutter/AppFrameworkInfo.plist +++ b/ios/Flutter/AppFrameworkInfo.plist @@ -21,6 +21,6 @@ CFBundleVersion 1.0 MinimumOSVersion - 11.0 + 12.0 diff --git a/ios/Podfile b/ios/Podfile index fdcc671..d97f17e 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '11.0' +# platform :ios, '12.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 6c95b00..fc35016 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -1,71 +1,42 @@ PODS: - Flutter (1.0.0) - - media_kit_libs_ios_video (1.0.4): - - Flutter - - media_kit_native_event_loop (1.0.0): - - Flutter - - media_kit_video (0.0.1): - - Flutter - network_info_plus (0.0.1): - Flutter - - package_info_plus (0.4.5): - - Flutter - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - - screen_brightness_ios (0.1.0): - - Flutter - - volume_controller (0.0.1): + - rive_common (0.0.1): - Flutter - - wakelock_plus (0.0.1): + - shared_preferences_foundation (0.0.1): - Flutter + - FlutterMacOS DEPENDENCIES: - Flutter (from `Flutter`) - - media_kit_libs_ios_video (from `.symlinks/plugins/media_kit_libs_ios_video/ios`) - - media_kit_native_event_loop (from `.symlinks/plugins/media_kit_native_event_loop/ios`) - - media_kit_video (from `.symlinks/plugins/media_kit_video/ios`) - network_info_plus (from `.symlinks/plugins/network_info_plus/ios`) - - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - - screen_brightness_ios (from `.symlinks/plugins/screen_brightness_ios/ios`) - - volume_controller (from `.symlinks/plugins/volume_controller/ios`) - - wakelock_plus (from `.symlinks/plugins/wakelock_plus/ios`) + - rive_common (from `.symlinks/plugins/rive_common/ios`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) EXTERNAL SOURCES: Flutter: :path: Flutter - media_kit_libs_ios_video: - :path: ".symlinks/plugins/media_kit_libs_ios_video/ios" - media_kit_native_event_loop: - :path: ".symlinks/plugins/media_kit_native_event_loop/ios" - media_kit_video: - :path: ".symlinks/plugins/media_kit_video/ios" network_info_plus: :path: ".symlinks/plugins/network_info_plus/ios" - package_info_plus: - :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" - screen_brightness_ios: - :path: ".symlinks/plugins/screen_brightness_ios/ios" - volume_controller: - :path: ".symlinks/plugins/volume_controller/ios" - wakelock_plus: - :path: ".symlinks/plugins/wakelock_plus/ios" + rive_common: + :path: ".symlinks/plugins/rive_common/ios" + shared_preferences_foundation: + :path: ".symlinks/plugins/shared_preferences_foundation/darwin" SPEC CHECKSUMS: - Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 - media_kit_libs_ios_video: a5fe24bc7875ccd6378a0978c13185e1344651c1 - media_kit_native_event_loop: e6b2ab20cf0746eb1c33be961fcf79667304fa2a - media_kit_video: 5da63f157170e5bf303bf85453b7ef6971218a2e + Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 network_info_plus: 6d0c3eb8367b8164fa3fb0c19875e3f59d49697f - package_info_plus: 115f4ad11e0698c8c1c5d8a689390df880f47e85 path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 - screen_brightness_ios: 715ca807df953bf676d339f11464e438143ee625 - volume_controller: 531ddf792994285c9b17f9d8a7e4dcdd29b3eae9 - wakelock_plus: 8b09852c8876491e4b6d179e17dfe2a0b5f60d47 + rive_common: 3a4c254c6e4db7e4b9e05daeb3d1f47ae4f7bf76 + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 -PODFILE CHECKSUM: 70d9d25280d0dd177a5f637cdb0f0b0b12c6a189 +PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 -COCOAPODS: 1.11.3 +COCOAPODS: 1.15.2 diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 603a2e6..0a7b83a 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -216,7 +216,7 @@ isa = PBXProject; attributes = { BuildIndependentTargetsInParallel = YES; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C8080294A63A400263BE5 = { @@ -453,7 +453,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; @@ -580,7 +580,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = iphoneos; @@ -629,7 +629,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = iphoneos; SUPPORTED_PLATFORMS = iphoneos; diff --git a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 87131a0..8e3ca5d 100644 --- a/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ { @@ -73,6 +79,10 @@ final valClientProvider = Provider((ref) { return ValClient(config: config, ref: ref); }); +final voiceAgentClientProvider = Provider((ref){ + return VoiceAgentClient("127.0.0.1", 51053, ref); +}); + final appLauncherProvider = Provider((ref) { return AppLauncher(ref: ref); }); @@ -148,3 +158,6 @@ final currentTimeProvider = StateNotifierProvider((ref) { return CurrentTimeNotifier(); }); + +final voiceAssistantStateProvider = + NotifierProvider(VoiceAssistantStateNotifier.new); diff --git a/lib/data/data_providers/voice_agent_client.dart b/lib/data/data_providers/voice_agent_client.dart new file mode 100644 index 0000000..5cc2614 --- /dev/null +++ b/lib/data/data_providers/voice_agent_client.dart @@ -0,0 +1,304 @@ +import 'dart:async'; +import 'package:flutter_ics_homescreen/data/models/voice_assistant_state.dart'; +import 'package:protos/val-api.dart'; + +import '../../export.dart'; + +class VoiceAgentClient { + late ClientChannel _channel; + late VoiceAgentServiceClient _client; + final Ref ref; + StreamSubscription? _wakeWordStatusSubscription; + + VoiceAgentClient(String host, int port, this.ref) { + // Initialize the client channel without connecting immediately + _channel = ClientChannel( + host, + port: port, + options: const ChannelOptions( + credentials: ChannelCredentials.insecure(), + ), + ); + debugPrint("Connecting to Voice Assistant"); + _client = VoiceAgentServiceClient(_channel); + + } + + Future checkServiceStatus() async { + final empty = Empty(); + try { + final response = await _client.checkServiceStatus(empty); + return response; + } catch (e) { + // Handle the error gracefully, such as returning an error status + return ServiceStatus()..status = false; + } + } + + Stream detectWakeWord() { + final empty = Empty(); + try { + return _client.detectWakeWord(empty); + } catch (e) { + // Handle the error gracefully, such as returning a default status + return const Stream.empty(); // An empty stream as a placeholder + } + } + + Future recognizeVoiceCommand( + Stream controlStream) async { + try { + final response = await _client.recognizeVoiceCommand(controlStream); + return response; + } catch (e) { + // Handle the error gracefully, such as returning a default RecognizeResult + return RecognizeResult()..status = RecognizeStatusType.REC_ERROR; + } + } + + Future recognizeTextCommandGrpc( + RecognizeTextControl controlInput) async { + try { + final response = await _client.recognizeTextCommand(controlInput); + return response; + } catch (e) { + // Handle the error gracefully, such as returning a default RecognizeResult + return RecognizeResult()..status = RecognizeStatusType.REC_ERROR; + } + } + + Future executeCommandGrpc(ExecuteInput input) async { + try { + final response = await _client.executeCommand(input); + return response; + } catch (e) { + // Handle the error gracefully, such as returning an error status + return ExecuteResult()..status = ExecuteStatusType.EXEC_ERROR; + } + } + + Future shutdown() async { + // await _channel.shutdown(); + } + + // Grpc helper methods + Future startWakeWordDetection() async { + // Capture the state before any async operations + _wakeWordStatusSubscription?.cancel(); + final isWakeWordModeActive = ref.read(voiceAssistantStateProvider.select((value) => value.isWakeWordMode)); + + if (isWakeWordModeActive) { + debugPrint("Wake Word Detection Started"); + } else { + debugPrint("Wake Word Detection Stopped"); + return; + } + _wakeWordStatusSubscription = detectWakeWord().listen( + (response) async { + if (response.status) { + await startVoiceAssistant(); + // Wait for 2-3 seconds and then restart wake word detection + await Future.delayed(const Duration(seconds: 3)); + startWakeWordDetection(); + } + if(!ref.read(voiceAssistantStateProvider.select((value) => value.isWakeWordMode))){ + _wakeWordStatusSubscription?.cancel(); + return; + } + }, + onError: (error) { + }, + cancelOnError: true, + ); + } + + Future startRecording() async { + String streamId = ""; + try { + // Create a RecognizeControl message to start recording + final controlMessage = RecognizeVoiceControl() + ..action = RecordAction.START + ..recordMode = RecordMode + .MANUAL; // You can change this to your desired record mode + + // Create a Stream with the control message + final controlStream = Stream.fromIterable([controlMessage]); + + // Call the gRPC method to start recording + final response = + await recognizeVoiceCommand(controlStream); + + streamId = response.streamId; + } catch (e) { + } + return streamId; + } + + Future stopRecording( + String streamId, String nluModel, String stt,bool isOnlineMode) async { + + try { + NLUModel model = NLUModel.RASA; + if (nluModel == "snips") { + model = NLUModel.SNIPS; + } + STTFramework sttFramework = STTFramework.VOSK; + if (stt == "whisper") { + sttFramework = STTFramework.WHISPER; + } + OnlineMode onlineMode = OnlineMode.OFFLINE; + if (isOnlineMode) { + onlineMode = OnlineMode.ONLINE; + } + // Create a RecognizeControl message to stop recording + final controlMessage = RecognizeVoiceControl() + ..action = RecordAction.STOP + ..nluModel = model + ..streamId = + streamId // Use the same stream ID as when starting recording + ..recordMode = RecordMode.MANUAL + ..sttFramework = sttFramework + ..onlineMode = onlineMode; + + + // Create a Stream with the control message + final controlStream = Stream.fromIterable([controlMessage]); + + // Call the gRPC method to stop recording + final response = + await recognizeVoiceCommand(controlStream); + + // Process and store the result + if (response.status == RecognizeStatusType.REC_SUCCESS) { + } else if (response.status == RecognizeStatusType.INTENT_NOT_RECOGNIZED) { + final command = response.command; + debugPrint("Command is : $command"); + } + else { + debugPrint('Failed to process your voice command. Please try again.'); + } + await shutdown(); + return response; + } catch (e) { + // addChatMessage(/**/'Failed to process your voice command. Please try again.'); + await shutdown(); + return RecognizeResult()..status = RecognizeStatusType.REC_ERROR; + } + // await voiceAgentClient.shutdown(); + } + + Future recognizeTextCommand(String command, String nluModel) async { + debugPrint("Recognizing Text Command: $command"); + try { + NLUModel model = NLUModel.RASA; + if (nluModel == "snips") { + model = NLUModel.SNIPS; + } + // Create a RecognizeControl message to stop recording + final controlMessage = RecognizeTextControl() + ..textCommand = command + ..nluModel = model; + + // Call the gRPC method to stop recording + final response = + await recognizeTextCommandGrpc(controlMessage); + debugPrint("Response is : $response"); + + // Process and store the result + if (response.status == RecognizeStatusType.REC_SUCCESS) { + // Do nothing + } else if (response.status == RecognizeStatusType.INTENT_NOT_RECOGNIZED) { + final command = response.command; + debugPrint("Command is : $command"); + } else { + debugPrint('Failed to process your voice command. Please try again.'); + } + return response; + } catch (e) { + return RecognizeResult()..status = RecognizeStatusType.REC_ERROR; + } + } + + Future executeCommand(RecognizeResult response) async { + try { + // Create an ExecuteInput message using the response from stopRecording + final executeInput = ExecuteInput() + ..intent = response.intent + ..intentSlots.addAll(response.intentSlots); + + // Call the gRPC method to execute the voice command + final execResponse = await executeCommandGrpc(executeInput); + + // Handle the response as needed + if (execResponse.status == ExecuteStatusType.EXEC_SUCCESS) { + final commandResponse = execResponse.response; + ref.read(voiceAssistantStateProvider.notifier).updateCommandResponse(commandResponse); + debugPrint("Command Response is : $commandResponse"); + // addChatMessage(commandResponse); + } else if (execResponse.status == ExecuteStatusType.KUKSA_CONN_ERROR) { + final commandResponse = execResponse.response; + // addChatMessage(commandResponse); + ref.read(voiceAssistantStateProvider.notifier).updateCommandResponse(commandResponse); + } else { + ref.read(voiceAssistantStateProvider.notifier).updateCommandResponse("Sorry, I couldn't execute your command. Please try again."); + } + } catch (e) { + } + await shutdown(); + } + + + Future startVoiceAssistant()async { + ref.read(voiceAssistantStateProvider.notifier).updateCommand(null); + ref.read(voiceAssistantStateProvider.notifier).updateCommandResponse(null); + + SttModel stt = ref.read(voiceAssistantStateProvider.select((value)=>value.sttModel)); + bool isOnlineMode = ref.read(voiceAssistantStateProvider.select((value)=>value.isOnlineMode)); + String nluModel = "snips"; + String sttModel = "whisper"; + if(stt == SttModel.vosk){ + sttModel = "vosk"; + } + bool isOverlayEnabled = ref.read(voiceAssistantStateProvider.select((value)=>value.voiceAssistantOverlay)); + bool overlayState = ref.read(voiceAssistantStateProvider.select((value)=>value.showOverLay)); + + String streamId = await startRecording(); + if (streamId.isNotEmpty) { + debugPrint('Recording started. Please speak your command.'); + if(isOverlayEnabled){ + if(!overlayState){ + ref.read(voiceAssistantStateProvider.notifier).toggleShowOverlay(); + } + } + ref.read(voiceAssistantStateProvider.notifier).updateButtonPressed(true); + ref.read(voiceAssistantStateProvider.notifier).updateIsRecording(); + ref.read(voiceAssistantStateProvider.notifier).updateIsCommandProcessing(false); + + // wait for the recording time + await Future.delayed(Duration(seconds: ref.watch(voiceAssistantStateProvider.select((value)=>value.recordingTime)))); + + ref.read(voiceAssistantStateProvider.notifier).updateIsRecording(); + ref.read(voiceAssistantStateProvider.notifier).updateIsCommandProcessing(true); + + // stop the recording and process the command + RecognizeResult recognizeResult = await stopRecording(streamId, nluModel, sttModel,isOnlineMode); + + ref.read(voiceAssistantStateProvider.notifier).updateCommand(recognizeResult.command); + debugPrint('Recording stopped. Processing the command...'); + + // Execute the command + await executeCommand(recognizeResult); + + ref.read(voiceAssistantStateProvider.notifier).updateIsCommandProcessing(false); + ref.read(voiceAssistantStateProvider.notifier).updateButtonPressed(false); + ref.read(voiceAssistantStateProvider.notifier).updateCommand(null); + ref.read(voiceAssistantStateProvider.notifier).updateCommandResponse(null); + + } else { + debugPrint('Failed to start recording. Please try again.'); + } + + } + + +} diff --git a/lib/data/data_providers/voice_assistant_notifier.dart b/lib/data/data_providers/voice_assistant_notifier.dart new file mode 100644 index 0000000..805807e --- /dev/null +++ b/lib/data/data_providers/voice_assistant_notifier.dart @@ -0,0 +1,153 @@ +import 'package:protos/val-api.dart'; + +import '../../export.dart'; +import '../models/voice_assistant_state.dart'; + + +class VoiceAssistantStateNotifier extends Notifier{ + @override + VoiceAssistantState build() { + return const VoiceAssistantState.initial(); + } + + void updateVoiceAssistantState(VoiceAssistantState newState){ + state = newState; + } + + void updateVoiceAssistantStateWith({ + bool? isWakeWordMode, + bool? isVoiceAssistantEnable, + bool? voiceAssistantOverlay, + bool? isOnlineMode, + String? wakeWord, + SttModel? sttModel, + String? streamId, + bool? isCommandProcessing, + String? commandProcessingText, + int? recordingTime, + bool? buttonPressed, + bool? isRecording, + String? command, + String? commandResponse, + bool? isWakeWordDetected, + bool? showOverLay, + }){ + state = state.copyWith( + isWakeWordMode: isWakeWordMode, + isVoiceAssistantEnable: isVoiceAssistantEnable, + voiceAssistantOverlay: voiceAssistantOverlay, + isOnlineMode: isOnlineMode, + wakeWord: wakeWord, + sttModel: sttModel, + streamId: streamId, + isCommandProcessing: isCommandProcessing, + commandProcessingText: commandProcessingText, + recordingTime: recordingTime, + buttonPressed: buttonPressed, + isRecording: isRecording, + command: command, + commandResponse: commandResponse, + isWakeWordDetected: isWakeWordDetected, + showOverLay: showOverLay, + ); + } + + void resetToDefaults(){ + state = const VoiceAssistantState.initial(); + } + + void updateWakeWordDetected(bool isWakeWordDetected){ + state = state.copyWith(isWakeWordDetected: isWakeWordDetected); + } + + void toggleShowOverlay(){ + state = state.copyWith(showOverLay: !state.showOverLay); + } + + void toggleWakeWordMode(){ + state = state.copyWith(isWakeWordMode: !state.isWakeWordMode); + if(state.isWakeWordMode){ + var voiceAgentClient = ref.read(voiceAgentClientProvider); + voiceAgentClient.startWakeWordDetection(); + } + } + + Future toggleVoiceAssistant() async { + bool prevState = state.isVoiceAssistantEnable; + if(!prevState){ + var voiceAgentClient = ref.read(voiceAgentClientProvider); + ServiceStatus status = await voiceAgentClient.checkServiceStatus(); + if(status.status){ + state = state.copyWith(isVoiceAssistantEnable: !state.isVoiceAssistantEnable); + state = state.copyWith(wakeWord: status.wakeWord); + } + else{ + debugPrint("Failed to start the Voice Assistant"); + } + } + else{ + state = state.copyWith(isVoiceAssistantEnable: !state.isVoiceAssistantEnable); + } + } + + void toggleVoiceAssistantOverlay(){ + state = state.copyWith(voiceAssistantOverlay: !state.voiceAssistantOverlay); + } + + void toggleOnlineMode(){ + state = state.copyWith(isOnlineMode: !state.isOnlineMode); + } + + void updateWakeWord(String wakeWord){ + state = state.copyWith(wakeWord: wakeWord); + } + + void updateSttModel(SttModel sttModel){ + state = state.copyWith(sttModel: sttModel); + } + + void updateStreamId(String streamId){ + state = state.copyWith(streamId: streamId); + } + + void updateIsCommandProcessing(bool isCommandProcessing){ + state = state.copyWith(isCommandProcessing: isCommandProcessing); + } + + void updateCommandProcessingText(String commandProcessingText){ + state = state.copyWith(commandProcessingText: commandProcessingText); + } + + void updateRecordingTime(int recordingTime){ + state = state.copyWith(recordingTime: recordingTime); + } + + void updateIsRecording(){ + state = state.copyWith(isRecording: !state.isRecording); + } + + void updateCommand(String? command){ + state = state.copyWith(command: command); + } + + void updateCommandResponse(String? commandResponse){ + state = state.copyWith(commandResponse: commandResponse); + } + + + void toggleButtonPressed(){ + bool prevState = state.buttonPressed; + state = state.copyWith(buttonPressed: !state.buttonPressed); + if(prevState){ + return; + } + var voiceAgentClient = ref.read(voiceAgentClientProvider); + if(!prevState){ + voiceAgentClient.startVoiceAssistant(); + } + } + + void updateButtonPressed(bool buttonPressed){ + state = state.copyWith(buttonPressed: buttonPressed); + } +} \ No newline at end of file diff --git a/lib/data/models/voice_assistant_state.dart b/lib/data/models/voice_assistant_state.dart new file mode 100644 index 0000000..60e61ce --- /dev/null +++ b/lib/data/models/voice_assistant_state.dart @@ -0,0 +1,101 @@ +enum SttModel { + whisper, + vosk +} + +class VoiceAssistantState{ + final bool isWakeWordMode; + final bool isVoiceAssistantEnable; + final bool voiceAssistantOverlay; + final bool isOnlineMode; + final String wakeWord; + final SttModel sttModel; + final String streamId; + final bool isCommandProcessing; + final String commandProcessingText; + final int recordingTime; + final bool buttonPressed; + final bool isRecording; + final String command; + final String commandResponse; + final bool isWakeWordDetected; + final bool showOverLay; + + + const VoiceAssistantState({ + required this.isWakeWordMode, + required this.isVoiceAssistantEnable, + required this.voiceAssistantOverlay, + required this.isOnlineMode, + required this.wakeWord, + required this.sttModel, + required this.streamId, + required this.isCommandProcessing, + required this.commandProcessingText, + required this.recordingTime, + required this.buttonPressed, + required this.isRecording, + required this.command, + required this.commandResponse, + required this.isWakeWordDetected, + required this.showOverLay, + }); + + const VoiceAssistantState.initial() + : wakeWord = "hello auto", + sttModel = SttModel.whisper, + streamId = "", + isWakeWordMode = false, + isVoiceAssistantEnable = false, + voiceAssistantOverlay = false, + isOnlineMode = false, + isCommandProcessing = false, + commandProcessingText = "Processing...", + recordingTime = 4, + buttonPressed = false, + isRecording = false, + command = "", + commandResponse = "", + isWakeWordDetected = false, + showOverLay = false; + + VoiceAssistantState copyWith({ + bool? isWakeWordMode, + bool? isVoiceAssistantEnable, + bool? voiceAssistantOverlay, + bool? isOnlineMode, + String? wakeWord, + SttModel? sttModel, + String? streamId, + bool? isCommandProcessing, + String? commandProcessingText, + int? recordingTime, + bool? buttonPressed, + bool? isRecording, + String? command, + String? commandResponse, + bool? isWakeWordDetected, + bool? showOverLay, + }) { + return VoiceAssistantState( + isVoiceAssistantEnable : isVoiceAssistantEnable ?? this.isVoiceAssistantEnable, + isWakeWordMode : isWakeWordMode ?? this.isWakeWordMode, + voiceAssistantOverlay : voiceAssistantOverlay ?? this.voiceAssistantOverlay, + isOnlineMode : isOnlineMode ?? this.isOnlineMode, + wakeWord : wakeWord ?? this.wakeWord, + sttModel : sttModel ?? this.sttModel, + streamId : streamId ?? this.streamId, + isCommandProcessing : isCommandProcessing ?? this.isCommandProcessing, + commandProcessingText : commandProcessingText ?? this.commandProcessingText, + recordingTime : recordingTime ?? this.recordingTime, + buttonPressed : buttonPressed ?? this.buttonPressed, + isRecording : isRecording ?? this.isRecording, + command : command ?? this.command, + commandResponse : commandResponse ?? this.commandResponse, + isWakeWordDetected: isWakeWordDetected ?? this.isWakeWordDetected, + showOverLay: showOverLay ?? this.showOverLay, + ); + } + + +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index c7d84ee..0f3b270 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -5,7 +5,7 @@ import 'export.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); runApp(DevicePreview( - enabled: debugDisplay, + // enabled: debugDisplay, tools: const [ ...DevicePreview.defaultTools, ], diff --git a/lib/presentation/common_widget/voice_assistant_button.dart b/lib/presentation/common_widget/voice_assistant_button.dart new file mode 100644 index 0000000..0565d85 --- /dev/null +++ b/lib/presentation/common_widget/voice_assistant_button.dart @@ -0,0 +1,200 @@ +import 'package:flutter_ics_homescreen/export.dart'; + +class VoiceAssistantButton extends ConsumerStatefulWidget { + const VoiceAssistantButton({super.key}); + + @override + ConsumerState createState() => _VoiceAssistantButtonState(); +} + +class _VoiceAssistantButtonState extends ConsumerState with SingleTickerProviderStateMixin { + bool _showOverlay = false; + late AnimationController _animationController; + late Animation _pulseAnimation; + int overlayLock = 0; + + @override + void initState() { + super.initState(); + _animationController = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 700), + )..stop(); // Stop the animation initially + + _pulseAnimation = Tween(begin: 1.0, end: 1.05).animate( + CurvedAnimation(parent: _animationController, curve: Curves.easeInOut), + ); + } + + @override + void dispose() { + _animationController.dispose(); + super.dispose(); + } + + void _onTap() { + ref.read(voiceAssistantStateProvider.notifier).updateCommandResponse(""); + ref.read(voiceAssistantStateProvider.notifier).updateCommand(""); + // if (_isOverlayEnabled) { + // Future.delayed(Duration(milliseconds: 100), () { + // _showAssistantPopup(context); + // }); + // } + ref.read(voiceAssistantStateProvider.notifier).toggleButtonPressed(); + } + + void _showAssistantPopup(BuildContext context) { + showModalBottomSheet( + context: context, + isScrollControlled: true, + backgroundColor: Colors.transparent, + builder: (context) { + return Consumer( + builder: (context, ref, child) { + final String? command = ref.watch(voiceAssistantStateProvider.select((value) => value.command)); + final String? commandResponse = ref.watch(voiceAssistantStateProvider.select((value) => value.commandResponse)); + final bool isRecording = ref.watch(voiceAssistantStateProvider.select((value)=>value.isRecording)); + final bool isProcessing = ref.watch(voiceAssistantStateProvider.select((value)=>value.isCommandProcessing)); + + if (isRecording) { + _animationController.repeat(reverse: true); + } else { + _animationController.stop(); + } + + return Container( + height: MediaQuery.of(context).size.height * 0.35, + decoration: const BoxDecoration( + image: DecorationImage( + image: AssetImage('assets/VoiceAssistantBottomSheetBg.png'), + fit: BoxFit.cover, + ), + ), + child: Padding( + padding: const EdgeInsets.fromLTRB(30, 0, 40, 0), + child: Column( + mainAxisAlignment: MainAxisAlignment.end, + children: [ + if(!isRecording && !isProcessing) + Column( + children: [ + Text( + command ?? "No Command Detected", + style: const TextStyle( + color: Colors.white70, + fontSize: 43, + fontWeight: FontWeight.w400, + ), + ), + SizedBox( + height: MediaQuery.of(context).size.height * 0.03 + ), + Text( + commandResponse ?? "No Response", + style: const TextStyle( + color: Color.fromRGBO(41, 95, 248, 1), + fontSize: 43, + fontWeight: FontWeight.w800, + fontStyle: FontStyle.italic, + ), + ), + SizedBox( + height: MediaQuery.of(context).size.height * 0.02, + ), + ], + ), + + if(isRecording) + Column( + children: [ + const Text("Listening...", + style: TextStyle( + color: Colors.white70, + fontSize: 43, + fontWeight: FontWeight.w400, + ), + ), + SizedBox( + height: MediaQuery.of(context).size.height*0.02, + ), + ScaleTransition( + scale: _pulseAnimation, // Apply the pulse animation here + child: SvgPicture.asset( + 'assets/VoiceControlButton.svg', + fit: BoxFit.cover, + semanticsLabel: 'Voice Assistant', + ), + ), + ], + ), + + if(!isRecording && isProcessing) + Column( + children: [ + const Text("Processing...", + style: TextStyle( + color: Colors.white70, + fontSize: 43, + fontWeight: FontWeight.w400, + ), + ), + SizedBox( + height: MediaQuery.of(context).size.height*0.05, + ), + Lottie.asset( + 'animations/LoadingAnimation.json', + fit: BoxFit.cover, + repeat: true, + ), + ], + ), + SizedBox( + height: MediaQuery.of(context).size.height * 0.035, + ), + ], + ), + ), + ); + }, + ); + }, + ).whenComplete(() { + ref.read(voiceAssistantStateProvider.notifier).updateCommandResponse(null); + ref.read(voiceAssistantStateProvider.notifier).updateCommand(null); + ref.read(voiceAssistantStateProvider.notifier).toggleShowOverlay(); + overlayLock = 0; + }); + } + + @override + Widget build(BuildContext context) { + _showOverlay = ref.watch(voiceAssistantStateProvider.select((value) => value.showOverLay)); + + if (_showOverlay && overlayLock == 0) { + overlayLock = 1; + WidgetsBinding.instance.addPostFrameCallback((_) { + _showAssistantPopup(context); + }); + + } + else if(!_showOverlay && overlayLock == 1){ + overlayLock = 0; + } + + String svgPath = ref.watch(voiceAssistantStateProvider.select((value) => value.buttonPressed)) + ? 'assets/VoiceAssistantActive.svg' + : 'assets/VoiceAssistantEnabled.svg'; + + return GestureDetector( + onTap: _onTap, + child: Container( + padding: EdgeInsets.zero, + child: SvgPicture.asset( + svgPath, + fit: BoxFit.cover, + semanticsLabel: 'Voice Assistant', + ), + ), + ); + } +} diff --git a/lib/presentation/router/routes/routes.dart b/lib/presentation/router/routes/routes.dart index 328d495..24eab3a 100644 --- a/lib/presentation/router/routes/routes.dart +++ b/lib/presentation/router/routes/routes.dart @@ -3,6 +3,8 @@ import 'package:flutter_ics_homescreen/presentation/screens/settings/settings_sc import 'package:flutter_ics_homescreen/presentation/screens/settings/settings_screens/date_time/time/time_screen.dart'; import '../../../../export.dart'; +import '../../screens/settings/settings_screens/voice_assistant/voice_assistant_screen.dart'; +import '../../screens/settings/settings_screens/voice_assistant/widgets/stt_model/stt_model_screen.dart'; List> onGenerateAppViewPages( AppState state, @@ -57,5 +59,9 @@ List> onGenerateAppViewPages( return [TimePage.page()]; case AppState.year: return [SelectYearPage.page()]; + case AppState.voiceAssistant: + return [VoiceAssistantPage.page()]; + case AppState.sttModel: + return [STTModelPage.page()]; } } diff --git a/lib/presentation/screens/home/home.dart b/lib/presentation/screens/home/home.dart index 0ee52ac..28b679a 100644 --- a/lib/presentation/screens/home/home.dart +++ b/lib/presentation/screens/home/home.dart @@ -1,4 +1,6 @@ import 'package:flutter_ics_homescreen/export.dart'; + +import '../../common_widget/voice_assistant_button.dart'; // import 'package:media_kit_video/media_kit_video.dart'; final bkgImageProvider = Provider((ref) { @@ -76,6 +78,15 @@ class HomeScreenState extends ConsumerState { height: 500, child: const VolumeFanControl()), ), + // Voice Assistant Button + if (appState != AppState.splash && ref.watch(voiceAssistantStateProvider.select((value)=>value.isVoiceAssistantEnable))) + Positioned( + top: MediaQuery.of(context).size.height * 0.82, + child: Container( + padding: const EdgeInsets.only(left: 8), + child: const VoiceAssistantButton() + ), + ), ], ), bottomNavigationBar: diff --git a/lib/presentation/screens/settings/settings_screens/voice_assistant/voice_assistant_screen.dart b/lib/presentation/screens/settings/settings_screens/voice_assistant/voice_assistant_screen.dart new file mode 100644 index 0000000..e1f38ae --- /dev/null +++ b/lib/presentation/screens/settings/settings_screens/voice_assistant/voice_assistant_screen.dart @@ -0,0 +1,28 @@ + +import 'package:flutter_ics_homescreen/export.dart'; +import 'widgets/voice_assistant_content.dart'; + +class VoiceAssistantPage extends ConsumerWidget{ + const VoiceAssistantPage({super.key}); + + static Page page() => const MaterialPage(child: VoiceAssistantPage()); + @override + Widget build(BuildContext context,WidgetRef ref) { + + return Scaffold( + body: Column( + children: [ + CommonTitle( + title: 'Voice Assistant', + hasBackButton: true, + onPressed: () { + ref.read(appProvider.notifier).back(); + }, + ), + Expanded(child: VoiceAssistantContent()), + ], + ), + ); + } +} + diff --git a/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/stt_model/stt_model_screen.dart b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/stt_model/stt_model_screen.dart new file mode 100644 index 0000000..614763d --- /dev/null +++ b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/stt_model/stt_model_screen.dart @@ -0,0 +1,120 @@ +import 'package:flutter_ics_homescreen/export.dart'; + +import '../../../../../../../data/models/voice_assistant_state.dart'; + +class STTModelPage extends ConsumerWidget { + const STTModelPage({super.key}); + + static Page page() => + const MaterialPage(child: STTModelPage()); + @override + Widget build(BuildContext context, WidgetRef ref) { + final SttModel sttModel = ref.watch(voiceAssistantStateProvider.select((value) => value.sttModel)); + + return Scaffold( + body: Column( + children: [ + CommonTitle( + title: 'Speech to Text Model', + hasBackButton: true, + onPressed: () { + context.flow().update((state) => AppState.voiceAssistant); + }, + ), + Expanded( + child: Padding( + padding: const EdgeInsets.symmetric(vertical: 0, horizontal: 144), + child: ListView( + children: [ + Container( + height: 130, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + stops: sttModel == SttModel.whisper + ? [0, 0.01, 0.8] + : [0.1, 1], + colors: sttModel == SttModel.whisper + ? [ + Colors.white, + Colors.blue, + const Color.fromARGB(16, 41, 98, 255) + ] + : [Colors.black, Colors.black12]), + ), + child: ListTile( + minVerticalPadding: 0.0, + contentPadding: const EdgeInsets.symmetric( + horizontal: 16.0, vertical: 40.0), + leading: Text( + 'Whisper AI', + style: Theme.of(context).textTheme.titleMedium, + ), + trailing: sttModel == SttModel.whisper + ? const Icon( + Icons.done, + color: AGLDemoColors.periwinkleColor, + size: 48, + ) + : null, + onTap: () { + ref + .read(voiceAssistantStateProvider.notifier) + .updateSttModel(SttModel.whisper); + }), + ), + const SizedBox( + height: 8, + ), + Container( + height: 130, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + stops: sttModel == SttModel.vosk + ? [0, 0.01, 0.8] + : [0.1, 1], + colors: sttModel == SttModel.vosk + ? [ + Colors.white, + Colors.blue, + const Color.fromARGB(16, 41, 98, 255) + ] + : [Colors.black, Colors.black12]), + ), + child: ListTile( + minVerticalPadding: 0.0, + contentPadding: const EdgeInsets.symmetric( + horizontal: 16.0, vertical: 40.0), + leading: Text( + 'Vosk', + style: Theme.of(context).textTheme.titleMedium, + ), + //title: Text(widget.title), + //enabled: isSwitchOn, + trailing: sttModel == SttModel.vosk + ? const Icon( + Icons.done, + color: AGLDemoColors.periwinkleColor, + size: 48, + ) + : null, + + onTap: () { + ref + .read(voiceAssistantStateProvider.notifier) + .updateSttModel(SttModel.vosk); + }, + ), + ), + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_content.dart b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_content.dart new file mode 100644 index 0000000..aa26e70 --- /dev/null +++ b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_content.dart @@ -0,0 +1,246 @@ +import 'package:flutter_ics_homescreen/export.dart'; + +import 'package:flutter_ics_homescreen/export.dart'; +import 'package:flutter_ics_homescreen/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_tile.dart'; + +import '../../../../../../core/utils/helpers.dart'; +import '../../../../../../data/models/voice_assistant_state.dart'; + +@immutable +class VoiceAssistantContent extends ConsumerWidget { + VoiceAssistantContent({Key? key}) : super(key: key); + bool isWakeWordMode = false; + bool isVoiceAssistantOverlay = false; + bool isOnlineMode = false; + SttModel sttModel = SttModel.whisper; + + @override + Widget build(BuildContext context, WidgetRef ref) { + isWakeWordMode = + ref.watch(voiceAssistantStateProvider.select((value) => value.isWakeWordMode)); + isVoiceAssistantOverlay = + ref.watch(voiceAssistantStateProvider.select((value) => value.voiceAssistantOverlay)); + isOnlineMode = + ref.watch(voiceAssistantStateProvider.select((value) => value.isOnlineMode)); + sttModel = + ref.watch(voiceAssistantStateProvider.select((value) => value.sttModel)); + + final wakeWordCallback = () { + ref.read(voiceAssistantStateProvider.notifier).toggleWakeWordMode(); + }; + + final voiceAssistantOverlayCallback = () { + ref.read(voiceAssistantStateProvider.notifier).toggleVoiceAssistantOverlay(); + }; + + final onlineModeCallback = () { + ref.read(voiceAssistantStateProvider.notifier).toggleOnlineMode(); + }; + + + return Column( + children: [ + Expanded( + child: ListView( + padding: const EdgeInsets.symmetric(vertical: 50, horizontal: 144), + children: [ + VoiceAssistantTile( + icon: Icons.insert_comment_outlined, + title: "Voice Assistant Overlay", + hasSwitch: true, + voidCallback: voiceAssistantOverlayCallback, + isSwitchOn: isVoiceAssistantOverlay + ), + VoiceAssistantTile( + icon: Icons.cloud_circle, + title: "Online Mode", + hasSwitch: true, + voidCallback: onlineModeCallback, + isSwitchOn: isOnlineMode + ), + VoiceAssistantTile( + icon: Icons.mic_none_outlined, + title: "Wake Word Mode", + hasSwitch: true, + voidCallback: wakeWordCallback, + isSwitchOn: isWakeWordMode + ), + if(ref.watch(voiceAssistantStateProvider.select((value) => value.isWakeWordMode))) + WakeWordTile(), + SttTile( + title: " Speech To Text", + sttName: sttModel==SttModel.whisper ? "Whisper AI" : "Vosk", + hasSwich: true, + voidCallback: () async { + context + .flow() + .update((next) => AppState.sttModel); + }), + ], + ) + ), + ], + ); + } +} + +class SttTile extends ConsumerStatefulWidget { + final IconData? icon; + final String title; + final String sttName; + final bool hasSwich; + final VoidCallback voidCallback; + final String? image; + const SttTile({ + Key? key, + this.icon, + required this.title, + required this.sttName, + required this.hasSwich, + required this.voidCallback, + this.image, + }) : super(key: key); + + @override + SttTileState createState() => SttTileState(); +} + +class SttTileState extends ConsumerState { + @override + Widget build(BuildContext context) { + return Column( + children: [ + Container( + margin: const EdgeInsets.symmetric(vertical: 8), + padding: const EdgeInsets.symmetric(vertical: 15), + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + stops: [0.3, 1], + colors: [Colors.black, Colors.black12]), + ), + //color: Color(0xFF0D113F), + child: ListTile( + contentPadding: + const EdgeInsets.symmetric(vertical: 17, horizontal: 24), + leading: Icon( + Icons.transcribe_outlined, + color: AGLDemoColors.periwinkleColor, + size: 48, + ), + title: Text( + widget.title, + style: TextStyle( + color: AGLDemoColors.periwinkleColor, + shadows: [ + Helpers.dropShadowRegular, + ], + fontSize: 40), + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + widget.sttName, + style: TextStyle( + color: AGLDemoColors.periwinkleColor, + shadows: [ + Helpers.dropShadowRegular, + ], + fontSize: 40, + ), + ), + const SizedBox( + width: 24, + ), + const Icon( + Icons.arrow_forward_ios, + color: AGLDemoColors.periwinkleColor, + size: 48, + ), + ], + ), + onTap: widget.voidCallback, + ), + ), + const SizedBox( + height: 8, + ) + ], + ); + } +} + + + +class WakeWordTile extends ConsumerStatefulWidget { + const WakeWordTile({Key? key}) : super(key: key); + + @override + WakeWordTileState createState() => WakeWordTileState(); +} + +class WakeWordTileState extends ConsumerState { + @override + Widget build(BuildContext context) { + return Column( + children: [ + Container( + margin: const EdgeInsets.symmetric(vertical: 8), + padding: const EdgeInsets.symmetric(vertical: 15), + decoration: const BoxDecoration( + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + stops: [0.3, 1], + colors: [Colors.black, Colors.black12]), + ), + //color: Color(0xFF0D113F), + child: ListTile( + contentPadding: + const EdgeInsets.symmetric(vertical: 17, horizontal: 24), + leading: Icon( + Icons.mic_none_outlined, + color: AGLDemoColors.periwinkleColor, + size: 48, + ), + title: Text( + "Wake Word", + style: TextStyle( + color: AGLDemoColors.periwinkleColor, + shadows: [ + Helpers.dropShadowRegular, + ], + fontSize: 40), + ), + trailing: Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.end, + children: [ + Text( + ref.watch(voiceAssistantStateProvider.select((value) => value.wakeWord)) ?? "Not Set", + style: TextStyle( + color: AGLDemoColors.periwinkleColor, + shadows: [ + Helpers.dropShadowRegular, + ], + fontSize: 40, + ), + ), + const SizedBox( + width: 50, + ), + + ], + ), + ), + ), + const SizedBox( + height: 8, + ) + ], + ); + } +} diff --git a/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_settings_list_tile.dart b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_settings_list_tile.dart new file mode 100644 index 0000000..73cfa0c --- /dev/null +++ b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_settings_list_tile.dart @@ -0,0 +1,111 @@ +import 'package:flutter_ics_homescreen/export.dart'; + +class VoiceAssistantSettingsTile extends ConsumerStatefulWidget { + final IconData icon; + final String title; + final bool hasSwich; + final VoidCallback voidCallback; + const VoiceAssistantSettingsTile({ + Key? key, + required this.icon, + required this.title, + required this.hasSwich, + required this.voidCallback, + }) : super(key: key); + + @override + VoiceAssistantSettingsTileState createState() => VoiceAssistantSettingsTileState(); +} + +class VoiceAssistantSettingsTileState extends ConsumerState { + bool isSwitchOn = true; + @override + Widget build(BuildContext context) { + isSwitchOn = ref.watch(voiceAssistantStateProvider.select((voiceAssistant) => voiceAssistant.isVoiceAssistantEnable)); + return Column( + children: [ + GestureDetector( + onTap: isSwitchOn ? widget.voidCallback : () {}, + child: Container( + height: 130, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + stops: isSwitchOn ? [0.3, 1] : [0.8, 1], + colors: isSwitchOn + ? [Colors.black, Colors.black12] + : [ + const Color.fromARGB(50, 0, 0, 0), + Colors.transparent + ], + ), + ), + child: Card( + // shape: RoundedRectangleBorder( + // borderRadius: BorderRadius.circular(12), + // ), + color: Colors.transparent, + elevation: 5, + child: Padding( + padding: + const EdgeInsets.symmetric(vertical: 0, horizontal: 24), + child: Row( + children: [ + Icon( + widget.icon, + color: AGLDemoColors.periwinkleColor, + size: 48, + ), + const SizedBox(width: 24), + Expanded( + child: Text( + widget.title, + style: const TextStyle(fontSize: 40), + ), + ), + widget.hasSwich + ? Container( + width: 126, + height: 80, + decoration: const ShapeDecoration( + color: + AGLDemoColors.gradientBackgroundDarkColor, + shape: StadiumBorder( + side: BorderSide( + color: Color(0xFF5477D4), + width: 4, + )), + ), + child: FittedBox( + fit: BoxFit.fill, + child: Switch( + value: isSwitchOn, + onChanged: (bool value) { + ref.read(voiceAssistantStateProvider.notifier).toggleVoiceAssistant(); + setState(() { + isSwitchOn = value; + }); + // This is called when the user toggles the switch. + }, + inactiveTrackColor: Colors.transparent, + activeTrackColor: Colors.transparent, + thumbColor: + MaterialStateProperty.all( + AGLDemoColors.periwinkleColor)), + ), + ) + : const SizedBox(), + ], + ), + ), + ) + ), + ), + const SizedBox( + height: 8, + ) + ], + ); + } +} diff --git a/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_tile.dart b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_tile.dart new file mode 100644 index 0000000..d4bdd48 --- /dev/null +++ b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_tile.dart @@ -0,0 +1,102 @@ +import 'package:flutter_ics_homescreen/export.dart'; + +class VoiceAssistantTile extends ConsumerStatefulWidget { + final IconData icon; + final String title; + final bool hasSwitch; + final VoidCallback voidCallback; + final bool isSwitchOn; + const VoiceAssistantTile({super.key, required this.icon, required this.title, required this.hasSwitch, required this.voidCallback,required this.isSwitchOn}); + + @override + ConsumerState createState() => _VoiceAssistantTileState(); +} + +class _VoiceAssistantTileState extends ConsumerState { + bool isSwitchOn = true; + @override + Widget build(BuildContext context) { + isSwitchOn = widget.isSwitchOn; + return Column( + children: [ + Container( + height: 130, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.centerLeft, + end: Alignment.centerRight, + stops: isSwitchOn ? [0.3, 1] : [0.8, 1], + colors: isSwitchOn + ? [Colors.black, Colors.black12] + : [ + const Color.fromARGB(50, 0, 0, 0), + Colors.transparent + ], + ), + ), + child: Card( + + color: Colors.transparent, + elevation: 5, + child: Padding( + padding: + const EdgeInsets.symmetric(vertical: 0, horizontal: 24), + child: Row( + children: [ + Icon( + widget.icon, + color: AGLDemoColors.periwinkleColor, + size: 48, + ), + const SizedBox(width: 24), + Expanded( + child: Text( + widget.title, + style: const TextStyle(fontSize: 40), + ), + ), + widget.hasSwitch + ? Container( + width: 126, + height: 80, + decoration: const ShapeDecoration( + color: + AGLDemoColors.gradientBackgroundDarkColor, + shape: StadiumBorder( + side: BorderSide( + color: Color(0xFF5477D4), + width: 4, + )), + ), + child: FittedBox( + fit: BoxFit.fill, + child: Switch( + value: isSwitchOn, + onChanged: (bool value) { + setState(() { + isSwitchOn = value; + }); + widget.voidCallback(); + }, + inactiveTrackColor: Colors.transparent, + activeTrackColor: Colors.transparent, + thumbColor: + MaterialStateProperty.all( + AGLDemoColors.periwinkleColor)), + ), + ) + : const SizedBox(), + ], + ), + ), + ) + ), + const SizedBox( + height: 14, + ) + ], + ); + } +} + + diff --git a/lib/presentation/screens/settings/widgets/settings_content.dart b/lib/presentation/screens/settings/widgets/settings_content.dart index 6d0df50..458677c 100644 --- a/lib/presentation/screens/settings/widgets/settings_content.dart +++ b/lib/presentation/screens/settings/widgets/settings_content.dart @@ -1,6 +1,7 @@ import 'package:flutter_ics_homescreen/export.dart'; import '../../../custom_icons/custom_icons.dart'; +import '../settings_screens/voice_assistant/widgets/voice_assistant_settings_list_tile.dart'; class Settings extends ConsumerWidget { const Settings({ @@ -55,6 +56,14 @@ class Settings extends ConsumerWidget { voidCallback: () { ref.read(appProvider.notifier).update(AppState.audioSettings); }), + VoiceAssistantSettingsTile( + icon: Icons.keyboard_voice_outlined, + title: "Voice Assistant", + hasSwich: true, + voidCallback: (){ + ref.read(appProvider.notifier).update(AppState.voiceAssistant); + } + ), SettingsTile( icon: Icons.person_2_outlined, title: 'Profiles', diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 09ccbe9..0d8a359 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -35,8 +35,8 @@ SPEC CHECKSUMS: network_info_plus: f4fbc7877ab7b3294500d9441dfa53cd54972d05 path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 rive_common: 0f0aadf670f0c6a7872dfe3e6186f112a5319108 - shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 + shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 -COCOAPODS: 1.12.1 +COCOAPODS: 1.15.2 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index d61464c..34ac868 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -259,7 +259,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0920; - LastUpgradeCheck = 1430; + LastUpgradeCheck = 1510; ORGANIZATIONNAME = ""; TargetAttributes = { 331C80D4294CF70F00263BE5 = { diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index b42f43e..6df6a1b 100644 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -1,6 +1,6 @@ create(); + Empty._() : super(); + factory Empty.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory Empty.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'Empty', createEmptyInstance: create) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + Empty clone() => Empty()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + Empty copyWith(void Function(Empty) updates) => super.copyWith((message) => updates(message as Empty)) as Empty; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static Empty create() => Empty._(); + Empty createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static Empty getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static Empty? _defaultInstance; +} + +class ServiceStatus extends $pb.GeneratedMessage { + factory ServiceStatus({ + $core.String? version, + $core.bool? status, + $core.String? wakeWord, + }) { + final $result = create(); + if (version != null) { + $result.version = version; + } + if (status != null) { + $result.status = status; + } + if (wakeWord != null) { + $result.wakeWord = wakeWord; + } + return $result; + } + ServiceStatus._() : super(); + factory ServiceStatus.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory ServiceStatus.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ServiceStatus', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'version') + ..aOB(2, _omitFieldNames ? '' : 'status') + ..aOS(3, _omitFieldNames ? '' : 'wakeWord') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + ServiceStatus clone() => ServiceStatus()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + ServiceStatus copyWith(void Function(ServiceStatus) updates) => super.copyWith((message) => updates(message as ServiceStatus)) as ServiceStatus; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static ServiceStatus create() => ServiceStatus._(); + ServiceStatus createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static ServiceStatus getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static ServiceStatus? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get version => $_getSZ(0); + @$pb.TagNumber(1) + set version($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasVersion() => $_has(0); + @$pb.TagNumber(1) + void clearVersion() => clearField(1); + + @$pb.TagNumber(2) + $core.bool get status => $_getBF(1); + @$pb.TagNumber(2) + set status($core.bool v) { $_setBool(1, v); } + @$pb.TagNumber(2) + $core.bool hasStatus() => $_has(1); + @$pb.TagNumber(2) + void clearStatus() => clearField(2); + + @$pb.TagNumber(3) + $core.String get wakeWord => $_getSZ(2); + @$pb.TagNumber(3) + set wakeWord($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasWakeWord() => $_has(2); + @$pb.TagNumber(3) + void clearWakeWord() => clearField(3); +} + +class VoiceAudio extends $pb.GeneratedMessage { + factory VoiceAudio({ + $core.List<$core.int>? audioChunk, + $core.String? audioFormat, + $core.int? sampleRate, + $core.String? language, + }) { + final $result = create(); + if (audioChunk != null) { + $result.audioChunk = audioChunk; + } + if (audioFormat != null) { + $result.audioFormat = audioFormat; + } + if (sampleRate != null) { + $result.sampleRate = sampleRate; + } + if (language != null) { + $result.language = language; + } + return $result; + } + VoiceAudio._() : super(); + factory VoiceAudio.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory VoiceAudio.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'VoiceAudio', createEmptyInstance: create) + ..a<$core.List<$core.int>>(1, _omitFieldNames ? '' : 'audioChunk', $pb.PbFieldType.OY) + ..aOS(2, _omitFieldNames ? '' : 'audioFormat') + ..a<$core.int>(3, _omitFieldNames ? '' : 'sampleRate', $pb.PbFieldType.O3) + ..aOS(4, _omitFieldNames ? '' : 'language') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + VoiceAudio clone() => VoiceAudio()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + VoiceAudio copyWith(void Function(VoiceAudio) updates) => super.copyWith((message) => updates(message as VoiceAudio)) as VoiceAudio; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static VoiceAudio create() => VoiceAudio._(); + VoiceAudio createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static VoiceAudio getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static VoiceAudio? _defaultInstance; + + @$pb.TagNumber(1) + $core.List<$core.int> get audioChunk => $_getN(0); + @$pb.TagNumber(1) + set audioChunk($core.List<$core.int> v) { $_setBytes(0, v); } + @$pb.TagNumber(1) + $core.bool hasAudioChunk() => $_has(0); + @$pb.TagNumber(1) + void clearAudioChunk() => clearField(1); + + @$pb.TagNumber(2) + $core.String get audioFormat => $_getSZ(1); + @$pb.TagNumber(2) + set audioFormat($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasAudioFormat() => $_has(1); + @$pb.TagNumber(2) + void clearAudioFormat() => clearField(2); + + @$pb.TagNumber(3) + $core.int get sampleRate => $_getIZ(2); + @$pb.TagNumber(3) + set sampleRate($core.int v) { $_setSignedInt32(2, v); } + @$pb.TagNumber(3) + $core.bool hasSampleRate() => $_has(2); + @$pb.TagNumber(3) + void clearSampleRate() => clearField(3); + + @$pb.TagNumber(4) + $core.String get language => $_getSZ(3); + @$pb.TagNumber(4) + set language($core.String v) { $_setString(3, v); } + @$pb.TagNumber(4) + $core.bool hasLanguage() => $_has(3); + @$pb.TagNumber(4) + void clearLanguage() => clearField(4); +} + +class WakeWordStatus extends $pb.GeneratedMessage { + factory WakeWordStatus({ + $core.bool? status, + }) { + final $result = create(); + if (status != null) { + $result.status = status; + } + return $result; + } + WakeWordStatus._() : super(); + factory WakeWordStatus.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory WakeWordStatus.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'WakeWordStatus', createEmptyInstance: create) + ..aOB(1, _omitFieldNames ? '' : 'status') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + WakeWordStatus clone() => WakeWordStatus()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + WakeWordStatus copyWith(void Function(WakeWordStatus) updates) => super.copyWith((message) => updates(message as WakeWordStatus)) as WakeWordStatus; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static WakeWordStatus create() => WakeWordStatus._(); + WakeWordStatus createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static WakeWordStatus getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static WakeWordStatus? _defaultInstance; + + @$pb.TagNumber(1) + $core.bool get status => $_getBF(0); + @$pb.TagNumber(1) + set status($core.bool v) { $_setBool(0, v); } + @$pb.TagNumber(1) + $core.bool hasStatus() => $_has(0); + @$pb.TagNumber(1) + void clearStatus() => clearField(1); +} + +class S_RecognizeVoiceControl extends $pb.GeneratedMessage { + factory S_RecognizeVoiceControl({ + VoiceAudio? audioStream, + NLUModel? nluModel, + $core.String? streamId, + STTFramework? sttFramework, + }) { + final $result = create(); + if (audioStream != null) { + $result.audioStream = audioStream; + } + if (nluModel != null) { + $result.nluModel = nluModel; + } + if (streamId != null) { + $result.streamId = streamId; + } + if (sttFramework != null) { + $result.sttFramework = sttFramework; + } + return $result; + } + S_RecognizeVoiceControl._() : super(); + factory S_RecognizeVoiceControl.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory S_RecognizeVoiceControl.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'S_RecognizeVoiceControl', createEmptyInstance: create) + ..aOM(1, _omitFieldNames ? '' : 'audioStream', subBuilder: VoiceAudio.create) + ..e(2, _omitFieldNames ? '' : 'nluModel', $pb.PbFieldType.OE, defaultOrMaker: NLUModel.SNIPS, valueOf: NLUModel.valueOf, enumValues: NLUModel.values) + ..aOS(3, _omitFieldNames ? '' : 'streamId') + ..e(4, _omitFieldNames ? '' : 'sttFramework', $pb.PbFieldType.OE, defaultOrMaker: STTFramework.VOSK, valueOf: STTFramework.valueOf, enumValues: STTFramework.values) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + S_RecognizeVoiceControl clone() => S_RecognizeVoiceControl()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + S_RecognizeVoiceControl copyWith(void Function(S_RecognizeVoiceControl) updates) => super.copyWith((message) => updates(message as S_RecognizeVoiceControl)) as S_RecognizeVoiceControl; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static S_RecognizeVoiceControl create() => S_RecognizeVoiceControl._(); + S_RecognizeVoiceControl createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static S_RecognizeVoiceControl getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static S_RecognizeVoiceControl? _defaultInstance; + + @$pb.TagNumber(1) + VoiceAudio get audioStream => $_getN(0); + @$pb.TagNumber(1) + set audioStream(VoiceAudio v) { setField(1, v); } + @$pb.TagNumber(1) + $core.bool hasAudioStream() => $_has(0); + @$pb.TagNumber(1) + void clearAudioStream() => clearField(1); + @$pb.TagNumber(1) + VoiceAudio ensureAudioStream() => $_ensure(0); + + @$pb.TagNumber(2) + NLUModel get nluModel => $_getN(1); + @$pb.TagNumber(2) + set nluModel(NLUModel v) { setField(2, v); } + @$pb.TagNumber(2) + $core.bool hasNluModel() => $_has(1); + @$pb.TagNumber(2) + void clearNluModel() => clearField(2); + + @$pb.TagNumber(3) + $core.String get streamId => $_getSZ(2); + @$pb.TagNumber(3) + set streamId($core.String v) { $_setString(2, v); } + @$pb.TagNumber(3) + $core.bool hasStreamId() => $_has(2); + @$pb.TagNumber(3) + void clearStreamId() => clearField(3); + + @$pb.TagNumber(4) + STTFramework get sttFramework => $_getN(3); + @$pb.TagNumber(4) + set sttFramework(STTFramework v) { setField(4, v); } + @$pb.TagNumber(4) + $core.bool hasSttFramework() => $_has(3); + @$pb.TagNumber(4) + void clearSttFramework() => clearField(4); +} + +class RecognizeVoiceControl extends $pb.GeneratedMessage { + factory RecognizeVoiceControl({ + RecordAction? action, + NLUModel? nluModel, + RecordMode? recordMode, + $core.String? streamId, + STTFramework? sttFramework, + OnlineMode? onlineMode, + }) { + final $result = create(); + if (action != null) { + $result.action = action; + } + if (nluModel != null) { + $result.nluModel = nluModel; + } + if (recordMode != null) { + $result.recordMode = recordMode; + } + if (streamId != null) { + $result.streamId = streamId; + } + if (sttFramework != null) { + $result.sttFramework = sttFramework; + } + if (onlineMode != null) { + $result.onlineMode = onlineMode; + } + return $result; + } + RecognizeVoiceControl._() : super(); + factory RecognizeVoiceControl.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory RecognizeVoiceControl.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'RecognizeVoiceControl', createEmptyInstance: create) + ..e(1, _omitFieldNames ? '' : 'action', $pb.PbFieldType.OE, defaultOrMaker: RecordAction.START, valueOf: RecordAction.valueOf, enumValues: RecordAction.values) + ..e(2, _omitFieldNames ? '' : 'nluModel', $pb.PbFieldType.OE, defaultOrMaker: NLUModel.SNIPS, valueOf: NLUModel.valueOf, enumValues: NLUModel.values) + ..e(3, _omitFieldNames ? '' : 'recordMode', $pb.PbFieldType.OE, defaultOrMaker: RecordMode.MANUAL, valueOf: RecordMode.valueOf, enumValues: RecordMode.values) + ..aOS(4, _omitFieldNames ? '' : 'streamId') + ..e(5, _omitFieldNames ? '' : 'sttFramework', $pb.PbFieldType.OE, defaultOrMaker: STTFramework.VOSK, valueOf: STTFramework.valueOf, enumValues: STTFramework.values) + ..e(6, _omitFieldNames ? '' : 'onlineMode', $pb.PbFieldType.OE, defaultOrMaker: OnlineMode.ONLINE, valueOf: OnlineMode.valueOf, enumValues: OnlineMode.values) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + RecognizeVoiceControl clone() => RecognizeVoiceControl()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + RecognizeVoiceControl copyWith(void Function(RecognizeVoiceControl) updates) => super.copyWith((message) => updates(message as RecognizeVoiceControl)) as RecognizeVoiceControl; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static RecognizeVoiceControl create() => RecognizeVoiceControl._(); + RecognizeVoiceControl createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static RecognizeVoiceControl getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static RecognizeVoiceControl? _defaultInstance; + + @$pb.TagNumber(1) + RecordAction get action => $_getN(0); + @$pb.TagNumber(1) + set action(RecordAction v) { setField(1, v); } + @$pb.TagNumber(1) + $core.bool hasAction() => $_has(0); + @$pb.TagNumber(1) + void clearAction() => clearField(1); + + @$pb.TagNumber(2) + NLUModel get nluModel => $_getN(1); + @$pb.TagNumber(2) + set nluModel(NLUModel v) { setField(2, v); } + @$pb.TagNumber(2) + $core.bool hasNluModel() => $_has(1); + @$pb.TagNumber(2) + void clearNluModel() => clearField(2); + + @$pb.TagNumber(3) + RecordMode get recordMode => $_getN(2); + @$pb.TagNumber(3) + set recordMode(RecordMode v) { setField(3, v); } + @$pb.TagNumber(3) + $core.bool hasRecordMode() => $_has(2); + @$pb.TagNumber(3) + void clearRecordMode() => clearField(3); + + @$pb.TagNumber(4) + $core.String get streamId => $_getSZ(3); + @$pb.TagNumber(4) + set streamId($core.String v) { $_setString(3, v); } + @$pb.TagNumber(4) + $core.bool hasStreamId() => $_has(3); + @$pb.TagNumber(4) + void clearStreamId() => clearField(4); + + @$pb.TagNumber(5) + STTFramework get sttFramework => $_getN(4); + @$pb.TagNumber(5) + set sttFramework(STTFramework v) { setField(5, v); } + @$pb.TagNumber(5) + $core.bool hasSttFramework() => $_has(4); + @$pb.TagNumber(5) + void clearSttFramework() => clearField(5); + + @$pb.TagNumber(6) + OnlineMode get onlineMode => $_getN(5); + @$pb.TagNumber(6) + set onlineMode(OnlineMode v) { setField(6, v); } + @$pb.TagNumber(6) + $core.bool hasOnlineMode() => $_has(5); + @$pb.TagNumber(6) + void clearOnlineMode() => clearField(6); +} + +class RecognizeTextControl extends $pb.GeneratedMessage { + factory RecognizeTextControl({ + $core.String? textCommand, + NLUModel? nluModel, + }) { + final $result = create(); + if (textCommand != null) { + $result.textCommand = textCommand; + } + if (nluModel != null) { + $result.nluModel = nluModel; + } + return $result; + } + RecognizeTextControl._() : super(); + factory RecognizeTextControl.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory RecognizeTextControl.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'RecognizeTextControl', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'textCommand') + ..e(2, _omitFieldNames ? '' : 'nluModel', $pb.PbFieldType.OE, defaultOrMaker: NLUModel.SNIPS, valueOf: NLUModel.valueOf, enumValues: NLUModel.values) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + RecognizeTextControl clone() => RecognizeTextControl()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + RecognizeTextControl copyWith(void Function(RecognizeTextControl) updates) => super.copyWith((message) => updates(message as RecognizeTextControl)) as RecognizeTextControl; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static RecognizeTextControl create() => RecognizeTextControl._(); + RecognizeTextControl createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static RecognizeTextControl getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static RecognizeTextControl? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get textCommand => $_getSZ(0); + @$pb.TagNumber(1) + set textCommand($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasTextCommand() => $_has(0); + @$pb.TagNumber(1) + void clearTextCommand() => clearField(1); + + @$pb.TagNumber(2) + NLUModel get nluModel => $_getN(1); + @$pb.TagNumber(2) + set nluModel(NLUModel v) { setField(2, v); } + @$pb.TagNumber(2) + $core.bool hasNluModel() => $_has(1); + @$pb.TagNumber(2) + void clearNluModel() => clearField(2); +} + +class IntentSlot extends $pb.GeneratedMessage { + factory IntentSlot({ + $core.String? name, + $core.String? value, + }) { + final $result = create(); + if (name != null) { + $result.name = name; + } + if (value != null) { + $result.value = value; + } + return $result; + } + IntentSlot._() : super(); + factory IntentSlot.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory IntentSlot.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'IntentSlot', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'name') + ..aOS(2, _omitFieldNames ? '' : 'value') + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + IntentSlot clone() => IntentSlot()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + IntentSlot copyWith(void Function(IntentSlot) updates) => super.copyWith((message) => updates(message as IntentSlot)) as IntentSlot; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static IntentSlot create() => IntentSlot._(); + IntentSlot createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static IntentSlot getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static IntentSlot? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get name => $_getSZ(0); + @$pb.TagNumber(1) + set name($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasName() => $_has(0); + @$pb.TagNumber(1) + void clearName() => clearField(1); + + @$pb.TagNumber(2) + $core.String get value => $_getSZ(1); + @$pb.TagNumber(2) + set value($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasValue() => $_has(1); + @$pb.TagNumber(2) + void clearValue() => clearField(2); +} + +class RecognizeResult extends $pb.GeneratedMessage { + factory RecognizeResult({ + $core.String? command, + $core.String? intent, + $core.Iterable? intentSlots, + $core.String? streamId, + RecognizeStatusType? status, + }) { + final $result = create(); + if (command != null) { + $result.command = command; + } + if (intent != null) { + $result.intent = intent; + } + if (intentSlots != null) { + $result.intentSlots.addAll(intentSlots); + } + if (streamId != null) { + $result.streamId = streamId; + } + if (status != null) { + $result.status = status; + } + return $result; + } + RecognizeResult._() : super(); + factory RecognizeResult.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory RecognizeResult.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'RecognizeResult', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'command') + ..aOS(2, _omitFieldNames ? '' : 'intent') + ..pc(3, _omitFieldNames ? '' : 'intentSlots', $pb.PbFieldType.PM, subBuilder: IntentSlot.create) + ..aOS(4, _omitFieldNames ? '' : 'streamId') + ..e(5, _omitFieldNames ? '' : 'status', $pb.PbFieldType.OE, defaultOrMaker: RecognizeStatusType.REC_ERROR, valueOf: RecognizeStatusType.valueOf, enumValues: RecognizeStatusType.values) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + RecognizeResult clone() => RecognizeResult()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + RecognizeResult copyWith(void Function(RecognizeResult) updates) => super.copyWith((message) => updates(message as RecognizeResult)) as RecognizeResult; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static RecognizeResult create() => RecognizeResult._(); + RecognizeResult createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static RecognizeResult getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static RecognizeResult? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get command => $_getSZ(0); + @$pb.TagNumber(1) + set command($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasCommand() => $_has(0); + @$pb.TagNumber(1) + void clearCommand() => clearField(1); + + @$pb.TagNumber(2) + $core.String get intent => $_getSZ(1); + @$pb.TagNumber(2) + set intent($core.String v) { $_setString(1, v); } + @$pb.TagNumber(2) + $core.bool hasIntent() => $_has(1); + @$pb.TagNumber(2) + void clearIntent() => clearField(2); + + @$pb.TagNumber(3) + $core.List get intentSlots => $_getList(2); + + @$pb.TagNumber(4) + $core.String get streamId => $_getSZ(3); + @$pb.TagNumber(4) + set streamId($core.String v) { $_setString(3, v); } + @$pb.TagNumber(4) + $core.bool hasStreamId() => $_has(3); + @$pb.TagNumber(4) + void clearStreamId() => clearField(4); + + @$pb.TagNumber(5) + RecognizeStatusType get status => $_getN(4); + @$pb.TagNumber(5) + set status(RecognizeStatusType v) { setField(5, v); } + @$pb.TagNumber(5) + $core.bool hasStatus() => $_has(4); + @$pb.TagNumber(5) + void clearStatus() => clearField(5); +} + +class ExecuteInput extends $pb.GeneratedMessage { + factory ExecuteInput({ + $core.String? intent, + $core.Iterable? intentSlots, + }) { + final $result = create(); + if (intent != null) { + $result.intent = intent; + } + if (intentSlots != null) { + $result.intentSlots.addAll(intentSlots); + } + return $result; + } + ExecuteInput._() : super(); + factory ExecuteInput.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory ExecuteInput.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ExecuteInput', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'intent') + ..pc(2, _omitFieldNames ? '' : 'intentSlots', $pb.PbFieldType.PM, subBuilder: IntentSlot.create) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + ExecuteInput clone() => ExecuteInput()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + ExecuteInput copyWith(void Function(ExecuteInput) updates) => super.copyWith((message) => updates(message as ExecuteInput)) as ExecuteInput; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static ExecuteInput create() => ExecuteInput._(); + ExecuteInput createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static ExecuteInput getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static ExecuteInput? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get intent => $_getSZ(0); + @$pb.TagNumber(1) + set intent($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasIntent() => $_has(0); + @$pb.TagNumber(1) + void clearIntent() => clearField(1); + + @$pb.TagNumber(2) + $core.List get intentSlots => $_getList(1); +} + +class ExecuteResult extends $pb.GeneratedMessage { + factory ExecuteResult({ + $core.String? response, + ExecuteStatusType? status, + }) { + final $result = create(); + if (response != null) { + $result.response = response; + } + if (status != null) { + $result.status = status; + } + return $result; + } + ExecuteResult._() : super(); + factory ExecuteResult.fromBuffer($core.List<$core.int> i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromBuffer(i, r); + factory ExecuteResult.fromJson($core.String i, [$pb.ExtensionRegistry r = $pb.ExtensionRegistry.EMPTY]) => create()..mergeFromJson(i, r); + + static final $pb.BuilderInfo _i = $pb.BuilderInfo(_omitMessageNames ? '' : 'ExecuteResult', createEmptyInstance: create) + ..aOS(1, _omitFieldNames ? '' : 'response') + ..e(2, _omitFieldNames ? '' : 'status', $pb.PbFieldType.OE, defaultOrMaker: ExecuteStatusType.EXEC_ERROR, valueOf: ExecuteStatusType.valueOf, enumValues: ExecuteStatusType.values) + ..hasRequiredFields = false + ; + + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.deepCopy] instead. ' + 'Will be removed in next major version') + ExecuteResult clone() => ExecuteResult()..mergeFromMessage(this); + @$core.Deprecated( + 'Using this can add significant overhead to your binary. ' + 'Use [GeneratedMessageGenericExtensions.rebuild] instead. ' + 'Will be removed in next major version') + ExecuteResult copyWith(void Function(ExecuteResult) updates) => super.copyWith((message) => updates(message as ExecuteResult)) as ExecuteResult; + + $pb.BuilderInfo get info_ => _i; + + @$core.pragma('dart2js:noInline') + static ExecuteResult create() => ExecuteResult._(); + ExecuteResult createEmptyInstance() => create(); + static $pb.PbList createRepeated() => $pb.PbList(); + @$core.pragma('dart2js:noInline') + static ExecuteResult getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor(create); + static ExecuteResult? _defaultInstance; + + @$pb.TagNumber(1) + $core.String get response => $_getSZ(0); + @$pb.TagNumber(1) + set response($core.String v) { $_setString(0, v); } + @$pb.TagNumber(1) + $core.bool hasResponse() => $_has(0); + @$pb.TagNumber(1) + void clearResponse() => clearField(1); + + @$pb.TagNumber(2) + ExecuteStatusType get status => $_getN(1); + @$pb.TagNumber(2) + set status(ExecuteStatusType v) { setField(2, v); } + @$pb.TagNumber(2) + $core.bool hasStatus() => $_has(1); + @$pb.TagNumber(2) + void clearStatus() => clearField(2); +} + +class VoiceAgentServiceApi { + $pb.RpcClient _client; + VoiceAgentServiceApi(this._client); + + $async.Future checkServiceStatus($pb.ClientContext? ctx, Empty request) => + _client.invoke(ctx, 'VoiceAgentService', 'CheckServiceStatus', request, ServiceStatus()) + ; + $async.Future s_DetectWakeWord($pb.ClientContext? ctx, VoiceAudio request) => + _client.invoke(ctx, 'VoiceAgentService', 'S_DetectWakeWord', request, WakeWordStatus()) + ; + $async.Future detectWakeWord($pb.ClientContext? ctx, Empty request) => + _client.invoke(ctx, 'VoiceAgentService', 'DetectWakeWord', request, WakeWordStatus()) + ; + $async.Future s_RecognizeVoiceCommand($pb.ClientContext? ctx, S_RecognizeVoiceControl request) => + _client.invoke(ctx, 'VoiceAgentService', 'S_RecognizeVoiceCommand', request, RecognizeResult()) + ; + $async.Future recognizeVoiceCommand($pb.ClientContext? ctx, RecognizeVoiceControl request) => + _client.invoke(ctx, 'VoiceAgentService', 'RecognizeVoiceCommand', request, RecognizeResult()) + ; + $async.Future recognizeTextCommand($pb.ClientContext? ctx, RecognizeTextControl request) => + _client.invoke(ctx, 'VoiceAgentService', 'RecognizeTextCommand', request, RecognizeResult()) + ; + $async.Future executeCommand($pb.ClientContext? ctx, ExecuteInput request) => + _client.invoke(ctx, 'VoiceAgentService', 'ExecuteCommand', request, ExecuteResult()) + ; +} + + +const _omitFieldNames = $core.bool.fromEnvironment('protobuf.omit_field_names'); +const _omitMessageNames = $core.bool.fromEnvironment('protobuf.omit_message_names'); diff --git a/protos/lib/src/generated/voice_agent/voice_agent.pbenum.dart b/protos/lib/src/generated/voice_agent/voice_agent.pbenum.dart new file mode 100644 index 0000000..51e7427 --- /dev/null +++ b/protos/lib/src/generated/voice_agent/voice_agent.pbenum.dart @@ -0,0 +1,138 @@ +// +// Generated code. Do not modify. +// source: voice_agent.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:core' as $core; + +import 'package:protobuf/protobuf.dart' as $pb; + +class STTFramework extends $pb.ProtobufEnum { + static const STTFramework VOSK = STTFramework._(0, _omitEnumNames ? '' : 'VOSK'); + static const STTFramework WHISPER = STTFramework._(1, _omitEnumNames ? '' : 'WHISPER'); + + static const $core.List values = [ + VOSK, + WHISPER, + ]; + + static final $core.Map<$core.int, STTFramework> _byValue = $pb.ProtobufEnum.initByValue(values); + static STTFramework? valueOf($core.int value) => _byValue[value]; + + const STTFramework._($core.int v, $core.String n) : super(v, n); +} + +class OnlineMode extends $pb.ProtobufEnum { + static const OnlineMode ONLINE = OnlineMode._(0, _omitEnumNames ? '' : 'ONLINE'); + static const OnlineMode OFFLINE = OnlineMode._(1, _omitEnumNames ? '' : 'OFFLINE'); + + static const $core.List values = [ + ONLINE, + OFFLINE, + ]; + + static final $core.Map<$core.int, OnlineMode> _byValue = $pb.ProtobufEnum.initByValue(values); + static OnlineMode? valueOf($core.int value) => _byValue[value]; + + const OnlineMode._($core.int v, $core.String n) : super(v, n); +} + +class RecordAction extends $pb.ProtobufEnum { + static const RecordAction START = RecordAction._(0, _omitEnumNames ? '' : 'START'); + static const RecordAction STOP = RecordAction._(1, _omitEnumNames ? '' : 'STOP'); + + static const $core.List values = [ + START, + STOP, + ]; + + static final $core.Map<$core.int, RecordAction> _byValue = $pb.ProtobufEnum.initByValue(values); + static RecordAction? valueOf($core.int value) => _byValue[value]; + + const RecordAction._($core.int v, $core.String n) : super(v, n); +} + +class NLUModel extends $pb.ProtobufEnum { + static const NLUModel SNIPS = NLUModel._(0, _omitEnumNames ? '' : 'SNIPS'); + static const NLUModel RASA = NLUModel._(1, _omitEnumNames ? '' : 'RASA'); + + static const $core.List values = [ + SNIPS, + RASA, + ]; + + static final $core.Map<$core.int, NLUModel> _byValue = $pb.ProtobufEnum.initByValue(values); + static NLUModel? valueOf($core.int value) => _byValue[value]; + + const NLUModel._($core.int v, $core.String n) : super(v, n); +} + +class RecordMode extends $pb.ProtobufEnum { + static const RecordMode MANUAL = RecordMode._(0, _omitEnumNames ? '' : 'MANUAL'); + static const RecordMode AUTO = RecordMode._(1, _omitEnumNames ? '' : 'AUTO'); + + static const $core.List values = [ + MANUAL, + AUTO, + ]; + + static final $core.Map<$core.int, RecordMode> _byValue = $pb.ProtobufEnum.initByValue(values); + static RecordMode? valueOf($core.int value) => _byValue[value]; + + const RecordMode._($core.int v, $core.String n) : super(v, n); +} + +class RecognizeStatusType extends $pb.ProtobufEnum { + static const RecognizeStatusType REC_ERROR = RecognizeStatusType._(0, _omitEnumNames ? '' : 'REC_ERROR'); + static const RecognizeStatusType REC_SUCCESS = RecognizeStatusType._(1, _omitEnumNames ? '' : 'REC_SUCCESS'); + static const RecognizeStatusType REC_PROCESSING = RecognizeStatusType._(2, _omitEnumNames ? '' : 'REC_PROCESSING'); + static const RecognizeStatusType VOICE_NOT_RECOGNIZED = RecognizeStatusType._(3, _omitEnumNames ? '' : 'VOICE_NOT_RECOGNIZED'); + static const RecognizeStatusType INTENT_NOT_RECOGNIZED = RecognizeStatusType._(4, _omitEnumNames ? '' : 'INTENT_NOT_RECOGNIZED'); + static const RecognizeStatusType TEXT_NOT_RECOGNIZED = RecognizeStatusType._(5, _omitEnumNames ? '' : 'TEXT_NOT_RECOGNIZED'); + static const RecognizeStatusType NLU_MODEL_NOT_SUPPORTED = RecognizeStatusType._(6, _omitEnumNames ? '' : 'NLU_MODEL_NOT_SUPPORTED'); + + static const $core.List values = [ + REC_ERROR, + REC_SUCCESS, + REC_PROCESSING, + VOICE_NOT_RECOGNIZED, + INTENT_NOT_RECOGNIZED, + TEXT_NOT_RECOGNIZED, + NLU_MODEL_NOT_SUPPORTED, + ]; + + static final $core.Map<$core.int, RecognizeStatusType> _byValue = $pb.ProtobufEnum.initByValue(values); + static RecognizeStatusType? valueOf($core.int value) => _byValue[value]; + + const RecognizeStatusType._($core.int v, $core.String n) : super(v, n); +} + +class ExecuteStatusType extends $pb.ProtobufEnum { + static const ExecuteStatusType EXEC_ERROR = ExecuteStatusType._(0, _omitEnumNames ? '' : 'EXEC_ERROR'); + static const ExecuteStatusType EXEC_SUCCESS = ExecuteStatusType._(1, _omitEnumNames ? '' : 'EXEC_SUCCESS'); + static const ExecuteStatusType KUKSA_CONN_ERROR = ExecuteStatusType._(2, _omitEnumNames ? '' : 'KUKSA_CONN_ERROR'); + static const ExecuteStatusType INTENT_NOT_SUPPORTED = ExecuteStatusType._(3, _omitEnumNames ? '' : 'INTENT_NOT_SUPPORTED'); + static const ExecuteStatusType INTENT_SLOTS_INCOMPLETE = ExecuteStatusType._(4, _omitEnumNames ? '' : 'INTENT_SLOTS_INCOMPLETE'); + + static const $core.List values = [ + EXEC_ERROR, + EXEC_SUCCESS, + KUKSA_CONN_ERROR, + INTENT_NOT_SUPPORTED, + INTENT_SLOTS_INCOMPLETE, + ]; + + static final $core.Map<$core.int, ExecuteStatusType> _byValue = $pb.ProtobufEnum.initByValue(values); + static ExecuteStatusType? valueOf($core.int value) => _byValue[value]; + + const ExecuteStatusType._($core.int v, $core.String n) : super(v, n); +} + + +const _omitEnumNames = $core.bool.fromEnvironment('protobuf.omit_enum_names'); diff --git a/protos/lib/src/generated/voice_agent/voice_agent.pbgrpc.dart b/protos/lib/src/generated/voice_agent/voice_agent.pbgrpc.dart new file mode 100644 index 0000000..5b31b44 --- /dev/null +++ b/protos/lib/src/generated/voice_agent/voice_agent.pbgrpc.dart @@ -0,0 +1,167 @@ +// +// Generated code. Do not modify. +// source: voice_agent.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:async' as $async; +import 'dart:core' as $core; + +import 'package:grpc/service_api.dart' as $grpc; +import 'package:protobuf/protobuf.dart' as $pb; + +import 'voice_agent.pb.dart' as $0; + +export 'voice_agent.pb.dart'; + +// @$pb.GrpcServiceName('VoiceAgentService') +class VoiceAgentServiceClient extends $grpc.Client { + static final _$checkServiceStatus = $grpc.ClientMethod<$0.Empty, $0.ServiceStatus>( + '/VoiceAgentService/CheckServiceStatus', + ($0.Empty value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.ServiceStatus.fromBuffer(value)); + static final _$s_DetectWakeWord = $grpc.ClientMethod<$0.VoiceAudio, $0.WakeWordStatus>( + '/VoiceAgentService/S_DetectWakeWord', + ($0.VoiceAudio value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.WakeWordStatus.fromBuffer(value)); + static final _$detectWakeWord = $grpc.ClientMethod<$0.Empty, $0.WakeWordStatus>( + '/VoiceAgentService/DetectWakeWord', + ($0.Empty value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.WakeWordStatus.fromBuffer(value)); + static final _$s_RecognizeVoiceCommand = $grpc.ClientMethod<$0.S_RecognizeVoiceControl, $0.RecognizeResult>( + '/VoiceAgentService/S_RecognizeVoiceCommand', + ($0.S_RecognizeVoiceControl value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.RecognizeResult.fromBuffer(value)); + static final _$recognizeVoiceCommand = $grpc.ClientMethod<$0.RecognizeVoiceControl, $0.RecognizeResult>( + '/VoiceAgentService/RecognizeVoiceCommand', + ($0.RecognizeVoiceControl value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.RecognizeResult.fromBuffer(value)); + static final _$recognizeTextCommand = $grpc.ClientMethod<$0.RecognizeTextControl, $0.RecognizeResult>( + '/VoiceAgentService/RecognizeTextCommand', + ($0.RecognizeTextControl value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.RecognizeResult.fromBuffer(value)); + static final _$executeCommand = $grpc.ClientMethod<$0.ExecuteInput, $0.ExecuteResult>( + '/VoiceAgentService/ExecuteCommand', + ($0.ExecuteInput value) => value.writeToBuffer(), + ($core.List<$core.int> value) => $0.ExecuteResult.fromBuffer(value)); + + VoiceAgentServiceClient($grpc.ClientChannel channel, + {$grpc.CallOptions? options, + $core.Iterable<$grpc.ClientInterceptor>? interceptors}) + : super(channel, options: options, + interceptors: interceptors); + + $grpc.ResponseFuture<$0.ServiceStatus> checkServiceStatus($0.Empty request, {$grpc.CallOptions? options}) { + return $createUnaryCall(_$checkServiceStatus, request, options: options); + } + + $grpc.ResponseStream<$0.WakeWordStatus> s_DetectWakeWord($async.Stream<$0.VoiceAudio> request, {$grpc.CallOptions? options}) { + return $createStreamingCall(_$s_DetectWakeWord, request, options: options); + } + + $grpc.ResponseStream<$0.WakeWordStatus> detectWakeWord($0.Empty request, {$grpc.CallOptions? options}) { + return $createStreamingCall(_$detectWakeWord, $async.Stream.fromIterable([request]), options: options); + } + + $grpc.ResponseFuture<$0.RecognizeResult> s_RecognizeVoiceCommand($async.Stream<$0.S_RecognizeVoiceControl> request, {$grpc.CallOptions? options}) { + return $createStreamingCall(_$s_RecognizeVoiceCommand, request, options: options).single; + } + + $grpc.ResponseFuture<$0.RecognizeResult> recognizeVoiceCommand($async.Stream<$0.RecognizeVoiceControl> request, {$grpc.CallOptions? options}) { + return $createStreamingCall(_$recognizeVoiceCommand, request, options: options).single; + } + + $grpc.ResponseFuture<$0.RecognizeResult> recognizeTextCommand($0.RecognizeTextControl request, {$grpc.CallOptions? options}) { + return $createUnaryCall(_$recognizeTextCommand, request, options: options); + } + + $grpc.ResponseFuture<$0.ExecuteResult> executeCommand($0.ExecuteInput request, {$grpc.CallOptions? options}) { + return $createUnaryCall(_$executeCommand, request, options: options); + } +} + +// @$pb.GrpcServiceName('VoiceAgentService') +abstract class VoiceAgentServiceBase extends $grpc.Service { + $core.String get $name => 'VoiceAgentService'; + + VoiceAgentServiceBase() { + $addMethod($grpc.ServiceMethod<$0.Empty, $0.ServiceStatus>( + 'CheckServiceStatus', + checkServiceStatus_Pre, + false, + false, + ($core.List<$core.int> value) => $0.Empty.fromBuffer(value), + ($0.ServiceStatus value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.VoiceAudio, $0.WakeWordStatus>( + 'S_DetectWakeWord', + s_DetectWakeWord, + true, + true, + ($core.List<$core.int> value) => $0.VoiceAudio.fromBuffer(value), + ($0.WakeWordStatus value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.Empty, $0.WakeWordStatus>( + 'DetectWakeWord', + detectWakeWord_Pre, + false, + true, + ($core.List<$core.int> value) => $0.Empty.fromBuffer(value), + ($0.WakeWordStatus value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.S_RecognizeVoiceControl, $0.RecognizeResult>( + 'S_RecognizeVoiceCommand', + s_RecognizeVoiceCommand, + true, + false, + ($core.List<$core.int> value) => $0.S_RecognizeVoiceControl.fromBuffer(value), + ($0.RecognizeResult value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.RecognizeVoiceControl, $0.RecognizeResult>( + 'RecognizeVoiceCommand', + recognizeVoiceCommand, + true, + false, + ($core.List<$core.int> value) => $0.RecognizeVoiceControl.fromBuffer(value), + ($0.RecognizeResult value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.RecognizeTextControl, $0.RecognizeResult>( + 'RecognizeTextCommand', + recognizeTextCommand_Pre, + false, + false, + ($core.List<$core.int> value) => $0.RecognizeTextControl.fromBuffer(value), + ($0.RecognizeResult value) => value.writeToBuffer())); + $addMethod($grpc.ServiceMethod<$0.ExecuteInput, $0.ExecuteResult>( + 'ExecuteCommand', + executeCommand_Pre, + false, + false, + ($core.List<$core.int> value) => $0.ExecuteInput.fromBuffer(value), + ($0.ExecuteResult value) => value.writeToBuffer())); + } + + $async.Future<$0.ServiceStatus> checkServiceStatus_Pre($grpc.ServiceCall call, $async.Future<$0.Empty> request) async { + return checkServiceStatus(call, await request); + } + + $async.Stream<$0.WakeWordStatus> detectWakeWord_Pre($grpc.ServiceCall call, $async.Future<$0.Empty> request) async* { + yield* detectWakeWord(call, await request); + } + + $async.Future<$0.RecognizeResult> recognizeTextCommand_Pre($grpc.ServiceCall call, $async.Future<$0.RecognizeTextControl> request) async { + return recognizeTextCommand(call, await request); + } + + $async.Future<$0.ExecuteResult> executeCommand_Pre($grpc.ServiceCall call, $async.Future<$0.ExecuteInput> request) async { + return executeCommand(call, await request); + } + + $async.Future<$0.ServiceStatus> checkServiceStatus($grpc.ServiceCall call, $0.Empty request); + $async.Stream<$0.WakeWordStatus> s_DetectWakeWord($grpc.ServiceCall call, $async.Stream<$0.VoiceAudio> request); + $async.Stream<$0.WakeWordStatus> detectWakeWord($grpc.ServiceCall call, $0.Empty request); + $async.Future<$0.RecognizeResult> s_RecognizeVoiceCommand($grpc.ServiceCall call, $async.Stream<$0.S_RecognizeVoiceControl> request); + $async.Future<$0.RecognizeResult> recognizeVoiceCommand($grpc.ServiceCall call, $async.Stream<$0.RecognizeVoiceControl> request); + $async.Future<$0.RecognizeResult> recognizeTextCommand($grpc.ServiceCall call, $0.RecognizeTextControl request); + $async.Future<$0.ExecuteResult> executeCommand($grpc.ServiceCall call, $0.ExecuteInput request); +} diff --git a/protos/lib/src/generated/voice_agent/voice_agent.pbjson.dart b/protos/lib/src/generated/voice_agent/voice_agent.pbjson.dart new file mode 100644 index 0000000..1b8365d --- /dev/null +++ b/protos/lib/src/generated/voice_agent/voice_agent.pbjson.dart @@ -0,0 +1,324 @@ +// +// Generated code. Do not modify. +// source: voice_agent.proto +// +// @dart = 2.12 + +// ignore_for_file: annotate_overrides, camel_case_types, comment_references +// ignore_for_file: constant_identifier_names, library_prefixes +// ignore_for_file: non_constant_identifier_names, prefer_final_fields +// ignore_for_file: unnecessary_import, unnecessary_this, unused_import + +import 'dart:convert' as $convert; +import 'dart:core' as $core; +import 'dart:typed_data' as $typed_data; + +@$core.Deprecated('Use sTTFrameworkDescriptor instead') +const STTFramework$json = { + '1': 'STTFramework', + '2': [ + {'1': 'VOSK', '2': 0}, + {'1': 'WHISPER', '2': 1}, + ], +}; + +/// Descriptor for `STTFramework`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List sTTFrameworkDescriptor = $convert.base64Decode( + 'CgxTVFRGcmFtZXdvcmsSCAoEVk9TSxAAEgsKB1dISVNQRVIQAQ=='); + +@$core.Deprecated('Use onlineModeDescriptor instead') +const OnlineMode$json = { + '1': 'OnlineMode', + '2': [ + {'1': 'ONLINE', '2': 0}, + {'1': 'OFFLINE', '2': 1}, + ], +}; + +/// Descriptor for `OnlineMode`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List onlineModeDescriptor = $convert.base64Decode( + 'CgpPbmxpbmVNb2RlEgoKBk9OTElORRAAEgsKB09GRkxJTkUQAQ=='); + +@$core.Deprecated('Use recordActionDescriptor instead') +const RecordAction$json = { + '1': 'RecordAction', + '2': [ + {'1': 'START', '2': 0}, + {'1': 'STOP', '2': 1}, + ], +}; + +/// Descriptor for `RecordAction`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List recordActionDescriptor = $convert.base64Decode( + 'CgxSZWNvcmRBY3Rpb24SCQoFU1RBUlQQABIICgRTVE9QEAE='); + +@$core.Deprecated('Use nLUModelDescriptor instead') +const NLUModel$json = { + '1': 'NLUModel', + '2': [ + {'1': 'SNIPS', '2': 0}, + {'1': 'RASA', '2': 1}, + ], +}; + +/// Descriptor for `NLUModel`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List nLUModelDescriptor = $convert.base64Decode( + 'CghOTFVNb2RlbBIJCgVTTklQUxAAEggKBFJBU0EQAQ=='); + +@$core.Deprecated('Use recordModeDescriptor instead') +const RecordMode$json = { + '1': 'RecordMode', + '2': [ + {'1': 'MANUAL', '2': 0}, + {'1': 'AUTO', '2': 1}, + ], +}; + +/// Descriptor for `RecordMode`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List recordModeDescriptor = $convert.base64Decode( + 'CgpSZWNvcmRNb2RlEgoKBk1BTlVBTBAAEggKBEFVVE8QAQ=='); + +@$core.Deprecated('Use recognizeStatusTypeDescriptor instead') +const RecognizeStatusType$json = { + '1': 'RecognizeStatusType', + '2': [ + {'1': 'REC_ERROR', '2': 0}, + {'1': 'REC_SUCCESS', '2': 1}, + {'1': 'REC_PROCESSING', '2': 2}, + {'1': 'VOICE_NOT_RECOGNIZED', '2': 3}, + {'1': 'INTENT_NOT_RECOGNIZED', '2': 4}, + {'1': 'TEXT_NOT_RECOGNIZED', '2': 5}, + {'1': 'NLU_MODEL_NOT_SUPPORTED', '2': 6}, + ], +}; + +/// Descriptor for `RecognizeStatusType`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List recognizeStatusTypeDescriptor = $convert.base64Decode( + 'ChNSZWNvZ25pemVTdGF0dXNUeXBlEg0KCVJFQ19FUlJPUhAAEg8KC1JFQ19TVUNDRVNTEAESEg' + 'oOUkVDX1BST0NFU1NJTkcQAhIYChRWT0lDRV9OT1RfUkVDT0dOSVpFRBADEhkKFUlOVEVOVF9O' + 'T1RfUkVDT0dOSVpFRBAEEhcKE1RFWFRfTk9UX1JFQ09HTklaRUQQBRIbChdOTFVfTU9ERUxfTk' + '9UX1NVUFBPUlRFRBAG'); + +@$core.Deprecated('Use executeStatusTypeDescriptor instead') +const ExecuteStatusType$json = { + '1': 'ExecuteStatusType', + '2': [ + {'1': 'EXEC_ERROR', '2': 0}, + {'1': 'EXEC_SUCCESS', '2': 1}, + {'1': 'KUKSA_CONN_ERROR', '2': 2}, + {'1': 'INTENT_NOT_SUPPORTED', '2': 3}, + {'1': 'INTENT_SLOTS_INCOMPLETE', '2': 4}, + ], +}; + +/// Descriptor for `ExecuteStatusType`. Decode as a `google.protobuf.EnumDescriptorProto`. +final $typed_data.Uint8List executeStatusTypeDescriptor = $convert.base64Decode( + 'ChFFeGVjdXRlU3RhdHVzVHlwZRIOCgpFWEVDX0VSUk9SEAASEAoMRVhFQ19TVUNDRVNTEAESFA' + 'oQS1VLU0FfQ09OTl9FUlJPUhACEhgKFElOVEVOVF9OT1RfU1VQUE9SVEVEEAMSGwoXSU5URU5U' + 'X1NMT1RTX0lOQ09NUExFVEUQBA=='); + +@$core.Deprecated('Use emptyDescriptor instead') +const Empty$json = { + '1': 'Empty', +}; + +/// Descriptor for `Empty`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List emptyDescriptor = $convert.base64Decode( + 'CgVFbXB0eQ=='); + +@$core.Deprecated('Use serviceStatusDescriptor instead') +const ServiceStatus$json = { + '1': 'ServiceStatus', + '2': [ + {'1': 'version', '3': 1, '4': 1, '5': 9, '10': 'version'}, + {'1': 'status', '3': 2, '4': 1, '5': 8, '10': 'status'}, + {'1': 'wake_word', '3': 3, '4': 1, '5': 9, '10': 'wakeWord'}, + ], +}; + +/// Descriptor for `ServiceStatus`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List serviceStatusDescriptor = $convert.base64Decode( + 'Cg1TZXJ2aWNlU3RhdHVzEhgKB3ZlcnNpb24YASABKAlSB3ZlcnNpb24SFgoGc3RhdHVzGAIgAS' + 'gIUgZzdGF0dXMSGwoJd2FrZV93b3JkGAMgASgJUgh3YWtlV29yZA=='); + +@$core.Deprecated('Use voiceAudioDescriptor instead') +const VoiceAudio$json = { + '1': 'VoiceAudio', + '2': [ + {'1': 'audio_chunk', '3': 1, '4': 1, '5': 12, '10': 'audioChunk'}, + {'1': 'audio_format', '3': 2, '4': 1, '5': 9, '10': 'audioFormat'}, + {'1': 'sample_rate', '3': 3, '4': 1, '5': 5, '10': 'sampleRate'}, + {'1': 'language', '3': 4, '4': 1, '5': 9, '10': 'language'}, + ], +}; + +/// Descriptor for `VoiceAudio`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List voiceAudioDescriptor = $convert.base64Decode( + 'CgpWb2ljZUF1ZGlvEh8KC2F1ZGlvX2NodW5rGAEgASgMUgphdWRpb0NodW5rEiEKDGF1ZGlvX2' + 'Zvcm1hdBgCIAEoCVILYXVkaW9Gb3JtYXQSHwoLc2FtcGxlX3JhdGUYAyABKAVSCnNhbXBsZVJh' + 'dGUSGgoIbGFuZ3VhZ2UYBCABKAlSCGxhbmd1YWdl'); + +@$core.Deprecated('Use wakeWordStatusDescriptor instead') +const WakeWordStatus$json = { + '1': 'WakeWordStatus', + '2': [ + {'1': 'status', '3': 1, '4': 1, '5': 8, '10': 'status'}, + ], +}; + +/// Descriptor for `WakeWordStatus`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List wakeWordStatusDescriptor = $convert.base64Decode( + 'Cg5XYWtlV29yZFN0YXR1cxIWCgZzdGF0dXMYASABKAhSBnN0YXR1cw=='); + +@$core.Deprecated('Use s_RecognizeVoiceControlDescriptor instead') +const S_RecognizeVoiceControl$json = { + '1': 'S_RecognizeVoiceControl', + '2': [ + {'1': 'audio_stream', '3': 1, '4': 1, '5': 11, '6': '.VoiceAudio', '10': 'audioStream'}, + {'1': 'nlu_model', '3': 2, '4': 1, '5': 14, '6': '.NLUModel', '10': 'nluModel'}, + {'1': 'stream_id', '3': 3, '4': 1, '5': 9, '10': 'streamId'}, + {'1': 'stt_framework', '3': 4, '4': 1, '5': 14, '6': '.STTFramework', '10': 'sttFramework'}, + ], +}; + +/// Descriptor for `S_RecognizeVoiceControl`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List s_RecognizeVoiceControlDescriptor = $convert.base64Decode( + 'ChdTX1JlY29nbml6ZVZvaWNlQ29udHJvbBIuCgxhdWRpb19zdHJlYW0YASABKAsyCy5Wb2ljZU' + 'F1ZGlvUgthdWRpb1N0cmVhbRImCglubHVfbW9kZWwYAiABKA4yCS5OTFVNb2RlbFIIbmx1TW9k' + 'ZWwSGwoJc3RyZWFtX2lkGAMgASgJUghzdHJlYW1JZBIyCg1zdHRfZnJhbWV3b3JrGAQgASgOMg' + '0uU1RURnJhbWV3b3JrUgxzdHRGcmFtZXdvcms='); + +@$core.Deprecated('Use recognizeVoiceControlDescriptor instead') +const RecognizeVoiceControl$json = { + '1': 'RecognizeVoiceControl', + '2': [ + {'1': 'action', '3': 1, '4': 1, '5': 14, '6': '.RecordAction', '10': 'action'}, + {'1': 'nlu_model', '3': 2, '4': 1, '5': 14, '6': '.NLUModel', '10': 'nluModel'}, + {'1': 'record_mode', '3': 3, '4': 1, '5': 14, '6': '.RecordMode', '10': 'recordMode'}, + {'1': 'stream_id', '3': 4, '4': 1, '5': 9, '10': 'streamId'}, + {'1': 'stt_framework', '3': 5, '4': 1, '5': 14, '6': '.STTFramework', '10': 'sttFramework'}, + {'1': 'online_mode', '3': 6, '4': 1, '5': 14, '6': '.OnlineMode', '10': 'onlineMode'}, + ], +}; + +/// Descriptor for `RecognizeVoiceControl`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List recognizeVoiceControlDescriptor = $convert.base64Decode( + 'ChVSZWNvZ25pemVWb2ljZUNvbnRyb2wSJQoGYWN0aW9uGAEgASgOMg0uUmVjb3JkQWN0aW9uUg' + 'ZhY3Rpb24SJgoJbmx1X21vZGVsGAIgASgOMgkuTkxVTW9kZWxSCG5sdU1vZGVsEiwKC3JlY29y' + 'ZF9tb2RlGAMgASgOMgsuUmVjb3JkTW9kZVIKcmVjb3JkTW9kZRIbCglzdHJlYW1faWQYBCABKA' + 'lSCHN0cmVhbUlkEjIKDXN0dF9mcmFtZXdvcmsYBSABKA4yDS5TVFRGcmFtZXdvcmtSDHN0dEZy' + 'YW1ld29yaxIsCgtvbmxpbmVfbW9kZRgGIAEoDjILLk9ubGluZU1vZGVSCm9ubGluZU1vZGU='); + +@$core.Deprecated('Use recognizeTextControlDescriptor instead') +const RecognizeTextControl$json = { + '1': 'RecognizeTextControl', + '2': [ + {'1': 'text_command', '3': 1, '4': 1, '5': 9, '10': 'textCommand'}, + {'1': 'nlu_model', '3': 2, '4': 1, '5': 14, '6': '.NLUModel', '10': 'nluModel'}, + ], +}; + +/// Descriptor for `RecognizeTextControl`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List recognizeTextControlDescriptor = $convert.base64Decode( + 'ChRSZWNvZ25pemVUZXh0Q29udHJvbBIhCgx0ZXh0X2NvbW1hbmQYASABKAlSC3RleHRDb21tYW' + '5kEiYKCW5sdV9tb2RlbBgCIAEoDjIJLk5MVU1vZGVsUghubHVNb2RlbA=='); + +@$core.Deprecated('Use intentSlotDescriptor instead') +const IntentSlot$json = { + '1': 'IntentSlot', + '2': [ + {'1': 'name', '3': 1, '4': 1, '5': 9, '10': 'name'}, + {'1': 'value', '3': 2, '4': 1, '5': 9, '10': 'value'}, + ], +}; + +/// Descriptor for `IntentSlot`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List intentSlotDescriptor = $convert.base64Decode( + 'CgpJbnRlbnRTbG90EhIKBG5hbWUYASABKAlSBG5hbWUSFAoFdmFsdWUYAiABKAlSBXZhbHVl'); + +@$core.Deprecated('Use recognizeResultDescriptor instead') +const RecognizeResult$json = { + '1': 'RecognizeResult', + '2': [ + {'1': 'command', '3': 1, '4': 1, '5': 9, '10': 'command'}, + {'1': 'intent', '3': 2, '4': 1, '5': 9, '10': 'intent'}, + {'1': 'intent_slots', '3': 3, '4': 3, '5': 11, '6': '.IntentSlot', '10': 'intentSlots'}, + {'1': 'stream_id', '3': 4, '4': 1, '5': 9, '10': 'streamId'}, + {'1': 'status', '3': 5, '4': 1, '5': 14, '6': '.RecognizeStatusType', '10': 'status'}, + ], +}; + +/// Descriptor for `RecognizeResult`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List recognizeResultDescriptor = $convert.base64Decode( + 'Cg9SZWNvZ25pemVSZXN1bHQSGAoHY29tbWFuZBgBIAEoCVIHY29tbWFuZBIWCgZpbnRlbnQYAi' + 'ABKAlSBmludGVudBIuCgxpbnRlbnRfc2xvdHMYAyADKAsyCy5JbnRlbnRTbG90UgtpbnRlbnRT' + 'bG90cxIbCglzdHJlYW1faWQYBCABKAlSCHN0cmVhbUlkEiwKBnN0YXR1cxgFIAEoDjIULlJlY2' + '9nbml6ZVN0YXR1c1R5cGVSBnN0YXR1cw=='); + +@$core.Deprecated('Use executeInputDescriptor instead') +const ExecuteInput$json = { + '1': 'ExecuteInput', + '2': [ + {'1': 'intent', '3': 1, '4': 1, '5': 9, '10': 'intent'}, + {'1': 'intent_slots', '3': 2, '4': 3, '5': 11, '6': '.IntentSlot', '10': 'intentSlots'}, + ], +}; + +/// Descriptor for `ExecuteInput`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List executeInputDescriptor = $convert.base64Decode( + 'CgxFeGVjdXRlSW5wdXQSFgoGaW50ZW50GAEgASgJUgZpbnRlbnQSLgoMaW50ZW50X3Nsb3RzGA' + 'IgAygLMgsuSW50ZW50U2xvdFILaW50ZW50U2xvdHM='); + +@$core.Deprecated('Use executeResultDescriptor instead') +const ExecuteResult$json = { + '1': 'ExecuteResult', + '2': [ + {'1': 'response', '3': 1, '4': 1, '5': 9, '10': 'response'}, + {'1': 'status', '3': 2, '4': 1, '5': 14, '6': '.ExecuteStatusType', '10': 'status'}, + ], +}; + +/// Descriptor for `ExecuteResult`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List executeResultDescriptor = $convert.base64Decode( + 'Cg1FeGVjdXRlUmVzdWx0EhoKCHJlc3BvbnNlGAEgASgJUghyZXNwb25zZRIqCgZzdGF0dXMYAi' + 'ABKA4yEi5FeGVjdXRlU3RhdHVzVHlwZVIGc3RhdHVz'); + +const $core.Map<$core.String, $core.dynamic> VoiceAgentServiceBase$json = { + '1': 'VoiceAgentService', + '2': [ + {'1': 'CheckServiceStatus', '2': '.Empty', '3': '.ServiceStatus'}, + {'1': 'S_DetectWakeWord', '2': '.VoiceAudio', '3': '.WakeWordStatus', '5': true, '6': true}, + {'1': 'DetectWakeWord', '2': '.Empty', '3': '.WakeWordStatus', '6': true}, + {'1': 'S_RecognizeVoiceCommand', '2': '.S_RecognizeVoiceControl', '3': '.RecognizeResult', '5': true}, + {'1': 'RecognizeVoiceCommand', '2': '.RecognizeVoiceControl', '3': '.RecognizeResult', '5': true}, + {'1': 'RecognizeTextCommand', '2': '.RecognizeTextControl', '3': '.RecognizeResult'}, + {'1': 'ExecuteCommand', '2': '.ExecuteInput', '3': '.ExecuteResult'}, + ], +}; + +@$core.Deprecated('Use voiceAgentServiceDescriptor instead') +const $core.Map<$core.String, $core.Map<$core.String, $core.dynamic>> VoiceAgentServiceBase$messageJson = { + '.Empty': Empty$json, + '.ServiceStatus': ServiceStatus$json, + '.VoiceAudio': VoiceAudio$json, + '.WakeWordStatus': WakeWordStatus$json, + '.S_RecognizeVoiceControl': S_RecognizeVoiceControl$json, + '.RecognizeResult': RecognizeResult$json, + '.IntentSlot': IntentSlot$json, + '.RecognizeVoiceControl': RecognizeVoiceControl$json, + '.RecognizeTextControl': RecognizeTextControl$json, + '.ExecuteInput': ExecuteInput$json, + '.ExecuteResult': ExecuteResult$json, +}; + +/// Descriptor for `VoiceAgentService`. Decode as a `google.protobuf.ServiceDescriptorProto`. +final $typed_data.Uint8List voiceAgentServiceDescriptor = $convert.base64Decode( + 'ChFWb2ljZUFnZW50U2VydmljZRIsChJDaGVja1NlcnZpY2VTdGF0dXMSBi5FbXB0eRoOLlNlcn' + 'ZpY2VTdGF0dXMSNAoQU19EZXRlY3RXYWtlV29yZBILLlZvaWNlQXVkaW8aDy5XYWtlV29yZFN0' + 'YXR1cygBMAESKwoORGV0ZWN0V2FrZVdvcmQSBi5FbXB0eRoPLldha2VXb3JkU3RhdHVzMAESRw' + 'oXU19SZWNvZ25pemVWb2ljZUNvbW1hbmQSGC5TX1JlY29nbml6ZVZvaWNlQ29udHJvbBoQLlJl' + 'Y29nbml6ZVJlc3VsdCgBEkMKFVJlY29nbml6ZVZvaWNlQ29tbWFuZBIWLlJlY29nbml6ZVZvaW' + 'NlQ29udHJvbBoQLlJlY29nbml6ZVJlc3VsdCgBEj8KFFJlY29nbml6ZVRleHRDb21tYW5kEhUu' + 'UmVjb2duaXplVGV4dENvbnRyb2waEC5SZWNvZ25pemVSZXN1bHQSLwoORXhlY3V0ZUNvbW1hbm' + 'QSDS5FeGVjdXRlSW5wdXQaDi5FeGVjdXRlUmVzdWx0'); + diff --git a/protos/lib/val-api.dart b/protos/lib/val-api.dart index de6dfac..713a393 100644 --- a/protos/lib/val-api.dart +++ b/protos/lib/val-api.dart @@ -13,4 +13,9 @@ export 'src/generated/kuksa/val/v1/val.pbenum.dart'; export 'src/generated/kuksa/val/v1/val.pbjson.dart'; export 'src/generated/kuksa/val/v1/val.pbgrpc.dart'; +export 'src/generated/voice_agent/voice_agent.pb.dart'; +export 'src/generated/voice_agent/voice_agent.pbenum.dart'; +export 'src/generated/voice_agent/voice_agent.pbjson.dart'; +export 'src/generated/voice_agent/voice_agent.pbgrpc.dart'; + export 'package:grpc/grpc.dart'; diff --git a/protos/protos/voice_agent/voice_agent.proto b/protos/protos/voice_agent/voice_agent.proto new file mode 100644 index 0000000..09b9461 --- /dev/null +++ b/protos/protos/voice_agent/voice_agent.proto @@ -0,0 +1,119 @@ +syntax = "proto3"; + + +service VoiceAgentService { + rpc CheckServiceStatus(Empty) returns (ServiceStatus); + rpc S_DetectWakeWord(stream VoiceAudio) returns (stream WakeWordStatus); // Stream version of DetectWakeWord, assumes audio is coming from client + rpc DetectWakeWord(Empty) returns (stream WakeWordStatus); + rpc S_RecognizeVoiceCommand(stream S_RecognizeVoiceControl) returns (RecognizeResult); // Stream version of RecognizeVoiceCommand, assumes audio is coming from client + rpc RecognizeVoiceCommand(stream RecognizeVoiceControl) returns (RecognizeResult); + rpc RecognizeTextCommand(RecognizeTextControl) returns (RecognizeResult); + rpc ExecuteCommand(ExecuteInput) returns (ExecuteResult); +} + +enum STTFramework { + VOSK = 0; + WHISPER = 1; +} + +enum OnlineMode { + ONLINE = 0; + OFFLINE = 1; +} + +enum RecordAction { + START = 0; + STOP = 1; +} + +enum NLUModel { + SNIPS = 0; + RASA = 1; +} + +enum RecordMode { + MANUAL = 0; + AUTO = 1; +} + +enum RecognizeStatusType { + REC_ERROR = 0; + REC_SUCCESS = 1; + REC_PROCESSING = 2; + VOICE_NOT_RECOGNIZED = 3; + INTENT_NOT_RECOGNIZED = 4; + TEXT_NOT_RECOGNIZED = 5; + NLU_MODEL_NOT_SUPPORTED = 6; +} + +enum ExecuteStatusType { + EXEC_ERROR = 0; + EXEC_SUCCESS = 1; + KUKSA_CONN_ERROR = 2; + INTENT_NOT_SUPPORTED = 3; + INTENT_SLOTS_INCOMPLETE = 4; +} + + +message Empty {} + +message ServiceStatus { + string version = 1; + bool status = 2; + string wake_word = 3; +} + +message VoiceAudio { + bytes audio_chunk = 1; + string audio_format = 2; + int32 sample_rate = 3; + string language = 4; +} + +message WakeWordStatus { + bool status = 1; +} + +message S_RecognizeVoiceControl { + VoiceAudio audio_stream = 1; + NLUModel nlu_model = 2; + string stream_id = 3; + STTFramework stt_framework = 4; +} + +message RecognizeVoiceControl { + RecordAction action = 1; + NLUModel nlu_model = 2; + RecordMode record_mode = 3; + string stream_id = 4; + STTFramework stt_framework = 5; + OnlineMode online_mode = 6; +} + +message RecognizeTextControl { + string text_command = 1; + NLUModel nlu_model = 2; +} + +message IntentSlot { + string name = 1; + string value = 2; +} + +message RecognizeResult { + string command = 1; + string intent = 2; + repeated IntentSlot intent_slots = 3; + string stream_id = 4; + RecognizeStatusType status = 5; +} + +message ExecuteInput { + string intent = 1; + repeated IntentSlot intent_slots = 2; +} + +message ExecuteResult { + string response = 1; + ExecuteStatusType status = 2; +} diff --git a/pubspec.lock b/pubspec.lock index f790a83..91d7903 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1,6 +1,14 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + animated_svg: + dependency: "direct main" + description: + name: animated_svg + sha256: "354ae07276f776ec14e31d0ec348fe4ba903f0dc2b77ff261768217ed972ebce" + url: "https://pub.dev" + source: hosted + version: "2.1.0" archive: dependency: transitive description: @@ -109,18 +117,18 @@ packages: dependency: transitive description: name: device_frame - sha256: afe76182aec178d171953d9b4a50a43c57c7cf3c77d8b09a48bf30c8fa04dd9d + sha256: d031a06f5d6f4750009672db98a5aa1536aa4a231713852469ce394779a23d75 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" device_preview: dependency: "direct main" description: name: device_preview - sha256: "2f097bf31b929e15e6756dbe0ec1bcb63952ab9ed51c25dc5a2c722d2b21fdaf" + sha256: a694acdd3894b4c7d600f4ee413afc4ff917f76026b97ab06575fe886429ef19 url: "https://pub.dev" source: hosted - version: "1.1.0" + version: "1.2.0" fake_async: dependency: transitive description: @@ -207,10 +215,10 @@ packages: dependency: "direct main" description: name: flutter_svg - sha256: "8c5d68a82add3ca76d792f058b186a0599414f279f00ece4830b9b231b570338" + sha256: "7b4ca6cf3304575fe9c8ec64813c8d02ee41d2afe60bcfe0678bcb5375d596a2" url: "https://pub.dev" source: hosted - version: "2.0.7" + version: "2.0.10+1" flutter_test: dependency: "direct dev" description: flutter @@ -305,10 +313,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" js: dependency: transitive description: @@ -321,34 +329,34 @@ packages: dependency: transitive description: name: json_annotation - sha256: b10a7b2ff83d83c777edba3c6a0f97045ddadd56c944e1a23a3fdf43a1bf4467 + sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" url: "https://pub.dev" source: hosted - version: "4.8.1" + version: "4.9.0" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" lints: dependency: transitive description: @@ -385,10 +393,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" nested: dependency: transitive description: @@ -544,10 +552,10 @@ packages: dependency: transitive description: name: provider - sha256: cdbe7530b12ecd9eb455bdaa2fcb8d4dad22e80b8afb4798b41479d5ce26847f + sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c url: "https://pub.dev" source: hosted - version: "6.0.5" + version: "6.1.2" rive: dependency: "direct main" description: @@ -576,10 +584,10 @@ packages: dependency: transitive description: name: shared_preferences - sha256: "81429e4481e1ccfb51ede496e916348668fd0921627779233bd24cc3ff6abd02" + sha256: d3bbe5553a986e83980916ded2f0b435ef2e1893dfaa29d5a7a790d0eca12180 url: "https://pub.dev" source: hosted - version: "2.2.2" + version: "2.2.3" shared_preferences_android: dependency: transitive description: @@ -592,10 +600,10 @@ packages: dependency: transitive description: name: shared_preferences_foundation - sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7" + sha256: "0a8a893bf4fd1152f93fec03a415d11c27c74454d96e2318a7ac38dd18683ab7" url: "https://pub.dev" source: hosted - version: "2.3.4" + version: "2.4.0" shared_preferences_linux: dependency: transitive description: @@ -693,10 +701,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" typed_data: dependency: transitive description: @@ -717,26 +725,26 @@ packages: dependency: transitive description: name: vector_graphics - sha256: b16dadf7eb610e20da044c141b4a0199a5e8082ca21daba68322756f953ce714 + sha256: "32c3c684e02f9bc0afb0ae0aa653337a2fe022e8ab064bcd7ffda27a74e288e3" url: "https://pub.dev" source: hosted - version: "1.1.9" + version: "1.1.11+1" vector_graphics_codec: dependency: transitive description: name: vector_graphics_codec - sha256: a4b01403d5c613db115e30e71eca33f7e9e09f2d3c52c3fb84e16333ecddc539 + sha256: c86987475f162fadff579e7320c7ddda04cd2fdeffbe1129227a85d9ac9e03da url: "https://pub.dev" source: hosted - version: "1.1.9" + version: "1.1.11+1" vector_graphics_compiler: dependency: transitive description: name: vector_graphics_compiler - sha256: d26c0e2f237476426523eb25512e4c09fa27c6d33ed659a0e69d79e20b5dc47f + sha256: "12faff3f73b1741a36ca7e31b292ddeb629af819ca9efe9953b70bd63fc8cd81" url: "https://pub.dev" source: hosted - version: "1.1.9" + version: "1.1.11+1" vector_math: dependency: transitive description: @@ -749,10 +757,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" win32: dependency: transitive description: @@ -786,5 +794,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.2.0-0 <3.13.2" - flutter: ">=3.13.0" + dart: ">=3.4.0 <3.13.2" + flutter: ">=3.22.0" diff --git a/pubspec.yaml b/pubspec.yaml index 411e082..f903070 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,13 +49,17 @@ dependencies: yaml: ^3.1.2 gradient_borders: ^1.0.0 lottie: ^2.7.0 - device_preview: ^1.1.0 + device_preview: ^1.2.0 rive: ^0.12.3 new_virtual_keyboard: ^0.1.4 auto_size_text: ^3.0.0 get_ip_address: ^0.0.6 flutter_calendar_carousel: ^2.4.2 dart_mpd: ^0.3.2 + animated_svg: ^2.1.0 + +dependency_overrides: + intl: ^0.19.0 dev_dependencies: flutter_test: