diff options
author | Anuj Solanki <anuj603362@gmail.com> | 2024-09-29 21:31:03 +0530 |
---|---|---|
committer | Jan-Simon Moeller <jsmoeller@linuxfoundation.org> | 2024-10-07 10:52:41 +0000 |
commit | f870bbe3c49d421ff8ea561752b3b0a38ad04e96 (patch) | |
tree | e6607974a3ea6591f622a3ebe8010561b0a6ad26 | |
parent | 29ae7d2d9e04bd8e3a7d37dcfa87a02dd1ab385f (diff) |
Integrate voice assistant into flutter-ics-homescreen
- Implement voice-agent client to connect with agl-service-voiceagent
for command execution, wake word detection.
- Add a setting tile on the settings page for configuring voice
assistant settings.
- Add toggle buttons for wake word mode, online mode, overlay and
speech-to-text model in the voice assistant settings.
- Add a button on the homepage to start the voice assistant.
- Update gRPC protos to retrieve online-mode status from the voice
service.
- Make online-mode tile conditional in voice-assistant settings,
removing it from the UI if not enabled in the service.
- Automatically hide the overlay 3 seconds after command execution.
Bug-AGL: SPEC-5200
Change-Id: I4efaaf16ebc570b28816dc7203364efe2b658c2e
Signed-off-by: Anuj Solanki <anuj603362@gmail.com>
27 files changed, 3334 insertions, 4 deletions
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 @@ +<svg width="86" height="86" viewBox="0 0 86 86" fill="none" xmlns="http://www.w3.org/2000/svg"> +<g filter="url(#filter0_d_1_1652)"> +<rect x="2" y="1" width="80" height="80" rx="40" fill="url(#paint0_linear_1_1652)" shape-rendering="crispEdges"/> +<rect x="2" y="1" width="80" height="80" rx="40" fill="url(#paint1_linear_1_1652)" shape-rendering="crispEdges"/> +<rect x="2.5" y="1.5" width="79" height="79" rx="39.5" stroke="url(#paint2_linear_1_1652)" shape-rendering="crispEdges"/> +<g filter="url(#filter1_i_1_1652)"> +<rect x="10" y="9" width="64" height="64" rx="32" fill="url(#paint3_radial_1_1652)" fill-opacity="0.5"/> +<rect x="10.5" y="9.5" width="63" height="63" rx="31.5" stroke="url(#paint4_linear_1_1652)"/> +<path d="M34 30.3333C34 25.915 37.5817 22.3333 42 22.3333C46.4183 22.3333 50 25.915 50 30.3333V35.6666C50 40.0849 46.4183 43.6666 42 43.6666C37.5817 43.6666 34 40.0849 34 35.6666L34 30.3333Z" fill="white"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M29.3703 34.5072C30.4953 34.5072 31.4073 35.423 31.4073 36.5527C31.4073 42.4271 36.1498 47.6666 41.9999 47.6666C47.85 47.6666 52.5925 42.4271 52.5925 36.5527C52.5925 35.423 53.5045 34.5072 54.6295 34.5072C55.7546 34.5072 56.6666 35.423 56.6666 36.5527C56.6666 43.9923 51.8026 50.671 44.6666 51.6666V57C44.6666 58.3333 43.3333 59.6666 41.9999 59.6666C40.6666 59.6666 39.3333 58.3333 39.3333 57V51.6666C32.1972 50.671 27.3333 43.9923 27.3333 36.5527C27.3333 35.423 28.2453 34.5072 29.3703 34.5072Z" fill="white"/> +</g> +</g> +<defs> +<filter id="filter0_d_1_1652" x="0" y="0" width="86" height="86" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dx="1" dy="2"/> +<feGaussianBlur stdDeviation="1.5"/> +<feComposite in2="hardAlpha" operator="out"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.7 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1_1652"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1_1652" result="shape"/> +</filter> +<filter id="filter1_i_1_1652" x="10" y="9" width="65" height="66" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dx="1" dy="2"/> +<feGaussianBlur stdDeviation="8"/> +<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0"/> +<feBlend mode="normal" in2="shape" result="effect1_innerShadow_1_1652"/> +</filter> +<linearGradient id="paint0_linear_1_1652" x1="2" y1="41" x2="82" y2="41" gradientUnits="userSpaceOnUse"> +<stop offset="0.0001" stop-color="#1C7692"/> +<stop offset="1" stop-color="#1A727E"/> +</linearGradient> +<linearGradient id="paint1_linear_1_1652" x1="2" y1="54.3334" x2="82.0274" y2="54.1669" gradientUnits="userSpaceOnUse"> +<stop stop-color="#2998FF"/> +<stop offset="1" stop-color="#29CCFF" stop-opacity="0.7"/> +</linearGradient> +<linearGradient id="paint2_linear_1_1652" x1="2" y1="1" x2="96.7074" y2="26.6089" gradientUnits="userSpaceOnUse"> +<stop stop-color="#C1D8FF"/> +<stop offset="1" stop-color="#C1D8FF" stop-opacity="0.25"/> +</linearGradient> +<radialGradient id="paint3_radial_1_1652" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(42 41) rotate(90) scale(32)"> +<stop stop-color="#0C1039"/> +<stop offset="1"/> +<stop offset="1"/> +</radialGradient> +<linearGradient id="paint4_linear_1_1652" x1="10" y1="9" x2="85.7659" y2="29.4871" gradientUnits="userSpaceOnUse"> +<stop stop-color="#C1D8FF"/> +<stop offset="1" stop-color="#C1D8FF" stop-opacity="0.25"/> +</linearGradient> +</defs> +</svg> 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 @@ +<svg width="774" height="224" viewBox="0 0 774 224" fill="none" xmlns="http://www.w3.org/2000/svg"> +<rect width="774" height="224" fill="url(#paint0_linear_7_673)"/> +<defs> +<linearGradient id="paint0_linear_7_673" x1="387" y1="0" x2="387" y2="224" gradientUnits="userSpaceOnUse"> +<stop stop-color="#0C1039" stop-opacity="0"/> +<stop offset="0.5" stop-opacity="0.95"/> +</linearGradient> +</defs> +</svg> diff --git a/assets/VoiceAssistantBottomSheetBg.png b/assets/VoiceAssistantBottomSheetBg.png Binary files differnew file mode 100644 index 0000000..059864a --- /dev/null +++ b/assets/VoiceAssistantBottomSheetBg.png 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 @@ +<svg width="86" height="86" viewBox="0 0 86 86" fill="none" xmlns="http://www.w3.org/2000/svg"> +<g filter="url(#filter0_d_1_1648)"> +<rect x="2" y="1" width="80" height="80" rx="40" fill="url(#paint0_linear_1_1648)" shape-rendering="crispEdges"/> +<rect x="2" y="1" width="80" height="80" rx="40" fill="url(#paint1_linear_1_1648)" fill-opacity="0.15" shape-rendering="crispEdges"/> +<rect x="2.5" y="1.5" width="79" height="79" rx="39.5" stroke="url(#paint2_linear_1_1648)" shape-rendering="crispEdges"/> +<g filter="url(#filter1_d_1_1648)"> +<path d="M34 30.3333C34 25.915 37.5817 22.3333 42 22.3333C46.4183 22.3333 50 25.915 50 30.3333V35.6666C50 40.0849 46.4183 43.6666 42 43.6666C37.5817 43.6666 34 40.0849 34 35.6666L34 30.3333Z" fill="white"/> +</g> +<g filter="url(#filter2_d_1_1648)"> +<path fill-rule="evenodd" clip-rule="evenodd" d="M29.3703 34.5072C30.4953 34.5072 31.4073 35.423 31.4073 36.5527C31.4073 42.4271 36.1498 47.6666 41.9999 47.6666C47.85 47.6666 52.5925 42.4271 52.5925 36.5527C52.5925 35.423 53.5045 34.5072 54.6295 34.5072C55.7546 34.5072 56.6666 35.423 56.6666 36.5527C56.6666 43.9923 51.8026 50.671 44.6666 51.6666V57C44.6666 58.3333 43.3333 59.6666 41.9999 59.6666C40.6666 59.6666 39.3333 58.3333 39.3333 57V51.6666C32.1972 50.671 27.3333 43.9923 27.3333 36.5527C27.3333 35.423 28.2453 34.5072 29.3703 34.5072Z" fill="white"/> +</g> +</g> +<defs> +<filter id="filter0_d_1_1648" x="0" y="0" width="86" height="86" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dx="1" dy="2"/> +<feGaussianBlur stdDeviation="1.5"/> +<feComposite in2="hardAlpha" operator="out"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.7 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1_1648"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1_1648" result="shape"/> +</filter> +<filter id="filter1_d_1_1648" x="32" y="21.3333" width="22" height="27.3333" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dx="1" dy="2"/> +<feGaussianBlur stdDeviation="1.5"/> +<feComposite in2="hardAlpha" operator="out"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.7 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1_1648"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1_1648" result="shape"/> +</filter> +<filter id="filter2_d_1_1648" x="25.3333" y="33.5072" width="35.3333" height="31.1594" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dx="1" dy="2"/> +<feGaussianBlur stdDeviation="1.5"/> +<feComposite in2="hardAlpha" operator="out"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.7 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_1_1648"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_1_1648" result="shape"/> +</filter> +<linearGradient id="paint0_linear_1_1648" x1="2" y1="41" x2="82" y2="41" gradientUnits="userSpaceOnUse"> +<stop stop-color="#009DCE"/> +<stop offset="1" stop-color="#005D7A"/> +</linearGradient> +<linearGradient id="paint1_linear_1_1648" x1="2" y1="54.3334" x2="82.0274" y2="54.1669" gradientUnits="userSpaceOnUse"> +<stop stop-color="#2962FF"/> +<stop offset="1" stop-color="#2962FF" stop-opacity="0.5"/> +</linearGradient> +<linearGradient id="paint2_linear_1_1648" x1="2" y1="1" x2="96.7074" y2="26.6089" gradientUnits="userSpaceOnUse"> +<stop stop-color="#29CCFF"/> +<stop offset="1" stop-color="#29F2FF" stop-opacity="0.2"/> +</linearGradient> +</defs> +</svg> 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 @@ +<svg width="80" height="80" viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg"> +<g filter="url(#filter0_i_1_1650)"> +<rect width="80" height="80" rx="40" fill="url(#paint0_linear_1_1650)"/> +<rect width="80" height="80" rx="40" fill="url(#paint1_linear_1_1650)" fill-opacity="0.5"/> +<rect x="0.5" y="0.5" width="79" height="79" rx="39.5" stroke="url(#paint2_linear_1_1650)"/> +<path d="M32 29.3333C32 24.915 35.5817 21.3333 40 21.3333C44.4183 21.3333 48 24.915 48 29.3333V34.6666C48 39.0849 44.4183 42.6666 40 42.6666C35.5817 42.6666 32 39.0849 32 34.6666L32 29.3333Z" fill="white"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M27.3703 33.5072C28.4953 33.5072 29.4073 34.423 29.4073 35.5527C29.4073 41.4271 34.1498 46.6666 39.9999 46.6666C45.85 46.6666 50.5925 41.4271 50.5925 35.5527C50.5925 34.423 51.5045 33.5072 52.6295 33.5072C53.7546 33.5072 54.6666 34.423 54.6666 35.5527C54.6666 42.9923 49.8026 49.671 42.6666 50.6666V56C42.6666 57.3333 41.3333 58.6666 39.9999 58.6666C38.6666 58.6666 37.3333 57.3333 37.3333 56V50.6666C30.1972 49.671 25.3333 42.9923 25.3333 35.5527C25.3333 34.423 26.2453 33.5072 27.3703 33.5072Z" fill="white"/> +</g> +<defs> +<filter id="filter0_i_1_1650" x="0" y="0" width="81" height="82" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dx="1" dy="2"/> +<feGaussianBlur stdDeviation="8"/> +<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0"/> +<feBlend mode="normal" in2="shape" result="effect1_innerShadow_1_1650"/> +</filter> +<linearGradient id="paint0_linear_1_1650" x1="40" y1="0" x2="40" y2="80" gradientUnits="userSpaceOnUse"> +<stop stop-color="#1A5A7E"/> +<stop offset="1" stop-color="#1C7692"/> +</linearGradient> +<linearGradient id="paint1_linear_1_1650" x1="0" y1="0" x2="95.9119" y2="31.4055" gradientUnits="userSpaceOnUse"> +<stop stop-color="#2962FF" stop-opacity="0.5"/> +<stop offset="1" stop-color="#2962FF"/> +</linearGradient> +<linearGradient id="paint2_linear_1_1650" x1="0" y1="0" x2="94.7074" y2="25.6089" gradientUnits="userSpaceOnUse"> +<stop stop-color="#C1D8FF" stop-opacity="0.2"/> +<stop offset="1" stop-color="#C1D8FF"/> +</linearGradient> +</defs> +</svg> 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 @@ +<svg width="183" height="184" viewBox="0 0 183 184" fill="none" xmlns="http://www.w3.org/2000/svg"> +<g filter="url(#filter0_dd_7_3273)"> +<rect x="14" y="13" width="160" height="160" rx="80" fill="url(#paint0_linear_7_3273)" shape-rendering="crispEdges"/> +<rect x="14" y="13" width="160" height="160" rx="80" fill="url(#paint1_linear_7_3273)" shape-rendering="crispEdges"/> +<rect x="14.5" y="13.5" width="159" height="159" rx="79.5" stroke="url(#paint2_linear_7_3273)" shape-rendering="crispEdges"/> +<g filter="url(#filter1_i_7_3273)"> +<rect x="30" y="29" width="128" height="128" rx="64" fill="url(#paint3_radial_7_3273)" fill-opacity="0.5"/> +<rect x="30.5" y="29.5" width="127" height="127" rx="63.5" stroke="url(#paint4_linear_7_3273)"/> +<path d="M78 71.6667C78 62.8301 85.1634 55.6667 94 55.6667C102.837 55.6667 110 62.8301 110 71.6667V82.3334C110 91.1699 102.837 98.3334 94 98.3334C85.1634 98.3334 78 91.1699 78 82.3334L78 71.6667Z" fill="white"/> +<path fill-rule="evenodd" clip-rule="evenodd" d="M68.7408 80.0145C70.9909 80.0145 72.8149 81.8461 72.8149 84.1055C72.8149 95.8542 82.2998 106.333 94.0001 106.333C105.7 106.333 115.185 95.8542 115.185 84.1055C115.185 81.8461 117.009 80.0145 119.259 80.0145C121.509 80.0145 123.333 81.8461 123.333 84.1055C123.333 98.9848 113.605 112.342 99.3334 114.333V125C99.3334 127.667 96.6667 130.333 94.0001 130.333C91.3334 130.333 88.6667 127.667 88.6667 125V114.333C74.3947 112.342 64.6667 98.9848 64.6667 84.1055C64.6667 81.8461 66.4908 80.0145 68.7408 80.0145Z" fill="white"/> +</g> +</g> +<defs> +<filter id="filter0_dd_7_3273" x="0" y="0" width="182.3" height="183.3" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dx="3" dy="5"/> +<feGaussianBlur stdDeviation="2.65"/> +<feComposite in2="hardAlpha" operator="out"/> +<feColorMatrix type="matrix" values="0 0 0 0 0.05 0 0 0 0 0.772 0 0 0 0 1 0 0 0 0.5 0"/> +<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_7_3273"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dx="-8" dy="-7"/> +<feGaussianBlur stdDeviation="3"/> +<feComposite in2="hardAlpha" operator="out"/> +<feColorMatrix type="matrix" values="0 0 0 0 0.56 0 0 0 0 0 0 0 0 0 1 0 0 0 0.7 0"/> +<feBlend mode="normal" in2="effect1_dropShadow_7_3273" result="effect2_dropShadow_7_3273"/> +<feBlend mode="normal" in="SourceGraphic" in2="effect2_dropShadow_7_3273" result="shape"/> +</filter> +<filter id="filter1_i_7_3273" x="30" y="29" width="129" height="130" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> +<feFlood flood-opacity="0" result="BackgroundImageFix"/> +<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/> +<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/> +<feOffset dx="1" dy="2"/> +<feGaussianBlur stdDeviation="8"/> +<feComposite in2="hardAlpha" operator="arithmetic" k2="-1" k3="1"/> +<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0"/> +<feBlend mode="normal" in2="shape" result="effect1_innerShadow_7_3273"/> +</filter> +<linearGradient id="paint0_linear_7_3273" x1="14" y1="93.0001" x2="174" y2="93.0001" gradientUnits="userSpaceOnUse"> +<stop offset="0.0001" stop-color="#1C7692"/> +<stop offset="1" stop-color="#1A727E"/> +</linearGradient> +<linearGradient id="paint1_linear_7_3273" x1="14" y1="119.667" x2="174.055" y2="119.334" gradientUnits="userSpaceOnUse"> +<stop stop-color="#2998FF"/> +<stop offset="1" stop-color="#29CCFF" stop-opacity="0.7"/> +</linearGradient> +<linearGradient id="paint2_linear_7_3273" x1="14" y1="13" x2="203.415" y2="64.2177" gradientUnits="userSpaceOnUse"> +<stop stop-color="#C1D8FF"/> +<stop offset="1" stop-color="#C1D8FF" stop-opacity="0.25"/> +</linearGradient> +<radialGradient id="paint3_radial_7_3273" cx="0" cy="0" r="1" gradientUnits="userSpaceOnUse" gradientTransform="translate(94 93) rotate(90) scale(64)"> +<stop stop-color="#0C1039"/> +<stop offset="1"/> +<stop offset="1"/> +</radialGradient> +<linearGradient id="paint4_linear_7_3273" x1="30" y1="29" x2="181.532" y2="69.9742" gradientUnits="userSpaceOnUse"> +<stop stop-color="#C1D8FF"/> +<stop offset="1" stop-color="#C1D8FF" stop-opacity="0.25"/> +</linearGradient> +</defs> +</svg> diff --git a/lib/data/data_providers/app_config_provider.dart b/lib/data/data_providers/app_config_provider.dart index b82eb54..9e187a9 100644 --- a/lib/data/data_providers/app_config_provider.dart +++ b/lib/data/data_providers/app_config_provider.dart @@ -88,6 +88,21 @@ class MpdConfig { } } +class VoiceAgentConfig { + final String hostname; + final int port; + + static String defaultHostname = 'localhost'; + static int defaultPort = 51053; + + VoiceAgentConfig({required this.hostname,required this.port}); + + static VoiceAgentConfig defaultConfig() { + return VoiceAgentConfig( + hostname: VoiceAgentConfig.defaultHostname, port: VoiceAgentConfig.defaultPort); + } +} + class AppConfig { final bool disableBkgAnimation; final bool plainBackground; @@ -96,6 +111,7 @@ class AppConfig { final RadioConfig radioConfig; final StorageConfig storageConfig; final MpdConfig mpdConfig; + final VoiceAgentConfig voiceAgentConfig; static String configFilePath = '/etc/xdg/AGL/ics-homescreen.yaml'; @@ -106,7 +122,8 @@ class AppConfig { required this.kuksaConfig, required this.radioConfig, required this.storageConfig, - required this.mpdConfig}); + required this.mpdConfig, + required this.voiceAgentConfig}); static KuksaConfig parseKuksaConfig(YamlMap kuksaMap) { try { @@ -238,6 +255,25 @@ class AppConfig { return MpdConfig.defaultConfig(); } } + + static VoiceAgentConfig parseVoiceAgentConfig(YamlMap voiceAgentMap) { + try { + String hostname = VoiceAgentConfig.defaultHostname; + if (voiceAgentMap.containsKey('hostname')) { + hostname = voiceAgentMap['hostname']; + } + + int port = VoiceAgentConfig.defaultPort; + if (voiceAgentMap.containsKey('port')) { + port = voiceAgentMap['port']; + } + + return VoiceAgentConfig(hostname: hostname, port: port); + } catch (_) { + debugPrint("Invalid VoiceAgent configuration, using defaults"); + return VoiceAgentConfig.defaultConfig(); + } + } } final appConfigProvider = Provider((ref) { @@ -281,6 +317,13 @@ final appConfigProvider = Provider((ref) { mpdConfig = MpdConfig.defaultConfig(); } + VoiceAgentConfig voiceAgentConfig; + if(yamlMap.containsKey('voiceAgent')){ + voiceAgentConfig = AppConfig.parseVoiceAgentConfig(yamlMap['voiceAgent']); + } else { + voiceAgentConfig = VoiceAgentConfig.defaultConfig(); + } + bool disableBkgAnimation = disableBkgAnimationDefault; if (yamlMap.containsKey('disable-bg-animation')) { var value = yamlMap['disable-bg-animation']; @@ -312,7 +355,8 @@ final appConfigProvider = Provider((ref) { kuksaConfig: kuksaConfig, radioConfig: radioConfig, storageConfig: storageConfig, - mpdConfig: mpdConfig); + mpdConfig: mpdConfig, + voiceAgentConfig: voiceAgentConfig); } catch (_) { return AppConfig( disableBkgAnimation: false, @@ -321,6 +365,7 @@ final appConfigProvider = Provider((ref) { kuksaConfig: KuksaConfig.defaultConfig(), radioConfig: RadioConfig.defaultConfig(), storageConfig: StorageConfig.defaultConfig(), - mpdConfig: MpdConfig.defaultConfig()); + mpdConfig: MpdConfig.defaultConfig(), + voiceAgentConfig: VoiceAgentConfig.defaultConfig()); } }); diff --git a/lib/data/data_providers/app_provider.dart b/lib/data/data_providers/app_provider.dart index 0f7ed0c..64c0e47 100644 --- a/lib/data/data_providers/app_provider.dart +++ b/lib/data/data_providers/app_provider.dart @@ -16,10 +16,14 @@ import 'package:flutter_ics_homescreen/data/data_providers/radio_client.dart'; import 'package:flutter_ics_homescreen/data/data_providers/storage_client.dart'; import 'package:flutter_ics_homescreen/data/data_providers/mpd_client.dart'; import 'package:flutter_ics_homescreen/data/data_providers/play_controller.dart'; +import 'package:flutter_ics_homescreen/data/data_providers/voice_agent_client.dart'; +import 'package:flutter_ics_homescreen/data/data_providers/voice_assistant_notifier.dart'; import 'package:flutter_ics_homescreen/export.dart'; import 'package:flutter_ics_homescreen/data/models/users.dart'; +import '../models/voice_assistant_state.dart'; + enum AppState { home, dashboard, @@ -44,7 +48,9 @@ enum AppState { clock, date, time, - year + year, + voiceAssistant, + sttModel, } class AppStateNotifier extends Notifier<AppState> { @@ -73,6 +79,11 @@ final valClientProvider = Provider((ref) { return ValClient(config: config, ref: ref); }); +final voiceAgentClientProvider = Provider((ref){ + VoiceAgentConfig config = ref.watch(appConfigProvider).voiceAgentConfig; + return VoiceAgentClient(config: config, ref: ref); +}); + final appLauncherProvider = Provider((ref) { return AppLauncher(ref: ref); }); @@ -154,3 +165,7 @@ final currentTimeProvider = StateNotifierProvider<CurrentTimeNotifier, DateTime>((ref) { return CurrentTimeNotifier(); }); + + +final voiceAssistantStateProvider = + NotifierProvider<VoiceAssistantStateNotifier, VoiceAssistantState>(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..295e138 --- /dev/null +++ b/lib/data/data_providers/voice_agent_client.dart @@ -0,0 +1,312 @@ +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 { + final VoiceAgentConfig config; + late ClientChannel _channel; + late VoiceAgentServiceClient _client; + final Ref ref; + StreamSubscription<WakeWordStatus>? _wakeWordStatusSubscription; + + VoiceAgentClient({required this.config,required this.ref}) { + // Initialize the client channel without connecting immediately + String host = config.hostname; + int port = config.port; + _channel = ClientChannel( + host, + port: port, + options: const ChannelOptions( + credentials: ChannelCredentials.insecure(), + ), + ); + debugPrint("Connecting to Voice Assistant"); + _client = VoiceAgentServiceClient(_channel); + + } + + Future<ServiceStatus> 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<WakeWordStatus> 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<RecognizeResult> recognizeVoiceCommand( + Stream<RecognizeVoiceControl> 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<RecognizeResult> 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<ExecuteResult> 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<void> shutdown() async { + // await _channel.shutdown(); + } + + // Grpc helper methods + Future<void> 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: 2)); + startWakeWordDetection(); + } + if(!ref.read(voiceAssistantStateProvider.select((value) => value.isWakeWordMode))){ + _wakeWordStatusSubscription?.cancel(); + return; + } + }, + onError: (error) { + }, + cancelOnError: true, + ); + } + + Future<String> 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<RecognizeResult> 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<RecognizeResult> 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<void> 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"); + } else if (execResponse.status == ExecuteStatusType.KUKSA_CONN_ERROR) { + final commandResponse = execResponse.response; + 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<void> disableOverlay() async{ + await Future.delayed(Duration(seconds: 3)); + ref.read(voiceAssistantStateProvider.notifier).toggleShowOverlay(false); + } + + Future<void> 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(true); + } + } + + 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); + disableOverlay(); + + } 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..0bc681a --- /dev/null +++ b/lib/data/data_providers/voice_assistant_notifier.dart @@ -0,0 +1,148 @@ +import 'package:protos/val_api.dart'; + +import '../../export.dart'; +import '../models/voice_assistant_state.dart'; + + +class VoiceAssistantStateNotifier extends Notifier<VoiceAssistantState>{ + @override + VoiceAssistantState build() { + return const VoiceAssistantState.initial(); + } + + void updateVoiceAssistantState(VoiceAssistantState newState){ + state = newState; + } + + void updateVoiceAssistantStateWith({ + bool? isWakeWordMode, + bool? isVoiceAssistantEnable, + bool? voiceAssistantOverlay, + bool? isOnlineMode, + bool? isOnlineModeAvailable, + 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, + isOnlineModeAvailable: isOnlineModeAvailable, + 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(bool value){ + state = state.copyWith(showOverLay: value); + } + + bool toggleWakeWordMode(){ + state = state.copyWith(isWakeWordMode: !state.isWakeWordMode); + return state.isWakeWordMode; + } + + Future<void> toggleVoiceAssistant(ServiceStatus status) async { + bool prevState = state.isVoiceAssistantEnable; + if(!prevState){ + if(status.status){ + state = state.copyWith(isVoiceAssistantEnable: !state.isVoiceAssistantEnable); + state = state.copyWith(wakeWord: status.wakeWord); + state = state.copyWith(isOnlineModeAvailable: status.onlineMode); + } + else{ + debugPrint("Failed to start the Voice Assistant"); + } + } + else{ + state = state.copyWith(isVoiceAssistantEnable: !state.isVoiceAssistantEnable); + if(state.isWakeWordMode){ + state = state.copyWith(isWakeWordMode: false); + } + } + } + + 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); + } + + + bool toggleButtonPressed(){ + bool prevState = state.buttonPressed; + state = state.copyWith(buttonPressed: !state.buttonPressed); + return !prevState; + } + + 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..f898dd5 --- /dev/null +++ b/lib/data/models/voice_assistant_state.dart @@ -0,0 +1,104 @@ +enum SttModel { + whisper, + vosk +} + +class VoiceAssistantState{ + final bool isWakeWordMode; + final bool isVoiceAssistantEnable; + final bool voiceAssistantOverlay; + final bool isOnlineMode; + final bool isOnlineModeAvailable; + 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.isOnlineModeAvailable, + 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, + isOnlineModeAvailable = 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, + bool? isOnlineModeAvailable, + 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, + isOnlineModeAvailable : isOnlineModeAvailable ?? this.isOnlineModeAvailable, + 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/presentation/common_widget/voice_assistant_button.dart b/lib/presentation/common_widget/voice_assistant_button.dart new file mode 100644 index 0000000..2a82a0a --- /dev/null +++ b/lib/presentation/common_widget/voice_assistant_button.dart @@ -0,0 +1,214 @@ +import 'package:flutter_ics_homescreen/export.dart'; + +class VoiceAssistantButton extends ConsumerStatefulWidget { + const VoiceAssistantButton({super.key}); + + @override + ConsumerState<VoiceAssistantButton> createState() => _VoiceAssistantButtonState(); +} + +class _VoiceAssistantButtonState extends ConsumerState<VoiceAssistantButton> with SingleTickerProviderStateMixin { + bool _showOverlay = false; + late AnimationController _animationController; + late Animation<double> _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<double>(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(""); + bool state = ref.read(voiceAssistantStateProvider.notifier).toggleButtonPressed(); + if(state){ + var voiceAgentClient = ref.read(voiceAgentClientProvider); + voiceAgentClient.startVoiceAssistant(); + } + } + + 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, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + if(!isRecording && !isProcessing) + Column( + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + command ?? "No Command Detected", + textAlign: TextAlign.center, + style: const TextStyle( + color: Colors.white70, + fontSize: 43, + fontWeight: FontWeight.w400, + ), + ), + ), + SizedBox( + height: MediaQuery.of(context).size.height * 0.03 + ), + Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0), + child: Text( + commandResponse ?? "No Response", + textAlign: TextAlign.center, + style: const TextStyle( + color: Color.fromRGBO(41, 95, 248, 1), + fontSize: 43, + fontWeight: FontWeight.w800, + ), + ), + ), + + 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(false); + overlayLock = 0; + }); + } + + @override + Widget build(BuildContext context) { + _showOverlay = ref.watch(voiceAssistantStateProvider.select((value) => value.showOverLay)); + + if(_showOverlay){ + WidgetsBinding.instance!.addPostFrameCallback((_) { + if(overlayLock == 0){ + overlayLock = 1; + _showAssistantPopup(context); + } + }); + } + else if(overlayLock == 1){ + overlayLock = 0; + Navigator.of(context).pop(); + } + + String svgPath = ref.watch(voiceAssistantStateProvider.select((value) => value.buttonPressed)) + ? 'assets/VoiceAssistantActive.svg' + : 'assets/VoiceAssistantEnabled.svg'; + + return Padding( + padding: const EdgeInsets.only(left: 8), + child: 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<Page<dynamic>> onGenerateAppViewPages( AppState state, @@ -57,5 +59,9 @@ List<Page<dynamic>> 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..6e3e119 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<HomeScreen> { 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<void> page() => const MaterialPage<void>(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<void> page() => + const MaterialPage<void>(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<AppState>().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 + ? <Color>[ + Colors.white, + Colors.blue, + const Color.fromARGB(16, 41, 98, 255) + ] + : <Color>[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 + ? <Color>[ + Colors.white, + Colors.blue, + const Color.fromARGB(16, 41, 98, 255) + ] + : <Color>[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..924a219 --- /dev/null +++ b/lib/presentation/screens/settings/settings_screens/voice_assistant/widgets/voice_assistant_content.dart @@ -0,0 +1,251 @@ +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 = () { + bool status = ref.read(voiceAssistantStateProvider.notifier).toggleWakeWordMode(); + if(status){ + var voiceAgentClient = ref.read(voiceAgentClientProvider); + voiceAgentClient.startWakeWordDetection(); + } + }; + + 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 + ), + if(ref.watch(voiceAssistantStateProvider.select((value) => value.isOnlineModeAvailable))) + 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<AppState>() + .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<SttTile> { + @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: <Color>[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<WakeWordTile> { + @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: <Color>[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..ee0365a --- /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'; +import 'package:protos/val_api.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<VoiceAssistantSettingsTile> { + 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 + ? <Color>[Colors.black, Colors.black12] + : <Color>[ + 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.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) async { + var voiceAgentClient = ref.read(voiceAgentClientProvider); + ServiceStatus status = await voiceAgentClient.checkServiceStatus(); + ref.read(voiceAssistantStateProvider.notifier).toggleVoiceAssistant(status); + setState(() { + isSwitchOn = value; + }); + // This is called when the user toggles the switch. + }, + inactiveTrackColor: Colors.transparent, + activeTrackColor: Colors.transparent, + thumbColor: + MaterialStateProperty.all<Color>( + 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<VoiceAssistantTile> createState() => _VoiceAssistantTileState(); +} + +class _VoiceAssistantTileState extends ConsumerState<VoiceAssistantTile> { + 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 + ? <Color>[Colors.black, Colors.black12] + : <Color>[ + 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<Color>( + 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/protos/lib/src/generated/voice_agent/voice_agent.pb.dart b/protos/lib/src/generated/voice_agent/voice_agent.pb.dart new file mode 100644 index 0000000..eb6f360 --- /dev/null +++ b/protos/lib/src/generated/voice_agent/voice_agent.pb.dart @@ -0,0 +1,880 @@ +// +// 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:protobuf/protobuf.dart' as $pb; + +import 'voice_agent.pbenum.dart'; + +export 'voice_agent.pbenum.dart'; + +class Empty extends $pb.GeneratedMessage { + factory Empty() => 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<Empty> createRepeated() => $pb.PbList<Empty>(); + @$core.pragma('dart2js:noInline') + static Empty getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<Empty>(create); + static Empty? _defaultInstance; +} + +class ServiceStatus extends $pb.GeneratedMessage { + factory ServiceStatus({ + $core.String? version, + $core.bool? status, + $core.String? wakeWord, + $core.bool? onlineMode, + }) { + final $result = create(); + if (version != null) { + $result.version = version; + } + if (status != null) { + $result.status = status; + } + if (wakeWord != null) { + $result.wakeWord = wakeWord; + } + if (onlineMode != null) { + $result.onlineMode = onlineMode; + } + 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') + ..aOB(4, _omitFieldNames ? '' : 'onlineMode') + ..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<ServiceStatus> createRepeated() => $pb.PbList<ServiceStatus>(); + @$core.pragma('dart2js:noInline') + static ServiceStatus getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ServiceStatus>(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); + + @$pb.TagNumber(4) + $core.bool get onlineMode => $_getBF(3); + @$pb.TagNumber(4) + set onlineMode($core.bool v) { $_setBool(3, v); } + @$pb.TagNumber(4) + $core.bool hasOnlineMode() => $_has(3); + @$pb.TagNumber(4) + void clearOnlineMode() => clearField(4); +} + +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<VoiceAudio> createRepeated() => $pb.PbList<VoiceAudio>(); + @$core.pragma('dart2js:noInline') + static VoiceAudio getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<VoiceAudio>(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<WakeWordStatus> createRepeated() => $pb.PbList<WakeWordStatus>(); + @$core.pragma('dart2js:noInline') + static WakeWordStatus getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<WakeWordStatus>(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<VoiceAudio>(1, _omitFieldNames ? '' : 'audioStream', subBuilder: VoiceAudio.create) + ..e<NLUModel>(2, _omitFieldNames ? '' : 'nluModel', $pb.PbFieldType.OE, defaultOrMaker: NLUModel.SNIPS, valueOf: NLUModel.valueOf, enumValues: NLUModel.values) + ..aOS(3, _omitFieldNames ? '' : 'streamId') + ..e<STTFramework>(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<S_RecognizeVoiceControl> createRepeated() => $pb.PbList<S_RecognizeVoiceControl>(); + @$core.pragma('dart2js:noInline') + static S_RecognizeVoiceControl getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<S_RecognizeVoiceControl>(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<RecordAction>(1, _omitFieldNames ? '' : 'action', $pb.PbFieldType.OE, defaultOrMaker: RecordAction.START, valueOf: RecordAction.valueOf, enumValues: RecordAction.values) + ..e<NLUModel>(2, _omitFieldNames ? '' : 'nluModel', $pb.PbFieldType.OE, defaultOrMaker: NLUModel.SNIPS, valueOf: NLUModel.valueOf, enumValues: NLUModel.values) + ..e<RecordMode>(3, _omitFieldNames ? '' : 'recordMode', $pb.PbFieldType.OE, defaultOrMaker: RecordMode.MANUAL, valueOf: RecordMode.valueOf, enumValues: RecordMode.values) + ..aOS(4, _omitFieldNames ? '' : 'streamId') + ..e<STTFramework>(5, _omitFieldNames ? '' : 'sttFramework', $pb.PbFieldType.OE, defaultOrMaker: STTFramework.VOSK, valueOf: STTFramework.valueOf, enumValues: STTFramework.values) + ..e<OnlineMode>(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<RecognizeVoiceControl> createRepeated() => $pb.PbList<RecognizeVoiceControl>(); + @$core.pragma('dart2js:noInline') + static RecognizeVoiceControl getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RecognizeVoiceControl>(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<NLUModel>(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<RecognizeTextControl> createRepeated() => $pb.PbList<RecognizeTextControl>(); + @$core.pragma('dart2js:noInline') + static RecognizeTextControl getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RecognizeTextControl>(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<IntentSlot> createRepeated() => $pb.PbList<IntentSlot>(); + @$core.pragma('dart2js:noInline') + static IntentSlot getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<IntentSlot>(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<IntentSlot>? 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<IntentSlot>(3, _omitFieldNames ? '' : 'intentSlots', $pb.PbFieldType.PM, subBuilder: IntentSlot.create) + ..aOS(4, _omitFieldNames ? '' : 'streamId') + ..e<RecognizeStatusType>(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<RecognizeResult> createRepeated() => $pb.PbList<RecognizeResult>(); + @$core.pragma('dart2js:noInline') + static RecognizeResult getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<RecognizeResult>(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<IntentSlot> 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<IntentSlot>? 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<IntentSlot>(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<ExecuteInput> createRepeated() => $pb.PbList<ExecuteInput>(); + @$core.pragma('dart2js:noInline') + static ExecuteInput getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ExecuteInput>(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<IntentSlot> 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<ExecuteStatusType>(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<ExecuteResult> createRepeated() => $pb.PbList<ExecuteResult>(); + @$core.pragma('dart2js:noInline') + static ExecuteResult getDefault() => _defaultInstance ??= $pb.GeneratedMessage.$_defaultFor<ExecuteResult>(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<ServiceStatus> checkServiceStatus($pb.ClientContext? ctx, Empty request) => + _client.invoke<ServiceStatus>(ctx, 'VoiceAgentService', 'CheckServiceStatus', request, ServiceStatus()) + ; + $async.Future<WakeWordStatus> s_DetectWakeWord($pb.ClientContext? ctx, VoiceAudio request) => + _client.invoke<WakeWordStatus>(ctx, 'VoiceAgentService', 'S_DetectWakeWord', request, WakeWordStatus()) + ; + $async.Future<WakeWordStatus> detectWakeWord($pb.ClientContext? ctx, Empty request) => + _client.invoke<WakeWordStatus>(ctx, 'VoiceAgentService', 'DetectWakeWord', request, WakeWordStatus()) + ; + $async.Future<RecognizeResult> s_RecognizeVoiceCommand($pb.ClientContext? ctx, S_RecognizeVoiceControl request) => + _client.invoke<RecognizeResult>(ctx, 'VoiceAgentService', 'S_RecognizeVoiceCommand', request, RecognizeResult()) + ; + $async.Future<RecognizeResult> recognizeVoiceCommand($pb.ClientContext? ctx, RecognizeVoiceControl request) => + _client.invoke<RecognizeResult>(ctx, 'VoiceAgentService', 'RecognizeVoiceCommand', request, RecognizeResult()) + ; + $async.Future<RecognizeResult> recognizeTextCommand($pb.ClientContext? ctx, RecognizeTextControl request) => + _client.invoke<RecognizeResult>(ctx, 'VoiceAgentService', 'RecognizeTextCommand', request, RecognizeResult()) + ; + $async.Future<ExecuteResult> executeCommand($pb.ClientContext? ctx, ExecuteInput request) => + _client.invoke<ExecuteResult>(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<STTFramework> values = <STTFramework> [ + 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<OnlineMode> values = <OnlineMode> [ + 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<RecordAction> values = <RecordAction> [ + 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<NLUModel> values = <NLUModel> [ + 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<RecordMode> values = <RecordMode> [ + 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<RecognizeStatusType> values = <RecognizeStatusType> [ + 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<ExecuteStatusType> values = <ExecuteStatusType> [ + 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..e972432 --- /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..2a824c1 --- /dev/null +++ b/protos/lib/src/generated/voice_agent/voice_agent.pbjson.dart @@ -0,0 +1,326 @@ +// +// 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'}, + {'1': 'online_mode', '3': 4, '4': 1, '5': 8, '10': 'onlineMode'}, + ], +}; + +/// Descriptor for `ServiceStatus`. Decode as a `google.protobuf.DescriptorProto`. +final $typed_data.Uint8List serviceStatusDescriptor = $convert.base64Decode( + 'Cg1TZXJ2aWNlU3RhdHVzEhgKB3ZlcnNpb24YASABKAlSB3ZlcnNpb24SFgoGc3RhdHVzGAIgAS' + 'gIUgZzdGF0dXMSGwoJd2FrZV93b3JkGAMgASgJUgh3YWtlV29yZBIfCgtvbmxpbmVfbW9kZRgE' + 'IAEoCFIKb25saW5lTW9kZQ=='); + +@$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..f1164ff --- /dev/null +++ b/protos/protos/voice_agent/voice_agent.proto @@ -0,0 +1,120 @@ +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; + bool online_mode = 4; +} + +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; +} |