├── .gitignore
├── .metadata
├── LICENSE
├── README.md
├── Screenshot_homepage.jpg
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── rhasspy_mobile_app
│ │ │ │ ├── MainActivity.java
│ │ │ │ ├── StartRecordingAppWidgetProvider.java
│ │ │ │ └── WakeWordService.java
│ │ └── res
│ │ │ ├── drawable
│ │ │ ├── app_icon.png
│ │ │ └── launch_background.xml
│ │ │ ├── layout
│ │ │ └── start_recording_widget.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── values
│ │ │ └── styles.xml
│ │ │ └── xml
│ │ │ └── start_recoding_appwidget_info.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
├── settings.gradle
└── settings_aar.gradle
├── assets
└── images
│ └── rhasspy.png
├── ios
├── .gitignore
├── Flutter
│ ├── AppFrameworkInfo.plist
│ ├── Debug.xcconfig
│ └── Release.xcconfig
├── Runner.xcodeproj
│ ├── project.pbxproj
│ ├── project.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── xcshareddata
│ │ └── xcschemes
│ │ └── Runner.xcscheme
├── Runner.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── WorkspaceSettings.xcsettings
└── Runner
│ ├── AppDelegate.swift
│ ├── Assets.xcassets
│ ├── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── Icon-App-1024x1024@1x.png
│ │ ├── Icon-App-20x20@1x.png
│ │ ├── Icon-App-20x20@2x.png
│ │ ├── Icon-App-20x20@3x.png
│ │ ├── Icon-App-29x29@1x.png
│ │ ├── Icon-App-29x29@2x.png
│ │ ├── Icon-App-29x29@3x.png
│ │ ├── Icon-App-40x40@1x.png
│ │ ├── Icon-App-40x40@2x.png
│ │ ├── Icon-App-40x40@3x.png
│ │ ├── Icon-App-50x50@1x.png
│ │ ├── Icon-App-50x50@2x.png
│ │ ├── Icon-App-57x57@1x.png
│ │ ├── Icon-App-57x57@2x.png
│ │ ├── Icon-App-60x60@2x.png
│ │ ├── Icon-App-60x60@3x.png
│ │ ├── Icon-App-72x72@1x.png
│ │ ├── Icon-App-72x72@2x.png
│ │ ├── Icon-App-76x76@1x.png
│ │ ├── Icon-App-76x76@2x.png
│ │ └── Icon-App-83.5x83.5@2x.png
│ └── LaunchImage.imageset
│ │ ├── Contents.json
│ │ ├── LaunchImage.png
│ │ ├── LaunchImage@2x.png
│ │ ├── LaunchImage@3x.png
│ │ └── README.md
│ ├── Base.lproj
│ ├── LaunchScreen.storyboard
│ └── Main.storyboard
│ ├── Info.plist
│ └── Runner-Bridging-Header.h
├── lib
├── main.dart
├── rhasspy_dart
│ ├── exceptions.dart
│ ├── parse_messages.dart
│ ├── rhasspy_api.dart
│ ├── rhasspy_mqtt_api.dart
│ ├── rhasspy_mqtt_isolate.dart
│ └── utility
│ │ └── rhasspy_mqtt_logger.dart
├── screens
│ ├── app_settings.dart
│ └── home_page.dart
├── utils
│ ├── audio_recorder_isolate.dart
│ ├── constants.dart
│ ├── logger
│ │ ├── log_page.dart
│ │ └── logger.dart
│ └── utils.dart
├── wake_word
│ ├── udp_wake_word.dart
│ ├── wake_word_base.dart
│ └── wake_word_utils.dart
└── widget
│ └── Intent_viewer.dart
├── pubspec.lock
├── pubspec.yaml
├── test
├── rhasspy_api_test.dart
├── rhasspy_mqtt_test.dart
├── utils_test.dart
└── widget_test.dart
└── test_resources
└── audio
├── inputAudio1
└── outputAudio1.wav
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | .vscode
12 | # IntelliJ related
13 | *.iml
14 | *.ipr
15 | *.iws
16 | .idea/
17 |
18 | # The .vscode folder contains launch configuration and tasks you configure in
19 | # VS Code which you may wish to be included in version control, so this line
20 | # is commented out by default.
21 | #.vscode/
22 |
23 | # Flutter/Dart/Pub related
24 | **/doc/api/
25 | .dart_tool/
26 | .flutter-plugins
27 | .flutter-plugins-dependencies
28 | .packages
29 | .pub-cache/
30 | .pub/
31 | /build/
32 |
33 | # Web related
34 | lib/generated_plugin_registrant.dart
35 |
36 | # Symbolication related
37 | app.*.symbols
38 |
39 | # Obfuscation related
40 | app.*.map.json
41 |
42 | # Exceptions to above rules.
43 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
44 |
--------------------------------------------------------------------------------
/.metadata:
--------------------------------------------------------------------------------
1 | # This file tracks properties of this Flutter project.
2 | # Used by Flutter tool to assess capabilities and perform upgrades etc.
3 | #
4 | # This file should be version controlled and should not be manually edited.
5 |
6 | version:
7 | revision: b041144f833e05cf463b8887fa12efdec9493488
8 | channel: stable
9 |
10 | project_type: app
11 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 razzo04
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Rhasspy mobile app
2 |
3 | This is a simple mobile app that interfaces with rhasspy.
4 |
5 |
8 |
11 | # Features
12 | - Text to speak
13 | - Speech to text
14 | - ability to transcribe audio
15 | - Ssl connection and possibility to set self-signed certificates
16 | - Support Hermes protocol
17 | - Wake word over UDP
18 | - Android widget for listen to a command
19 |
20 | # Getting Started
21 | For android you can install the app by downloading the file with extension .apk present in each new [release](https://github.com/razzo04/rhasspy-mobile-app/releases) and then open it in your phone after accepting the installation from unknown sources. It is not yet available for ios.
22 |
23 | Once the app has been installed, it needs to be configured from version 1.7.0, the configuration of the app has been greatly simplified it is sufficient to insert in the text field called "Rhasspy ip" the ip and the port where rhasspy is running. If you are using the default port it will only be necessary to enter the ip. Once the entry is confirmed, a message should appear indicating whether a connection to rhasspy has occurred. If not, check the SSL settings and the logs which may contain useful information to understand the nature of the problem. Once you have made a connection to rhasspy you can click the auto setup button this will take care of generating a siteId if not specified and taking the MQTT credentials and adding the siteId to the various services so that the app can work. If the procedure does not work, check the logs and open an issue if necessary. If rhasspy does not have MQTT credentials, the app will check if it has them and if so it will send them and complete the setup procedure.
24 |
25 | # Building From Source
26 | To get started you need to install [flutter](https://flutter.dev/docs/get-started/install) and then you can download the repository.
27 | ```bash
28 | git clone https://github.com/razzo04/rhasspy-mobile-app.git
29 | cd rhasspy-mobile-app
30 | ```
31 | For build android.
32 | ```bash
33 | flutter build apk
34 | ```
35 | For build ios you need macOS and Xcode.
36 | ```bash
37 | flutter build ios
38 | ```
39 |
--------------------------------------------------------------------------------
/Screenshot_homepage.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/Screenshot_homepage.jpg
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | def localProperties = new Properties()
2 | def localPropertiesFile = rootProject.file('local.properties')
3 | if (localPropertiesFile.exists()) {
4 | localPropertiesFile.withReader('UTF-8') { reader ->
5 | localProperties.load(reader)
6 | }
7 | }
8 |
9 | def flutterRoot = localProperties.getProperty('flutter.sdk')
10 | if (flutterRoot == null) {
11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
12 | }
13 |
14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
15 | if (flutterVersionCode == null) {
16 | flutterVersionCode = '1'
17 | }
18 |
19 | def flutterVersionName = localProperties.getProperty('flutter.versionName')
20 | if (flutterVersionName == null) {
21 | flutterVersionName = '1.0'
22 | }
23 |
24 | apply plugin: 'com.android.application'
25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
26 |
27 | android {
28 | compileSdkVersion 30
29 |
30 | lintOptions {
31 | disable 'InvalidPackage'
32 | }
33 |
34 | defaultConfig {
35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
36 | applicationId "com.example.rhasspy_mobile_app"
37 | minSdkVersion 16
38 | targetSdkVersion 30
39 | versionCode flutterVersionCode.toInteger()
40 | versionName flutterVersionName
41 | }
42 |
43 | buildTypes {
44 | release {
45 | // TODO: Add your own signing config for the release build.
46 | // Signing with the debug keys for now, so `flutter run --release` works.
47 | signingConfig signingConfigs.debug
48 | }
49 | }
50 | }
51 |
52 | flutter {
53 | source '../..'
54 | }
55 |
--------------------------------------------------------------------------------
/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 |
2 | ## Flutter wrapper
3 | -keep class io.flutter.app.** { *; }
4 | -keep class io.flutter.plugin.** { *; }
5 | -keep class io.flutter.util.** { *; }
6 | -keep class io.flutter.view.** { *; }
7 | -keep class io.flutter.** { *; }
8 | -keep class io.flutter.plugins.** { *; }
9 | -dontwarn io.flutter.embedding.**
10 |
11 | ## Gson rules
12 | # Gson uses generic type information stored in a class file when working with fields. Proguard
13 | # removes such information by default, so configure it to keep all of it.
14 | -keepattributes Signature
15 |
16 | # For using GSON @Expose annotation
17 | -keepattributes *Annotation*
18 |
19 | # Gson specific classes
20 | -dontwarn sun.misc.**
21 | #-keep class com.google.gson.stream.** { *; }
22 |
23 | # Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
24 | # JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
25 | -keep class * implements com.google.gson.TypeAdapter
26 | -keep class * implements com.google.gson.TypeAdapterFactory
27 | -keep class * implements com.google.gson.JsonSerializer
28 | -keep class * implements com.google.gson.JsonDeserializer
29 |
30 | # Prevent R8 from leaving Data object members always null
31 | -keepclassmembers,allowobfuscation class * {
32 | @com.google.gson.annotations.SerializedName ;
33 | }
34 |
35 | ## flutter_local_notification plugin rules
36 | -keep class com.dexterous.** { *; }
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
20 |
23 |
30 |
34 |
38 |
43 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
60 |
61 |
63 |
66 |
67 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/example/rhasspy_mobile_app/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.example.rhasspy_mobile_app;
2 |
3 | import android.content.ComponentName;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.ServiceConnection;
7 | import android.os.Bundle;
8 | import android.os.Handler;
9 | import android.os.IBinder;
10 | import android.os.PersistableBundle;
11 | import android.util.Log;
12 |
13 | import androidx.annotation.NonNull;
14 | import androidx.annotation.Nullable;
15 | import androidx.core.content.ContextCompat;
16 |
17 | import java.util.Arrays;
18 | import java.util.List;
19 | import java.util.Objects;
20 |
21 | import io.flutter.FlutterInjector;
22 | import io.flutter.embedding.android.FlutterActivity;
23 | import io.flutter.embedding.engine.FlutterEngine;
24 | import io.flutter.embedding.engine.dart.DartExecutor;
25 | import io.flutter.embedding.engine.loader.FlutterLoader;
26 | import io.flutter.plugin.common.MethodChannel;
27 | import io.flutter.view.FlutterMain;
28 |
29 | public class MainActivity extends FlutterActivity {
30 | WakeWordService mService;
31 | private static final String CHANNEL = "rhasspy_mobile_app/widget";
32 | boolean mBound;
33 | private MethodChannel channel;
34 | public MethodChannel channel2;
35 | public MethodChannel wakeWordChannel;
36 |
37 | @Override
38 | public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
39 | super.onCreate(savedInstanceState, persistentState);
40 | if (!isTaskRoot()
41 | && getIntent().hasCategory(Intent.CATEGORY_LAUNCHER)
42 | && getIntent().getAction() != null
43 | && getIntent().getAction().equals(Intent.ACTION_MAIN)) {
44 | finish();
45 | }
46 |
47 |
48 | }
49 |
50 | @Override
51 | public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
52 | super.configureFlutterEngine(flutterEngine);
53 | channel = new MethodChannel(flutterEngine.getDartExecutor(), CHANNEL);
54 | Bundle extras = getIntent().getExtras();
55 | if(extras != null && extras.containsKey("StartRecording")) {
56 | Log.i("Home","Starting recording");
57 | DartExecutor.DartEntrypoint entryPoint = new DartExecutor.DartEntrypoint(FlutterInjector.instance().flutterLoader().findAppBundlePath(),"main");
58 | flutterEngine.getDartExecutor().executeDartEntrypoint(entryPoint);
59 | channel.invokeMethod("StartRecording", null);
60 | }
61 |
62 | channel2 =new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), "rhasspy_mobile_app");
63 | channel2.setMethodCallHandler((call, result) -> {
64 | if(call.method.equals("sendToBackground")){
65 | Log.i("Background", "sendToBackground");
66 | moveTaskToBack(true);
67 | }
68 | });
69 | wakeWordChannel =new MethodChannel(flutterEngine.getDartExecutor().getBinaryMessenger(), "wake_word");
70 | wakeWordChannel.setMethodCallHandler((call, result) -> {
71 | switch (call.method){
72 | case "stop":
73 | Log.i("WaKeWord", "Stopping the foreground-thread");
74 | //mService.stopService();
75 | unbindService(connection);
76 | mBound = false;
77 | Intent intent = new Intent(getApplicationContext(), WakeWordService.class);
78 | intent.setAction("Stop");
79 | ContextCompat.startForegroundService(getApplicationContext(), intent);
80 | result.success(true);
81 | break;
82 | case "pause":
83 | if(mBound){
84 | mService.pause();
85 | result.success(true);
86 | } else {
87 | result.error("NoRunningService","no service running",null);
88 | }
89 | break;
90 | case "resume":
91 | if(mBound){
92 | mService.resume();
93 | result.success(true);
94 | } else {
95 | result.error("NoRunningService","no service running",null);
96 | }
97 | break;
98 | case "start":
99 | Log.i("WaKeWord", "Starting the foreground-thread");
100 | Intent serviceIntent = new Intent(getActivity().getApplicationContext(), WakeWordService.class);
101 | serviceIntent.putExtra("wakeWordDetector",call.argument("wakeWordDetector").toString());
102 | switch (call.argument("wakeWordDetector").toString()){
103 | case "UDP":
104 | serviceIntent.putExtra("ip", call.argument("ip").toString());
105 | serviceIntent.putExtra("port", Integer.parseInt(call.argument("port").toString()));
106 | break;
107 | default:
108 | result.error("UnsupportedWakeWord",null,null);
109 | return;
110 | }
111 |
112 | ContextCompat.startForegroundService(getActivity(), serviceIntent);
113 | bindService(serviceIntent, connection,BIND_IMPORTANT);
114 | result.success(true);
115 | break;
116 | case "isRunning":
117 | Log.i("WaKeWord", "check if is listening");
118 |
119 | if(!mBound || mService == null){
120 | result.success(false);
121 | } else {
122 | result.success(true);
123 | }
124 | break;
125 | case "isListening":
126 | Log.i("WaKeWord", "check if is listening");
127 |
128 | if(!mBound || mService == null){
129 | result.success(false);
130 | } else {
131 | result.success(!mService.isPaused);
132 | }
133 | break;
134 | case "getWakeWordDetector":
135 | List availableWakeWordDetector = Arrays.asList("UDP");
136 | result.success(availableWakeWordDetector);
137 | break;
138 | }
139 | });
140 | }
141 |
142 |
143 | @Override
144 | protected void onNewIntent(@NonNull Intent intent) {
145 | super.onNewIntent(intent);
146 | Log.i("Home", "NewIntent");
147 | if(intent.getExtras() != null) {
148 | if (Objects.requireNonNull(intent.getExtras()).containsKey("StartRecording")) {
149 | Log.i("Home", "StartRecording");
150 | channel.invokeMethod("StartRecording", null);
151 | }
152 | }
153 |
154 |
155 | }
156 | private ServiceConnection connection = new ServiceConnection() {
157 |
158 | @Override
159 | public void onServiceConnected(ComponentName className,
160 | IBinder service) {
161 | WakeWordService.LocalBinder binder = (WakeWordService.LocalBinder) service;
162 | mService = binder.getService();
163 | mBound = true;
164 | }
165 |
166 | @Override
167 | public void onServiceDisconnected(ComponentName className) {
168 | mBound = false;
169 | }
170 | };
171 |
172 | @Override
173 | protected void onDestroy() {
174 | if(mBound || mService != null) unbindService(connection);
175 | super.onDestroy();
176 |
177 | }
178 | }
179 |
180 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/example/rhasspy_mobile_app/StartRecordingAppWidgetProvider.java:
--------------------------------------------------------------------------------
1 | package com.example.rhasspy_mobile_app;
2 |
3 | import android.app.PendingIntent;
4 | import android.appwidget.AppWidgetManager;
5 | import android.appwidget.AppWidgetProvider;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.widget.RemoteViews;
9 |
10 | import android.util.Log;
11 | import io.flutter.plugin.common.MethodChannel;
12 | import io.flutter.view.FlutterNativeView;
13 |
14 | public class StartRecordingAppWidgetProvider extends AppWidgetProvider {
15 | private static final String CHANNEL = "rhasspy_mobile_app/widget";
16 | private static MethodChannel channel = null;
17 | private static FlutterNativeView backgroundFlutterView = null;
18 |
19 |
20 | @Override
21 | public void onEnabled(Context context) {
22 | Log.i("HomeScreenWidget", "onEnabled!");
23 | }
24 | @Override
25 | public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
26 | for (int appWidgetId : appWidgetIds){
27 | Intent intent = new Intent(context, MainActivity.class).addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
28 | intent.putExtra("StartRecording","");
29 | PendingIntent pendingIntent = PendingIntent.getActivity(context,0,intent,PendingIntent.FLAG_UPDATE_CURRENT);
30 | RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.start_recording_widget);
31 | views.setOnClickPendingIntent(R.id.start_recording_widget_button, pendingIntent);
32 | appWidgetManager.updateAppWidget(appWidgetId, views);
33 | Log.i("HomeScreenWidget", "onUpdate!");
34 |
35 |
36 | }
37 | super.onUpdate(context, appWidgetManager, appWidgetIds);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/android/app/src/main/java/com/example/rhasspy_mobile_app/WakeWordService.java:
--------------------------------------------------------------------------------
1 | package com.example.rhasspy_mobile_app;
2 |
3 | import android.app.Notification;
4 | import android.app.NotificationChannel;
5 | import android.app.NotificationManager;
6 | import android.app.PendingIntent;
7 | import android.app.Service;
8 | import android.content.Intent;
9 | import android.media.AudioFormat;
10 | import android.media.AudioRecord;
11 | import android.media.MediaRecorder;
12 | import android.os.Binder;
13 | import android.os.Build;
14 | import android.os.IBinder;
15 | import android.util.Log;
16 |
17 | import androidx.annotation.Nullable;
18 | import androidx.core.app.NotificationCompat;
19 |
20 | import java.io.ByteArrayOutputStream;
21 | import java.net.DatagramPacket;
22 | import java.net.DatagramSocket;
23 | import java.net.InetAddress;
24 | import java.net.SocketException;
25 | import java.net.UnknownHostException;
26 |
27 | public class WakeWordService extends Service {
28 |
29 | public class LocalBinder extends Binder {
30 | WakeWordService getService() {
31 | return WakeWordService.this;
32 | }
33 | }
34 |
35 | private AudioRecord recorder;
36 | private static int BUFFER_SIZE = 2048;
37 | private static long byteRate = 16 * 16000 * 1 / 8;
38 | private int sampleRate = 16000;
39 | public boolean isActive = false;
40 | private static String TAG = "WakeWord";
41 | public boolean isPaused = false;
42 | private final IBinder binder = new LocalBinder();
43 | private InetAddress local;
44 | private int port;
45 | private DatagramSocket dsocket;
46 | private String wakeWordDetector;
47 |
48 | @Nullable
49 | @Override
50 | public IBinder onBind(Intent intent) {
51 | return binder;
52 | }
53 |
54 | @Override
55 | public int onStartCommand(Intent intent, int flags, int startId) {
56 | if (intent.getAction() != null) {
57 | if (intent.getAction().equals("Stop")) {
58 | Log.i(TAG, "Received stop ");
59 | stopForeground(true);
60 | stopSelfResult(startId);
61 | isActive = false;
62 | return START_NOT_STICKY;
63 | }
64 | }
65 | wakeWordDetector = intent.getStringExtra("wakeWordDetector");
66 | switch (wakeWordDetector) {
67 | case "UDP":
68 | try {
69 | dsocket = new DatagramSocket();
70 | } catch (SocketException e) {
71 | e.printStackTrace();
72 | }
73 | String ip = intent.getStringExtra("ip");
74 | port = intent.getIntExtra("port", 12101);
75 | try {
76 | local = InetAddress.getByName(ip);
77 | } catch (UnknownHostException e) {
78 | e.printStackTrace();
79 | }
80 | }
81 |
82 |
83 | createNotificationChannel();
84 | Intent notificationIntent = new Intent(this, MainActivity.class);
85 | PendingIntent pendingIntent = PendingIntent.getActivity(this,
86 | 0, notificationIntent, 0);
87 |
88 | Notification notification = new NotificationCompat.Builder(this, "Wake word")
89 | .setContentTitle("Wake Word")
90 | .setContentText("Listening for Wake Word")
91 | .setSmallIcon(R.drawable.app_icon)
92 | .setContentIntent(pendingIntent)
93 | .build();
94 |
95 | startForeground(1, notification);
96 |
97 | startRecorder();
98 |
99 |
100 | return super.onStartCommand(intent, flags, startId);
101 | }
102 |
103 |
104 | public void startRecorder() {
105 | Log.i(TAG, "Starting listening");
106 | isActive = true;
107 | startStreaming();
108 | }
109 |
110 | public void stopService() {
111 | Log.i(TAG, "Stopping the service");
112 | isActive = false;
113 | recorder.release();
114 | stopForeground(true);
115 | stopSelf();
116 | }
117 |
118 | public void pause() {
119 | Log.i(TAG, "pause the audio stream");
120 | isPaused = true;
121 | if (recorder != null) recorder.stop();
122 | }
123 |
124 | public void resume() {
125 | Log.i(TAG, "resume the audio stream");
126 | isPaused = false;
127 | if (recorder != null) recorder.startRecording();
128 | }
129 |
130 | private void createNotificationChannel() {
131 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
132 | NotificationChannel serviceChannel = new NotificationChannel(
133 | "Wake word",
134 | "Wake word",
135 | NotificationManager.IMPORTANCE_DEFAULT
136 | );
137 |
138 | NotificationManager manager = getSystemService(NotificationManager.class);
139 | manager.createNotificationChannel(serviceChannel);
140 | }
141 | }
142 |
143 | @Override
144 | public boolean onUnbind(Intent intent) {
145 | Log.i(TAG, "unbind");
146 | return super.onUnbind(intent);
147 |
148 |
149 | }
150 |
151 | private byte[] WaveHeader(long totalAudioLen,
152 | long longSampleRate, int channels, long byteRate) {
153 | long totalDataLen = totalAudioLen + 36;
154 | byte[] header = new byte[44];
155 | header[0] = 'R'; // RIFF/WAVE header
156 | header[1] = 'I';
157 | header[2] = 'F';
158 | header[3] = 'F';
159 | header[4] = (byte) (totalDataLen & 0xff);
160 | header[5] = (byte) ((totalDataLen >> 8) & 0xff);
161 | header[6] = (byte) ((totalDataLen >> 16) & 0xff);
162 | header[7] = (byte) ((totalDataLen >> 24) & 0xff);
163 | header[8] = 'W';
164 | header[9] = 'A';
165 | header[10] = 'V';
166 | header[11] = 'E';
167 | header[12] = 'f'; // 'fmt ' chunk
168 | header[13] = 'm';
169 | header[14] = 't';
170 | header[15] = ' ';
171 | header[16] = 16; // 4 bytes: size of 'fmt ' chunk
172 | header[17] = 0;
173 | header[18] = 0;
174 | header[19] = 0;
175 | header[20] = 1; // format = 1
176 | header[21] = 0;
177 | header[22] = (byte) channels;
178 | header[23] = 0;
179 | header[24] = (byte) (longSampleRate & 0xff);
180 | header[25] = (byte) ((longSampleRate >> 8) & 0xff);
181 | header[26] = (byte) ((longSampleRate >> 16) & 0xff);
182 | header[27] = (byte) ((longSampleRate >> 24) & 0xff);
183 | header[28] = (byte) (byteRate & 0xff);
184 | header[29] = (byte) ((byteRate >> 8) & 0xff);
185 | header[30] = (byte) ((byteRate >> 16) & 0xff);
186 | header[31] = (byte) ((byteRate >> 24) & 0xff);
187 | header[32] = (byte) (1); // block align
188 | header[33] = 0;
189 | header[34] = 16; // bits per sample
190 | header[35] = 0;
191 | header[36] = 'd';
192 | header[37] = 'a';
193 | header[38] = 't';
194 | header[39] = 'a';
195 | header[40] = (byte) (totalAudioLen & 0xff);
196 | header[41] = (byte) ((totalAudioLen >> 8) & 0xff);
197 | header[42] = (byte) ((totalAudioLen >> 16) & 0xff);
198 | header[43] = (byte) ((totalAudioLen >> 24) & 0xff);
199 | return header;
200 |
201 | }
202 |
203 | private void startStreaming() {
204 |
205 | Thread streamThread = new Thread(() -> {
206 | try {
207 | int bufferSize = BUFFER_SIZE;
208 | byte[] buffer = new byte[bufferSize];
209 |
210 | android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_AUDIO);
211 |
212 | recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
213 |
214 | Log.d(TAG, "start recording");
215 | recorder.startRecording();
216 |
217 | while (isActive) {
218 | if (isPaused) continue;
219 |
220 | int result = recorder.read(buffer, 0, buffer.length);
221 | if (result == AudioRecord.ERROR_BAD_VALUE || result == AudioRecord.ERROR_DEAD_OBJECT
222 | || result == AudioRecord.ERROR_INVALID_OPERATION || result == AudioRecord.ERROR) {
223 | recorder.stop();
224 | recorder.release();
225 | recorder = new AudioRecord(MediaRecorder.AudioSource.MIC, sampleRate, AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, bufferSize);
226 | recorder.startRecording();
227 | continue;
228 |
229 | }
230 | if (result == 0) {
231 | Log.i(TAG, "Silence receiving");
232 | recorder.stop();
233 | recorder.startRecording();
234 | continue;
235 |
236 | }
237 |
238 | ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
239 | outputStream.write(WaveHeader(buffer.length, sampleRate, 1, byteRate));
240 | outputStream.write(buffer);
241 |
242 | switch (wakeWordDetector) {
243 | case "UDP":
244 | try {
245 | DatagramPacket p = new DatagramPacket(outputStream.toByteArray(), outputStream.size(), local, port);
246 |
247 | dsocket.send(p);
248 |
249 | } catch (Exception e) {
250 | Log.e(TAG, "Exception: " + e);
251 | }
252 | }
253 |
254 | }
255 |
256 | Log.d(TAG, "AudioRecord finished recording");
257 | } catch (Exception e) {
258 | Log.e(TAG, "Exception: " + e);
259 | }
260 | });
261 |
262 | // start the thread
263 | streamThread.start();
264 | }
265 |
266 | @Override
267 | public void onDestroy() {
268 | Log.i(TAG, "Destroying");
269 | recorder.stop();
270 | recorder.release();
271 | isActive = false;
272 | recorder = null;
273 | dsocket.close();
274 | super.onDestroy();
275 | }
276 | }
277 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/app_icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/android/app/src/main/res/drawable/app_icon.png
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/layout/start_recording_widget.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/xml/start_recoding_appwidget_info.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | repositories {
3 | google()
4 | jcenter()
5 | }
6 |
7 | dependencies {
8 | classpath 'com.android.tools.build:gradle:4.0.2'
9 | }
10 | }
11 |
12 | allprojects {
13 | repositories {
14 | google()
15 | jcenter()
16 | }
17 | }
18 |
19 | rootProject.buildDir = '../build'
20 | subprojects {
21 | project.buildDir = "${rootProject.buildDir}/${project.name}"
22 | }
23 | subprojects {
24 | project.evaluationDependsOn(':app')
25 | }
26 |
27 | task clean(type: Delete) {
28 | delete rootProject.buildDir
29 | }
30 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 | android.enableR8=true
5 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Aug 29 13:02:54 CEST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | // Copyright 2014 The Flutter Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style license that can be
3 | // found in the LICENSE file.
4 |
5 | include ':app'
6 |
7 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
8 | def properties = new Properties()
9 |
10 | assert localPropertiesFile.exists()
11 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
12 |
13 | def flutterSdkPath = properties.getProperty("flutter.sdk")
14 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
15 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
16 |
--------------------------------------------------------------------------------
/android/settings_aar.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------
/assets/images/rhasspy.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/assets/images/rhasspy.png
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | *.mode1v3
2 | *.mode2v3
3 | *.moved-aside
4 | *.pbxuser
5 | *.perspectivev3
6 | **/*sync/
7 | .sconsign.dblite
8 | .tags*
9 | **/.vagrant/
10 | **/DerivedData/
11 | Icon?
12 | **/Pods/
13 | **/.symlinks/
14 | profile
15 | xcuserdata
16 | **/.generated/
17 | Flutter/App.framework
18 | Flutter/Flutter.framework
19 | Flutter/Flutter.podspec
20 | Flutter/Generated.xcconfig
21 | Flutter/app.flx
22 | Flutter/app.zip
23 | Flutter/flutter_assets/
24 | Flutter/flutter_export_environment.sh
25 | ServiceDefinitions.json
26 | Runner/GeneratedPluginRegistrant.*
27 |
28 | # Exceptions to above rules.
29 | !default.mode1v3
30 | !default.mode2v3
31 | !default.pbxuser
32 | !default.perspectivev3
33 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | App
9 | CFBundleIdentifier
10 | io.flutter.flutter.app
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | App
15 | CFBundlePackageType
16 | FMWK
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | MinimumOSVersion
24 | 8.0
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Flutter/Debug.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/ios/Flutter/Release.xcconfig:
--------------------------------------------------------------------------------
1 | #include "Generated.xcconfig"
2 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
16 | /* End PBXBuildFile section */
17 |
18 | /* Begin PBXCopyFilesBuildPhase section */
19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
20 | isa = PBXCopyFilesBuildPhase;
21 | buildActionMask = 2147483647;
22 | dstPath = "";
23 | dstSubfolderSpec = 10;
24 | files = (
25 | );
26 | name = "Embed Frameworks";
27 | runOnlyForDeploymentPostprocessing = 0;
28 | };
29 | /* End PBXCopyFilesBuildPhase section */
30 |
31 | /* Begin PBXFileReference section */
32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
45 | /* End PBXFileReference section */
46 |
47 | /* Begin PBXFrameworksBuildPhase section */
48 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
49 | isa = PBXFrameworksBuildPhase;
50 | buildActionMask = 2147483647;
51 | files = (
52 | );
53 | runOnlyForDeploymentPostprocessing = 0;
54 | };
55 | /* End PBXFrameworksBuildPhase section */
56 |
57 | /* Begin PBXGroup section */
58 | 9740EEB11CF90186004384FC /* Flutter */ = {
59 | isa = PBXGroup;
60 | children = (
61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
65 | );
66 | name = Flutter;
67 | sourceTree = "";
68 | };
69 | 97C146E51CF9000F007C117D = {
70 | isa = PBXGroup;
71 | children = (
72 | 9740EEB11CF90186004384FC /* Flutter */,
73 | 97C146F01CF9000F007C117D /* Runner */,
74 | 97C146EF1CF9000F007C117D /* Products */,
75 | );
76 | sourceTree = "";
77 | };
78 | 97C146EF1CF9000F007C117D /* Products */ = {
79 | isa = PBXGroup;
80 | children = (
81 | 97C146EE1CF9000F007C117D /* Runner.app */,
82 | );
83 | name = Products;
84 | sourceTree = "";
85 | };
86 | 97C146F01CF9000F007C117D /* Runner */ = {
87 | isa = PBXGroup;
88 | children = (
89 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
92 | 97C147021CF9000F007C117D /* Info.plist */,
93 | 97C146F11CF9000F007C117D /* Supporting Files */,
94 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
95 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
96 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
97 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
98 | );
99 | path = Runner;
100 | sourceTree = "";
101 | };
102 | 97C146F11CF9000F007C117D /* Supporting Files */ = {
103 | isa = PBXGroup;
104 | children = (
105 | );
106 | name = "Supporting Files";
107 | sourceTree = "";
108 | };
109 | /* End PBXGroup section */
110 |
111 | /* Begin PBXNativeTarget section */
112 | 97C146ED1CF9000F007C117D /* Runner */ = {
113 | isa = PBXNativeTarget;
114 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
115 | buildPhases = (
116 | 9740EEB61CF901F6004384FC /* Run Script */,
117 | 97C146EA1CF9000F007C117D /* Sources */,
118 | 97C146EB1CF9000F007C117D /* Frameworks */,
119 | 97C146EC1CF9000F007C117D /* Resources */,
120 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
121 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
122 | );
123 | buildRules = (
124 | );
125 | dependencies = (
126 | );
127 | name = Runner;
128 | productName = Runner;
129 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
130 | productType = "com.apple.product-type.application";
131 | };
132 | /* End PBXNativeTarget section */
133 |
134 | /* Begin PBXProject section */
135 | 97C146E61CF9000F007C117D /* Project object */ = {
136 | isa = PBXProject;
137 | attributes = {
138 | LastUpgradeCheck = 1020;
139 | ORGANIZATIONNAME = "";
140 | TargetAttributes = {
141 | 97C146ED1CF9000F007C117D = {
142 | CreatedOnToolsVersion = 7.3.1;
143 | LastSwiftMigration = 1100;
144 | };
145 | };
146 | };
147 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
148 | compatibilityVersion = "Xcode 9.3";
149 | developmentRegion = en;
150 | hasScannedForEncodings = 0;
151 | knownRegions = (
152 | en,
153 | Base,
154 | );
155 | mainGroup = 97C146E51CF9000F007C117D;
156 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
157 | projectDirPath = "";
158 | projectRoot = "";
159 | targets = (
160 | 97C146ED1CF9000F007C117D /* Runner */,
161 | );
162 | };
163 | /* End PBXProject section */
164 |
165 | /* Begin PBXResourcesBuildPhase section */
166 | 97C146EC1CF9000F007C117D /* Resources */ = {
167 | isa = PBXResourcesBuildPhase;
168 | buildActionMask = 2147483647;
169 | files = (
170 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
171 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
172 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
173 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
174 | );
175 | runOnlyForDeploymentPostprocessing = 0;
176 | };
177 | /* End PBXResourcesBuildPhase section */
178 |
179 | /* Begin PBXShellScriptBuildPhase section */
180 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
181 | isa = PBXShellScriptBuildPhase;
182 | buildActionMask = 2147483647;
183 | files = (
184 | );
185 | inputPaths = (
186 | );
187 | name = "Thin Binary";
188 | outputPaths = (
189 | );
190 | runOnlyForDeploymentPostprocessing = 0;
191 | shellPath = /bin/sh;
192 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
193 | };
194 | 9740EEB61CF901F6004384FC /* Run Script */ = {
195 | isa = PBXShellScriptBuildPhase;
196 | buildActionMask = 2147483647;
197 | files = (
198 | );
199 | inputPaths = (
200 | );
201 | name = "Run Script";
202 | outputPaths = (
203 | );
204 | runOnlyForDeploymentPostprocessing = 0;
205 | shellPath = /bin/sh;
206 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
207 | };
208 | /* End PBXShellScriptBuildPhase section */
209 |
210 | /* Begin PBXSourcesBuildPhase section */
211 | 97C146EA1CF9000F007C117D /* Sources */ = {
212 | isa = PBXSourcesBuildPhase;
213 | buildActionMask = 2147483647;
214 | files = (
215 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
216 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
217 | );
218 | runOnlyForDeploymentPostprocessing = 0;
219 | };
220 | /* End PBXSourcesBuildPhase section */
221 |
222 | /* Begin PBXVariantGroup section */
223 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
224 | isa = PBXVariantGroup;
225 | children = (
226 | 97C146FB1CF9000F007C117D /* Base */,
227 | );
228 | name = Main.storyboard;
229 | sourceTree = "";
230 | };
231 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
232 | isa = PBXVariantGroup;
233 | children = (
234 | 97C147001CF9000F007C117D /* Base */,
235 | );
236 | name = LaunchScreen.storyboard;
237 | sourceTree = "";
238 | };
239 | /* End PBXVariantGroup section */
240 |
241 | /* Begin XCBuildConfiguration section */
242 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
243 | isa = XCBuildConfiguration;
244 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
245 | buildSettings = {
246 | ALWAYS_SEARCH_USER_PATHS = NO;
247 | CLANG_ANALYZER_NONNULL = YES;
248 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
249 | CLANG_CXX_LIBRARY = "libc++";
250 | CLANG_ENABLE_MODULES = YES;
251 | CLANG_ENABLE_OBJC_ARC = YES;
252 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
253 | CLANG_WARN_BOOL_CONVERSION = YES;
254 | CLANG_WARN_COMMA = YES;
255 | CLANG_WARN_CONSTANT_CONVERSION = YES;
256 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
257 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
258 | CLANG_WARN_EMPTY_BODY = YES;
259 | CLANG_WARN_ENUM_CONVERSION = YES;
260 | CLANG_WARN_INFINITE_RECURSION = YES;
261 | CLANG_WARN_INT_CONVERSION = YES;
262 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
263 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
264 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
265 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
266 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
267 | CLANG_WARN_STRICT_PROTOTYPES = YES;
268 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
269 | CLANG_WARN_UNREACHABLE_CODE = YES;
270 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
271 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
272 | COPY_PHASE_STRIP = NO;
273 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
274 | ENABLE_NS_ASSERTIONS = NO;
275 | ENABLE_STRICT_OBJC_MSGSEND = YES;
276 | GCC_C_LANGUAGE_STANDARD = gnu99;
277 | GCC_NO_COMMON_BLOCKS = YES;
278 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
279 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
280 | GCC_WARN_UNDECLARED_SELECTOR = YES;
281 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
282 | GCC_WARN_UNUSED_FUNCTION = YES;
283 | GCC_WARN_UNUSED_VARIABLE = YES;
284 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
285 | MTL_ENABLE_DEBUG_INFO = NO;
286 | SDKROOT = iphoneos;
287 | SUPPORTED_PLATFORMS = iphoneos;
288 | TARGETED_DEVICE_FAMILY = "1,2";
289 | VALIDATE_PRODUCT = YES;
290 | };
291 | name = Profile;
292 | };
293 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
294 | isa = XCBuildConfiguration;
295 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
296 | buildSettings = {
297 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
298 | CLANG_ENABLE_MODULES = YES;
299 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
300 | ENABLE_BITCODE = NO;
301 | FRAMEWORK_SEARCH_PATHS = (
302 | "$(inherited)",
303 | "$(PROJECT_DIR)/Flutter",
304 | );
305 | INFOPLIST_FILE = Runner/Info.plist;
306 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
307 | LIBRARY_SEARCH_PATHS = (
308 | "$(inherited)",
309 | "$(PROJECT_DIR)/Flutter",
310 | );
311 | PRODUCT_BUNDLE_IDENTIFIER = com.example.rhasspyMobileApp;
312 | PRODUCT_NAME = "$(TARGET_NAME)";
313 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
314 | SWIFT_VERSION = 5.0;
315 | VERSIONING_SYSTEM = "apple-generic";
316 | };
317 | name = Profile;
318 | };
319 | 97C147031CF9000F007C117D /* Debug */ = {
320 | isa = XCBuildConfiguration;
321 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
322 | buildSettings = {
323 | ALWAYS_SEARCH_USER_PATHS = NO;
324 | CLANG_ANALYZER_NONNULL = YES;
325 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
326 | CLANG_CXX_LIBRARY = "libc++";
327 | CLANG_ENABLE_MODULES = YES;
328 | CLANG_ENABLE_OBJC_ARC = YES;
329 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
330 | CLANG_WARN_BOOL_CONVERSION = YES;
331 | CLANG_WARN_COMMA = YES;
332 | CLANG_WARN_CONSTANT_CONVERSION = YES;
333 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
334 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
335 | CLANG_WARN_EMPTY_BODY = YES;
336 | CLANG_WARN_ENUM_CONVERSION = YES;
337 | CLANG_WARN_INFINITE_RECURSION = YES;
338 | CLANG_WARN_INT_CONVERSION = YES;
339 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
340 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
341 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
342 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
343 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
344 | CLANG_WARN_STRICT_PROTOTYPES = YES;
345 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
346 | CLANG_WARN_UNREACHABLE_CODE = YES;
347 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
348 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
349 | COPY_PHASE_STRIP = NO;
350 | DEBUG_INFORMATION_FORMAT = dwarf;
351 | ENABLE_STRICT_OBJC_MSGSEND = YES;
352 | ENABLE_TESTABILITY = YES;
353 | GCC_C_LANGUAGE_STANDARD = gnu99;
354 | GCC_DYNAMIC_NO_PIC = NO;
355 | GCC_NO_COMMON_BLOCKS = YES;
356 | GCC_OPTIMIZATION_LEVEL = 0;
357 | GCC_PREPROCESSOR_DEFINITIONS = (
358 | "DEBUG=1",
359 | "$(inherited)",
360 | );
361 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
362 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
363 | GCC_WARN_UNDECLARED_SELECTOR = YES;
364 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
365 | GCC_WARN_UNUSED_FUNCTION = YES;
366 | GCC_WARN_UNUSED_VARIABLE = YES;
367 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
368 | MTL_ENABLE_DEBUG_INFO = YES;
369 | ONLY_ACTIVE_ARCH = YES;
370 | SDKROOT = iphoneos;
371 | TARGETED_DEVICE_FAMILY = "1,2";
372 | };
373 | name = Debug;
374 | };
375 | 97C147041CF9000F007C117D /* Release */ = {
376 | isa = XCBuildConfiguration;
377 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
378 | buildSettings = {
379 | ALWAYS_SEARCH_USER_PATHS = NO;
380 | CLANG_ANALYZER_NONNULL = YES;
381 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
382 | CLANG_CXX_LIBRARY = "libc++";
383 | CLANG_ENABLE_MODULES = YES;
384 | CLANG_ENABLE_OBJC_ARC = YES;
385 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
386 | CLANG_WARN_BOOL_CONVERSION = YES;
387 | CLANG_WARN_COMMA = YES;
388 | CLANG_WARN_CONSTANT_CONVERSION = YES;
389 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
390 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
391 | CLANG_WARN_EMPTY_BODY = YES;
392 | CLANG_WARN_ENUM_CONVERSION = YES;
393 | CLANG_WARN_INFINITE_RECURSION = YES;
394 | CLANG_WARN_INT_CONVERSION = YES;
395 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
396 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
397 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
398 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
399 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
400 | CLANG_WARN_STRICT_PROTOTYPES = YES;
401 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
402 | CLANG_WARN_UNREACHABLE_CODE = YES;
403 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
404 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
405 | COPY_PHASE_STRIP = NO;
406 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
407 | ENABLE_NS_ASSERTIONS = NO;
408 | ENABLE_STRICT_OBJC_MSGSEND = YES;
409 | GCC_C_LANGUAGE_STANDARD = gnu99;
410 | GCC_NO_COMMON_BLOCKS = YES;
411 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
412 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
413 | GCC_WARN_UNDECLARED_SELECTOR = YES;
414 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
415 | GCC_WARN_UNUSED_FUNCTION = YES;
416 | GCC_WARN_UNUSED_VARIABLE = YES;
417 | IPHONEOS_DEPLOYMENT_TARGET = 8.0;
418 | MTL_ENABLE_DEBUG_INFO = NO;
419 | SDKROOT = iphoneos;
420 | SUPPORTED_PLATFORMS = iphoneos;
421 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
422 | TARGETED_DEVICE_FAMILY = "1,2";
423 | VALIDATE_PRODUCT = YES;
424 | };
425 | name = Release;
426 | };
427 | 97C147061CF9000F007C117D /* Debug */ = {
428 | isa = XCBuildConfiguration;
429 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
430 | buildSettings = {
431 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
432 | CLANG_ENABLE_MODULES = YES;
433 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
434 | ENABLE_BITCODE = NO;
435 | FRAMEWORK_SEARCH_PATHS = (
436 | "$(inherited)",
437 | "$(PROJECT_DIR)/Flutter",
438 | );
439 | INFOPLIST_FILE = Runner/Info.plist;
440 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
441 | LIBRARY_SEARCH_PATHS = (
442 | "$(inherited)",
443 | "$(PROJECT_DIR)/Flutter",
444 | );
445 | PRODUCT_BUNDLE_IDENTIFIER = com.example.rhasspyMobileApp;
446 | PRODUCT_NAME = "$(TARGET_NAME)";
447 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
448 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
449 | SWIFT_VERSION = 5.0;
450 | VERSIONING_SYSTEM = "apple-generic";
451 | };
452 | name = Debug;
453 | };
454 | 97C147071CF9000F007C117D /* Release */ = {
455 | isa = XCBuildConfiguration;
456 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
457 | buildSettings = {
458 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
459 | CLANG_ENABLE_MODULES = YES;
460 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
461 | ENABLE_BITCODE = NO;
462 | FRAMEWORK_SEARCH_PATHS = (
463 | "$(inherited)",
464 | "$(PROJECT_DIR)/Flutter",
465 | );
466 | INFOPLIST_FILE = Runner/Info.plist;
467 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
468 | LIBRARY_SEARCH_PATHS = (
469 | "$(inherited)",
470 | "$(PROJECT_DIR)/Flutter",
471 | );
472 | PRODUCT_BUNDLE_IDENTIFIER = com.example.rhasspyMobileApp;
473 | PRODUCT_NAME = "$(TARGET_NAME)";
474 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
475 | SWIFT_VERSION = 5.0;
476 | VERSIONING_SYSTEM = "apple-generic";
477 | };
478 | name = Release;
479 | };
480 | /* End XCBuildConfiguration section */
481 |
482 | /* Begin XCConfigurationList section */
483 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
484 | isa = XCConfigurationList;
485 | buildConfigurations = (
486 | 97C147031CF9000F007C117D /* Debug */,
487 | 97C147041CF9000F007C117D /* Release */,
488 | 249021D3217E4FDB00AE95B9 /* Profile */,
489 | );
490 | defaultConfigurationIsVisible = 0;
491 | defaultConfigurationName = Release;
492 | };
493 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
494 | isa = XCConfigurationList;
495 | buildConfigurations = (
496 | 97C147061CF9000F007C117D /* Debug */,
497 | 97C147071CF9000F007C117D /* Release */,
498 | 249021D4217E4FDB00AE95B9 /* Profile */,
499 | );
500 | defaultConfigurationIsVisible = 0;
501 | defaultConfigurationName = Release;
502 | };
503 | /* End XCConfigurationList section */
504 | };
505 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
506 | }
507 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
39 |
40 |
41 |
42 |
43 |
44 |
54 |
56 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
75 |
81 |
82 |
83 |
84 |
86 |
87 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PreviewsEnabled
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/ios/Runner/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | import UIKit
2 | import Flutter
3 |
4 | @UIApplicationMain
5 | @objc class AppDelegate: FlutterAppDelegate {
6 | override func application(
7 | _ application: UIApplication,
8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
9 | ) -> Bool {
10 | GeneratedPluginRegistrant.register(with: self)
11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions)
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "20x20",
5 | "idiom" : "iphone",
6 | "filename" : "Icon-App-20x20@2x.png",
7 | "scale" : "2x"
8 | },
9 | {
10 | "size" : "20x20",
11 | "idiom" : "iphone",
12 | "filename" : "Icon-App-20x20@3x.png",
13 | "scale" : "3x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "Icon-App-29x29@1x.png",
19 | "scale" : "1x"
20 | },
21 | {
22 | "size" : "29x29",
23 | "idiom" : "iphone",
24 | "filename" : "Icon-App-29x29@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "29x29",
29 | "idiom" : "iphone",
30 | "filename" : "Icon-App-29x29@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "40x40",
35 | "idiom" : "iphone",
36 | "filename" : "Icon-App-40x40@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "40x40",
41 | "idiom" : "iphone",
42 | "filename" : "Icon-App-40x40@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "60x60",
47 | "idiom" : "iphone",
48 | "filename" : "Icon-App-60x60@2x.png",
49 | "scale" : "2x"
50 | },
51 | {
52 | "size" : "60x60",
53 | "idiom" : "iphone",
54 | "filename" : "Icon-App-60x60@3x.png",
55 | "scale" : "3x"
56 | },
57 | {
58 | "size" : "20x20",
59 | "idiom" : "ipad",
60 | "filename" : "Icon-App-20x20@1x.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "20x20",
65 | "idiom" : "ipad",
66 | "filename" : "Icon-App-20x20@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "29x29",
71 | "idiom" : "ipad",
72 | "filename" : "Icon-App-29x29@1x.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "29x29",
77 | "idiom" : "ipad",
78 | "filename" : "Icon-App-29x29@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "40x40",
83 | "idiom" : "ipad",
84 | "filename" : "Icon-App-40x40@1x.png",
85 | "scale" : "1x"
86 | },
87 | {
88 | "size" : "40x40",
89 | "idiom" : "ipad",
90 | "filename" : "Icon-App-40x40@2x.png",
91 | "scale" : "2x"
92 | },
93 | {
94 | "size" : "76x76",
95 | "idiom" : "ipad",
96 | "filename" : "Icon-App-76x76@1x.png",
97 | "scale" : "1x"
98 | },
99 | {
100 | "size" : "76x76",
101 | "idiom" : "ipad",
102 | "filename" : "Icon-App-76x76@2x.png",
103 | "scale" : "2x"
104 | },
105 | {
106 | "size" : "83.5x83.5",
107 | "idiom" : "ipad",
108 | "filename" : "Icon-App-83.5x83.5@2x.png",
109 | "scale" : "2x"
110 | },
111 | {
112 | "size" : "1024x1024",
113 | "idiom" : "ios-marketing",
114 | "filename" : "Icon-App-1024x1024@1x.png",
115 | "scale" : "1x"
116 | }
117 | ],
118 | "info" : {
119 | "version" : 1,
120 | "author" : "xcode"
121 | }
122 | }
123 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-50x50@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-57x57@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-72x72@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "filename" : "LaunchImage.png",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "filename" : "LaunchImage@2x.png",
11 | "scale" : "2x"
12 | },
13 | {
14 | "idiom" : "universal",
15 | "filename" : "LaunchImage@3x.png",
16 | "scale" : "3x"
17 | }
18 | ],
19 | "info" : {
20 | "version" : 1,
21 | "author" : "xcode"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/razzo04/rhasspy-mobile-app/3c59971270eab0278cd5dbf6adac4064b5f14908/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md:
--------------------------------------------------------------------------------
1 | # Launch Screen Assets
2 |
3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory.
4 |
5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/ios/Runner/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/ios/Runner/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | $(DEVELOPMENT_LANGUAGE)
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | rhasspy_mobile_app
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | $(FLUTTER_BUILD_NAME)
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | $(FLUTTER_BUILD_NUMBER)
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UISupportedInterfaceOrientations
30 |
31 | UIInterfaceOrientationPortrait
32 | UIInterfaceOrientationLandscapeLeft
33 | UIInterfaceOrientationLandscapeRight
34 |
35 | UISupportedInterfaceOrientations~ipad
36 |
37 | UIInterfaceOrientationPortrait
38 | UIInterfaceOrientationPortraitUpsideDown
39 | UIInterfaceOrientationLandscapeLeft
40 | UIInterfaceOrientationLandscapeRight
41 |
42 | UIViewControllerBasedStatusBarAppearance
43 |
44 |
45 |
46 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:io';
3 |
4 | import 'package:flutter/foundation.dart';
5 | import 'package:flutter/material.dart';
6 | import 'package:rhasspy_mobile_app/utils/logger/logger.dart';
7 | import 'package:path_provider/path_provider.dart';
8 | import 'package:provider/provider.dart';
9 | import 'package:rhasspy_mobile_app/screens/app_settings.dart';
10 | import 'package:rhasspy_mobile_app/screens/home_page.dart';
11 | import 'package:shared_preferences/shared_preferences.dart';
12 | import 'package:rhasspy_mobile_app/utils/constants.dart';
13 | import 'rhasspy_dart/rhasspy_mqtt_isolate.dart';
14 | import 'utils/utils.dart';
15 |
16 | RhasspyMqttIsolate rhasspyMqttIsolate;
17 | Logger log;
18 | void setupLogger() async {
19 | MemoryLogOutput memoryOutput = MemoryLogOutput();
20 | WidgetsFlutterBinding.ensureInitialized();
21 | File logFile;
22 | if (Platform.isAndroid) {
23 | logFile = File((await getExternalStorageDirectory()).path + "/logs.txt");
24 | } else {
25 | logFile =
26 | File((await getApplicationDocumentsDirectory()).path + "/logs.txt");
27 | }
28 | log = Logger(
29 | logOutput: MultiOutput([
30 | memoryOutput,
31 | ConsoleOutput(printer: const SimplePrinter(includeStackTrace: false)),
32 | FileOutput(
33 | overrideExisting: true,
34 | file: logFile,
35 | )
36 | ]));
37 | FlutterError.onError = (FlutterErrorDetails details) {
38 | if (!kReleaseMode) FlutterError.dumpErrorToConsole(details);
39 | log.log(Level.error, details.toString(),
40 | stackTrace: details.stack,
41 | tag: details.exceptionAsString(),
42 | includeTime: true);
43 | };
44 | }
45 |
46 | Future setupMqtt() async {
47 | if (rhasspyMqttIsolate != null) return rhasspyMqttIsolate;
48 | String certificatePath;
49 | WidgetsFlutterBinding.ensureInitialized();
50 | Directory appDocDirectory = await getApplicationDocumentsDirectory();
51 | certificatePath = appDocDirectory.path + "/mqttCertificate.pem";
52 | if (!File(certificatePath).existsSync()) {
53 | certificatePath = null;
54 | }
55 | SharedPreferences prefs = await SharedPreferences.getInstance();
56 | log.d("Starting mqtt...", mqttTag);
57 | rhasspyMqttIsolate = RhasspyMqttIsolate(
58 | prefs.getString("MQTTHOST") ?? "",
59 | prefs.getInt("MQTTPORT") ?? 1883,
60 | prefs.getBool("MQTTSSL") ?? false,
61 | prefs.getString("MQTTUSERNAME") ?? "",
62 | prefs.getString("MQTTPASSWORD") ?? "",
63 | prefs.getString("SITEID") ?? "",
64 | pemFilePath: certificatePath,
65 | );
66 | if (prefs.containsKey("MQTT") && prefs.getBool("MQTT")) {
67 | log.d("Connecting to mqtt...", mqttTag);
68 | rhasspyMqttIsolate.connect();
69 | }
70 | return rhasspyMqttIsolate;
71 | }
72 |
73 | void main() {
74 | setupLogger();
75 | runZonedGuarded>(() async {
76 | runApp(MyApp());
77 | }, (Object error, StackTrace stackTrace) {
78 | log.log(Level.error, error.toString(),
79 | stackTrace: stackTrace, includeTime: true);
80 | }, zoneSpecification: ZoneSpecification(print: (self, parent, zone, message) {
81 | parent.print(zone, message);
82 | }));
83 | }
84 |
85 | class MyApp extends StatelessWidget {
86 | @override
87 | Widget build(BuildContext context) {
88 | return FutureProvider(
89 | create: (_) => setupMqtt(),
90 | child: MaterialApp(
91 | debugShowCheckedModeBanner: false,
92 | title: 'Rhasspy mobile app',
93 | onGenerateRoute: (RouteSettings settings) {
94 | var screen;
95 | switch (settings.name) {
96 | case HomePage.routeName:
97 | screen = HomePage();
98 | break;
99 | case AppSettings.routeName:
100 | screen = AppSettings();
101 | break;
102 | }
103 | return MaterialPageRoute(
104 | builder: (context) => screen, settings: settings);
105 | },
106 | theme: ThemeData(
107 | primarySwatch: createMaterialColor(Color.fromARGB(255, 52, 58, 64)),
108 | visualDensity: VisualDensity.adaptivePlatformDensity,
109 | ),
110 | home: HomePage()),
111 | lazy: false,
112 | );
113 | }
114 | }
115 |
--------------------------------------------------------------------------------
/lib/rhasspy_dart/exceptions.dart:
--------------------------------------------------------------------------------
1 | class NotConnected implements Exception {}
2 |
--------------------------------------------------------------------------------
/lib/rhasspy_dart/parse_messages.dart:
--------------------------------------------------------------------------------
1 | class AsrTextCaptured {
2 | String text;
3 | double likelihood;
4 | double seconds;
5 | String siteId;
6 | String sessionId;
7 | String wakewordId;
8 |
9 | AsrTextCaptured(
10 | {this.text,
11 | this.likelihood,
12 | this.seconds,
13 | this.siteId,
14 | this.sessionId,
15 | this.wakewordId});
16 |
17 | AsrTextCaptured.fromJson(Map json) {
18 | text = json['text'];
19 | if (json['likelihood'] is double) {
20 | likelihood = json['likelihood'];
21 | } else {
22 | likelihood = double.parse(json['likelihood'].toString());
23 | }
24 | if (json['seconds'] is double) {
25 | seconds = json['seconds'];
26 | } else {
27 | seconds = double.parse(json['seconds'].toString());
28 | }
29 |
30 | siteId = json['siteId'];
31 | sessionId = json['sessionId'];
32 | wakewordId = json['wakewordId'];
33 | }
34 |
35 | Map toJson() {
36 | final Map data = new Map();
37 | data['text'] = this.text;
38 | data['likelihood'] = this.likelihood;
39 | data['seconds'] = this.seconds;
40 | data['siteId'] = this.siteId;
41 | data['sessionId'] = this.sessionId;
42 | data['wakewordId'] = this.wakewordId;
43 | return data;
44 | }
45 | }
46 |
47 | class NluIntentParsed {
48 | String input;
49 | Intent intent;
50 | String siteId;
51 | String id;
52 | List slots;
53 | String sessionId;
54 |
55 | NluIntentParsed(
56 | {this.input,
57 | this.intent,
58 | this.siteId,
59 | this.id,
60 | this.slots,
61 | this.sessionId});
62 |
63 | NluIntentParsed.fromJson(Map json) {
64 | input = json['input'];
65 | intent =
66 | json['intent'] != null ? new Intent.fromJson(json['intent']) : null;
67 | siteId = json['siteId'];
68 | id = json['id'];
69 | if (json['slots'] != null) {
70 | slots = new List();
71 | json['slots'].forEach((v) {
72 | slots.add(new Slots.fromJson(v));
73 | });
74 | }
75 | sessionId = json['sessionId'];
76 | }
77 |
78 | Map toJson() {
79 | final Map data = new Map();
80 | data['input'] = this.input;
81 | if (this.intent != null) {
82 | data['intent'] = this.intent.toJson();
83 | }
84 | data['siteId'] = this.siteId;
85 | data['id'] = this.id;
86 | if (this.slots != null) {
87 | data['slots'] = this.slots.map((v) => v.toJson()).toList();
88 | }
89 | data['sessionId'] = this.sessionId;
90 | return data;
91 | }
92 | }
93 |
94 | class Intent {
95 | String intentName;
96 | double confidenceScore;
97 |
98 | Intent({this.intentName, this.confidenceScore});
99 |
100 | Intent.fromJson(Map json) {
101 | intentName = json['intentName'];
102 | confidenceScore = json['confidenceScore'];
103 | }
104 |
105 | Map toJson() {
106 | final Map data = new Map();
107 | data['intentName'] = this.intentName;
108 | data['confidenceScore'] = this.confidenceScore;
109 | return data;
110 | }
111 | }
112 |
113 | class Slots {
114 | String entity;
115 | Value value;
116 | String slotName;
117 | String rawValue;
118 | double confidence;
119 | Range range;
120 |
121 | Slots(
122 | {this.entity,
123 | this.value,
124 | this.slotName,
125 | this.rawValue,
126 | this.confidence,
127 | this.range});
128 |
129 | Slots.fromJson(Map json) {
130 | entity = json['entity'];
131 | value = json['value'] != null ? new Value.fromJson(json['value']) : null;
132 | slotName = json['slotName'];
133 | rawValue = json['rawValue'];
134 | confidence = json['confidence'];
135 | range = json['range'] != null ? new Range.fromJson(json['range']) : null;
136 | }
137 |
138 | Map toJson() {
139 | final Map data = new Map();
140 | data['entity'] = this.entity;
141 | if (this.value != null) {
142 | data['value'] = this.value.toJson();
143 | }
144 | data['slotName'] = this.slotName;
145 | data['rawValue'] = this.rawValue;
146 | data['confidence'] = this.confidence;
147 | if (this.range != null) {
148 | data['range'] = this.range.toJson();
149 | }
150 | return data;
151 | }
152 | }
153 |
154 | class Value {
155 | String kind;
156 | String value;
157 |
158 | Value({this.kind, this.value});
159 |
160 | Value.fromJson(Map json) {
161 | kind = json['kind'];
162 | value = json['value'].toString();
163 | }
164 |
165 | Map toJson() {
166 | final Map data = new Map();
167 | data['kind'] = this.kind;
168 | data['value'] = this.value;
169 | return data;
170 | }
171 | }
172 |
173 | class Range {
174 | int start;
175 | int end;
176 | int rawStart;
177 | int rawEnd;
178 |
179 | Range({this.start, this.end, this.rawStart, this.rawEnd});
180 |
181 | Range.fromJson(Map json) {
182 | start = json['start'];
183 | end = json['end'];
184 | rawStart = json['rawStart'];
185 | rawEnd = json['rawEnd'];
186 | }
187 |
188 | Map toJson() {
189 | final Map data = new Map();
190 | data['start'] = this.start;
191 | data['end'] = this.end;
192 | data['rawStart'] = this.rawStart;
193 | data['rawEnd'] = this.rawEnd;
194 | return data;
195 | }
196 | }
197 |
198 | class DialogueEndSession {
199 | String sessionId;
200 | String text;
201 | String customData;
202 |
203 | DialogueEndSession({this.sessionId, this.text, this.customData});
204 |
205 | DialogueEndSession.fromJson(Map json) {
206 | sessionId = json['sessionId'];
207 | text = json['text'];
208 | customData = json['customData'];
209 | }
210 |
211 | Map toJson() {
212 | final Map data = new Map();
213 | data['sessionId'] = this.sessionId;
214 | data['text'] = this.text;
215 | data['customData'] = this.customData;
216 | return data;
217 | }
218 | }
219 |
220 | class DialogueContinueSession {
221 | String sessionId;
222 | String customData;
223 | String text;
224 | List intentFilter;
225 | bool sendIntentNotRecognized;
226 | List slot;
227 | String lang;
228 |
229 | DialogueContinueSession(
230 | {this.sessionId,
231 | this.customData,
232 | this.text,
233 | this.intentFilter,
234 | this.sendIntentNotRecognized,
235 | this.slot,
236 | this.lang});
237 |
238 | DialogueContinueSession.fromJson(Map json) {
239 | sessionId = json['sessionId'];
240 | customData = json['customData'];
241 | text = json['text'];
242 | if (json['intentFilter'] != null)
243 | intentFilter = json['intentFilter'].cast();
244 | sendIntentNotRecognized = json['sendIntentNotRecognized'];
245 | if (json['slots'] != null) {
246 | slot = new List();
247 | json['slots'].forEach((v) {
248 | slot.add(new Slots.fromJson(v));
249 | });
250 | }
251 | lang = json['lang'];
252 | }
253 |
254 | Map toJson() {
255 | final Map data = new Map();
256 | data['sessionId'] = this.sessionId;
257 | data['customData'] = this.customData;
258 | data['text'] = this.text;
259 | data['intentFilter'] = this.intentFilter;
260 | data['sendIntentNotRecognized'] = this.sendIntentNotRecognized;
261 | data['slot'] = this.slot;
262 | data['lang'] = this.lang;
263 | return data;
264 | }
265 | }
266 |
267 | class DialogueStartSession {
268 | String siteId;
269 | String customData;
270 | Init init;
271 |
272 | DialogueStartSession({this.siteId, this.customData, this.init});
273 |
274 | DialogueStartSession.fromJson(Map json) {
275 | siteId = json['siteId'];
276 | customData = json['customData'];
277 | init = json['init'] != null ? new Init.fromJson(json['init']) : null;
278 | }
279 |
280 | Map toJson() {
281 | final Map data = new Map();
282 | data['siteId'] = this.siteId;
283 | data['customData'] = this.customData;
284 | if (this.init != null) {
285 | data['init'] = this.init.toJson();
286 | }
287 | return data;
288 | }
289 | }
290 |
291 | class Init {
292 | String type;
293 | String text;
294 | bool canBeEnqueued;
295 | List intentFilter;
296 |
297 | Init({this.type, this.text, this.canBeEnqueued, this.intentFilter});
298 |
299 | Init.fromJson(Map json) {
300 | type = json['type'];
301 | text = json['text'];
302 | canBeEnqueued = json['canBeEnqueued'];
303 | if (intentFilter != null)
304 | intentFilter = json['intentFilter'].cast();
305 | }
306 |
307 | Map toJson() {
308 | final Map data = new Map();
309 | data['type'] = this.type;
310 | data['text'] = this.text;
311 | data['canBeEnqueued'] = this.canBeEnqueued;
312 | data['intentFilter'] = this.intentFilter;
313 | return data;
314 | }
315 | }
316 |
317 | class DialogueSessionStarted {
318 | String sessionId;
319 | String siteId;
320 | String customData;
321 | String lang;
322 |
323 | DialogueSessionStarted(
324 | {this.sessionId, this.siteId, this.customData, this.lang});
325 |
326 | DialogueSessionStarted.fromJson(Map json) {
327 | sessionId = json['sessionId'];
328 | siteId = json['siteId'];
329 | customData = json['customData'];
330 | lang = json['lang'];
331 | }
332 |
333 | Map toJson() {
334 | final Map data = new Map();
335 | data['sessionId'] = this.sessionId;
336 | data['siteId'] = this.siteId;
337 | data['customData'] = this.customData;
338 | data['lang'] = this.lang;
339 | return data;
340 | }
341 | }
342 |
343 | class DialogueSessionEnded {
344 | Termination termination;
345 | String sessionId;
346 | String siteId;
347 | String customData;
348 |
349 | DialogueSessionEnded(
350 | {this.termination, this.sessionId, this.siteId, this.customData});
351 |
352 | DialogueSessionEnded.fromJson(Map json) {
353 | termination = json['termination'] != null
354 | ? new Termination.fromJson(json['termination'])
355 | : null;
356 | sessionId = json['sessionId'];
357 | siteId = json['siteId'];
358 | customData = json['customData'];
359 | }
360 |
361 | Map toJson() {
362 | final Map data = new Map();
363 | if (this.termination != null) {
364 | data['termination'] = this.termination.toJson();
365 | }
366 | data['sessionId'] = this.sessionId;
367 | data['siteId'] = this.siteId;
368 | data['customData'] = this.customData;
369 | return data;
370 | }
371 | }
372 |
373 | class Termination {
374 | String reason;
375 |
376 | Termination({this.reason});
377 |
378 | Termination.fromJson(Map json) {
379 | reason = json['reason'];
380 | }
381 |
382 | Map toJson() {
383 | final Map data = new Map();
384 | data['reason'] = this.reason;
385 | return data;
386 | }
387 | }
388 |
389 | class NluIntentNotRecognized {
390 | String input;
391 | String siteId;
392 | String id;
393 | String customData;
394 | String sessionId;
395 |
396 | NluIntentNotRecognized(
397 | {this.input, this.siteId, this.id, this.customData, this.sessionId});
398 |
399 | NluIntentNotRecognized.fromJson(Map json) {
400 | input = json['input'];
401 | siteId = json['siteId'];
402 | id = json['id'];
403 | customData = json['customData'];
404 | sessionId = json['sessionId'];
405 | }
406 |
407 | Map toJson() {
408 | final Map data = new Map();
409 | data['input'] = this.input;
410 | data['siteId'] = this.siteId;
411 | data['id'] = this.id;
412 | data['customData'] = this.customData;
413 | data['sessionId'] = this.sessionId;
414 | return data;
415 | }
416 | }
417 |
418 | class HotwordDetected {
419 | String modelId;
420 | String modelVersion;
421 | String modelType;
422 | double currentSensitivity;
423 | String siteId;
424 | String sessionId;
425 | bool sendAudioCaptured;
426 | String lang;
427 |
428 | HotwordDetected(
429 | {this.modelId,
430 | this.modelVersion,
431 | this.modelType,
432 | this.currentSensitivity,
433 | this.siteId,
434 | this.sessionId,
435 | this.sendAudioCaptured,
436 | this.lang});
437 |
438 | HotwordDetected.fromJson(Map json) {
439 | modelId = json['modelId'];
440 | modelVersion = json['modelVersion'];
441 | modelType = json['modelType'];
442 | currentSensitivity = json['currentSensitivity'];
443 | siteId = json['siteId'];
444 | sessionId = json['sessionId'];
445 | sendAudioCaptured = json['sendAudioCaptured'];
446 | lang = json['lang'];
447 | }
448 |
449 | Map toJson() {
450 | final Map data = new Map();
451 | data['modelId'] = this.modelId;
452 | data['modelVersion'] = this.modelVersion;
453 | data['modelType'] = this.modelType;
454 | data['currentSensitivity'] = this.currentSensitivity;
455 | data['siteId'] = this.siteId;
456 | data['sessionId'] = this.sessionId;
457 | data['sendAudioCaptured'] = this.sendAudioCaptured;
458 | data['lang'] = this.lang;
459 | return data;
460 | }
461 | }
462 |
463 | class HotwordToggle {
464 | String siteId;
465 | String reason;
466 |
467 | HotwordToggle({this.siteId, this.reason});
468 |
469 | HotwordToggle.fromJson(Map json) {
470 | siteId = json['siteId'];
471 | reason = json['reason'];
472 | }
473 |
474 | Map toJson() {
475 | final Map data = new Map();
476 | data['siteId'] = this.siteId;
477 | data['reason'] = this.reason;
478 | return data;
479 | }
480 | }
481 |
482 | class AudioSetVolume {
483 | double volume;
484 | String siteId;
485 |
486 | AudioSetVolume({this.volume, this.siteId});
487 |
488 | AudioSetVolume.fromJson(Map json) {
489 | volume = json['volume'];
490 | siteId = json['siteId'];
491 | }
492 |
493 | Map toJson() {
494 | final Map data = new Map();
495 | data['volume'] = this.volume;
496 | data['siteId'] = this.siteId;
497 | return data;
498 | }
499 | }
500 |
--------------------------------------------------------------------------------
/lib/rhasspy_dart/rhasspy_api.dart:
--------------------------------------------------------------------------------
1 | import 'dart:io';
2 | import 'dart:typed_data';
3 | import 'dart:convert';
4 | import 'package:dio/adapter.dart';
5 | import 'package:dio/dio.dart';
6 | import 'package:rhasspy_mobile_app/rhasspy_dart/parse_messages.dart';
7 |
8 | enum ProfileLayers {
9 | all,
10 | defaults,
11 | profile,
12 | }
13 |
14 | class MqttSettings {
15 | bool enabled = false;
16 | String host = "";
17 | String password = "";
18 | List siteIds = [];
19 | String username = "";
20 | int port;
21 |
22 | MqttSettings(
23 | {this.enabled, this.host, this.password, this.siteIds, this.username});
24 | MqttSettings.empty() : enabled = false;
25 | MqttSettings.fromJson(Map json) {
26 | enabled = json.containsKey("enabled")
27 | ? json['enabled'] == "true" || json['enabled'] == true
28 | ? true
29 | : false
30 | : false;
31 | host = json['host'];
32 | password = json['password'];
33 | if (json.containsKey("site_id")) {
34 | siteIds = (json["site_id"] as String).split(",");
35 | } else {
36 | siteIds = [];
37 | }
38 |
39 | siteIds = json["site_id"] != null && json["site_id"] != ""
40 | ? (json['site_id'] as String).split(",")
41 | : [];
42 | username = json['username'];
43 | port = json.containsKey("port") ? int.parse(json["port"]) : 1883;
44 | }
45 |
46 | Map toJson() {
47 | final Map data = new Map();
48 | data['enabled'] = this.enabled;
49 | data['host'] = this.host;
50 | data['password'] = this.password;
51 | data['site_id'] =
52 | this.siteIds.length != 1 ? this.siteIds.join(",") : this.siteIds[0];
53 | data['username'] = this.username;
54 | return data;
55 | }
56 | }
57 |
58 | class RhasspyProfile {
59 | Map _profile;
60 | MqttSettings mqttSettings;
61 | List get siteIds => mqttSettings.siteIds;
62 | bool get isMqttEnable => mqttSettings.enabled;
63 | set siteIds(siteId) {
64 | if (siteId is String) {
65 | mqttSettings.siteIds.add(siteId);
66 | } else {
67 | mqttSettings.siteIds.addAll(siteId);
68 | }
69 | }
70 |
71 | bool get isDialogueRhasspy {
72 | if (_profile.containsKey("dialogue") &&
73 | _profile["dialogue"]["system"] == "rhasspy") {
74 | return true;
75 | } else {
76 | return false;
77 | }
78 | }
79 |
80 | void setDialogueSystem(String system) {
81 | if (!_profile.containsKey("dialogue")) {
82 | _profile["dialogue"] = Map();
83 | }
84 | _profile["dialogue"]["system"] = system;
85 | }
86 |
87 | bool containsSiteId(String siteId) {
88 | for (String item in mqttSettings.siteIds) {
89 | if (siteId == item) return true;
90 | }
91 | return false;
92 | }
93 |
94 | bool isNewInstallation() {
95 | if (_profile.containsKey("language")) {
96 | return true;
97 | } else {
98 | return false;
99 | }
100 | }
101 |
102 | bool compareMqttSettings(
103 | String host, String username, String password, int port) {
104 | if (mqttSettings.host == host &&
105 | mqttSettings.username == username &&
106 | mqttSettings.password == password &&
107 | mqttSettings.port == port) {
108 | return true;
109 | } else {
110 | return false;
111 | }
112 | }
113 |
114 | void addSiteId(String siteId) {
115 | if (containsSiteId(siteId)) return;
116 | mqttSettings.siteIds.add(siteId);
117 | }
118 |
119 | RhasspyProfile.fromJson(Map json) {
120 | _profile = json;
121 | mqttSettings = _profile.containsKey("mqtt")
122 | ? MqttSettings.fromJson(_profile["mqtt"])
123 | : MqttSettings.empty();
124 | }
125 | Map toJson() {
126 | _profile["mqtt"] = mqttSettings.toJson();
127 | return _profile;
128 | }
129 | }
130 |
131 | class RhasspyApi {
132 | String ip;
133 | int port;
134 | bool ssl;
135 | String baseUrl;
136 | Dio dio;
137 | SecurityContext securityContext;
138 |
139 | static const Map profileString = {
140 | ProfileLayers.all: "all",
141 | ProfileLayers.defaults: "defaults",
142 | ProfileLayers.profile: "profile"
143 | };
144 |
145 | RhasspyApi(this.ip, this.port, this.ssl, {this.securityContext}) {
146 | if (!ssl) {
147 | baseUrl = "http://" + ip + ":" + port.toString();
148 | dio = Dio(BaseOptions(baseUrl: baseUrl));
149 | } else {
150 | baseUrl = "https://" + ip + ":" + port.toString();
151 | dio = Dio(BaseOptions(baseUrl: baseUrl));
152 | (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
153 | (client) {
154 | return HttpClient(context: securityContext);
155 | };
156 | }
157 | }
158 |
159 | /// check if it is possible to establish a connection with rhasspy.
160 | /// Its return codes are 0 connection successfully,
161 | /// 1 connection failed, 2 bad certificate.
162 | Future checkConnection() async {
163 | // Recreate the object to change the timeout parameters
164 | Dio dio = Dio(
165 | BaseOptions(baseUrl: baseUrl, connectTimeout: 1000, receiveTimeout: 1000),
166 | );
167 | (dio.httpClientAdapter as DefaultHttpClientAdapter).onHttpClientCreate =
168 | (client) {
169 | return HttpClient(context: securityContext);
170 | };
171 | try {
172 | var response = await dio.get("/api/intents");
173 | if (response.statusCode == 200) return 0;
174 | } on DioError catch (e) {
175 | if (e.error is HandshakeException) {
176 | return 2;
177 | }
178 | return 1;
179 | }
180 | return 0;
181 | }
182 |
183 | Future getIntent() async {
184 | Response response = await dio.get("/api/intents");
185 | return response.data.toString();
186 | }
187 |
188 | Future