├── .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 | ![Frameshot1](docs/frameshot1.png) 21 | ![Frameshot2](docs/frameshot2.png) 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 | ![Screenshot1](docs/screenshot1.png) 32 | ![Screenshot2](docs/screenshot2.png) 33 | 34 | ### Architecture 35 | ![Architecture](docs/Frame%20App%20Architecture%20-%20Transcription%20-%20Google%20Speech%20API%20-%20Frame%20Microphone.svg) 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 | --------------------------------------------------------------------------------