├── .gitignore
├── .metadata
├── LICENSE.md
├── README.md
├── analysis_options.yaml
├── android
├── .gitignore
├── app
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── debug
│ │ └── AndroidManifest.xml
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── kotlin
│ │ │ └── com
│ │ │ │ └── example
│ │ │ │ └── frame_flutter_stt_host
│ │ │ │ └── MainActivity.kt
│ │ └── res
│ │ │ ├── drawable-v21
│ │ │ └── launch_background.xml
│ │ │ ├── drawable
│ │ │ └── launch_background.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-night
│ │ │ └── styles.xml
│ │ │ └── values
│ │ │ └── styles.xml
│ │ └── profile
│ │ └── AndroidManifest.xml
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ └── gradle-wrapper.properties
└── settings.gradle
├── assets
├── frame_app.lua
└── frame_app.min.lua
├── docs
├── Frame App Architecture - Transcription - Google Speech API - Frame Microphone.svg
├── framecast1.mp4
├── framecast2.mp4
├── frameshot1.png
├── frameshot2.png
├── screenshot1.png
└── screenshot2.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-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.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
└── RunnerTests
│ └── RunnerTests.swift
├── lib
├── foreground_service.dart
└── main.dart
├── pubspec.lock
└── pubspec.yaml
/.gitignore:
--------------------------------------------------------------------------------
1 | # Miscellaneous
2 | *.class
3 | *.log
4 | *.pyc
5 | *.swp
6 | .DS_Store
7 | .atom/
8 | .buildlog/
9 | .history
10 | .svn/
11 | migrate_working_dir/
12 |
13 | # IntelliJ related
14 | *.iml
15 | *.ipr
16 | *.iws
17 | .idea/
18 |
19 | # The .vscode folder contains launch configuration and tasks you configure in
20 | # VS Code which you may wish to be included in version control, so this line
21 | # is commented out by default.
22 | .vscode/
23 |
24 | # Flutter/Dart/Pub related
25 | **/doc/api/
26 | **/ios/Flutter/.last_build_id
27 | .dart_tool/
28 | .flutter-plugins
29 | .flutter-plugins-dependencies
30 | .pub-cache/
31 | .pub/
32 | /build/
33 |
34 | # Symbolication related
35 | app.*.symbols
36 |
37 | # Obfuscation related
38 | app.*.map.json
39 |
40 | # Android Studio will place build artifacts here
41 | /android/app/debug
42 | /android/app/profile
43 | /android/app/release
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: "761747bfc538b5af34aa0d3fac380f1bc331ec49"
8 | channel: "stable"
9 |
10 | project_type: app
11 |
12 | # Tracks metadata for the flutter migrate command
13 | migration:
14 | platforms:
15 | - platform: root
16 | create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
17 | base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
18 | - platform: android
19 | create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
20 | base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
21 | - platform: ios
22 | create_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
23 | base_revision: 761747bfc538b5af34aa0d3fac380f1bc331ec49
24 |
25 | # User provided section
26 |
27 | # List of Local paths (relative to this file) that should be
28 | # ignored by the migrate tool.
29 | #
30 | # Files that are not part of the templates will be ignored by default.
31 | unmanaged_files:
32 | - 'lib/main.dart'
33 | - 'ios/Runner.xcodeproj/project.pbxproj'
34 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | BSD 3-Clause License
2 |
3 | Copyright (c) 2024, CitizenOneX
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | 1. Redistributions of source code must retain the above copyright notice, this
9 | list of conditions and the following disclaimer.
10 |
11 | 2. Redistributions in binary form must reproduce the above copyright notice,
12 | this list of conditions and the following disclaimer in the documentation
13 | and/or other materials provided with the distribution.
14 |
15 | 3. Neither the name of the copyright holder nor the names of its
16 | contributors may be used to endorse or promote products derived from
17 | this software without specific prior written permission.
18 |
19 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Frame Transcribe - Google Cloud Speech API (online speech-to-text, live captioning, Frame microphone)
2 |
3 | Connects to Brilliant Labs Frame, streams audio from the Frame microphone, which is sent through a [Google's cloud-based streaming transcription service](https://cloud.google.com/speech-to-text), and displays the streaming text on the Frame display in real-time.
4 |
5 | Google's cloud-based speech-to-text streaming API [supports many languages](https://cloud.google.com/speech-to-text/v2/docs/speech-to-text-supported-languages), and this app presently uses the `telephony` model type.
6 |
7 | Frame display however only shows Latin script in plain text, so for now this demonstration app is useful for a subset of those languages.
8 |
9 | ## Google Cloud API Setup
10 |
11 | **NOTE:** At the time of writing, [Google provides 60 minutes of speech-to-text per month for free](https://cloud.google.com/speech-to-text/pricing) but a Google Cloud project must be set up with billing enabled and the Speech-to-Text API enabled. Google seems to reserve the right to use unbilled transcriptions for its training data, but not paid API usage, so as always consider what content you transmit.
12 |
13 | * Follow [the instructions](https://cloud.google.com/speech-to-text/docs/before-you-begin) for setting up your own Google Cloud project with the Speech-to-Text API enabled (use v2)
14 | * Create a "Service Account" for authorization and keep the JSON string handy to input into the corresponding text box in the app (only once - it will be persisted)
15 | * Put your project-id in the corresponding text box in the app (e.g. `frame-transcribe`) - also persisted
16 | * Put in a language code from the [supported languages list](https://cloud.google.com/speech-to-text/v2/docs/speech-to-text-supported-languages) (`telephony` models only, for now) e.g. `en-US` or `de-DE` keeping in mind the Latin-script-only restriction of the Frame display in plain text
17 |
18 |
19 | ### Frameshots
20 | 
21 | 
22 |
23 | ### Framecasts
24 |
25 | https://github.com/user-attachments/assets/593bc473-377a-4e88-b77e-1c894c906939
26 |
27 | https://github.com/user-attachments/assets/15d4e98b-54dd-4d4e-bd9a-99a47f81335f
28 |
29 | ### Screenshots
30 |
31 | 
32 | 
33 |
34 | ### Architecture
35 | 
36 |
37 | ### See Also
38 | - [Frame Speech-to-text (Phone Microphone)](https://github.com/CitizenOneX/frame_flutter_stt_host)
39 |
--------------------------------------------------------------------------------
/analysis_options.yaml:
--------------------------------------------------------------------------------
1 | include: package:flutter_lints/flutter.yaml
2 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | gradle-wrapper.jar
2 | /.gradle
3 | /captures/
4 | /gradlew
5 | /gradlew.bat
6 | /local.properties
7 | GeneratedPluginRegistrant.java
8 |
9 | # Remember to never publicly share your keystore.
10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
11 | key.properties
12 | **/*.keystore
13 | **/*.jks
14 |
--------------------------------------------------------------------------------
/android/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id "com.android.application"
3 | id "kotlin-android"
4 | // The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
5 | id "dev.flutter.flutter-gradle-plugin"
6 | }
7 |
8 | def localProperties = new Properties()
9 | def localPropertiesFile = rootProject.file("local.properties")
10 | if (localPropertiesFile.exists()) {
11 | localPropertiesFile.withReader("UTF-8") { reader ->
12 | localProperties.load(reader)
13 | }
14 | }
15 |
16 | def flutterVersionCode = localProperties.getProperty("flutter.versionCode")
17 | if (flutterVersionCode == null) {
18 | flutterVersionCode = "1"
19 | }
20 |
21 | def flutterVersionName = localProperties.getProperty("flutter.versionName")
22 | if (flutterVersionName == null) {
23 | flutterVersionName = "1.0"
24 | }
25 |
26 | android {
27 | namespace = "com.example.frame_transcribe_googlespeech"
28 | compileSdk = flutter.compileSdkVersion
29 | ndkVersion = flutter.ndkVersion
30 |
31 | compileOptions {
32 | sourceCompatibility = JavaVersion.VERSION_17
33 | targetCompatibility = JavaVersion.VERSION_17
34 | }
35 |
36 | defaultConfig {
37 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
38 | applicationId = "com.example.frame_transcribe_googlespeech"
39 | // You can update the following values to match your application needs.
40 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.
41 | minSdk = 23
42 | targetSdk = flutter.targetSdkVersion
43 | versionCode = flutterVersionCode.toInteger()
44 | versionName = flutterVersionName
45 | }
46 |
47 | buildTypes {
48 | release {
49 | // TODO: Add your own signing config for the release build.
50 | // Signing with the debug keys for now, so `flutter run --release` works.
51 | signingConfig = signingConfigs.debug
52 | }
53 | }
54 | }
55 |
56 | flutter {
57 | source = "../.."
58 | }
59 |
--------------------------------------------------------------------------------
/android/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | -keep class com.lib.flutter_blue_plus.* { *; }
2 | -keep class com.sun.jna.* { *; }
3 | -keepclassmembers class * extends com.sun.jna.* { public *; }
--------------------------------------------------------------------------------
/android/app/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
30 |
39 |
43 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
58 |
59 |
61 |
64 |
65 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/android/app/src/main/kotlin/com/example/frame_flutter_stt_host/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package com.example.frame_transcribe_googlespeech
2 |
3 | import io.flutter.embedding.android.FlutterActivity
4 |
5 | class MainActivity: FlutterActivity()
6 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable-v21/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/drawable/launch_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/android/app/src/main/res/values-night/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
9 |
15 |
18 |
19 |
--------------------------------------------------------------------------------
/android/app/src/profile/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | allprojects {
2 | repositories {
3 | google()
4 | mavenCentral()
5 | }
6 | }
7 |
8 | rootProject.buildDir = "../build"
9 | subprojects {
10 | project.buildDir = "${rootProject.buildDir}/${project.name}"
11 | }
12 | subprojects {
13 | project.evaluationDependsOn(":app")
14 | }
15 |
16 | tasks.register("clean", Delete) {
17 | delete rootProject.buildDir
18 | }
19 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError
2 | android.useAndroidX=true
3 | android.enableJetifier=true
4 |
--------------------------------------------------------------------------------
/android/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | zipStoreBase=GRADLE_USER_HOME
4 | zipStorePath=wrapper/dists
5 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
6 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | buildscript {
3 | repositories {
4 | mavenCentral()
5 | maven {
6 | // r8 maven
7 | url = uri("https://storage.googleapis.com/r8-releases/raw")
8 | }
9 | }
10 | dependencies {
11 | // r8 version
12 | classpath("com.android.tools:r8:8.2.16-dev")
13 | }
14 | }
15 |
16 | def flutterSdkPath = {
17 | def properties = new Properties()
18 | file("local.properties").withInputStream { properties.load(it) }
19 | def flutterSdkPath = properties.getProperty("flutter.sdk")
20 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
21 | return flutterSdkPath
22 | }()
23 |
24 | includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
25 |
26 | repositories {
27 | google()
28 | mavenCentral()
29 | gradlePluginPortal()
30 | }
31 | }
32 |
33 | plugins {
34 | id "dev.flutter.flutter-plugin-loader" version "1.0.0"
35 | id "com.android.application" version "8.0.0" apply false
36 | id "org.jetbrains.kotlin.android" version "1.8.20" apply false
37 | }
38 |
39 | include ":app"
40 |
--------------------------------------------------------------------------------
/assets/frame_app.lua:
--------------------------------------------------------------------------------
1 | local data = require('data.min')
2 | local battery = require('battery.min')
3 | local code = require('code.min')
4 | local plain_text = require('plain_text.min')
5 |
6 | -- Phone to Frame flags
7 | TEXT_MSG = 0x0b
8 | AUDIO_SUBS_MSG = 0x30
9 | TAP_SUBS_MSG = 0x10
10 |
11 | -- register the message parser so it's automatically called when matching data comes in
12 | data.parsers[TEXT_MSG] = plain_text.parse_plain_text
13 | data.parsers[AUDIO_SUBS_MSG] = code.parse_code
14 | data.parsers[TAP_SUBS_MSG] = code.parse_code
15 |
16 | -- Frame to Phone flags
17 | AUDIO_DATA_NON_FINAL_MSG = 0x05
18 | AUDIO_DATA_FINAL_MSG = 0x06
19 | TAP_MSG = 0x09
20 |
21 | function handle_tap()
22 | pcall(frame.bluetooth.send, string.char(TAP_MSG))
23 | end
24 |
25 | -- draw the current text on the display
26 | function print_text()
27 | local i = 0
28 | for line in data.app_data[TEXT_MSG].string:gmatch("([^\n]*)\n?") do
29 | if line ~= "" then
30 | frame.display.text(line, 1, i * 60 + 1)
31 | i = i + 1
32 | end
33 | end
34 | end
35 |
36 | -- Main app loop
37 | function app_loop()
38 | -- clear the display
39 | frame.display.text(" ", 1, 1)
40 | frame.display.show()
41 | local last_batt_update = 0
42 | local streaming = false
43 | local audio_data = ''
44 | local mtu = frame.bluetooth.max_length()
45 | -- data buffer needs to be even for reading from microphone
46 | if mtu % 2 == 1 then mtu = mtu - 1 end
47 |
48 | while true do
49 | rc, err = pcall(
50 | function()
51 | -- process any raw items, if ready (parse into image or text, then clear raw)
52 | local items_ready = data.process_raw_items()
53 |
54 | if items_ready > 0 then
55 |
56 | if (data.app_data[TEXT_MSG] ~= nil and data.app_data[TEXT_MSG].string ~= nil) then
57 | print_text()
58 | frame.display.show()
59 | end
60 |
61 | if (data.app_data[TAP_SUBS_MSG] ~= nil) then
62 |
63 | if data.app_data[TAP_SUBS_MSG].value == 1 then
64 | -- start subscription to tap events
65 | frame.imu.tap_callback(handle_tap)
66 | else
67 | -- cancel subscription to tap events
68 | frame.imu.tap_callback(nil)
69 | end
70 |
71 | data.app_data[TAP_SUBS_MSG] = nil
72 | end
73 |
74 | if (data.app_data[AUDIO_SUBS_MSG] ~= nil) then
75 |
76 | if data.app_data[AUDIO_SUBS_MSG].value == 1 then
77 | -- start subscription to audio
78 | audio_data = ''
79 | pcall(frame.microphone.start, {sample_rate=8000, bit_depth=16})
80 | streaming = true
81 | frame.display.text("Streaming Audio", 1, 1)
82 | frame.display.show()
83 | else
84 | -- cancel subscription to audio
85 | pcall(frame.microphone.stop)
86 | -- clear the display
87 | frame.display.text(" ", 1, 1)
88 | frame.display.show()
89 | end
90 |
91 | data.app_data[AUDIO_SUBS_MSG] = nil
92 | end
93 | end
94 | end
95 | )
96 | -- Catch the break signal here and clean up the display
97 | if rc == false then
98 | -- send the error back on the stdout stream
99 | print(err)
100 | frame.display.text(" ", 1, 1)
101 | frame.display.show()
102 | frame.sleep(0.04)
103 | break
104 | end
105 |
106 | -- send any pending audio data back
107 | -- Streams until AUDIO_SUBS_MSG (with value 0) is sent from phone
108 | -- (prioritize the reading and sending about 20x compared to checking for other events e.g. AUDIO_SUBS_MSG/0)
109 | if streaming then
110 | for i=1,20 do
111 | audio_data = frame.microphone.read(mtu)
112 |
113 | -- Calling frame.microphone.stop() will allow this to break the loop
114 | if audio_data == nil then
115 | -- send an end-of-stream message back to the phone
116 | pcall(frame.bluetooth.send, string.char(AUDIO_DATA_FINAL_MSG))
117 | frame.sleep(0.0025)
118 | streaming = false
119 | break
120 |
121 | -- send the data that was read
122 | elseif audio_data ~= '' then
123 | pcall(frame.bluetooth.send, string.char(AUDIO_DATA_NON_FINAL_MSG) .. audio_data)
124 | frame.sleep(0.0025)
125 | end
126 | end
127 | end
128 |
129 | -- periodic battery level updates, 120s
130 | last_batt_update = battery.send_batt_if_elapsed(last_batt_update, 120)
131 |
132 | if not streaming then frame.sleep(0.1) end
133 | end
134 | end
135 |
136 | -- run the main app loop
137 | app_loop()
--------------------------------------------------------------------------------
/assets/frame_app.min.lua:
--------------------------------------------------------------------------------
1 | local a=require('data.min')local b=require('battery.min')local c=require('code.min')local d=require('plain_text.min')TEXT_MSG=0x0b;AUDIO_SUBS_MSG=0x30;TAP_SUBS_MSG=0x10;a.parsers[TEXT_MSG]=d.parse_plain_text;a.parsers[AUDIO_SUBS_MSG]=c.parse_code;a.parsers[TAP_SUBS_MSG]=c.parse_code;AUDIO_DATA_NON_FINAL_MSG=0x05;AUDIO_DATA_FINAL_MSG=0x06;TAP_MSG=0x09;function handle_tap()pcall(frame.bluetooth.send,string.char(TAP_MSG))end;function print_text()local e=0;for f in a.app_data[TEXT_MSG].string:gmatch("([^\n]*)\n?")do if f~=""then frame.display.text(f,1,e*60+1)e=e+1 end end end;function app_loop()frame.display.text(" ",1,1)frame.display.show()local g=0;local h=false;local i=''local j=frame.bluetooth.max_length()if j%2==1 then j=j-1 end;while true do rc,err=pcall(function()local k=a.process_raw_items()if k>0 then if a.app_data[TEXT_MSG]~=nil and a.app_data[TEXT_MSG].string~=nil then print_text()frame.display.show()end;if a.app_data[TAP_SUBS_MSG]~=nil then if a.app_data[TAP_SUBS_MSG].value==1 then frame.imu.tap_callback(handle_tap)else frame.imu.tap_callback(nil)end;a.app_data[TAP_SUBS_MSG]=nil end;if a.app_data[AUDIO_SUBS_MSG]~=nil then if a.app_data[AUDIO_SUBS_MSG].value==1 then i=''pcall(frame.microphone.start,{sample_rate=8000,bit_depth=16})h=true;frame.display.text("Streaming Audio",1,1)frame.display.show()else pcall(frame.microphone.stop)frame.display.text(" ",1,1)frame.display.show()end;a.app_data[AUDIO_SUBS_MSG]=nil end end end)if rc==false then print(err)frame.display.text(" ",1,1)frame.display.show()frame.sleep(0.04)break end;if h then for e=1,20 do i=frame.microphone.read(j)if i==nil then pcall(frame.bluetooth.send,string.char(AUDIO_DATA_FINAL_MSG))frame.sleep(0.0025)h=false;break elseif i~=''then pcall(frame.bluetooth.send,string.char(AUDIO_DATA_NON_FINAL_MSG)..i)frame.sleep(0.0025)end end end;g=b.send_batt_if_elapsed(g,120)if not h then frame.sleep(0.1)end end end;app_loop()
--------------------------------------------------------------------------------
/docs/framecast1.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/docs/framecast1.mp4
--------------------------------------------------------------------------------
/docs/framecast2.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/docs/framecast2.mp4
--------------------------------------------------------------------------------
/docs/frameshot1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/docs/frameshot1.png
--------------------------------------------------------------------------------
/docs/frameshot2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/docs/frameshot2.png
--------------------------------------------------------------------------------
/docs/screenshot1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/docs/screenshot1.png
--------------------------------------------------------------------------------
/docs/screenshot2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/docs/screenshot2.png
--------------------------------------------------------------------------------
/ios/.gitignore:
--------------------------------------------------------------------------------
1 | **/dgph
2 | *.mode1v3
3 | *.mode2v3
4 | *.moved-aside
5 | *.pbxuser
6 | *.perspectivev3
7 | **/*sync/
8 | .sconsign.dblite
9 | .tags*
10 | **/.vagrant/
11 | **/DerivedData/
12 | Icon?
13 | **/Pods/
14 | **/.symlinks/
15 | profile
16 | xcuserdata
17 | **/.generated/
18 | Flutter/App.framework
19 | Flutter/Flutter.framework
20 | Flutter/Flutter.podspec
21 | Flutter/Generated.xcconfig
22 | Flutter/ephemeral/
23 | Flutter/app.flx
24 | Flutter/app.zip
25 | Flutter/flutter_assets/
26 | Flutter/flutter_export_environment.sh
27 | ServiceDefinitions.json
28 | Runner/GeneratedPluginRegistrant.*
29 |
30 | # Exceptions to above rules.
31 | !default.mode1v3
32 | !default.mode2v3
33 | !default.pbxuser
34 | !default.perspectivev3
35 |
--------------------------------------------------------------------------------
/ios/Flutter/AppFrameworkInfo.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
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 | 12.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 = 54;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
11 | 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
17 | /* End PBXBuildFile section */
18 |
19 | /* Begin PBXContainerItemProxy section */
20 | 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
21 | isa = PBXContainerItemProxy;
22 | containerPortal = 97C146E61CF9000F007C117D /* Project object */;
23 | proxyType = 1;
24 | remoteGlobalIDString = 97C146ED1CF9000F007C117D;
25 | remoteInfo = Runner;
26 | };
27 | /* End PBXContainerItemProxy section */
28 |
29 | /* Begin PBXCopyFilesBuildPhase section */
30 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
31 | isa = PBXCopyFilesBuildPhase;
32 | buildActionMask = 2147483647;
33 | dstPath = "";
34 | dstSubfolderSpec = 10;
35 | files = (
36 | );
37 | name = "Embed Frameworks";
38 | runOnlyForDeploymentPostprocessing = 0;
39 | };
40 | /* End PBXCopyFilesBuildPhase section */
41 |
42 | /* Begin PBXFileReference section */
43 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
44 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
45 | 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
46 | 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
47 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
48 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
49 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
50 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
51 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
52 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
53 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
54 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
55 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
56 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
57 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
58 | /* End PBXFileReference section */
59 |
60 | /* Begin PBXFrameworksBuildPhase section */
61 | 97C146EB1CF9000F007C117D /* Frameworks */ = {
62 | isa = PBXFrameworksBuildPhase;
63 | buildActionMask = 2147483647;
64 | files = (
65 | );
66 | runOnlyForDeploymentPostprocessing = 0;
67 | };
68 | /* End PBXFrameworksBuildPhase section */
69 |
70 | /* Begin PBXGroup section */
71 | 331C8082294A63A400263BE5 /* RunnerTests */ = {
72 | isa = PBXGroup;
73 | children = (
74 | 331C807B294A618700263BE5 /* RunnerTests.swift */,
75 | );
76 | path = RunnerTests;
77 | sourceTree = "";
78 | };
79 | 9740EEB11CF90186004384FC /* Flutter */ = {
80 | isa = PBXGroup;
81 | children = (
82 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
83 | 9740EEB21CF90195004384FC /* Debug.xcconfig */,
84 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
85 | 9740EEB31CF90195004384FC /* Generated.xcconfig */,
86 | );
87 | name = Flutter;
88 | sourceTree = "";
89 | };
90 | 97C146E51CF9000F007C117D = {
91 | isa = PBXGroup;
92 | children = (
93 | 9740EEB11CF90186004384FC /* Flutter */,
94 | 97C146F01CF9000F007C117D /* Runner */,
95 | 97C146EF1CF9000F007C117D /* Products */,
96 | 331C8082294A63A400263BE5 /* RunnerTests */,
97 | );
98 | sourceTree = "";
99 | };
100 | 97C146EF1CF9000F007C117D /* Products */ = {
101 | isa = PBXGroup;
102 | children = (
103 | 97C146EE1CF9000F007C117D /* Runner.app */,
104 | 331C8081294A63A400263BE5 /* RunnerTests.xctest */,
105 | );
106 | name = Products;
107 | sourceTree = "";
108 | };
109 | 97C146F01CF9000F007C117D /* Runner */ = {
110 | isa = PBXGroup;
111 | children = (
112 | 97C146FA1CF9000F007C117D /* Main.storyboard */,
113 | 97C146FD1CF9000F007C117D /* Assets.xcassets */,
114 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
115 | 97C147021CF9000F007C117D /* Info.plist */,
116 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
117 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
118 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
119 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
120 | );
121 | path = Runner;
122 | sourceTree = "";
123 | };
124 | /* End PBXGroup section */
125 |
126 | /* Begin PBXNativeTarget section */
127 | 331C8080294A63A400263BE5 /* RunnerTests */ = {
128 | isa = PBXNativeTarget;
129 | buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
130 | buildPhases = (
131 | 331C807D294A63A400263BE5 /* Sources */,
132 | 331C807F294A63A400263BE5 /* Resources */,
133 | );
134 | buildRules = (
135 | );
136 | dependencies = (
137 | 331C8086294A63A400263BE5 /* PBXTargetDependency */,
138 | );
139 | name = RunnerTests;
140 | productName = RunnerTests;
141 | productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
142 | productType = "com.apple.product-type.bundle.unit-test";
143 | };
144 | 97C146ED1CF9000F007C117D /* Runner */ = {
145 | isa = PBXNativeTarget;
146 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
147 | buildPhases = (
148 | 9740EEB61CF901F6004384FC /* Run Script */,
149 | 97C146EA1CF9000F007C117D /* Sources */,
150 | 97C146EB1CF9000F007C117D /* Frameworks */,
151 | 97C146EC1CF9000F007C117D /* Resources */,
152 | 9705A1C41CF9048500538489 /* Embed Frameworks */,
153 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
154 | );
155 | buildRules = (
156 | );
157 | dependencies = (
158 | );
159 | name = Runner;
160 | productName = Runner;
161 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
162 | productType = "com.apple.product-type.application";
163 | };
164 | /* End PBXNativeTarget section */
165 |
166 | /* Begin PBXProject section */
167 | 97C146E61CF9000F007C117D /* Project object */ = {
168 | isa = PBXProject;
169 | attributes = {
170 | BuildIndependentTargetsInParallel = YES;
171 | LastUpgradeCheck = 1510;
172 | ORGANIZATIONNAME = "";
173 | TargetAttributes = {
174 | 331C8080294A63A400263BE5 = {
175 | CreatedOnToolsVersion = 14.0;
176 | TestTargetID = 97C146ED1CF9000F007C117D;
177 | };
178 | 97C146ED1CF9000F007C117D = {
179 | CreatedOnToolsVersion = 7.3.1;
180 | LastSwiftMigration = 1100;
181 | };
182 | };
183 | };
184 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
185 | compatibilityVersion = "Xcode 9.3";
186 | developmentRegion = en;
187 | hasScannedForEncodings = 0;
188 | knownRegions = (
189 | en,
190 | Base,
191 | );
192 | mainGroup = 97C146E51CF9000F007C117D;
193 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
194 | projectDirPath = "";
195 | projectRoot = "";
196 | targets = (
197 | 97C146ED1CF9000F007C117D /* Runner */,
198 | 331C8080294A63A400263BE5 /* RunnerTests */,
199 | );
200 | };
201 | /* End PBXProject section */
202 |
203 | /* Begin PBXResourcesBuildPhase section */
204 | 331C807F294A63A400263BE5 /* Resources */ = {
205 | isa = PBXResourcesBuildPhase;
206 | buildActionMask = 2147483647;
207 | files = (
208 | );
209 | runOnlyForDeploymentPostprocessing = 0;
210 | };
211 | 97C146EC1CF9000F007C117D /* Resources */ = {
212 | isa = PBXResourcesBuildPhase;
213 | buildActionMask = 2147483647;
214 | files = (
215 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
216 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
217 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
218 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
219 | );
220 | runOnlyForDeploymentPostprocessing = 0;
221 | };
222 | /* End PBXResourcesBuildPhase section */
223 |
224 | /* Begin PBXShellScriptBuildPhase section */
225 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
226 | isa = PBXShellScriptBuildPhase;
227 | alwaysOutOfDate = 1;
228 | buildActionMask = 2147483647;
229 | files = (
230 | );
231 | inputPaths = (
232 | "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
233 | );
234 | name = "Thin Binary";
235 | outputPaths = (
236 | );
237 | runOnlyForDeploymentPostprocessing = 0;
238 | shellPath = /bin/sh;
239 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
240 | };
241 | 9740EEB61CF901F6004384FC /* Run Script */ = {
242 | isa = PBXShellScriptBuildPhase;
243 | alwaysOutOfDate = 1;
244 | buildActionMask = 2147483647;
245 | files = (
246 | );
247 | inputPaths = (
248 | );
249 | name = "Run Script";
250 | outputPaths = (
251 | );
252 | runOnlyForDeploymentPostprocessing = 0;
253 | shellPath = /bin/sh;
254 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
255 | };
256 | /* End PBXShellScriptBuildPhase section */
257 |
258 | /* Begin PBXSourcesBuildPhase section */
259 | 331C807D294A63A400263BE5 /* Sources */ = {
260 | isa = PBXSourcesBuildPhase;
261 | buildActionMask = 2147483647;
262 | files = (
263 | 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
264 | );
265 | runOnlyForDeploymentPostprocessing = 0;
266 | };
267 | 97C146EA1CF9000F007C117D /* Sources */ = {
268 | isa = PBXSourcesBuildPhase;
269 | buildActionMask = 2147483647;
270 | files = (
271 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
272 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
273 | );
274 | runOnlyForDeploymentPostprocessing = 0;
275 | };
276 | /* End PBXSourcesBuildPhase section */
277 |
278 | /* Begin PBXTargetDependency section */
279 | 331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
280 | isa = PBXTargetDependency;
281 | target = 97C146ED1CF9000F007C117D /* Runner */;
282 | targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
283 | };
284 | /* End PBXTargetDependency section */
285 |
286 | /* Begin PBXVariantGroup section */
287 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
288 | isa = PBXVariantGroup;
289 | children = (
290 | 97C146FB1CF9000F007C117D /* Base */,
291 | );
292 | name = Main.storyboard;
293 | sourceTree = "";
294 | };
295 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
296 | isa = PBXVariantGroup;
297 | children = (
298 | 97C147001CF9000F007C117D /* Base */,
299 | );
300 | name = LaunchScreen.storyboard;
301 | sourceTree = "";
302 | };
303 | /* End PBXVariantGroup section */
304 |
305 | /* Begin XCBuildConfiguration section */
306 | 249021D3217E4FDB00AE95B9 /* Profile */ = {
307 | isa = XCBuildConfiguration;
308 | buildSettings = {
309 | ALWAYS_SEARCH_USER_PATHS = NO;
310 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
311 | CLANG_ANALYZER_NONNULL = YES;
312 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
313 | CLANG_CXX_LIBRARY = "libc++";
314 | CLANG_ENABLE_MODULES = YES;
315 | CLANG_ENABLE_OBJC_ARC = YES;
316 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
317 | CLANG_WARN_BOOL_CONVERSION = YES;
318 | CLANG_WARN_COMMA = YES;
319 | CLANG_WARN_CONSTANT_CONVERSION = YES;
320 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
321 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
322 | CLANG_WARN_EMPTY_BODY = YES;
323 | CLANG_WARN_ENUM_CONVERSION = YES;
324 | CLANG_WARN_INFINITE_RECURSION = YES;
325 | CLANG_WARN_INT_CONVERSION = YES;
326 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
327 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
328 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
329 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
330 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
331 | CLANG_WARN_STRICT_PROTOTYPES = YES;
332 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
333 | CLANG_WARN_UNREACHABLE_CODE = YES;
334 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
335 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
336 | COPY_PHASE_STRIP = NO;
337 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
338 | ENABLE_NS_ASSERTIONS = NO;
339 | ENABLE_STRICT_OBJC_MSGSEND = YES;
340 | ENABLE_USER_SCRIPT_SANDBOXING = NO;
341 | GCC_C_LANGUAGE_STANDARD = gnu99;
342 | GCC_NO_COMMON_BLOCKS = YES;
343 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
344 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
345 | GCC_WARN_UNDECLARED_SELECTOR = YES;
346 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
347 | GCC_WARN_UNUSED_FUNCTION = YES;
348 | GCC_WARN_UNUSED_VARIABLE = YES;
349 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
350 | MTL_ENABLE_DEBUG_INFO = NO;
351 | SDKROOT = iphoneos;
352 | SUPPORTED_PLATFORMS = iphoneos;
353 | TARGETED_DEVICE_FAMILY = "1,2";
354 | VALIDATE_PRODUCT = YES;
355 | };
356 | name = Profile;
357 | };
358 | 249021D4217E4FDB00AE95B9 /* Profile */ = {
359 | isa = XCBuildConfiguration;
360 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
361 | buildSettings = {
362 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
363 | CLANG_ENABLE_MODULES = YES;
364 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
365 | ENABLE_BITCODE = NO;
366 | INFOPLIST_FILE = Runner/Info.plist;
367 | LD_RUNPATH_SEARCH_PATHS = (
368 | "$(inherited)",
369 | "@executable_path/Frameworks",
370 | );
371 | PRODUCT_BUNDLE_IDENTIFIER = com.example.frameFlutterSttHost;
372 | PRODUCT_NAME = "$(TARGET_NAME)";
373 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
374 | SWIFT_VERSION = 5.0;
375 | VERSIONING_SYSTEM = "apple-generic";
376 | };
377 | name = Profile;
378 | };
379 | 331C8088294A63A400263BE5 /* Debug */ = {
380 | isa = XCBuildConfiguration;
381 | buildSettings = {
382 | BUNDLE_LOADER = "$(TEST_HOST)";
383 | CODE_SIGN_STYLE = Automatic;
384 | CURRENT_PROJECT_VERSION = 1;
385 | GENERATE_INFOPLIST_FILE = YES;
386 | MARKETING_VERSION = 1.0;
387 | PRODUCT_BUNDLE_IDENTIFIER = com.example.frameFlutterSttHost.RunnerTests;
388 | PRODUCT_NAME = "$(TARGET_NAME)";
389 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
390 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
391 | SWIFT_VERSION = 5.0;
392 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
393 | };
394 | name = Debug;
395 | };
396 | 331C8089294A63A400263BE5 /* Release */ = {
397 | isa = XCBuildConfiguration;
398 | buildSettings = {
399 | BUNDLE_LOADER = "$(TEST_HOST)";
400 | CODE_SIGN_STYLE = Automatic;
401 | CURRENT_PROJECT_VERSION = 1;
402 | GENERATE_INFOPLIST_FILE = YES;
403 | MARKETING_VERSION = 1.0;
404 | PRODUCT_BUNDLE_IDENTIFIER = com.example.frameFlutterSttHost.RunnerTests;
405 | PRODUCT_NAME = "$(TARGET_NAME)";
406 | SWIFT_VERSION = 5.0;
407 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
408 | };
409 | name = Release;
410 | };
411 | 331C808A294A63A400263BE5 /* Profile */ = {
412 | isa = XCBuildConfiguration;
413 | buildSettings = {
414 | BUNDLE_LOADER = "$(TEST_HOST)";
415 | CODE_SIGN_STYLE = Automatic;
416 | CURRENT_PROJECT_VERSION = 1;
417 | GENERATE_INFOPLIST_FILE = YES;
418 | MARKETING_VERSION = 1.0;
419 | PRODUCT_BUNDLE_IDENTIFIER = com.example.frameFlutterSttHost.RunnerTests;
420 | PRODUCT_NAME = "$(TARGET_NAME)";
421 | SWIFT_VERSION = 5.0;
422 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
423 | };
424 | name = Profile;
425 | };
426 | 97C147031CF9000F007C117D /* Debug */ = {
427 | isa = XCBuildConfiguration;
428 | buildSettings = {
429 | ALWAYS_SEARCH_USER_PATHS = NO;
430 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
431 | CLANG_ANALYZER_NONNULL = YES;
432 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
433 | CLANG_CXX_LIBRARY = "libc++";
434 | CLANG_ENABLE_MODULES = YES;
435 | CLANG_ENABLE_OBJC_ARC = YES;
436 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
437 | CLANG_WARN_BOOL_CONVERSION = YES;
438 | CLANG_WARN_COMMA = YES;
439 | CLANG_WARN_CONSTANT_CONVERSION = YES;
440 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
441 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
442 | CLANG_WARN_EMPTY_BODY = YES;
443 | CLANG_WARN_ENUM_CONVERSION = YES;
444 | CLANG_WARN_INFINITE_RECURSION = YES;
445 | CLANG_WARN_INT_CONVERSION = YES;
446 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
447 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
448 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
449 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
450 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
451 | CLANG_WARN_STRICT_PROTOTYPES = YES;
452 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
453 | CLANG_WARN_UNREACHABLE_CODE = YES;
454 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
455 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
456 | COPY_PHASE_STRIP = NO;
457 | DEBUG_INFORMATION_FORMAT = dwarf;
458 | ENABLE_STRICT_OBJC_MSGSEND = YES;
459 | ENABLE_TESTABILITY = YES;
460 | ENABLE_USER_SCRIPT_SANDBOXING = NO;
461 | GCC_C_LANGUAGE_STANDARD = gnu99;
462 | GCC_DYNAMIC_NO_PIC = NO;
463 | GCC_NO_COMMON_BLOCKS = YES;
464 | GCC_OPTIMIZATION_LEVEL = 0;
465 | GCC_PREPROCESSOR_DEFINITIONS = (
466 | "DEBUG=1",
467 | "$(inherited)",
468 | );
469 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
470 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
471 | GCC_WARN_UNDECLARED_SELECTOR = YES;
472 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
473 | GCC_WARN_UNUSED_FUNCTION = YES;
474 | GCC_WARN_UNUSED_VARIABLE = YES;
475 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
476 | MTL_ENABLE_DEBUG_INFO = YES;
477 | ONLY_ACTIVE_ARCH = YES;
478 | SDKROOT = iphoneos;
479 | TARGETED_DEVICE_FAMILY = "1,2";
480 | };
481 | name = Debug;
482 | };
483 | 97C147041CF9000F007C117D /* Release */ = {
484 | isa = XCBuildConfiguration;
485 | buildSettings = {
486 | ALWAYS_SEARCH_USER_PATHS = NO;
487 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
488 | CLANG_ANALYZER_NONNULL = YES;
489 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
490 | CLANG_CXX_LIBRARY = "libc++";
491 | CLANG_ENABLE_MODULES = YES;
492 | CLANG_ENABLE_OBJC_ARC = YES;
493 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
494 | CLANG_WARN_BOOL_CONVERSION = YES;
495 | CLANG_WARN_COMMA = YES;
496 | CLANG_WARN_CONSTANT_CONVERSION = YES;
497 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
498 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
499 | CLANG_WARN_EMPTY_BODY = YES;
500 | CLANG_WARN_ENUM_CONVERSION = YES;
501 | CLANG_WARN_INFINITE_RECURSION = YES;
502 | CLANG_WARN_INT_CONVERSION = YES;
503 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
504 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
505 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
506 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
507 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
508 | CLANG_WARN_STRICT_PROTOTYPES = YES;
509 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
510 | CLANG_WARN_UNREACHABLE_CODE = YES;
511 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
512 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
513 | COPY_PHASE_STRIP = NO;
514 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
515 | ENABLE_NS_ASSERTIONS = NO;
516 | ENABLE_STRICT_OBJC_MSGSEND = YES;
517 | ENABLE_USER_SCRIPT_SANDBOXING = NO;
518 | GCC_C_LANGUAGE_STANDARD = gnu99;
519 | GCC_NO_COMMON_BLOCKS = YES;
520 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
521 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
522 | GCC_WARN_UNDECLARED_SELECTOR = YES;
523 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
524 | GCC_WARN_UNUSED_FUNCTION = YES;
525 | GCC_WARN_UNUSED_VARIABLE = YES;
526 | IPHONEOS_DEPLOYMENT_TARGET = 12.0;
527 | MTL_ENABLE_DEBUG_INFO = NO;
528 | SDKROOT = iphoneos;
529 | SUPPORTED_PLATFORMS = iphoneos;
530 | SWIFT_COMPILATION_MODE = wholemodule;
531 | SWIFT_OPTIMIZATION_LEVEL = "-O";
532 | TARGETED_DEVICE_FAMILY = "1,2";
533 | VALIDATE_PRODUCT = YES;
534 | };
535 | name = Release;
536 | };
537 | 97C147061CF9000F007C117D /* Debug */ = {
538 | isa = XCBuildConfiguration;
539 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
540 | buildSettings = {
541 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
542 | CLANG_ENABLE_MODULES = YES;
543 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
544 | ENABLE_BITCODE = NO;
545 | INFOPLIST_FILE = Runner/Info.plist;
546 | LD_RUNPATH_SEARCH_PATHS = (
547 | "$(inherited)",
548 | "@executable_path/Frameworks",
549 | );
550 | PRODUCT_BUNDLE_IDENTIFIER = com.example.frameFlutterSttHost;
551 | PRODUCT_NAME = "$(TARGET_NAME)";
552 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
553 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
554 | SWIFT_VERSION = 5.0;
555 | VERSIONING_SYSTEM = "apple-generic";
556 | };
557 | name = Debug;
558 | };
559 | 97C147071CF9000F007C117D /* Release */ = {
560 | isa = XCBuildConfiguration;
561 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
562 | buildSettings = {
563 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
564 | CLANG_ENABLE_MODULES = YES;
565 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
566 | ENABLE_BITCODE = NO;
567 | INFOPLIST_FILE = Runner/Info.plist;
568 | LD_RUNPATH_SEARCH_PATHS = (
569 | "$(inherited)",
570 | "@executable_path/Frameworks",
571 | );
572 | PRODUCT_BUNDLE_IDENTIFIER = com.example.frameFlutterSttHost;
573 | PRODUCT_NAME = "$(TARGET_NAME)";
574 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
575 | SWIFT_VERSION = 5.0;
576 | VERSIONING_SYSTEM = "apple-generic";
577 | };
578 | name = Release;
579 | };
580 | /* End XCBuildConfiguration section */
581 |
582 | /* Begin XCConfigurationList section */
583 | 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
584 | isa = XCConfigurationList;
585 | buildConfigurations = (
586 | 331C8088294A63A400263BE5 /* Debug */,
587 | 331C8089294A63A400263BE5 /* Release */,
588 | 331C808A294A63A400263BE5 /* Profile */,
589 | );
590 | defaultConfigurationIsVisible = 0;
591 | defaultConfigurationName = Release;
592 | };
593 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
594 | isa = XCConfigurationList;
595 | buildConfigurations = (
596 | 97C147031CF9000F007C117D /* Debug */,
597 | 97C147041CF9000F007C117D /* Release */,
598 | 249021D3217E4FDB00AE95B9 /* Profile */,
599 | );
600 | defaultConfigurationIsVisible = 0;
601 | defaultConfigurationName = Release;
602 | };
603 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
604 | isa = XCConfigurationList;
605 | buildConfigurations = (
606 | 97C147061CF9000F007C117D /* Debug */,
607 | 97C147071CF9000F007C117D /* Release */,
608 | 249021D4217E4FDB00AE95B9 /* Profile */,
609 | );
610 | defaultConfigurationIsVisible = 0;
611 | defaultConfigurationName = Release;
612 | };
613 | /* End XCConfigurationList section */
614 | };
615 | rootObject = 97C146E61CF9000F007C117D /* Project object */;
616 | }
617 |
--------------------------------------------------------------------------------
/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 |
37 |
38 |
39 |
40 |
43 |
49 |
50 |
51 |
52 |
53 |
63 |
65 |
71 |
72 |
73 |
74 |
80 |
82 |
88 |
89 |
90 |
91 |
93 |
94 |
97 |
98 |
99 |
--------------------------------------------------------------------------------
/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 Flutter
2 | import UIKit
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/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/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/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/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/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/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/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/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/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/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/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/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/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/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/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/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/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/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/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/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/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/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/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/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/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/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/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
--------------------------------------------------------------------------------
/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CitizenOneX/frame_transcribe_googlespeech/1fe8f0bffd2d095dcbe95cf5b0d25dff5412a649/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 | CFBundleDisplayName
8 | Frame Transcribe - Google Cloud Speech
9 | CFBundleExecutable
10 | $(EXECUTABLE_NAME)
11 | CFBundleIdentifier
12 | $(PRODUCT_BUNDLE_IDENTIFIER)
13 | CFBundleInfoDictionaryVersion
14 | 6.0
15 | CFBundleName
16 | Frame Transcribe - Google Cloud Speech
17 | CFBundlePackageType
18 | APPL
19 | CFBundleShortVersionString
20 | $(FLUTTER_BUILD_NAME)
21 | CFBundleSignature
22 | ????
23 | CFBundleVersion
24 | $(FLUTTER_BUILD_NUMBER)
25 | LSRequiresIPhoneOS
26 |
27 | UILaunchStoryboardName
28 | LaunchScreen
29 | UIMainStoryboardFile
30 | Main
31 | UISupportedInterfaceOrientations
32 |
33 | UIInterfaceOrientationPortrait
34 | UIInterfaceOrientationLandscapeLeft
35 | UIInterfaceOrientationLandscapeRight
36 |
37 | UISupportedInterfaceOrientations~ipad
38 |
39 | UIInterfaceOrientationPortrait
40 | UIInterfaceOrientationPortraitUpsideDown
41 | UIInterfaceOrientationLandscapeLeft
42 | UIInterfaceOrientationLandscapeRight
43 |
44 | CADisableMinimumFrameDurationOnPhone
45 |
46 | UIApplicationSupportsIndirectInputEvents
47 |
48 | NSBluetoothAlwaysUsageDescription
49 | This app needs Bluetooth to function
50 | UIBackgroundModes
51 |
52 | bluetooth-central
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/ios/Runner/Runner-Bridging-Header.h:
--------------------------------------------------------------------------------
1 | #import "GeneratedPluginRegistrant.h"
2 |
--------------------------------------------------------------------------------
/ios/RunnerTests/RunnerTests.swift:
--------------------------------------------------------------------------------
1 | import Flutter
2 | import UIKit
3 | import XCTest
4 |
5 | class RunnerTests: XCTestCase {
6 |
7 | func testExample() {
8 | // If you add code to the Runner application, consider adding tests here.
9 | // See https://developer.apple.com/documentation/xctest for more information about using XCTest.
10 | }
11 |
12 | }
13 |
--------------------------------------------------------------------------------
/lib/foreground_service.dart:
--------------------------------------------------------------------------------
1 | import 'dart:isolate';
2 | import 'package:flutter_foreground_task/flutter_foreground_task.dart';
3 | import 'package:logging/logging.dart';
4 |
5 | final _log = Logger("Foreground task");
6 |
7 | void initializeForegroundService() {
8 | FlutterForegroundTask.init(
9 | androidNotificationOptions: AndroidNotificationOptions(
10 | channelId: 'foreground_service',
11 | channelName: 'Frame Service',
12 | channelImportance: NotificationChannelImportance.MIN,
13 | iconData: null,
14 | ),
15 | iosNotificationOptions: const IOSNotificationOptions(
16 | showNotification: false,
17 | playSound: false,
18 | ),
19 | foregroundTaskOptions: const ForegroundTaskOptions(
20 | isOnceEvent: true,
21 | ),
22 | );
23 | }
24 |
25 | Future startForegroundService() async {
26 | if (!(await FlutterForegroundTask.isRunningService)) {
27 | FlutterForegroundTask.startService(
28 | notificationTitle: 'Frame is connected',
29 | notificationText: 'Tap to return to the app',
30 | callback: _startForegroundCallback,
31 | );
32 | }
33 | }
34 |
35 | @pragma('vm:entry-point')
36 | void _startForegroundCallback() {
37 | FlutterForegroundTask.setTaskHandler(_ForegroundFirstTaskHandler());
38 | }
39 |
40 | class _ForegroundFirstTaskHandler extends TaskHandler {
41 | @override
42 | void onStart(DateTime timestamp, SendPort? sendPort) async {
43 | _log.info("Starting foreground task");
44 | }
45 |
46 | @override
47 | void onRepeatEvent(DateTime timestamp, SendPort? sendPort) async {
48 | _log.info("Foreground repeat event triggered");
49 | }
50 |
51 | @override
52 | void onDestroy(DateTime timestamp, SendPort? sendPort) async {
53 | _log.info("Destroying foreground task");
54 | FlutterForegroundTask.stopService();
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/lib/main.dart:
--------------------------------------------------------------------------------
1 | import 'dart:async';
2 | import 'dart:typed_data';
3 |
4 | import 'package:flutter/material.dart';
5 | import 'package:flutter_foreground_task/flutter_foreground_task.dart';
6 | import 'package:google_speech/endless_streaming_service_v2.dart';
7 | import 'package:google_speech/generated/google/cloud/speech/v2/cloud_speech.pb.dart';
8 | import 'package:google_speech/google_speech.dart';
9 | import 'package:logging/logging.dart';
10 | import 'package:share_plus/share_plus.dart';
11 | import 'package:shared_preferences/shared_preferences.dart';
12 | import 'package:simple_frame_app/rx/audio.dart';
13 | import 'package:simple_frame_app/rx/tap.dart';
14 | import 'package:simple_frame_app/tx/code.dart';
15 | import 'package:simple_frame_app/tx/plain_text.dart';
16 | import 'package:simple_frame_app/text_utils.dart';
17 | import 'package:simple_frame_app/simple_frame_app.dart';
18 | import 'foreground_service.dart';
19 |
20 | void main() {
21 | // Set up Android foreground service
22 | initializeForegroundService();
23 |
24 | runApp(const MainApp());
25 | }
26 |
27 | final _log = Logger("MainApp");
28 |
29 | class MainApp extends StatefulWidget {
30 | const MainApp({super.key});
31 |
32 | @override
33 | MainAppState createState() => MainAppState();
34 | }
35 |
36 | /// SimpleFrameAppState mixin helps to manage the lifecycle of the Frame connection outside of this file
37 | class MainAppState extends State with SimpleFrameAppState {
38 |
39 | MainAppState() {
40 | // filter logging
41 | hierarchicalLoggingEnabled = true;
42 | Logger.root.level = Level.INFO;
43 | Logger('Bluetooth').level = Level.OFF;
44 | Logger('RxAudio').level = Level.FINE;
45 | Logger('RxTap').level = Level.FINE;
46 |
47 | Logger.root.onRecord.listen((record) {
48 | debugPrint('${record.level.name}: [${record.loggerName}] ${record.time}: ${record.message}');
49 | });
50 | }
51 |
52 | /// speech to text application members
53 | final TextEditingController _serviceAccountJsonController = TextEditingController();
54 | final TextEditingController _projectIdController = TextEditingController();
55 | final TextEditingController _languageCodeController = TextEditingController();
56 | StreamSubscription? _recognitionSubs;
57 | // 16-bit linear PCM from Frame mic
58 | Stream? _audioSampleStream;
59 | static const _sampleRate = 8000;
60 |
61 | // tap subscription and audio streaming status
62 | StreamSubscription? _tapSubs;
63 | bool _streaming = false;
64 |
65 | // transcription UI display
66 | String _partialResult = '';
67 | final List _transcript = List.empty(growable: true);
68 | final ScrollController _transcriptController = ScrollController();
69 | final ScrollController _partialResultController = ScrollController();
70 | static const _textStyle = TextStyle(fontSize: 24);
71 | String? _errorMsg;
72 |
73 | @override
74 | void initState() {
75 | super.initState();
76 |
77 | // load up the saved text field data
78 | _loadPrefs()
79 | // then kick off the connection to Frame and start the app if possible
80 | .then((_) => tryScanAndConnectAndStart(andRun: true));
81 | }
82 |
83 | Future _loadPrefs() async {
84 | final prefs = await SharedPreferences.getInstance();
85 | setState(() {
86 | _serviceAccountJsonController.text = prefs.getString('service_account_json') ?? '';
87 | _projectIdController.text = prefs.getString('project_id') ?? '';
88 | _languageCodeController.text = prefs.getString('language_code') ?? '';
89 | });
90 | }
91 |
92 | Future _savePrefs() async {
93 | final prefs = await SharedPreferences.getInstance();
94 |
95 | await prefs.setString('service_account_json', _serviceAccountJsonController.text);
96 | await prefs.setString('project_id', _projectIdController.text);
97 | await prefs.setString('language_code', _languageCodeController.text);
98 | }
99 |
100 | RecognitionConfigV2 _getRecognitionConfig() => RecognitionConfigV2(
101 | model: RecognitionModelV2.telephony, // TODO try .long, .telephony (for self vs others' speech)
102 | languageCodes: [_languageCodeController.text], // TODO try multi-codes e.g. ['de-DE', 'en-US'], how does it choose the transcription language, or do I get transcriptions in both as alternatives?
103 | features: RecognitionFeatures(),
104 | explicitDecodingConfig: ExplicitDecodingConfig(
105 | encoding: ExplicitDecodingConfig_AudioEncoding.LINEAR16,
106 | sampleRateHertz: _sampleRate,
107 | audioChannelCount: 1,
108 | )
109 | );
110 |
111 | /// This application uses google cloud speech-to-text to listen to audio from the Frame mic, convert to text,
112 | /// and send the text to the Frame in real-time. It has a running main loop in this function
113 | /// and also on the Frame (frame_app.lua)
114 | @override
115 | Future run() async {
116 | // validate Google Cloud Speech-to-Text API parameters exist at least
117 | _errorMsg = null;
118 | if (_serviceAccountJsonController.text.isEmpty || _projectIdController.text.isEmpty || _languageCodeController.text.isEmpty) {
119 | setState(() {
120 | _errorMsg = 'Error: Set values for service account, project id and language code';
121 | });
122 |
123 | return;
124 | }
125 |
126 | setState(() {
127 | currentState = ApplicationState.running;
128 | _partialResult = '';
129 | _transcript.clear();
130 | });
131 |
132 | try {
133 | // listen for double taps to start/stop transcribing
134 | _tapSubs?.cancel();
135 | _tapSubs = RxTap().attach(frame!.dataResponse)
136 | .listen((taps) async {
137 | if (taps >= 2) {
138 | if (!_streaming) {
139 | _streaming = true;
140 | // clear the display, the transcribed text will start showing
141 | await frame!.sendMessage(TxPlainText(msgCode: 0x0b, text: ' '));
142 | await _startRecognition();
143 | }
144 | else {
145 | await _stopRecognition();
146 | _streaming = false;
147 |
148 | // prompt the user to begin tapping
149 | await frame!.sendMessage(TxPlainText(msgCode: 0x0b, text: 'Double-Tap to resume!'));
150 | }
151 | }
152 | // ignore spurious 1-taps
153 | });
154 |
155 | // let Frame know to subscribe for taps and send them to us
156 | await frame!.sendMessage(TxCode(msgCode: 0x10, value: 1));
157 |
158 | // prompt the user to begin tapping
159 | await frame!.sendMessage(TxPlainText(msgCode: 0x0b, text: 'Double-Tap to begin!'));
160 |
161 | } catch (e) {
162 | _errorMsg = 'Error executing application logic: $e';
163 | _log.fine(_errorMsg);
164 |
165 | setState(() {
166 | currentState = ApplicationState.ready;
167 | });
168 | }
169 | }
170 |
171 | /// Once running(), audio streaming is controlled by taps. But the user can cancel
172 | /// here as well, whether they are currently streaming audio or not.
173 | @override
174 | Future cancel() async {
175 | setState(() {
176 | currentState = ApplicationState.canceling;
177 | });
178 |
179 | // cancel the subscription for taps
180 | _tapSubs?.cancel();
181 |
182 | // cancel the endless recognition subscription if it's running
183 | _recognitionSubs?.cancel();
184 |
185 | // tell the Frame to stop streaming audio (regardless of if we are currently)
186 | await frame!.sendMessage(TxCode(msgCode: 0x30, value: 0));
187 |
188 | // let Frame know to stop sending taps too
189 | await frame!.sendMessage(TxCode(msgCode: 0x10, value: 0));
190 |
191 | // clear the display
192 | await frame!.sendMessage(TxPlainText(msgCode: 0x0b, text: ' '));
193 |
194 | setState(() {
195 | currentState = ApplicationState.ready;
196 | });
197 | }
198 |
199 | /// When we receive a tap to start the transcribing, we need to start
200 | /// audio streaming on Frame and start streaming recognition on the google speech API end
201 | Future _startRecognition() async {
202 | final speechToText = EndlessStreamingServiceV2.viaServiceAccount(
203 | ServiceAccount.fromString(_serviceAccountJsonController.text),
204 | projectId: _projectIdController.text,
205 | );
206 |
207 | final recognitionConfig = _getRecognitionConfig();
208 |
209 | // tell Frame to start streaming audio
210 | await frame!.sendMessage(TxCode(msgCode: 0x30, value: 1));
211 |
212 | try {
213 | // TODO put this object where I can call on it later to detach()
214 | var rxAudio = RxAudio(streaming: true);
215 | // the audio stream from Frame, which needs to be closed() to stop the streaming recognition
216 | _audioSampleStream = rxAudio.attach(frame!.dataResponse);
217 |
218 | String prevText = '';
219 |
220 | speechToText.endlessStreamingRecognize(
221 | StreamingRecognitionConfigV2(
222 | config: recognitionConfig,
223 | streamingFeatures: StreamingRecognitionFeatures(interimResults: true)
224 | ),
225 | _audioSampleStream!,
226 | restartTime: const Duration(seconds: 120),
227 | transitionBufferTime: const Duration(milliseconds: 500)
228 | );
229 |
230 | // note: we stop the previous recognition stream by stopping its audio stream first
231 | // but also manage stream subscription here
232 | _recognitionSubs?.cancel();
233 | _recognitionSubs = speechToText.endlessStream.listen((data) async {
234 | // log the streamed results
235 | _log.fine(() => 'Result: $data');
236 |
237 | // just use the first alternative for now (consider other language alternatives if provided?)
238 | // and concatenate all the pieces (regardless of stability or whether this result(s) is final or not)
239 | final currentText =
240 | data.results.where((e) => e.alternatives.isNotEmpty)
241 | .map((e) => e.alternatives.first.transcript)
242 | .join();
243 |
244 | if (_log.isLoggable(Level.FINE)) {
245 | _log.fine(() => 'Recognized text: $currentText');
246 | }
247 |
248 | // if the current text is different from the previous text, send an update to Frame
249 | // (there are many responses that split in different places, have different stability values,
250 | // but concatenate to the same string in our case, so don't update Frame display)
251 | if (currentText != prevText) { // TODO (&& Latin-only script?)
252 | // Frame can display 6 lines of plain text, so work out the text wrapping
253 | List wrappedText = TextUtils.wrapText(currentText, 640, 4);
254 |
255 | // then send the bottom 6 lines joined with newlines as a single string
256 | // (they get split and drawn on the Lua side)
257 | String displayText = wrappedText.sublist(wrappedText.length <= 6 ? 0 : wrappedText.length - 6)
258 | .join('\n');
259 |
260 | await frame!.sendMessage(TxPlainText(msgCode: 0x0b, text: displayText));
261 | }
262 |
263 | // When a complete utterance is detected, isFinal is set and we can append
264 | // the utterance to the final transcript.
265 | // In between, there are non-final fragments, and they can be shown as previews but not appended
266 | // to the final transcript. A newline doesn't need to be added either.
267 | if (data.results.first.isFinal) {
268 | // data.results.first.alternatives.first.transcript should match currentText
269 | // (the complete utterance) when isFinal is true
270 | assert(currentText == data.results.first.alternatives.first.transcript);
271 |
272 | // add currentText to the official transcript and clear out the interim
273 | setState(() {
274 | _transcript.add(currentText);
275 | _partialResult = '';
276 | });
277 | _scrollToBottom();
278 |
279 | } else {
280 | // interim results, show the interim result but don't add to the official transcript yet
281 | setState(() {
282 | _partialResult = currentText;
283 | });
284 | _scrollToBottom();
285 | }
286 |
287 | }, onError: (error) {
288 | _log.warning('Error occurred in endless stream: $error');
289 | setState(() {
290 | currentState = ApplicationState.ready;
291 | });
292 | }, onDone: () {
293 | // audio stream was stopped so Recognizer stream stopped
294 | _log.info('Endless Stream is done');
295 | _recognitionSubs?.cancel();
296 | speechToText.dispose();
297 | setState(() {
298 | currentState = ApplicationState.ready;
299 | });
300 | });
301 |
302 | } catch (e) {
303 | _log.warning(() => 'Error executing application logic: $e');
304 | }
305 | }
306 |
307 | /// When we receive a tap to stop transcribing, cancel the audio streaming from Frame,
308 | /// which will send "final chunk" message, which will close the audio stream
309 | /// and the streaming recognizer stream will stop because its audio stream stopped
310 | Future _stopRecognition() async {
311 | // tell Frame to stop streaming audio
312 | await frame!.sendMessage(TxCode(msgCode: 0x30, value: 0));
313 |
314 | // TODO we should also be able to send
315 | // rxAudio.detach() to close the controller controlling our audio stream
316 | }
317 |
318 | void _scrollToBottom() {
319 | WidgetsBinding.instance.addPostFrameCallback((_) {
320 | if (_transcriptController.hasClients) {
321 | _transcriptController.animateTo(
322 | _transcriptController.position.maxScrollExtent,
323 | duration: const Duration(milliseconds: 300),
324 | curve: Curves.easeOut,
325 | );
326 | }
327 | if (_partialResultController.hasClients) {
328 | _partialResultController.animateTo(
329 | _partialResultController.position.maxScrollExtent,
330 | duration: const Duration(milliseconds: 300),
331 | curve: Curves.easeOut,
332 | );
333 | }
334 | });
335 | }
336 |
337 | @override
338 | Widget build(BuildContext context) {
339 | startForegroundService();
340 | return WithForegroundTask(
341 | child: MaterialApp(
342 | title: 'Transcribe - Google Cloud Speech',
343 | theme: ThemeData.dark(),
344 | home: Scaffold(
345 | appBar: AppBar(
346 | title: const Text('Transcribe - Google Cloud Speech'),
347 | actions: [getBatteryWidget()]
348 | ),
349 | body: Center(
350 | child: Container(
351 | margin: const EdgeInsets.symmetric(horizontal: 16),
352 | child: Column(
353 | mainAxisAlignment: MainAxisAlignment.center,
354 | children: [
355 | TextField(controller: _serviceAccountJsonController, obscureText: false, decoration: const InputDecoration(hintText: 'Enter Service Account JSON'),),
356 | TextField(controller: _projectIdController, obscureText: false, decoration: const InputDecoration(hintText: 'Enter Project Id'),),
357 | TextField(controller: _languageCodeController, obscureText: false, decoration: const InputDecoration(hintText: 'Enter Language Code e.g. en-US'),),
358 | if (_errorMsg != null) Text(_errorMsg!, style: const TextStyle(backgroundColor: Colors.red)),
359 | ElevatedButton(onPressed: _savePrefs, child: const Text('Save')),
360 |
361 | Expanded(child: Column(
362 | mainAxisAlignment: MainAxisAlignment.center,
363 | children: [
364 | Expanded(
365 | child: ListView.builder(
366 | controller: _transcriptController, // Auto-scroll controller
367 | itemCount: _transcript.length,
368 | itemBuilder: (context, index) {
369 | return Text(
370 | _transcript[index],
371 | style: _textStyle,
372 | );
373 | },
374 | ),
375 | ),
376 | const Divider(),
377 | ConstrainedBox(
378 | constraints: BoxConstraints(
379 | maxHeight: _textStyle.fontSize! * 5
380 | ),
381 | child: Align(alignment: Alignment.centerLeft,
382 | child: SingleChildScrollView(
383 | controller: _partialResultController,
384 | child: Text(_partialResult, style: _textStyle)
385 | )
386 | ),
387 | ),
388 | ],
389 | )),
390 | ],
391 | ),
392 | ),
393 | ),
394 | floatingActionButton: Stack(
395 | children: [
396 | if (_transcript.isNotEmpty) Positioned(
397 | bottom: 90,
398 | right: 20,
399 | child: FloatingActionButton(
400 | onPressed: () {
401 | Share.share(_transcript.join('\n'));
402 | },
403 | child: const Icon(Icons.share)),
404 | ),
405 | Positioned(
406 | bottom: 20,
407 | right: 20,
408 | child: getFloatingActionButtonWidget(const Icon(Icons.mic), const Icon(Icons.mic_off)) ?? Container(),
409 | ),
410 | ]
411 | ),
412 | persistentFooterButtons: getFooterButtonsWidget(),
413 | ),
414 | )
415 | );
416 | }
417 | }
418 |
--------------------------------------------------------------------------------
/pubspec.lock:
--------------------------------------------------------------------------------
1 | # Generated by pub
2 | # See https://dart.dev/tools/pub/glossary#lockfile
3 | packages:
4 | archive:
5 | dependency: transitive
6 | description:
7 | name: archive
8 | sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
9 | url: "https://pub.dev"
10 | source: hosted
11 | version: "3.6.1"
12 | args:
13 | dependency: transitive
14 | description:
15 | name: args
16 | sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a"
17 | url: "https://pub.dev"
18 | source: hosted
19 | version: "2.5.0"
20 | async:
21 | dependency: transitive
22 | description:
23 | name: async
24 | sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c"
25 | url: "https://pub.dev"
26 | source: hosted
27 | version: "2.11.0"
28 | boolean_selector:
29 | dependency: transitive
30 | description:
31 | name: boolean_selector
32 | sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66"
33 | url: "https://pub.dev"
34 | source: hosted
35 | version: "2.1.1"
36 | characters:
37 | dependency: transitive
38 | description:
39 | name: characters
40 | sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605"
41 | url: "https://pub.dev"
42 | source: hosted
43 | version: "1.3.0"
44 | clock:
45 | dependency: transitive
46 | description:
47 | name: clock
48 | sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf
49 | url: "https://pub.dev"
50 | source: hosted
51 | version: "1.1.1"
52 | collection:
53 | dependency: transitive
54 | description:
55 | name: collection
56 | sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a
57 | url: "https://pub.dev"
58 | source: hosted
59 | version: "1.18.0"
60 | cross_file:
61 | dependency: transitive
62 | description:
63 | name: cross_file
64 | sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
65 | url: "https://pub.dev"
66 | source: hosted
67 | version: "0.3.4+2"
68 | crypto:
69 | dependency: transitive
70 | description:
71 | name: crypto
72 | sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
73 | url: "https://pub.dev"
74 | source: hosted
75 | version: "3.0.3"
76 | fake_async:
77 | dependency: transitive
78 | description:
79 | name: fake_async
80 | sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78"
81 | url: "https://pub.dev"
82 | source: hosted
83 | version: "1.3.1"
84 | ffi:
85 | dependency: transitive
86 | description:
87 | name: ffi
88 | sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
89 | url: "https://pub.dev"
90 | source: hosted
91 | version: "2.1.3"
92 | file:
93 | dependency: transitive
94 | description:
95 | name: file
96 | sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
97 | url: "https://pub.dev"
98 | source: hosted
99 | version: "7.0.1"
100 | fixnum:
101 | dependency: transitive
102 | description:
103 | name: fixnum
104 | sha256: "25517a4deb0c03aa0f32fd12db525856438902d9c16536311e76cdc57b31d7d1"
105 | url: "https://pub.dev"
106 | source: hosted
107 | version: "1.1.0"
108 | flutter:
109 | dependency: "direct main"
110 | description: flutter
111 | source: sdk
112 | version: "0.0.0"
113 | flutter_blue_plus:
114 | dependency: transitive
115 | description:
116 | name: flutter_blue_plus
117 | sha256: "55d37e9339765fef9439f3759a2bddaf9c8db1f90d46ba5a5d834fb28e0ab809"
118 | url: "https://pub.dev"
119 | source: hosted
120 | version: "1.32.12"
121 | flutter_foreground_task:
122 | dependency: "direct main"
123 | description:
124 | name: flutter_foreground_task
125 | sha256: "6cf10a27f5e344cd2ecad0752d3a5f4ec32846d82fda8753b3fe2480ebb832a3"
126 | url: "https://pub.dev"
127 | source: hosted
128 | version: "6.5.0"
129 | flutter_lints:
130 | dependency: "direct dev"
131 | description:
132 | name: flutter_lints
133 | sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1"
134 | url: "https://pub.dev"
135 | source: hosted
136 | version: "3.0.2"
137 | flutter_test:
138 | dependency: "direct dev"
139 | description: flutter
140 | source: sdk
141 | version: "0.0.0"
142 | flutter_web_plugins:
143 | dependency: transitive
144 | description: flutter
145 | source: sdk
146 | version: "0.0.0"
147 | google_identity_services_web:
148 | dependency: transitive
149 | description:
150 | name: google_identity_services_web
151 | sha256: "5be191523702ba8d7a01ca97c17fca096822ccf246b0a9f11923a6ded06199b6"
152 | url: "https://pub.dev"
153 | source: hosted
154 | version: "0.3.1+4"
155 | google_speech:
156 | dependency: "direct main"
157 | description:
158 | name: google_speech
159 | sha256: "4df9ffc46a3fa042e2e3b9466d50461b778dfbdb7e47d673efe421ce28da72bb"
160 | url: "https://pub.dev"
161 | source: hosted
162 | version: "5.3.0"
163 | googleapis_auth:
164 | dependency: transitive
165 | description:
166 | name: googleapis_auth
167 | sha256: befd71383a955535060acde8792e7efc11d2fccd03dd1d3ec434e85b68775938
168 | url: "https://pub.dev"
169 | source: hosted
170 | version: "1.6.0"
171 | grpc:
172 | dependency: transitive
173 | description:
174 | name: grpc
175 | sha256: e93ee3bce45c134bf44e9728119102358c7cd69de7832d9a874e2e74eb8cab40
176 | url: "https://pub.dev"
177 | source: hosted
178 | version: "3.2.4"
179 | http:
180 | dependency: transitive
181 | description:
182 | name: http
183 | sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010
184 | url: "https://pub.dev"
185 | source: hosted
186 | version: "1.2.2"
187 | http2:
188 | dependency: transitive
189 | description:
190 | name: http2
191 | sha256: "9ced024a160b77aba8fb8674e38f70875e321d319e6f303ec18e87bd5a4b0c1d"
192 | url: "https://pub.dev"
193 | source: hosted
194 | version: "2.3.0"
195 | http_parser:
196 | dependency: transitive
197 | description:
198 | name: http_parser
199 | sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b"
200 | url: "https://pub.dev"
201 | source: hosted
202 | version: "4.0.2"
203 | image:
204 | dependency: transitive
205 | description:
206 | name: image
207 | sha256: "2237616a36c0d69aef7549ab439b833fb7f9fb9fc861af2cc9ac3eedddd69ca8"
208 | url: "https://pub.dev"
209 | source: hosted
210 | version: "4.2.0"
211 | leak_tracker:
212 | dependency: transitive
213 | description:
214 | name: leak_tracker
215 | sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
216 | url: "https://pub.dev"
217 | source: hosted
218 | version: "10.0.4"
219 | leak_tracker_flutter_testing:
220 | dependency: transitive
221 | description:
222 | name: leak_tracker_flutter_testing
223 | sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
224 | url: "https://pub.dev"
225 | source: hosted
226 | version: "3.0.3"
227 | leak_tracker_testing:
228 | dependency: transitive
229 | description:
230 | name: leak_tracker_testing
231 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
232 | url: "https://pub.dev"
233 | source: hosted
234 | version: "3.0.1"
235 | lints:
236 | dependency: transitive
237 | description:
238 | name: lints
239 | sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
240 | url: "https://pub.dev"
241 | source: hosted
242 | version: "3.0.0"
243 | logging:
244 | dependency: "direct main"
245 | description:
246 | name: logging
247 | sha256: "623a88c9594aa774443aa3eb2d41807a48486b5613e67599fb4c41c0ad47c340"
248 | url: "https://pub.dev"
249 | source: hosted
250 | version: "1.2.0"
251 | matcher:
252 | dependency: transitive
253 | description:
254 | name: matcher
255 | sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb
256 | url: "https://pub.dev"
257 | source: hosted
258 | version: "0.12.16+1"
259 | material_color_utilities:
260 | dependency: transitive
261 | description:
262 | name: material_color_utilities
263 | sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
264 | url: "https://pub.dev"
265 | source: hosted
266 | version: "0.8.0"
267 | meta:
268 | dependency: transitive
269 | description:
270 | name: meta
271 | sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
272 | url: "https://pub.dev"
273 | source: hosted
274 | version: "1.12.0"
275 | mime:
276 | dependency: transitive
277 | description:
278 | name: mime
279 | sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
280 | url: "https://pub.dev"
281 | source: hosted
282 | version: "2.0.0"
283 | path:
284 | dependency: transitive
285 | description:
286 | name: path
287 | sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af"
288 | url: "https://pub.dev"
289 | source: hosted
290 | version: "1.9.0"
291 | path_provider:
292 | dependency: transitive
293 | description:
294 | name: path_provider
295 | sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378
296 | url: "https://pub.dev"
297 | source: hosted
298 | version: "2.1.4"
299 | path_provider_android:
300 | dependency: transitive
301 | description:
302 | name: path_provider_android
303 | sha256: "6f01f8e37ec30b07bc424b4deabac37cacb1bc7e2e515ad74486039918a37eb7"
304 | url: "https://pub.dev"
305 | source: hosted
306 | version: "2.2.10"
307 | path_provider_foundation:
308 | dependency: transitive
309 | description:
310 | name: path_provider_foundation
311 | sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16
312 | url: "https://pub.dev"
313 | source: hosted
314 | version: "2.4.0"
315 | path_provider_linux:
316 | dependency: transitive
317 | description:
318 | name: path_provider_linux
319 | sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
320 | url: "https://pub.dev"
321 | source: hosted
322 | version: "2.2.1"
323 | path_provider_platform_interface:
324 | dependency: transitive
325 | description:
326 | name: path_provider_platform_interface
327 | sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
328 | url: "https://pub.dev"
329 | source: hosted
330 | version: "2.1.2"
331 | path_provider_windows:
332 | dependency: transitive
333 | description:
334 | name: path_provider_windows
335 | sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
336 | url: "https://pub.dev"
337 | source: hosted
338 | version: "2.3.0"
339 | petitparser:
340 | dependency: transitive
341 | description:
342 | name: petitparser
343 | sha256: c15605cd28af66339f8eb6fbe0e541bfe2d1b72d5825efc6598f3e0a31b9ad27
344 | url: "https://pub.dev"
345 | source: hosted
346 | version: "6.0.2"
347 | platform:
348 | dependency: transitive
349 | description:
350 | name: platform
351 | sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65"
352 | url: "https://pub.dev"
353 | source: hosted
354 | version: "3.1.5"
355 | plugin_platform_interface:
356 | dependency: transitive
357 | description:
358 | name: plugin_platform_interface
359 | sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
360 | url: "https://pub.dev"
361 | source: hosted
362 | version: "2.1.8"
363 | protobuf:
364 | dependency: transitive
365 | description:
366 | name: protobuf
367 | sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d"
368 | url: "https://pub.dev"
369 | source: hosted
370 | version: "3.1.0"
371 | rxdart:
372 | dependency: transitive
373 | description:
374 | name: rxdart
375 | sha256: "0c7c0cedd93788d996e33041ffecda924cc54389199cde4e6a34b440f50044cb"
376 | url: "https://pub.dev"
377 | source: hosted
378 | version: "0.27.7"
379 | share_plus:
380 | dependency: "direct main"
381 | description:
382 | name: share_plus
383 | sha256: "334fcdf0ef9c0df0e3b428faebcac9568f35c747d59831474b2fc56e156d244e"
384 | url: "https://pub.dev"
385 | source: hosted
386 | version: "10.1.0"
387 | share_plus_platform_interface:
388 | dependency: transitive
389 | description:
390 | name: share_plus_platform_interface
391 | sha256: c57c0bbfec7142e3a0f55633be504b796af72e60e3c791b44d5a017b985f7a48
392 | url: "https://pub.dev"
393 | source: hosted
394 | version: "5.0.1"
395 | shared_preferences:
396 | dependency: "direct main"
397 | description:
398 | name: shared_preferences
399 | sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051"
400 | url: "https://pub.dev"
401 | source: hosted
402 | version: "2.3.2"
403 | shared_preferences_android:
404 | dependency: transitive
405 | description:
406 | name: shared_preferences_android
407 | sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e"
408 | url: "https://pub.dev"
409 | source: hosted
410 | version: "2.3.2"
411 | shared_preferences_foundation:
412 | dependency: transitive
413 | description:
414 | name: shared_preferences_foundation
415 | sha256: "07e050c7cd39bad516f8d64c455f04508d09df104be326d8c02551590a0d513d"
416 | url: "https://pub.dev"
417 | source: hosted
418 | version: "2.5.3"
419 | shared_preferences_linux:
420 | dependency: transitive
421 | description:
422 | name: shared_preferences_linux
423 | sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
424 | url: "https://pub.dev"
425 | source: hosted
426 | version: "2.4.1"
427 | shared_preferences_platform_interface:
428 | dependency: transitive
429 | description:
430 | name: shared_preferences_platform_interface
431 | sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
432 | url: "https://pub.dev"
433 | source: hosted
434 | version: "2.4.1"
435 | shared_preferences_web:
436 | dependency: transitive
437 | description:
438 | name: shared_preferences_web
439 | sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e
440 | url: "https://pub.dev"
441 | source: hosted
442 | version: "2.4.2"
443 | shared_preferences_windows:
444 | dependency: transitive
445 | description:
446 | name: shared_preferences_windows
447 | sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
448 | url: "https://pub.dev"
449 | source: hosted
450 | version: "2.4.1"
451 | simple_frame_app:
452 | dependency: "direct main"
453 | description:
454 | name: simple_frame_app
455 | sha256: ecd3e06d5f83afaf43f643454d264258a1c90209bc55e817ddf686dfc87ad43b
456 | url: "https://pub.dev"
457 | source: hosted
458 | version: "2.0.0"
459 | sky_engine:
460 | dependency: transitive
461 | description: flutter
462 | source: sdk
463 | version: "0.0.99"
464 | source_span:
465 | dependency: transitive
466 | description:
467 | name: source_span
468 | sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c"
469 | url: "https://pub.dev"
470 | source: hosted
471 | version: "1.10.0"
472 | sprintf:
473 | dependency: transitive
474 | description:
475 | name: sprintf
476 | sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
477 | url: "https://pub.dev"
478 | source: hosted
479 | version: "7.0.0"
480 | stack_trace:
481 | dependency: transitive
482 | description:
483 | name: stack_trace
484 | sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b"
485 | url: "https://pub.dev"
486 | source: hosted
487 | version: "1.11.1"
488 | stream_channel:
489 | dependency: transitive
490 | description:
491 | name: stream_channel
492 | sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7
493 | url: "https://pub.dev"
494 | source: hosted
495 | version: "2.1.2"
496 | string_scanner:
497 | dependency: transitive
498 | description:
499 | name: string_scanner
500 | sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde"
501 | url: "https://pub.dev"
502 | source: hosted
503 | version: "1.2.0"
504 | term_glyph:
505 | dependency: transitive
506 | description:
507 | name: term_glyph
508 | sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84
509 | url: "https://pub.dev"
510 | source: hosted
511 | version: "1.2.1"
512 | test_api:
513 | dependency: transitive
514 | description:
515 | name: test_api
516 | sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
517 | url: "https://pub.dev"
518 | source: hosted
519 | version: "0.7.0"
520 | typed_data:
521 | dependency: transitive
522 | description:
523 | name: typed_data
524 | sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c
525 | url: "https://pub.dev"
526 | source: hosted
527 | version: "1.3.2"
528 | url_launcher_linux:
529 | dependency: transitive
530 | description:
531 | name: url_launcher_linux
532 | sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af
533 | url: "https://pub.dev"
534 | source: hosted
535 | version: "3.2.0"
536 | url_launcher_platform_interface:
537 | dependency: transitive
538 | description:
539 | name: url_launcher_platform_interface
540 | sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
541 | url: "https://pub.dev"
542 | source: hosted
543 | version: "2.3.2"
544 | url_launcher_web:
545 | dependency: transitive
546 | description:
547 | name: url_launcher_web
548 | sha256: "772638d3b34c779ede05ba3d38af34657a05ac55b06279ea6edd409e323dca8e"
549 | url: "https://pub.dev"
550 | source: hosted
551 | version: "2.3.3"
552 | url_launcher_windows:
553 | dependency: transitive
554 | description:
555 | name: url_launcher_windows
556 | sha256: "44cf3aabcedde30f2dba119a9dea3b0f2672fbe6fa96e85536251d678216b3c4"
557 | url: "https://pub.dev"
558 | source: hosted
559 | version: "3.1.3"
560 | uuid:
561 | dependency: transitive
562 | description:
563 | name: uuid
564 | sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
565 | url: "https://pub.dev"
566 | source: hosted
567 | version: "4.5.1"
568 | vector_math:
569 | dependency: transitive
570 | description:
571 | name: vector_math
572 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
573 | url: "https://pub.dev"
574 | source: hosted
575 | version: "2.1.4"
576 | vm_service:
577 | dependency: transitive
578 | description:
579 | name: vm_service
580 | sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
581 | url: "https://pub.dev"
582 | source: hosted
583 | version: "14.2.1"
584 | web:
585 | dependency: transitive
586 | description:
587 | name: web
588 | sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb
589 | url: "https://pub.dev"
590 | source: hosted
591 | version: "1.1.0"
592 | win32:
593 | dependency: transitive
594 | description:
595 | name: win32
596 | sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a"
597 | url: "https://pub.dev"
598 | source: hosted
599 | version: "5.5.4"
600 | xdg_directories:
601 | dependency: transitive
602 | description:
603 | name: xdg_directories
604 | sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
605 | url: "https://pub.dev"
606 | source: hosted
607 | version: "1.1.0"
608 | xml:
609 | dependency: transitive
610 | description:
611 | name: xml
612 | sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
613 | url: "https://pub.dev"
614 | source: hosted
615 | version: "6.5.0"
616 | sdks:
617 | dart: ">=3.4.4 <4.0.0"
618 | flutter: ">=3.22.0"
619 |
--------------------------------------------------------------------------------
/pubspec.yaml:
--------------------------------------------------------------------------------
1 | name: frame_transcribe_googlespeech
2 | description: "Connects to Frame, streams audio from the Frame microphone, which is sent through a cloud-based streaming transcription service, and displays the streaming text on the Frame display."
3 | publish_to: 'none'
4 | version: 0.1.0
5 |
6 | environment:
7 | sdk: '>=3.4.3 <4.0.0'
8 |
9 | dependencies:
10 | flutter:
11 | sdk: flutter
12 | logging: ^1.2.0
13 | google_speech: ^5.3.0
14 | simple_frame_app: ^2.0.0
15 | shared_preferences: ^2.3.2
16 | share_plus: ^10.1.0
17 | flutter_foreground_task: ^6.5.0
18 |
19 | dev_dependencies:
20 | flutter_test:
21 | sdk: flutter
22 | flutter_lints: ^3.0.0
23 |
24 | flutter:
25 | uses-material-design: true
26 | assets:
27 | - packages/simple_frame_app/lua/battery.min.lua
28 | - packages/simple_frame_app/lua/data.min.lua
29 | - packages/simple_frame_app/lua/code.min.lua
30 | - packages/simple_frame_app/lua/plain_text.min.lua
31 | - assets/frame_app.min.lua
32 |
--------------------------------------------------------------------------------