├── func ├── supabase │ ├── seed.sql │ ├── .gitignore │ ├── functions │ │ ├── .vscode │ │ │ ├── extensions.json │ │ │ └── settings.json │ │ ├── import_map.json │ │ ├── push │ │ │ └── index.ts │ │ ├── getImages │ │ │ └── index.ts │ │ └── generateImage │ │ │ └── index.ts │ ├── migrations │ │ ├── 20240203190429_remove_can_access_to_profiles.sql │ │ ├── 20240203121739_add_can_access_to_profiles.sql │ │ ├── 20240205032421_remote_schema.sql │ │ ├── 20240128145042_s4y_index.sql │ │ ├── 20240130002621_remote_schema.sql │ │ ├── 20240210211642_remote_schema.sql │ │ ├── 20240210204402_enable_push_notifications.sql │ │ ├── 20240522201932_remote_schema.sql │ │ ├── 20240203010132_remote_schema.sql │ │ ├── 20240203005801_remote_schema.sql │ │ └── 20240130002911_remote_schema.sql │ └── config.toml ├── .vscode │ ├── extensions.json │ └── settings.json ├── package.json ├── README.md └── .gitignore ├── app └── open_aac │ ├── devtools_options.yaml │ ├── linux │ ├── .gitignore │ ├── main.cc │ ├── my_application.h │ ├── my_application.cc │ └── CMakeLists.txt │ ├── ios │ ├── Runner │ │ ├── Runner-Bridging-Header.h │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Icon-20.png │ │ │ │ ├── Icon-29.png │ │ │ │ ├── Icon-40.png │ │ │ │ ├── Icon-76.png │ │ │ │ ├── Icon-20@2x.png │ │ │ │ ├── Icon-20@3x.png │ │ │ │ ├── Icon-29 1.png │ │ │ │ ├── Icon-29@2x.png │ │ │ │ ├── Icon-29@3x.png │ │ │ │ ├── Icon-40@2x.png │ │ │ │ ├── Icon-40@3x.png │ │ │ │ ├── Icon-60@2x.png │ │ │ │ ├── Icon-60@3x.png │ │ │ │ ├── Icon-76@2x.png │ │ │ │ ├── Icon-20@2x 1.png │ │ │ │ ├── Icon-29@2x 1.png │ │ │ │ ├── Icon-40@2x 1.png │ │ │ │ ├── Icon-83.5@2x.png │ │ │ │ ├── iTunesArtwork-1024.png │ │ │ │ └── Contents.json │ │ │ └── LaunchImage.imageset │ │ │ │ ├── logo@1x.png │ │ │ │ ├── logo@2x.png │ │ │ │ ├── logo@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ ├── Runner.entitlements │ │ ├── AppDelegate.swift │ │ ├── GoogleService-Info.plist │ │ ├── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── iOS Image │ │ ├── Icon-20.png │ │ ├── Icon-29.png │ │ ├── Icon-40.png │ │ ├── Icon-50.png │ │ ├── Icon-57.png │ │ ├── Icon-72.png │ │ ├── Icon-76.png │ │ ├── logo@1x.png │ │ ├── logo@2x.png │ │ ├── logo@3x.png │ │ ├── Icon-20@2x.png │ │ ├── Icon-20@3x.png │ │ ├── Icon-29@2x.png │ │ ├── Icon-29@3x.png │ │ ├── Icon-40@2x.png │ │ ├── Icon-40@3x.png │ │ ├── Icon-50@2x.png │ │ ├── Icon-57@2x.png │ │ ├── Icon-60@2x.png │ │ ├── Icon-60@3x.png │ │ ├── Icon-72@2x.png │ │ ├── Icon-76@2x.png │ │ ├── Icon-83.5@2x.png │ │ └── iTunesArtwork-1024.png │ ├── Runner.xcodeproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── firebase_app_id_file.json │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── RunnerTests │ │ └── RunnerTests.swift │ ├── .gitignore │ ├── Podfile │ └── Podfile.lock │ ├── web │ ├── favicon.png │ ├── icons │ │ ├── Icon-192.png │ │ ├── Icon-512.png │ │ ├── Icon-maskable-192.png │ │ └── Icon-maskable-512.png │ ├── manifest.json │ └── index.html │ ├── android │ ├── gradle.properties │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── launcher_icon.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── launcher_icon.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── launcher_icon.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── launcher_icon.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ ├── ic_launcher.png │ │ │ │ │ │ └── launcher_icon.png │ │ │ │ │ ├── drawable │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── drawable-v21 │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── values │ │ │ │ │ │ └── styles.xml │ │ │ │ │ └── values-night │ │ │ │ │ │ └── styles.xml │ │ │ │ ├── kotlin │ │ │ │ │ └── org │ │ │ │ │ │ └── learningo │ │ │ │ │ │ ├── open_aac │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ │ │ └── openaac │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── AndroidManifest.xml │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ ├── google-services.json │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── .gitignore │ ├── build.gradle │ └── settings.gradle │ ├── assets │ └── images │ │ └── _app │ │ ├── blank.png │ │ └── logo.png │ ├── macos │ ├── Runner │ │ ├── Configs │ │ │ ├── Debug.xcconfig │ │ │ ├── Release.xcconfig │ │ │ ├── Warnings.xcconfig │ │ │ └── AppInfo.xcconfig │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ ├── app_icon_16.png │ │ │ │ ├── app_icon_32.png │ │ │ │ ├── app_icon_64.png │ │ │ │ ├── app_icon_1024.png │ │ │ │ ├── app_icon_128.png │ │ │ │ ├── app_icon_256.png │ │ │ │ ├── app_icon_512.png │ │ │ │ └── Contents.json │ │ ├── AppDelegate.swift │ │ ├── Release.entitlements │ │ ├── DebugProfile.entitlements │ │ ├── MainFlutterWindow.swift │ │ └── Info.plist │ ├── .gitignore │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner.xcodeproj │ │ ├── project.xcworkspace │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── RunnerTests │ │ └── RunnerTests.swift │ └── Podfile │ ├── windows │ ├── runner │ │ ├── resources │ │ │ └── app_icon.ico │ │ ├── resource.h │ │ ├── utils.h │ │ ├── runner.exe.manifest │ │ ├── flutter_window.h │ │ ├── main.cpp │ │ ├── CMakeLists.txt │ │ ├── utils.cpp │ │ ├── flutter_window.cpp │ │ ├── Runner.rc │ │ └── win32_window.h │ ├── .gitignore │ └── CMakeLists.txt │ ├── analysis_options.yaml │ ├── test │ └── widget_test.dart │ ├── .gitignore │ ├── README.md │ ├── lib │ ├── pages │ │ ├── splash_page.dart │ │ └── login_page.dart │ ├── main.dart │ ├── firebase_options.dart │ ├── components │ │ └── avatar.dart │ ├── tts.dart │ └── ai.dart │ ├── .metadata │ └── pubspec.yaml ├── db ├── cli │ ├── CHANGELOG.md │ ├── test │ │ └── cli_test.dart │ ├── .gitignore │ ├── pubspec.yaml │ ├── upload_all_dirs.sh │ ├── README.md │ ├── analysis_options.yaml │ └── bin │ │ └── cli.dart ├── util │ ├── shortcuts.png │ └── ocr_fix.py └── README.md ├── docs ├── ipadHome.png ├── ipadLogin.png ├── ipadSettings.png ├── iphoneHome.png ├── iphoneLogin.png ├── iphoneSEHome.png ├── lets_speak.jpg ├── iphoneSELogin.png ├── iphoneSettings.png ├── generated_image.png ├── iphoneSESettings.png ├── OpenAAC_Image_Lookup_Flow.png └── OpenAAC_Image_Lookup_Flow.puml ├── openaac.code-workspace ├── .gitignore ├── img └── media-pipe.py └── README.md /func/supabase/seed.sql: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/open_aac/devtools_options.yaml: -------------------------------------------------------------------------------- 1 | extensions: 2 | -------------------------------------------------------------------------------- /app/open_aac/linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /db/cli/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 1.0.0 2 | 3 | - Initial version. 4 | -------------------------------------------------------------------------------- /db/cli/test/cli_test.dart: -------------------------------------------------------------------------------- 1 | 2 | void main() { 3 | 4 | } 5 | -------------------------------------------------------------------------------- /func/supabase/.gitignore: -------------------------------------------------------------------------------- 1 | # Supabase 2 | .branches 3 | .temp 4 | .env 5 | -------------------------------------------------------------------------------- /docs/ipadHome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/docs/ipadHome.png -------------------------------------------------------------------------------- /docs/ipadLogin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/docs/ipadLogin.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /db/util/shortcuts.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/db/util/shortcuts.png -------------------------------------------------------------------------------- /docs/ipadSettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/docs/ipadSettings.png -------------------------------------------------------------------------------- /docs/iphoneHome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/docs/iphoneHome.png -------------------------------------------------------------------------------- /docs/iphoneLogin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/docs/iphoneLogin.png -------------------------------------------------------------------------------- /docs/iphoneSEHome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/docs/iphoneSEHome.png -------------------------------------------------------------------------------- /docs/lets_speak.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/docs/lets_speak.jpg -------------------------------------------------------------------------------- /func/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["denoland.vscode-deno"] 3 | } 4 | -------------------------------------------------------------------------------- /docs/iphoneSELogin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/docs/iphoneSELogin.png -------------------------------------------------------------------------------- /docs/iphoneSettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/docs/iphoneSettings.png -------------------------------------------------------------------------------- /docs/generated_image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/docs/generated_image.png -------------------------------------------------------------------------------- /docs/iphoneSESettings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/docs/iphoneSESettings.png -------------------------------------------------------------------------------- /app/open_aac/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/web/favicon.png -------------------------------------------------------------------------------- /func/supabase/functions/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["denoland.vscode-deno"] 3 | } 4 | -------------------------------------------------------------------------------- /app/open_aac/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/web/icons/Icon-192.png -------------------------------------------------------------------------------- /app/open_aac/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/web/icons/Icon-512.png -------------------------------------------------------------------------------- /db/cli/.gitignore: -------------------------------------------------------------------------------- 1 | # https://dart.dev/guides/libraries/private-files 2 | # Created by `dart pub` 3 | .dart_tool/ 4 | run.sh -------------------------------------------------------------------------------- /docs/OpenAAC_Image_Lookup_Flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/docs/OpenAAC_Image_Lookup_Flow.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-20.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-29.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-40.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-50.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-50.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-57.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-72.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-76.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/logo@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/logo@1x.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/logo@2x.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/logo@3x.png -------------------------------------------------------------------------------- /func/supabase/migrations/20240203190429_remove_can_access_to_profiles.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE profiles 2 | DROP COLUMN "can_access"; -------------------------------------------------------------------------------- /app/open_aac/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx4G 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /app/open_aac/assets/images/_app/blank.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/assets/images/_app/blank.png -------------------------------------------------------------------------------- /app/open_aac/assets/images/_app/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/assets/images/_app/logo.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-20@2x.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-20@3x.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-29@2x.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-29@3x.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-40@2x.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-40@3x.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-50@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-50@2x.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-57@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-57@2x.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-60@2x.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-60@3x.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-72@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-72@2x.png -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-76@2x.png -------------------------------------------------------------------------------- /app/open_aac/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/Icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/Icon-83.5@2x.png -------------------------------------------------------------------------------- /app/open_aac/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /app/open_aac/web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /app/open_aac/web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /app/open_aac/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /app/open_aac/ios/iOS Image/iTunesArtwork-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/iOS Image/iTunesArtwork-1024.png -------------------------------------------------------------------------------- /app/open_aac/windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /func/supabase/migrations/20240203121739_add_can_access_to_profiles.sql: -------------------------------------------------------------------------------- 1 | ALTER TABLE profiles 2 | ADD COLUMN "can_access" BOOLEAN NOT NULL DEFAULT FALSE; -------------------------------------------------------------------------------- /func/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "devDependencies": { 3 | "supabase": "^1.136.3" 4 | }, 5 | "dependencies": { 6 | "@supabase/supabase-js": "^2.39.3" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /app/open_aac/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/open_aac/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/open_aac/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/open_aac/android/app/src/main/res/mipmap-hdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/android/app/src/main/res/mipmap-hdpi/launcher_icon.png -------------------------------------------------------------------------------- /app/open_aac/android/app/src/main/res/mipmap-mdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/android/app/src/main/res/mipmap-mdpi/launcher_icon.png -------------------------------------------------------------------------------- /app/open_aac/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/android/app/src/main/res/mipmap-xhdpi/launcher_icon.png -------------------------------------------------------------------------------- /app/open_aac/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/open_aac/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/open_aac/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /app/open_aac/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-20.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-29.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-40.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-20@2x.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-20@3x.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-29 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-29 1.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-29@2x.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-29@3x.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-40@2x.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-40@3x.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@2x.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-60@3x.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-76@2x.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/LaunchImage.imageset/logo@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/LaunchImage.imageset/logo@1x.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/LaunchImage.imageset/logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/LaunchImage.imageset/logo@2x.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/LaunchImage.imageset/logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/LaunchImage.imageset/logo@3x.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-20@2x 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-20@2x 1.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-29@2x 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-29@2x 1.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-40@2x 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-40@2x 1.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-83.5@2x.png -------------------------------------------------------------------------------- /app/open_aac/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /app/open_aac/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /app/open_aac/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /app/open_aac/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /app/open_aac/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /app/open_aac/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /app/open_aac/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/iTunesArtwork-1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/RonanOD/OpenAAC/HEAD/app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/iTunesArtwork-1024.png -------------------------------------------------------------------------------- /app/open_aac/android/app/src/main/kotlin/org/learningo/open_aac/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package org.learningo.open_aac 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() 6 | -------------------------------------------------------------------------------- /app/open_aac/android/app/src/main/kotlin/org/learningo/openaac/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package org.learningo.openaac 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /app/open_aac/linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /app/open_aac/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/open_aac/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /func/supabase/functions/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enable": true, 3 | "deno.importMap": "../import_map.json", 4 | "deno.unstable": true, 5 | "[typescript]": { 6 | "editor.defaultFormatter": "denoland.vscode-deno" 7 | } 8 | } -------------------------------------------------------------------------------- /func/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "deno.enablePaths": [ 3 | "supabase/functions" 4 | ], 5 | "deno.lint": true, 6 | "deno.unstable": true, 7 | "[typescript]": { 8 | "editor.defaultFormatter": "denoland.vscode-deno" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /func/supabase/migrations/20240205032421_remote_schema.sql: -------------------------------------------------------------------------------- 1 | create policy "Allow authenticated download 1ffg0oo_0" 2 | on "storage"."objects" 3 | as permissive 4 | for select 5 | to authenticated, service_role 6 | using ((bucket_id = 'images'::text)); 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/open_aac/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-7.5-all.zip 6 | -------------------------------------------------------------------------------- /app/open_aac/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /func/supabase/migrations/20240128145042_s4y_index.sql: -------------------------------------------------------------------------------- 1 | -- Adding Vector Index to s4y_images as outlined in the "Indexing" section https://supabase.com/blog/openai-embeddings-postgres-vector 2 | 3 | create index on s4y_images using ivfflat (embedding vector_cosine_ops) 4 | with 5 | (lists = 100); 6 | -------------------------------------------------------------------------------- /func/supabase/migrations/20240130002621_remote_schema.sql: -------------------------------------------------------------------------------- 1 | alter table "public"."s4y_images" enable row level security; 2 | 3 | create policy "Enable read access for all users" 4 | on "public"."s4y_images" 5 | as permissive 6 | for select 7 | to authenticated 8 | using (true); 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/open_aac/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Runner.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/open_aac/ios/firebase_app_id_file.json: -------------------------------------------------------------------------------- 1 | { 2 | "file_generated_by": "FlutterFire CLI", 3 | "purpose": "FirebaseAppID & ProjectID for this Firebase app in this directory", 4 | "GOOGLE_APP_ID": "1:61771573616:ios:9ab31991da19d56cbb87b5", 5 | "FIREBASE_PROJECT_ID": "openaac-411413", 6 | "GCM_SENDER_ID": "61771573616" 7 | } -------------------------------------------------------------------------------- /func/supabase/migrations/20240210211642_remote_schema.sql: -------------------------------------------------------------------------------- 1 | alter table "public"."notifications" enable row level security; 2 | 3 | create policy "Enable insert for authenticated users only" 4 | on "public"."notifications" 5 | as permissive 6 | for insert 7 | to authenticated 8 | with check (true); 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/open_aac/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /app/open_aac/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/open_aac/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/open_aac/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/open_aac/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/open_aac/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/open_aac/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/open_aac/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 | -------------------------------------------------------------------------------- /app/open_aac/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 | -------------------------------------------------------------------------------- /app/open_aac/macos/RunnerTests/RunnerTests.swift: -------------------------------------------------------------------------------- 1 | import FlutterMacOS 2 | import Cocoa 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 | -------------------------------------------------------------------------------- /app/open_aac/windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /app/open_aac/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | 2 | include: package:flutter_lints/flutter.yaml 3 | 4 | linter: 5 | rules: 6 | avoid_print: false 7 | prefer_const_constructors_in_immutables: false 8 | prefer_const_constructors: false 9 | prefer_const_literals_to_create_immutables: false 10 | prefer_final_fields: false 11 | unnecessary_breaks: true 12 | use_key_in_widget_constructors: false -------------------------------------------------------------------------------- /app/open_aac/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. -------------------------------------------------------------------------------- /app/open_aac/macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /func/supabase/migrations/20240210204402_enable_push_notifications.sql: -------------------------------------------------------------------------------- 1 | -- https://supabase.com/docs/guides/functions/examples/push-notifications?platform=fcm 2 | 3 | ALTER TABLE public.profiles 4 | ADD COLUMN fcm_token text; 5 | 6 | create table public.notifications ( 7 | id uuid not null default gen_random_uuid(), 8 | user_id uuid references auth.users(id) not null, 9 | created_at timestamp with time zone not null default now(), 10 | body text not null 11 | ); -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /app/open_aac/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/open_aac/linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /db/cli/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: cli 2 | description: A sample command-line application. 3 | version: 1.0.0 4 | # repository: https://github.com/my_org/my_repo 5 | 6 | environment: 7 | sdk: ^3.2.3 8 | 9 | # Add regular dependencies here. 10 | dependencies: 11 | args: ^2.4.2 12 | dart_openai: ^5.0.0 13 | image: ^4.1.3 14 | langchain: ^0.3.0 15 | langchain_openai: ^0.3.0 16 | pinecone: ^0.7.2 17 | supabase: ^2.0.6 18 | # path: ^1.8.0 19 | 20 | dev_dependencies: 21 | lints: ^3.0.0 22 | test: ^1.24.0 23 | -------------------------------------------------------------------------------- /func/README.md: -------------------------------------------------------------------------------- 1 | ## Supabase Database 2 | 3 | Supabase hosts a Postgres Database. Migrations are stored in the `migrations` folder. Guide to migrating is [here](https://supabase.com/docs/guides/cli/managing-environments). 4 | 5 | ## Supabase Functions 6 | 7 | We will use Supabase Edge functions to query text. See [this page](https://medium.com/flutter-community/supabase-edge-functions-in-dart-4125288c3e51) for details on Edge functions. 8 | 9 | Deployment advice taken from [this page](https://supabase.com/docs/guides/ai/examples/openai) -------------------------------------------------------------------------------- /app/open_aac/windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /app/open_aac/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "logo@1x.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "logo@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "logo@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /app/open_aac/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /app/open_aac/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | 12 | void main() { 13 | // :( 14 | } 15 | -------------------------------------------------------------------------------- /db/cli/upload_all_dirs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/zsh 2 | 3 | # Get the root folder from the user 4 | echo "Enter the root folder:" 5 | read root_folder 6 | 7 | # Iterate over all directories in the root folder 8 | for dir in "$root_folder"/*/ 9 | do 10 | # Remove trailing slash 11 | dir=${dir%/} 12 | 13 | echo "Uploading $dir..." 14 | 15 | # Run the Dart script with the directory path as an argument 16 | dart bin/cli.dart --supabase_path="$dir" 17 | 18 | # Wait for the user to hit Enter before continuing 19 | echo "Press Enter to continue..." 20 | read 21 | done -------------------------------------------------------------------------------- /app/open_aac/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/open_aac/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /openaac.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "name": "project-root", 5 | "path": "./" 6 | }, 7 | { 8 | "name": "flutter-client", 9 | "path": "app/open_aac" 10 | }, 11 | { 12 | "name": "supabase-functions", 13 | "path": "func/supabase/functions" 14 | } 15 | ], 16 | "settings": { 17 | "files.exclude": { 18 | "flutter/": true, 19 | "app/": true, 20 | "func/supabase/functions/": true 21 | }, 22 | "deno.importMap": "./func/supabase/functions/import_map.json" 23 | } 24 | } -------------------------------------------------------------------------------- /app/open_aac/macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /app/open_aac/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '2.0.0' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 10 | } 11 | } 12 | 13 | allprojects { 14 | repositories { 15 | google() 16 | mavenCentral() 17 | } 18 | } 19 | 20 | rootProject.buildDir = '../build' 21 | subprojects { 22 | project.buildDir = "${rootProject.buildDir}/${project.name}" 23 | } 24 | subprojects { 25 | project.evaluationDependsOn(':app') 26 | } 27 | 28 | tasks.register("clean", Delete) { 29 | delete rootProject.buildDir 30 | } 31 | -------------------------------------------------------------------------------- /app/open_aac/macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = open_aac 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = org.learningo.openaac 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2024 learningo.org. All rights reserved. 15 | -------------------------------------------------------------------------------- /app/open_aac/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 | -------------------------------------------------------------------------------- /app/open_aac/windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | #include 5 | #include 6 | 7 | // Creates a console for the process, and redirects stdout and stderr to 8 | // it for both the runner and the Flutter library. 9 | void CreateAndAttachConsole(); 10 | 11 | // Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string 12 | // encoded in UTF-8. Returns an empty std::string on failure. 13 | std::string Utf8FromUtf16(const wchar_t* utf16_string); 14 | 15 | // Gets the command line arguments passed in as a std::vector, 16 | // encoded in UTF-8. Returns an empty std::vector on failure. 17 | std::vector GetCommandLineArguments(); 18 | 19 | #endif // RUNNER_UTILS_H_ 20 | -------------------------------------------------------------------------------- /db/cli/README.md: -------------------------------------------------------------------------------- 1 | # OpenAAC DB CLI 2 | 3 | OpenAAC uses a hosted vector database (pinecone or supabase) to store and retrieve the vector embeddings of AAC symbols. 4 | 5 | If the database is hosted on Pinecone, it is accessible via the [pinecone](https://pub.dev/packages/pinecone) package. 6 | 7 | If on supabase, the [supabase](https://pub.dev/packages/supabase_flutter) package is used. 8 | 9 | The [OpenAI Embeddings API](https://platform.openai.com/docs/guides/embeddings) generates the vectors. 10 | 11 | The `cli.dart` program is used to upload the images to the database and to test lookup. 12 | 13 | The `update_all_dirs.sh` script can be used to update all the directories in a directory full of sub-directories of AAC icons. It will call the `cli.dart` program for each sub-directory. 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://www.dartlang.org/guides/libraries/private-files 2 | 3 | # Files and directories created by pub 4 | .dart_tool/ 5 | .packages 6 | build/ 7 | # If you're building an application, you may want to check-in your pubspec.lock 8 | pubspec.lock 9 | 10 | # Directory created by dartdoc 11 | # If you don't generate documentation locally you can remove this line. 12 | doc/api/ 13 | 14 | # dotenv environment variables file 15 | .env* 16 | 17 | # Avoid committing generated Javascript files: 18 | *.dart.js 19 | *.info.json # Produced by the --dump-info flag. 20 | *.js # When generated by dart2js. Don't specify *.js if your 21 | # project includes source files written in JavaScript. 22 | *.js_ 23 | *.js.deps 24 | *.js.map 25 | 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | 29 | # Custom 30 | flutter/ 31 | helloworld/ 32 | db/cli/images/ 33 | .DS_Store 34 | .vscode/ 35 | -------------------------------------------------------------------------------- /app/open_aac/.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 | 45 | /android/key.properties 46 | -------------------------------------------------------------------------------- /app/open_aac/android/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | def flutterSdkPath = { 3 | def properties = new Properties() 4 | file("local.properties").withInputStream { properties.load(it) } 5 | def flutterSdkPath = properties.getProperty("flutter.sdk") 6 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 7 | return flutterSdkPath 8 | } 9 | settings.ext.flutterSdkPath = flutterSdkPath() 10 | 11 | includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") 12 | 13 | repositories { 14 | google() 15 | mavenCentral() 16 | gradlePluginPortal() 17 | } 18 | 19 | plugins { 20 | id "dev.flutter.flutter-gradle-plugin" version "1.0.0" apply false 21 | } 22 | } 23 | 24 | plugins { 25 | id "dev.flutter.flutter-plugin-loader" version "1.0.0" 26 | id "com.android.application" version '7.4.2' apply false 27 | } 28 | 29 | include ":app" 30 | -------------------------------------------------------------------------------- /app/open_aac/windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /app/open_aac/README.md: -------------------------------------------------------------------------------- 1 | # open_aac 2 | 3 | The OpenAAC client is available on Android and iOS. 4 | 5 | ## Android Releases 6 | This section contains notes for each Android release. 7 | ### Beta 0.0.6 8 | * Improved client-server error handling 9 | ### Beta 0.0.5 10 | * Add version number to account page 11 | ### Beta 0.0.4 12 | * Improve async calls to Supabase to cut down on redundancy and cacheing 13 | ### Beta 0.0.3 14 | * Firebase Messaging in Client 15 | ### Beta 0.0.2 16 | * Change site location to [a specific app page](https://learningo.org/app). 17 | * Use Dalle-2 for image generation 18 | * Updated Migrations 19 | * Push Notifications 20 | ### Beta 0.0.1 21 | * First working version put on Android App Store (Test Only) 22 | * Includes all basic functionality 23 | 24 | ## iOS Releases 25 | This section contains notes for each iOS release. 26 | 27 | ### Beta 0.0.1 28 | * Pending 29 | 30 | #### TODO: 31 | * [IOS Client Messaging](https://firebase.google.com/docs/cloud-messaging/flutter/client#ios) 32 | -------------------------------------------------------------------------------- /app/open_aac/lib/pages/splash_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:openaac/main.dart'; 3 | 4 | class SplashPage extends StatefulWidget { 5 | const SplashPage({super.key}); 6 | 7 | @override 8 | _SplashPageState createState() => _SplashPageState(); 9 | } 10 | 11 | class _SplashPageState extends State { 12 | @override 13 | void initState() { 14 | super.initState(); 15 | _redirect(); 16 | } 17 | 18 | Future _redirect() async { 19 | await Future.delayed(Duration.zero); 20 | if (!mounted) { 21 | return; 22 | } 23 | 24 | final session = supabase.auth.currentSession; 25 | if (session != null) { 26 | Navigator.of(context).pushReplacementNamed('/home'); 27 | } else { 28 | Navigator.of(context).pushReplacementNamed('/login'); 29 | } 30 | } 31 | 32 | @override 33 | Widget build(BuildContext context) { 34 | return const Scaffold( 35 | body: Center(child: CircularProgressIndicator()), 36 | ); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /app/open_aac/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "open_aac", 3 | "short_name": "open_aac", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#hexcode", 7 | "theme_color": "#hexcode", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } -------------------------------------------------------------------------------- /app/open_aac/windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "win32_window.h" 10 | 11 | // A window that does nothing but host a Flutter view. 12 | class FlutterWindow : public Win32Window { 13 | public: 14 | // Creates a new FlutterWindow hosting a Flutter view running |project|. 15 | explicit FlutterWindow(const flutter::DartProject& project); 16 | virtual ~FlutterWindow(); 17 | 18 | protected: 19 | // Win32Window: 20 | bool OnCreate() override; 21 | void OnDestroy() override; 22 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 23 | LPARAM const lparam) noexcept override; 24 | 25 | private: 26 | // The project to run. 27 | flutter::DartProject project_; 28 | 29 | // The Flutter instance hosted by this window. 30 | std::unique_ptr flutter_controller_; 31 | }; 32 | 33 | #endif // RUNNER_FLUTTER_WINDOW_H_ 34 | -------------------------------------------------------------------------------- /app/open_aac/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /app/open_aac/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ANDROID_CLIENT_ID 6 | 61771573616-5a2gooulsqpo5c6rt20m36anar605sb2.apps.googleusercontent.com 7 | API_KEY 8 | AIzaSyBZuxmM9ZpsVDPU2X9Tz5I2A91f8AzByRg 9 | GCM_SENDER_ID 10 | 61771573616 11 | PLIST_VERSION 12 | 1 13 | BUNDLE_ID 14 | org.learningo.openaacapp 15 | PROJECT_ID 16 | openaac-411413 17 | STORAGE_BUCKET 18 | openaac-411413.appspot.com 19 | IS_ADS_ENABLED 20 | 21 | IS_ANALYTICS_ENABLED 22 | 23 | IS_APPINVITE_ENABLED 24 | 25 | IS_GCM_ENABLED 26 | 27 | IS_SIGNIN_ENABLED 28 | 29 | GOOGLE_APP_ID 30 | 1:61771573616:ios:fbe41ca5579b5a97bb87b5 31 | 32 | 33 | -------------------------------------------------------------------------------- /db/cli/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the static analysis results for your project (errors, 2 | # warnings, and lints). 3 | # 4 | # This enables the 'recommended' set of lints from `package:lints`. 5 | # This set helps identify many issues that may lead to problems when running 6 | # or consuming Dart code, and enforces writing Dart using a single, idiomatic 7 | # style and format. 8 | # 9 | # If you want a smaller set of lints you can change this to specify 10 | # 'package:lints/core.yaml'. These are just the most critical lints 11 | # (the recommended set includes the core lints). 12 | # The core lints are also what is used by pub.dev for scoring packages. 13 | 14 | include: package:lints/recommended.yaml 15 | 16 | # Uncomment the following section to specify additional rules. 17 | 18 | # linter: 19 | # rules: 20 | # - camel_case_types 21 | 22 | # analyzer: 23 | # exclude: 24 | # - path/to/excluded/files/** 25 | 26 | # For more information about the core and recommended set of lints, see 27 | # https://dart.dev/go/core-lints 28 | 29 | # For additional information about configuring this file, see 30 | # https://dart.dev/guides/language/analysis-options 31 | -------------------------------------------------------------------------------- /app/open_aac/android/app/google-services.json: -------------------------------------------------------------------------------- 1 | { 2 | "project_info": { 3 | "project_number": "61771573616", 4 | "project_id": "openaac-411413", 5 | "storage_bucket": "openaac-411413.appspot.com" 6 | }, 7 | "client": [ 8 | { 9 | "client_info": { 10 | "mobilesdk_app_id": "1:61771573616:android:7c950187a53fe86abb87b5", 11 | "android_client_info": { 12 | "package_name": "org.learningo.openaac" 13 | } 14 | }, 15 | "oauth_client": [ 16 | { 17 | "client_id": "61771573616-bdr4sgua9dvq0rln8hf9ofla9qpa73dv.apps.googleusercontent.com", 18 | "client_type": 3 19 | } 20 | ], 21 | "api_key": [ 22 | { 23 | "current_key": "AIzaSyDF1KqVWvak5Pjy4BUM5S3kAPqokfNx92M" 24 | } 25 | ], 26 | "services": { 27 | "appinvite_service": { 28 | "other_platform_oauth_client": [ 29 | { 30 | "client_id": "61771573616-bdr4sgua9dvq0rln8hf9ofla9qpa73dv.apps.googleusercontent.com", 31 | "client_type": 3 32 | } 33 | ] 34 | } 35 | } 36 | } 37 | ], 38 | "configuration_version": "1" 39 | } -------------------------------------------------------------------------------- /app/open_aac/macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /func/supabase/functions/import_map.json: -------------------------------------------------------------------------------- 1 | { 2 | "imports": { 3 | "denomailer": "https://deno.land/x/denomailer@0.12.0/mod.ts", 4 | "nacl": "https://cdn.skypack.dev/tweetnacl@v1.0.3?dts", 5 | "oak": "https://deno.land/x/oak@v11.1.0/mod.ts", 6 | "og_edge": "https://deno.land/x/og_edge@0.0.4/mod.ts", 7 | "openai": "https://esm.sh/openai@3.1.0", 8 | "grammy": "https://deno.land/x/grammy@v1.8.3/mod.ts", 9 | "react": "https://esm.sh/react@18.2.0", 10 | "std/server": "https://deno.land/std@0.177.0/http/server.ts", 11 | "stripe": "https://esm.sh/stripe@11.1.0?target=deno", 12 | "sift": "https://deno.land/x/sift@0.6.0/mod.ts", 13 | "@supabase/supabase-js": "https://esm.sh/@supabase/supabase-js@2.7.1", 14 | "postgres": "https://deno.land/x/postgres@v0.17.0/mod.ts", 15 | "puppeteer": "https://deno.land/x/puppeteer@16.2.0/mod.ts", 16 | "React": "https://esm.sh/react@18.2.0?deno-std=0.177.0", 17 | "@upstash_redis": "https://deno.land/x/upstash_redis@v1.19.3/mod.ts", 18 | "@upstash/ratelimit": "https://cdn.skypack.dev/@upstash/ratelimit@latest", 19 | "xhr_polyfill": "https://deno.land/x/xhr@0.3.0/mod.ts", 20 | "kysely": "https://esm.sh/kysely@0.23.4" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /docs/OpenAAC_Image_Lookup_Flow.puml: -------------------------------------------------------------------------------- 1 | @startuml 2 | 3 | skinparam component { 4 | FontColor black 5 | AttributeFontColor black 6 | FontSize 17 7 | AttributeFontSize 15 8 | AttributeFontname Droid Sans Mono 9 | BackgroundColor #6A9EFF 10 | BorderColor black 11 | ArrowColor #222266 12 | } 13 | 14 | title "OpenAAC Image Lookup Flow" 15 | skinparam componentStyle uml2 16 | 17 | left to right direction 18 | 19 | [OpenAAC Flutter App\n(Android and iOS)] as App 20 | (OpenAI Image Gen) as ImageGen 21 | (OpenAI Embedding Service) as EmbeddingSvc 22 | 23 | package "Supabase Cloud" { 24 | [Supabase Auth] as Auth 25 | [Supabase Edge Function] as Edge 26 | [Supabase Storage] as Storage 27 | [Supabase Postgres Service] as DBSvc 28 | database "Images Table With Vector Embeddings" as DBTable 29 | } 30 | 31 | App --> Auth: 1. User authenticates 32 | 33 | App -> Edge: 2. Get Image for word 34 | 35 | Edge -up-> EmbeddingSvc: 3. Convert word to embedding 36 | Edge -> DBSvc: 4. Call DB Vector Match Function 37 | 38 | DBSvc -> DBTable: 5. Run Cosine Similarity Match 39 | 40 | Edge -up-> ImageGen: 5.5 Generate image for a poor match 41 | 42 | App --> Storage: 6. Images retrieved and cached locally 43 | @enduml 44 | -------------------------------------------------------------------------------- /func/supabase/migrations/20240522201932_remote_schema.sql: -------------------------------------------------------------------------------- 1 | set check_function_bodies = off; 2 | 3 | CREATE OR REPLACE FUNCTION public.delete_storage_object(bucket text, object text, OUT status integer, OUT content text) 4 | RETURNS record 5 | LANGUAGE plpgsql 6 | SECURITY DEFINER 7 | AS $function$declare 8 | project_url text := 'https://bpcxexhrudktyrkkekne.supabase.co'; 9 | service_role_key text := 'update-as-needed'; -- full access needed 10 | url text := project_url||'/storage/v1/object/'||bucket||'/'||object; 11 | begin 12 | select 13 | into status, content 14 | result.status::int, result.content::text 15 | FROM extensions.http(( 16 | 'DELETE', 17 | url, 18 | ARRAY[extensions.http_header('authorization','Bearer '||service_role_key)], 19 | NULL, 20 | NULL)::extensions.http_request) as result; 21 | end;$function$ 22 | ; 23 | 24 | create policy "Enable delete for users based on user_id" 25 | on "public"."profiles" 26 | as permissive 27 | for delete 28 | to public 29 | using ((( SELECT auth.uid() AS uid) = id)); 30 | 31 | 32 | CREATE TRIGGER push_notifications AFTER INSERT ON public.notifications FOR EACH ROW EXECUTE FUNCTION supabase_functions.http_request('https://bpcxexhrudktyrkkekne.supabase.co/functions/v1/push', 'POST', '{"Content-type":"application/json"}', '{}', '1000'); 33 | 34 | 35 | -------------------------------------------------------------------------------- /app/open_aac/windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "utils.h" 7 | 8 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 9 | _In_ wchar_t *command_line, _In_ int show_command) { 10 | // Attach to console when present (e.g., 'flutter run') or create a 11 | // new console when running with a debugger. 12 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 13 | CreateAndAttachConsole(); 14 | } 15 | 16 | // Initialize COM, so that it is available for use in the library and/or 17 | // plugins. 18 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 19 | 20 | flutter::DartProject project(L"data"); 21 | 22 | std::vector command_line_arguments = 23 | GetCommandLineArguments(); 24 | 25 | project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); 26 | 27 | FlutterWindow window(project); 28 | Win32Window::Point origin(10, 10); 29 | Win32Window::Size size(1280, 720); 30 | if (!window.Create(L"open_aac", origin, size)) { 31 | return EXIT_FAILURE; 32 | } 33 | window.SetQuitOnClose(true); 34 | 35 | ::MSG msg; 36 | while (::GetMessage(&msg, nullptr, 0, 0)) { 37 | ::TranslateMessage(&msg); 38 | ::DispatchMessage(&msg); 39 | } 40 | 41 | ::CoUninitialize(); 42 | return EXIT_SUCCESS; 43 | } 44 | -------------------------------------------------------------------------------- /db/util/ocr_fix.py: -------------------------------------------------------------------------------- 1 | """ 2 | Using Mac OS shortcuts to generate OCR output (see shortcuts.png), gather the images 3 | in a directory to match the text of the OCR output. 4 | 5 | This python script takes a file directory as input. It will split the contents of the text file 6 | by the | character. The generated array should match text to file name. 7 | By add ".png" to the name, it will rename the image file in the same directory to 8 | the text value, replacing spaces with underscores. 9 | """ 10 | 11 | import os 12 | import glob 13 | 14 | # Get the subdirectory name from the user 15 | subdirectory = input("Enter the subdirectory name: ") 16 | 17 | # Get all txt files in the subdirectory 18 | txt_files = glob.glob(os.path.join(subdirectory, "*.txt")) 19 | 20 | for txt_file in txt_files: 21 | with open(txt_file, 'r') as f: 22 | content = f.read() 23 | content = content.replace("\r","") # strip out carriage returns 24 | content = content.replace("\n","") # strip out carriage returns 25 | tokens = content.split('|') 26 | words = tokens[:len(tokens)//2] 27 | names = tokens[len(tokens)//2:] 28 | 29 | for i in range(len(words)): 30 | image_name = names[i] + ".png" 31 | new_name = words[i].strip().replace(" ", "_") + ".png" 32 | os.rename(os.path.join(subdirectory, image_name), os.path.join(subdirectory, new_name)) 33 | print("Renamed " + image_name + " to " + new_name) -------------------------------------------------------------------------------- /app/open_aac/macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.14' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | target 'RunnerTests' do 35 | inherit! :search_paths 36 | end 37 | end 38 | 39 | post_install do |installer| 40 | installer.pods_project.targets.each do |target| 41 | flutter_additional_macos_build_settings(target) 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /app/open_aac/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '12.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | target 'RunnerTests' do 36 | inherit! :search_paths 37 | end 38 | end 39 | 40 | post_install do |installer| 41 | installer.pods_project.targets.each do |target| 42 | flutter_additional_ios_build_settings(target) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /func/supabase/migrations/20240203010132_remote_schema.sql: -------------------------------------------------------------------------------- 1 | CREATE TRIGGER before_delete_user BEFORE DELETE ON auth.users FOR EACH ROW EXECUTE FUNCTION delete_old_profile(); 2 | 3 | CREATE TRIGGER on_auth_user_created AFTER INSERT ON auth.users FOR EACH ROW EXECUTE FUNCTION handle_new_user(); 4 | 5 | 6 | set check_function_bodies = off; 7 | 8 | CREATE OR REPLACE FUNCTION storage.extension(name text) 9 | RETURNS text 10 | LANGUAGE plpgsql 11 | AS $function$ 12 | DECLARE 13 | _parts text[]; 14 | _filename text; 15 | BEGIN 16 | select string_to_array(name, '/') into _parts; 17 | select _parts[array_length(_parts,1)] into _filename; 18 | -- @todo return the last part instead of 2 19 | return split_part(_filename, '.', 2); 20 | END 21 | $function$ 22 | ; 23 | 24 | CREATE OR REPLACE FUNCTION storage.filename(name text) 25 | RETURNS text 26 | LANGUAGE plpgsql 27 | AS $function$ 28 | DECLARE 29 | _parts text[]; 30 | BEGIN 31 | select string_to_array(name, '/') into _parts; 32 | return _parts[array_length(_parts,1)]; 33 | END 34 | $function$ 35 | ; 36 | 37 | CREATE OR REPLACE FUNCTION storage.foldername(name text) 38 | RETURNS text[] 39 | LANGUAGE plpgsql 40 | AS $function$ 41 | DECLARE 42 | _parts text[]; 43 | BEGIN 44 | select string_to_array(name, '/') into _parts; 45 | return _parts[1:array_length(_parts,1)-1]; 46 | END 47 | $function$ 48 | ; 49 | 50 | create policy "Anyone can upload an avatar." 51 | on "storage"."objects" 52 | as permissive 53 | for insert 54 | to public 55 | with check ((bucket_id = 'avatars'::text)); 56 | 57 | 58 | create policy "Avatar images are publicly accessible." 59 | on "storage"."objects" 60 | as permissive 61 | for select 62 | to public 63 | using ((bucket_id = 'avatars'::text)); 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /app/open_aac/.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: "abb292a07e20d696c4568099f918f6c5f330e6b0" 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: abb292a07e20d696c4568099f918f6c5f330e6b0 17 | base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0 18 | - platform: android 19 | create_revision: abb292a07e20d696c4568099f918f6c5f330e6b0 20 | base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0 21 | - platform: ios 22 | create_revision: abb292a07e20d696c4568099f918f6c5f330e6b0 23 | base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0 24 | - platform: linux 25 | create_revision: abb292a07e20d696c4568099f918f6c5f330e6b0 26 | base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0 27 | - platform: macos 28 | create_revision: abb292a07e20d696c4568099f918f6c5f330e6b0 29 | base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0 30 | - platform: web 31 | create_revision: abb292a07e20d696c4568099f918f6c5f330e6b0 32 | base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0 33 | - platform: windows 34 | create_revision: abb292a07e20d696c4568099f918f6c5f330e6b0 35 | base_revision: abb292a07e20d696c4568099f918f6c5f330e6b0 36 | 37 | # User provided section 38 | 39 | # List of Local paths (relative to this file) that should be 40 | # ignored by the migrate tool. 41 | # 42 | # Files that are not part of the templates will be ignored by default. 43 | unmanaged_files: 44 | - 'lib/main.dart' 45 | - 'ios/Runner.xcodeproj/project.pbxproj' 46 | -------------------------------------------------------------------------------- /app/open_aac/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "version": 1, 4 | "author": "xcode" 5 | }, 6 | "images": [ 7 | { 8 | "size": "16x16", 9 | "idiom": "mac", 10 | "filename": "app_icon_16.png", 11 | "scale": "1x" 12 | }, 13 | { 14 | "size": "16x16", 15 | "idiom": "mac", 16 | "filename": "app_icon_32.png", 17 | "scale": "2x" 18 | }, 19 | { 20 | "size": "32x32", 21 | "idiom": "mac", 22 | "filename": "app_icon_32.png", 23 | "scale": "1x" 24 | }, 25 | { 26 | "size": "32x32", 27 | "idiom": "mac", 28 | "filename": "app_icon_64.png", 29 | "scale": "2x" 30 | }, 31 | { 32 | "size": "128x128", 33 | "idiom": "mac", 34 | "filename": "app_icon_128.png", 35 | "scale": "1x" 36 | }, 37 | { 38 | "size": "128x128", 39 | "idiom": "mac", 40 | "filename": "app_icon_256.png", 41 | "scale": "2x" 42 | }, 43 | { 44 | "size": "256x256", 45 | "idiom": "mac", 46 | "filename": "app_icon_256.png", 47 | "scale": "1x" 48 | }, 49 | { 50 | "size": "256x256", 51 | "idiom": "mac", 52 | "filename": "app_icon_512.png", 53 | "scale": "2x" 54 | }, 55 | { 56 | "size": "512x512", 57 | "idiom": "mac", 58 | "filename": "app_icon_512.png", 59 | "scale": "1x" 60 | }, 61 | { 62 | "size": "512x512", 63 | "idiom": "mac", 64 | "filename": "app_icon_1024.png", 65 | "scale": "2x" 66 | } 67 | ] 68 | } -------------------------------------------------------------------------------- /app/open_aac/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 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/open_aac/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:supabase_flutter/supabase_flutter.dart'; 3 | import 'package:firebase_core/firebase_core.dart'; 4 | import 'firebase_options.dart'; 5 | import 'package:flutter_dotenv/flutter_dotenv.dart'; 6 | 7 | import 'package:openaac/pages/splash_page.dart'; 8 | import 'package:openaac/pages/login_page.dart'; 9 | import 'package:openaac/pages/home_page.dart'; 10 | 11 | 12 | const appTitle = 'Learningo Open AAC'; 13 | 14 | final supabase = Supabase.instance.client; 15 | 16 | void main() async { 17 | await dotenv.load(fileName: ".env"); 18 | 19 | WidgetsFlutterBinding.ensureInitialized(); 20 | 21 | await Firebase.initializeApp( 22 | options: DefaultFirebaseOptions.currentPlatform, 23 | ); 24 | 25 | await Supabase.initialize( 26 | url: dotenv.get('SUPABASE_URL'), 27 | anonKey: dotenv.get('ANON_KEY'), 28 | ); 29 | 30 | runApp(OpenAAC()); 31 | } 32 | 33 | class OpenAAC extends StatelessWidget { 34 | @override 35 | Widget build(BuildContext context) { 36 | return MaterialApp( 37 | title: appTitle, 38 | theme: ThemeData( 39 | useMaterial3: true, 40 | colorScheme: ColorScheme.fromSeed(seedColor: Colors.green), 41 | textButtonTheme: TextButtonThemeData( 42 | style: TextButton.styleFrom( 43 | foregroundColor: Colors.green, 44 | ), 45 | ), 46 | elevatedButtonTheme: ElevatedButtonThemeData( 47 | style: ElevatedButton.styleFrom( 48 | foregroundColor: Colors.white, 49 | backgroundColor: Colors.green, 50 | ), 51 | ), 52 | ), 53 | 54 | initialRoute: '/', 55 | routes: { 56 | '/': (_) => const SplashPage(), 57 | '/login': (_) => const LoginPage(), 58 | '/home': (_) => const HomePage(), 59 | }, 60 | debugShowCheckedModeBanner: false, 61 | ); 62 | } 63 | } 64 | 65 | 66 | -------------------------------------------------------------------------------- /app/open_aac/windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.14) 2 | project(runner LANGUAGES CXX) 3 | 4 | # Define the application target. To change its name, change BINARY_NAME in the 5 | # top-level CMakeLists.txt, not the value here, or `flutter run` will no longer 6 | # work. 7 | # 8 | # Any new source files that you add to the application should be added here. 9 | add_executable(${BINARY_NAME} WIN32 10 | "flutter_window.cpp" 11 | "main.cpp" 12 | "utils.cpp" 13 | "win32_window.cpp" 14 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 15 | "Runner.rc" 16 | "runner.exe.manifest" 17 | ) 18 | 19 | # Apply the standard set of build settings. This can be removed for applications 20 | # that need different build settings. 21 | apply_standard_settings(${BINARY_NAME}) 22 | 23 | # Add preprocessor definitions for the build version. 24 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") 25 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") 26 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") 27 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") 28 | target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") 29 | 30 | # Disable Windows macros that collide with C++ standard library functions. 31 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 32 | 33 | # Add dependency libraries and include directories. Add any application-specific 34 | # dependencies here. 35 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 36 | target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") 37 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 38 | 39 | # Run the Flutter tool portions of the build. This must not be removed. 40 | add_dependencies(${BINARY_NAME} flutter_assemble) 41 | -------------------------------------------------------------------------------- /app/open_aac/windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | 24 | std::vector GetCommandLineArguments() { 25 | // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. 26 | int argc; 27 | wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); 28 | if (argv == nullptr) { 29 | return std::vector(); 30 | } 31 | 32 | std::vector command_line_arguments; 33 | 34 | // Skip the first argument as it's the binary name. 35 | for (int i = 1; i < argc; i++) { 36 | command_line_arguments.push_back(Utf8FromUtf16(argv[i])); 37 | } 38 | 39 | ::LocalFree(argv); 40 | 41 | return command_line_arguments; 42 | } 43 | 44 | std::string Utf8FromUtf16(const wchar_t* utf16_string) { 45 | if (utf16_string == nullptr) { 46 | return std::string(); 47 | } 48 | int target_length = ::WideCharToMultiByte( 49 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 50 | -1, nullptr, 0, nullptr, nullptr) 51 | -1; // remove the trailing null character 52 | int input_length = (int)wcslen(utf16_string); 53 | std::string utf8_string; 54 | if (target_length <= 0 || target_length > utf8_string.max_size()) { 55 | return utf8_string; 56 | } 57 | utf8_string.resize(target_length); 58 | int converted_length = ::WideCharToMultiByte( 59 | CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, 60 | input_length, utf8_string.data(), target_length, nullptr, nullptr); 61 | if (converted_length == 0) { 62 | return std::string(); 63 | } 64 | return utf8_string; 65 | } 66 | -------------------------------------------------------------------------------- /app/open_aac/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | open_aac 33 | 34 | 35 | 39 | 40 | 41 | 42 | 43 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /img/media-pipe.py: -------------------------------------------------------------------------------- 1 | # https://developers.google.com/mediapipe/solutions/vision/image_generator 2 | # Collab Notebook: https://colab.research.google.com/github/GoogleCloudPlatform/vertex-ai-samples/blob/main/notebooks/community/model_garden/model_garden_mediapipe_image_generation.ipynb 3 | import os 4 | from dotenv import load_dotenv 5 | import json 6 | from datetime import datetime 7 | from google.cloud import aiplatform 8 | 9 | load_dotenv() 10 | 11 | print(f"Training images for {os.getenv('PROJECT_ID')}") 12 | 13 | REGION = "us-central1" # @param {type: "string"} 14 | REGION_PREFIX = REGION.split("-")[0] 15 | assert REGION_PREFIX in ( 16 | "us", 17 | "europe", 18 | "asia", 19 | ), f'{REGION} is not supported. It must be prefixed by "us", "asia", or "europe".' 20 | 21 | now = datetime.now().strftime("%Y%m%d-%H%M%S") 22 | 23 | STAGING_BUCKET = os.path.join(os.getenv("BUCKET_URI"), "temp/%s" % now) 24 | 25 | MODEL_EXPORT_PATH = os.path.join(STAGING_BUCKET, "model") 26 | 27 | IMAGE_EXPORT_PATH = os.path.join(STAGING_BUCKET, "image") 28 | 29 | aiplatform.init(project=os.getenv("PROJECT_ID"), location=os.getenv("REGION"), staging_bucket=STAGING_BUCKET) 30 | 31 | TRAINING_JOB_DISPLAY_NAME = "mediapipe_stable_diffusion_%s" % now 32 | TRAINING_CONTAINER = f"{REGION_PREFIX}-docker.pkg.dev/vertex-ai-restricted/vertex-vision-model-garden-dockers/mediapipe-stable-diffusion-train" 33 | TRAINING_MACHINE_TYPE = "a2-highgpu-1g" 34 | TRAINING_ACCELERATOR_TYPE = "NVIDIA_TESLA_A100" 35 | TRAINING_ACCELERATOR_COUNT = 1 36 | 37 | PREDICTION_CONTAINER_URI = f"{REGION_PREFIX}-docker.pkg.dev/vertex-ai/vertex-vision-model-garden-dockers/pytorch-peft-serve" 38 | PREDICTION_PORT = 7080 39 | PREDICTION_ACCELERATOR_TYPE = "NVIDIA_TESLA_V100" 40 | PREDICTION_MACHINE_TYPE = "n1-standard-8" 41 | UPLOAD_MODEL_NAME = "mediapipe_stable_diffusion_model_%s" % now 42 | 43 | unet_url = "https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/unet/diffusion_pytorch_model.bin" # @param {type:"string"} 44 | vae_url = "https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/vae/diffusion_pytorch_model.bin" # @param {type:"string"} 45 | text_encoder_url = "https://huggingface.co/runwayml/stable-diffusion-v1-5/resolve/main/text_encoder/pytorch_model.bin" # @param {type:"string"} 46 | 47 | -------------------------------------------------------------------------------- /db/cli/bin/cli.dart: -------------------------------------------------------------------------------- 1 | import 'package:cli/cli.dart' as cli; 2 | import 'package:args/args.dart' show ArgParser; 3 | 4 | const String pineconePath = 'pinecone_path'; 5 | const String testPinecone = 'test_pinecone'; 6 | const String supabasePath = 'supabase_path'; 7 | const String testSupabase = 'test_supabase'; 8 | const String supabaseS3 = 'supabase_S3'; 9 | const String testImages = 'test_images'; 10 | 11 | // The main() function is the entry point of the application 12 | // Run the application with: dart bin/cli.dart 13 | // Process adapted from Python and this tutorial: https://docs.pinecone.io/docs/langchain 14 | void main(List args) async { 15 | var parser = ArgParser(); 16 | parser.addOption(pineconePath, abbr: 'p', help: 'Path to images to upload to Pinecone'); 17 | parser.addFlag(testPinecone, abbr: 'i', help: 'Input strings to test Pinecone embeddings'); 18 | parser.addOption(supabasePath, abbr: 's', help: 'Path to images to upload to Supabase'); 19 | parser.addFlag(testSupabase, abbr: 'u', help: 'Input strings to test Supabase embeddings'); 20 | parser.addOption(supabaseS3, abbr: 't', help: 'Path to images to upload to Supabase Storage'); 21 | parser.addFlag(testImages, abbr: 'g', help: 'Input strings to test image generation'); 22 | 23 | var inputs = parser.parse(args); 24 | 25 | if (inputs[pineconePath] != null) { 26 | print('Loading images to Pinecone from folder: ${inputs[pineconePath]}'); 27 | cli.loadPineconeImages(inputs[pineconePath]); 28 | } else if (inputs[supabasePath] != null) { 29 | print('Loading images to Supabase from folder: ${inputs[supabasePath]}'); 30 | cli.loadSupabaseImages(inputs[supabasePath]); 31 | } else if (inputs[testPinecone] == true) { 32 | print('Testing Pinecone embeddings'); 33 | cli.runTextTest(true); 34 | } else if (inputs[testSupabase] == true) { 35 | print('Testing Supabase embeddings'); 36 | cli.runTextTest(false); 37 | } else if (inputs[supabaseS3] != null) { 38 | print('Loading images to Supabase Storage from folder: ${inputs[supabaseS3]}'); 39 | cli.loadSupabaseStorage(inputs[supabaseS3]); 40 | } else if (inputs[testImages] == true) { 41 | print('Testing Image Generation'); 42 | cli.runImageTest(false); 43 | } else { 44 | print('Utility to load and test images in an embeddings database'); 45 | print(parser.usage); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /app/open_aac/windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(const flutter::DartProject& project) 8 | : project_(project) {} 9 | 10 | FlutterWindow::~FlutterWindow() {} 11 | 12 | bool FlutterWindow::OnCreate() { 13 | if (!Win32Window::OnCreate()) { 14 | return false; 15 | } 16 | 17 | RECT frame = GetClientArea(); 18 | 19 | // The size here must match the window dimensions to avoid unnecessary surface 20 | // creation / destruction in the startup path. 21 | flutter_controller_ = std::make_unique( 22 | frame.right - frame.left, frame.bottom - frame.top, project_); 23 | // Ensure that basic setup of the controller was successful. 24 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 25 | return false; 26 | } 27 | RegisterPlugins(flutter_controller_->engine()); 28 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 29 | 30 | flutter_controller_->engine()->SetNextFrameCallback([&]() { 31 | this->Show(); 32 | }); 33 | 34 | // Flutter can complete the first frame before the "show window" callback is 35 | // registered. The following call ensures a frame is pending to ensure the 36 | // window is shown. It is a no-op if the first frame hasn't completed yet. 37 | flutter_controller_->ForceRedraw(); 38 | 39 | return true; 40 | } 41 | 42 | void FlutterWindow::OnDestroy() { 43 | if (flutter_controller_) { 44 | flutter_controller_ = nullptr; 45 | } 46 | 47 | Win32Window::OnDestroy(); 48 | } 49 | 50 | LRESULT 51 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 52 | WPARAM const wparam, 53 | LPARAM const lparam) noexcept { 54 | // Give Flutter, including plugins, an opportunity to handle window messages. 55 | if (flutter_controller_) { 56 | std::optional result = 57 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 58 | lparam); 59 | if (result) { 60 | return *result; 61 | } 62 | } 63 | 64 | switch (message) { 65 | case WM_FONTCHANGE: 66 | flutter_controller_->engine()->ReloadSystemFonts(); 67 | break; 68 | } 69 | 70 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 71 | } 72 | -------------------------------------------------------------------------------- /func/supabase/functions/push/index.ts: -------------------------------------------------------------------------------- 1 | import { createClient } from 'npm:@supabase/supabase-js@2' 2 | import { JWT } from 'npm:google-auth-library@9' 3 | import serviceAccount from '../service-account.json' with { type: 'json' } 4 | 5 | interface Notification { 6 | id: string 7 | user_id: string 8 | body: string 9 | } 10 | 11 | interface WebhookPayload { 12 | type: 'INSERT' 13 | table: string 14 | record: Notification 15 | schema: 'public' 16 | } 17 | 18 | const supabase = createClient( 19 | Deno.env.get('SUPABASE_URL')!, 20 | Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')! 21 | ) 22 | 23 | Deno.serve(async (req) => { 24 | const payload: WebhookPayload = await req.json() 25 | 26 | const { data } = await supabase 27 | .from('profiles') 28 | .select('fcm_token') 29 | .eq('id', payload.record.user_id) 30 | .single() 31 | 32 | const fcmToken = data!.fcm_token as string 33 | 34 | const accessToken = await getAccessToken({ 35 | clientEmail: serviceAccount.client_email, 36 | privateKey: serviceAccount.private_key, 37 | }) 38 | 39 | const res = await fetch( 40 | `https://fcm.googleapis.com/v1/projects/${serviceAccount.project_id}/messages:send`, 41 | { 42 | method: 'POST', 43 | headers: { 44 | 'Content-Type': 'application/json', 45 | Authorization: `Bearer ${accessToken}`, 46 | }, 47 | body: JSON.stringify({ 48 | message: { 49 | token: fcmToken, 50 | notification: { 51 | title: `Notification from Supabase`, 52 | body: payload.record.body, 53 | }, 54 | }, 55 | }), 56 | } 57 | ) 58 | 59 | const resData = await res.json() 60 | if (res.status < 200 || 299 < res.status) { 61 | throw resData 62 | } 63 | 64 | return new Response(JSON.stringify(resData), { 65 | headers: { 'Content-Type': 'application/json' }, 66 | }) 67 | }) 68 | 69 | const getAccessToken = ({ 70 | clientEmail, 71 | privateKey, 72 | }: { 73 | clientEmail: string 74 | privateKey: string 75 | }): Promise => { 76 | return new Promise((resolve, reject) => { 77 | const jwtClient = new JWT({ 78 | email: clientEmail, 79 | key: privateKey, 80 | scopes: ['https://www.googleapis.com/auth/firebase.messaging'], 81 | }) 82 | jwtClient.authorize((err, tokens) => { 83 | if (err) { 84 | reject(err) 85 | return 86 | } 87 | resolve(tokens!.access_token!) 88 | }) 89 | }) 90 | } 91 | -------------------------------------------------------------------------------- /app/open_aac/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 | -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CADisableMinimumFrameDurationOnPhone 6 | 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleDisplayName 10 | Open Aac 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | open_aac 19 | CFBundlePackageType 20 | APPL 21 | CFBundleShortVersionString 22 | $(FLUTTER_BUILD_NAME) 23 | CFBundleSignature 24 | ???? 25 | CFBundleURLTypes 26 | 27 | 28 | CFBundleTypeRole 29 | Editor 30 | CFBundleURLSchemes 31 | 32 | io.supabase.flutterquickstart 33 | 34 | 35 | 36 | CFBundleVersion 37 | $(FLUTTER_BUILD_NUMBER) 38 | LSApplicationCategoryType 39 | 40 | LSRequiresIPhoneOS 41 | 42 | NSPhotoLibraryAddUsageDescription 43 | Open AAC Requires Access to Photos for Avatar Image Upload 44 | NSPhotoLibraryUsageDescription 45 | Open AAC Requires Access to Photos for Avatar Image Upload 46 | UIApplicationSupportsIndirectInputEvents 47 | 48 | UIBackgroundModes 49 | 50 | fetch 51 | remote-notification 52 | 53 | UILaunchStoryboardName 54 | LaunchScreen 55 | UIMainStoryboardFile 56 | Main 57 | UISupportedInterfaceOrientations 58 | 59 | UIInterfaceOrientationPortrait 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | UISupportedInterfaceOrientations~ipad 64 | 65 | UIInterfaceOrientationPortrait 66 | UIInterfaceOrientationPortraitUpsideDown 67 | UIInterfaceOrientationLandscapeLeft 68 | UIInterfaceOrientationLandscapeRight 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /func/supabase/migrations/20240203005801_remote_schema.sql: -------------------------------------------------------------------------------- 1 | create extension if not exists "http" with schema "extensions"; 2 | 3 | 4 | set check_function_bodies = off; 5 | 6 | CREATE OR REPLACE FUNCTION public.delete_avatar(avatar_url text, OUT status integer, OUT content text) 7 | RETURNS record 8 | LANGUAGE plpgsql 9 | SECURITY DEFINER 10 | AS $function$ 11 | begin 12 | select 13 | into status, content 14 | result.status, result.content 15 | from public.delete_storage_object('avatars', avatar_url) as result; 16 | end; 17 | $function$ 18 | ; 19 | 20 | CREATE OR REPLACE FUNCTION public.delete_old_avatar() 21 | RETURNS trigger 22 | LANGUAGE plpgsql 23 | SECURITY DEFINER 24 | AS $function$ 25 | declare 26 | status int; 27 | content text; 28 | avatar_name text; 29 | begin 30 | if coalesce(old.avatar_url, '') <> '' 31 | and (tg_op = 'DELETE' or (old.avatar_url <> coalesce(new.avatar_url, ''))) then 32 | -- extract avatar name 33 | avatar_name := old.avatar_url; 34 | select 35 | into status, content 36 | result.status, result.content 37 | from public.delete_avatar(avatar_name) as result; 38 | if status <> 200 then 39 | raise warning 'Could not delete avatar: % %', status, content; 40 | end if; 41 | end if; 42 | if tg_op = 'DELETE' then 43 | return old; 44 | end if; 45 | return new; 46 | end; 47 | $function$ 48 | ; 49 | 50 | CREATE OR REPLACE FUNCTION public.delete_old_profile() 51 | RETURNS trigger 52 | LANGUAGE plpgsql 53 | SECURITY DEFINER 54 | AS $function$ 55 | begin 56 | delete from public.profiles where id = old.id; 57 | return old; 58 | end; 59 | $function$ 60 | ; 61 | 62 | CREATE OR REPLACE FUNCTION public.delete_storage_object(bucket text, object text, OUT status integer, OUT content text) 63 | RETURNS record 64 | LANGUAGE plpgsql 65 | SECURITY DEFINER 66 | AS $function$ 67 | declare 68 | project_url text := ''; 69 | service_role_key text := ''; -- full access needed 70 | url text := project_url||'/storage/v1/object/'||bucket||'/'||object; 71 | begin 72 | select 73 | into status, content 74 | result.status::int, result.content::text 75 | FROM extensions.http(( 76 | 'DELETE', 77 | url, 78 | ARRAY[extensions.http_header('authorization','Bearer '||service_role_key)], 79 | NULL, 80 | NULL)::extensions.http_request) as result; 81 | end; 82 | $function$ 83 | ; 84 | 85 | CREATE TRIGGER before_profile_changes BEFORE DELETE OR UPDATE OF avatar_url ON public.profiles FOR EACH ROW EXECUTE FUNCTION delete_old_avatar(); 86 | 87 | 88 | -------------------------------------------------------------------------------- /app/open_aac/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 20 | 24 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 41 | 42 | 43 | 45 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /app/open_aac/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id "com.android.application" 3 | id "kotlin-android" 4 | id "dev.flutter.flutter-gradle-plugin" 5 | } 6 | 7 | def localProperties = new Properties() 8 | def localPropertiesFile = rootProject.file('local.properties') 9 | if (localPropertiesFile.exists()) { 10 | localPropertiesFile.withReader('UTF-8') { reader -> 11 | localProperties.load(reader) 12 | } 13 | } 14 | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 16 | if (flutterVersionCode == null) { 17 | flutterVersionCode = '1' 18 | } 19 | 20 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 21 | if (flutterVersionName == null) { 22 | flutterVersionName = '1.0' 23 | } 24 | 25 | def keystoreProperties = new Properties() 26 | def keystorePropertiesFile = rootProject.file('key.properties') 27 | if (keystorePropertiesFile.exists()) { 28 | keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) 29 | } 30 | 31 | android { 32 | namespace "org.learningo.openaac" 33 | compileSdkVersion flutter.compileSdkVersion 34 | ndkVersion flutter.ndkVersion 35 | 36 | compileOptions { 37 | sourceCompatibility JavaVersion.VERSION_1_8 38 | targetCompatibility JavaVersion.VERSION_1_8 39 | } 40 | 41 | kotlinOptions { 42 | jvmTarget = '1.8' 43 | } 44 | 45 | sourceSets { 46 | main.java.srcDirs += 'src/main/kotlin' 47 | } 48 | 49 | defaultConfig { 50 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 51 | applicationId "org.learningo.openaac" 52 | // You can update the following values to match your application needs. 53 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration. 54 | minSdkVersion 21 55 | targetSdkVersion 33 56 | versionCode flutterVersionCode.toInteger() 57 | versionName flutterVersionName 58 | } 59 | 60 | signingConfigs { 61 | release { 62 | keyAlias keystoreProperties['keyAlias'] 63 | keyPassword keystoreProperties['keyPassword'] 64 | storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null 65 | storePassword keystoreProperties['storePassword'] 66 | } 67 | } 68 | buildTypes { 69 | release { 70 | signingConfig signingConfigs.release 71 | } 72 | } 73 | } 74 | 75 | flutter { 76 | source '../..' 77 | } 78 | 79 | dependencies {} 80 | -------------------------------------------------------------------------------- /func/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | lerna-debug.log* 8 | .pnpm-debug.log* 9 | 10 | # Diagnostic reports (https://nodejs.org/api/report.html) 11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json 12 | 13 | # Runtime data 14 | pids 15 | *.pid 16 | *.seed 17 | *.pid.lock 18 | 19 | # Directory for instrumented libs generated by jscoverage/JSCover 20 | lib-cov 21 | 22 | # Coverage directory used by tools like istanbul 23 | coverage 24 | *.lcov 25 | 26 | # nyc test coverage 27 | .nyc_output 28 | 29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files) 30 | .grunt 31 | 32 | # Bower dependency directory (https://bower.io/) 33 | bower_components 34 | 35 | # node-waf configuration 36 | .lock-wscript 37 | 38 | # Compiled binary addons (https://nodejs.org/api/addons.html) 39 | build/Release 40 | 41 | # Dependency directories 42 | node_modules/ 43 | jspm_packages/ 44 | 45 | # Snowpack dependency directory (https://snowpack.dev/) 46 | web_modules/ 47 | 48 | # TypeScript cache 49 | *.tsbuildinfo 50 | 51 | # Optional npm cache directory 52 | .npm 53 | 54 | # Optional eslint cache 55 | .eslintcache 56 | 57 | # Optional stylelint cache 58 | .stylelintcache 59 | 60 | # Microbundle cache 61 | .rpt2_cache/ 62 | .rts2_cache_cjs/ 63 | .rts2_cache_es/ 64 | .rts2_cache_umd/ 65 | 66 | # Optional REPL history 67 | .node_repl_history 68 | 69 | # Output of 'npm pack' 70 | *.tgz 71 | 72 | # Yarn Integrity file 73 | .yarn-integrity 74 | 75 | # dotenv environment variable files 76 | .env 77 | .env.development.local 78 | .env.test.local 79 | .env.production.local 80 | .env.local 81 | 82 | # parcel-bundler cache (https://parceljs.org/) 83 | .cache 84 | .parcel-cache 85 | 86 | # Next.js build output 87 | .next 88 | out 89 | 90 | # Nuxt.js build / generate output 91 | .nuxt 92 | dist 93 | 94 | # Gatsby files 95 | .cache/ 96 | # Comment in the public line in if your project uses Gatsby and not Next.js 97 | # https://nextjs.org/blog/next-9-1#public-directory-support 98 | # public 99 | 100 | # vuepress build output 101 | .vuepress/dist 102 | 103 | # vuepress v2.x temp and cache directory 104 | .temp 105 | .cache 106 | 107 | # Docusaurus cache and generated files 108 | .docusaurus 109 | 110 | # Serverless directories 111 | .serverless/ 112 | 113 | # FuseBox cache 114 | .fusebox/ 115 | 116 | # DynamoDB Local files 117 | .dynamodb/ 118 | 119 | # TernJS port file 120 | .tern-port 121 | 122 | # Stores VSCode versions used for testing VSCode extensions 123 | .vscode-test 124 | 125 | # yarn v2 126 | .yarn/cache 127 | .yarn/unplugged 128 | .yarn/build-state.yml 129 | .yarn/install-state.gz 130 | .pnp.* 131 | 132 | supabase/functions/service-account.json 133 | -------------------------------------------------------------------------------- /app/open_aac/lib/firebase_options.dart: -------------------------------------------------------------------------------- 1 | // File generated by FlutterFire CLI. 2 | // ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members 3 | import 'package:firebase_core/firebase_core.dart' show FirebaseOptions; 4 | import 'package:flutter/foundation.dart' 5 | show defaultTargetPlatform, kIsWeb, TargetPlatform; 6 | 7 | /// Default [FirebaseOptions] for use with your Firebase apps. 8 | /// 9 | /// Example: 10 | /// ```dart 11 | /// import 'firebase_options.dart'; 12 | /// // ... 13 | /// await Firebase.initializeApp( 14 | /// options: DefaultFirebaseOptions.currentPlatform, 15 | /// ); 16 | /// ``` 17 | class DefaultFirebaseOptions { 18 | static FirebaseOptions get currentPlatform { 19 | if (kIsWeb) { 20 | throw UnsupportedError( 21 | 'DefaultFirebaseOptions have not been configured for web - ' 22 | 'you can reconfigure this by running the FlutterFire CLI again.', 23 | ); 24 | } 25 | switch (defaultTargetPlatform) { 26 | case TargetPlatform.android: 27 | return android; 28 | case TargetPlatform.iOS: 29 | return ios; 30 | case TargetPlatform.macOS: 31 | throw UnsupportedError( 32 | 'DefaultFirebaseOptions have not been configured for macos - ' 33 | 'you can reconfigure this by running the FlutterFire CLI again.', 34 | ); 35 | case TargetPlatform.windows: 36 | throw UnsupportedError( 37 | 'DefaultFirebaseOptions have not been configured for windows - ' 38 | 'you can reconfigure this by running the FlutterFire CLI again.', 39 | ); 40 | case TargetPlatform.linux: 41 | throw UnsupportedError( 42 | 'DefaultFirebaseOptions have not been configured for linux - ' 43 | 'you can reconfigure this by running the FlutterFire CLI again.', 44 | ); 45 | default: 46 | throw UnsupportedError( 47 | 'DefaultFirebaseOptions are not supported for this platform.', 48 | ); 49 | } 50 | } 51 | 52 | static const FirebaseOptions android = FirebaseOptions( 53 | apiKey: 'AIzaSyDF1KqVWvak5Pjy4BUM5S3kAPqokfNx92M', 54 | appId: '1:61771573616:android:7c950187a53fe86abb87b5', 55 | messagingSenderId: '61771573616', 56 | projectId: 'openaac-411413', 57 | storageBucket: 'openaac-411413.appspot.com', 58 | ); 59 | 60 | static const FirebaseOptions ios = FirebaseOptions( 61 | apiKey: 'AIzaSyBZuxmM9ZpsVDPU2X9Tz5I2A91f8AzByRg', 62 | appId: '1:61771573616:ios:9ab31991da19d56cbb87b5', 63 | messagingSenderId: '61771573616', 64 | projectId: 'openaac-411413', 65 | storageBucket: 'openaac-411413.appspot.com', 66 | androidClientId: '61771573616-5a2gooulsqpo5c6rt20m36anar605sb2.apps.googleusercontent.com', 67 | iosBundleId: 'org.learningo.openaac', 68 | ); 69 | } 70 | -------------------------------------------------------------------------------- /app/open_aac/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Icon-20@2x 1.png", 5 | "idiom" : "iphone", 6 | "scale" : "2x", 7 | "size" : "20x20" 8 | }, 9 | { 10 | "filename" : "Icon-20@3x.png", 11 | "idiom" : "iphone", 12 | "scale" : "3x", 13 | "size" : "20x20" 14 | }, 15 | { 16 | "filename" : "Icon-29.png", 17 | "idiom" : "iphone", 18 | "scale" : "1x", 19 | "size" : "29x29" 20 | }, 21 | { 22 | "filename" : "Icon-29@2x.png", 23 | "idiom" : "iphone", 24 | "scale" : "2x", 25 | "size" : "29x29" 26 | }, 27 | { 28 | "filename" : "Icon-29@3x.png", 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "29x29" 32 | }, 33 | { 34 | "filename" : "Icon-40@2x.png", 35 | "idiom" : "iphone", 36 | "scale" : "2x", 37 | "size" : "40x40" 38 | }, 39 | { 40 | "filename" : "Icon-40@3x.png", 41 | "idiom" : "iphone", 42 | "scale" : "3x", 43 | "size" : "40x40" 44 | }, 45 | { 46 | "filename" : "Icon-60@2x.png", 47 | "idiom" : "iphone", 48 | "scale" : "2x", 49 | "size" : "60x60" 50 | }, 51 | { 52 | "filename" : "Icon-60@3x.png", 53 | "idiom" : "iphone", 54 | "scale" : "3x", 55 | "size" : "60x60" 56 | }, 57 | { 58 | "filename" : "Icon-20.png", 59 | "idiom" : "ipad", 60 | "scale" : "1x", 61 | "size" : "20x20" 62 | }, 63 | { 64 | "filename" : "Icon-20@2x.png", 65 | "idiom" : "ipad", 66 | "scale" : "2x", 67 | "size" : "20x20" 68 | }, 69 | { 70 | "filename" : "Icon-29 1.png", 71 | "idiom" : "ipad", 72 | "scale" : "1x", 73 | "size" : "29x29" 74 | }, 75 | { 76 | "filename" : "Icon-29@2x 1.png", 77 | "idiom" : "ipad", 78 | "scale" : "2x", 79 | "size" : "29x29" 80 | }, 81 | { 82 | "filename" : "Icon-40.png", 83 | "idiom" : "ipad", 84 | "scale" : "1x", 85 | "size" : "40x40" 86 | }, 87 | { 88 | "filename" : "Icon-40@2x 1.png", 89 | "idiom" : "ipad", 90 | "scale" : "2x", 91 | "size" : "40x40" 92 | }, 93 | { 94 | "filename" : "Icon-76.png", 95 | "idiom" : "ipad", 96 | "scale" : "1x", 97 | "size" : "76x76" 98 | }, 99 | { 100 | "filename" : "Icon-76@2x.png", 101 | "idiom" : "ipad", 102 | "scale" : "2x", 103 | "size" : "76x76" 104 | }, 105 | { 106 | "filename" : "Icon-83.5@2x.png", 107 | "idiom" : "ipad", 108 | "scale" : "2x", 109 | "size" : "83.5x83.5" 110 | }, 111 | { 112 | "filename" : "iTunesArtwork-1024.png", 113 | "idiom" : "ios-marketing", 114 | "scale" : "1x", 115 | "size" : "1024x1024" 116 | } 117 | ], 118 | "info" : { 119 | "author" : "xcode", 120 | "version" : 1 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /app/open_aac/lib/components/avatar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:image_picker/image_picker.dart'; 3 | import 'package:supabase_flutter/supabase_flutter.dart'; 4 | import 'package:openaac/main.dart'; 5 | 6 | class Avatar extends StatefulWidget { 7 | const Avatar({ 8 | super.key, 9 | required this.imageUrl, 10 | required this.onUpload, 11 | }); 12 | 13 | final String? imageUrl; 14 | final void Function(String) onUpload; 15 | 16 | @override 17 | _AvatarState createState() => _AvatarState(); 18 | } 19 | 20 | class _AvatarState extends State { 21 | bool _isLoading = false; 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return Column( 26 | children: [ 27 | if (widget.imageUrl == null || widget.imageUrl!.isEmpty) 28 | Container( 29 | width: 150, 30 | height: 150, 31 | color: Colors.grey, 32 | child: const Center( 33 | child: Text('No Image'), 34 | ), 35 | ) 36 | else 37 | Image.network( 38 | widget.imageUrl!, 39 | width: 150, 40 | height: 150, 41 | fit: BoxFit.cover, 42 | ), 43 | ElevatedButton( 44 | onPressed: _isLoading ? null : _upload, 45 | child: const Text('Upload'), 46 | ), 47 | ], 48 | ); 49 | } 50 | 51 | Future _upload() async { 52 | final picker = ImagePicker(); 53 | final imageFile = await picker.pickImage( 54 | source: ImageSource.gallery, 55 | maxWidth: 300, 56 | maxHeight: 300, 57 | ); 58 | if (imageFile == null) { 59 | return; 60 | } 61 | setState(() => _isLoading = true); 62 | 63 | try { 64 | final bytes = await imageFile.readAsBytes(); 65 | final fileExt = imageFile.path.split('.').last; 66 | final fileName = '${DateTime.now().toIso8601String()}.$fileExt'; 67 | final filePath = fileName; 68 | await supabase.storage.from('avatars').uploadBinary( 69 | filePath, 70 | bytes, 71 | fileOptions: FileOptions(contentType: imageFile.mimeType), 72 | ); 73 | final imageUrlResponse = await supabase.storage 74 | .from('avatars') 75 | .createSignedUrl(filePath, 60 * 60 * 24 * 365 * 10); 76 | widget.onUpload(imageUrlResponse); 77 | } on StorageException catch (error) { 78 | if (mounted) { 79 | ScaffoldMessenger.of(context).showSnackBar( 80 | SnackBar( 81 | content: Text(error.message), 82 | backgroundColor: Theme.of(context).colorScheme.error, 83 | ), 84 | ); 85 | } 86 | } catch (error) { 87 | if (mounted) { 88 | ScaffoldMessenger.of(context).showSnackBar( 89 | SnackBar( 90 | content: const Text('Unexpected error occurred'), 91 | backgroundColor: Theme.of(context).colorScheme.error, 92 | ), 93 | ); 94 | } 95 | } 96 | 97 | setState(() => _isLoading = false); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /func/supabase/migrations/20240130002911_remote_schema.sql: -------------------------------------------------------------------------------- 1 | create table "public"."profiles" ( 2 | "id" uuid not null, 3 | "updated_at" timestamp with time zone, 4 | "username" text, 5 | "full_name" text, 6 | "avatar_url" text, 7 | "website" text 8 | ); 9 | 10 | 11 | alter table "public"."profiles" enable row level security; 12 | 13 | CREATE UNIQUE INDEX profiles_pkey ON public.profiles USING btree (id); 14 | 15 | CREATE UNIQUE INDEX profiles_username_key ON public.profiles USING btree (username); 16 | 17 | alter table "public"."profiles" add constraint "profiles_pkey" PRIMARY KEY using index "profiles_pkey"; 18 | 19 | alter table "public"."profiles" add constraint "profiles_id_fkey" FOREIGN KEY (id) REFERENCES auth.users(id) ON DELETE CASCADE not valid; 20 | 21 | alter table "public"."profiles" validate constraint "profiles_id_fkey"; 22 | 23 | alter table "public"."profiles" add constraint "profiles_username_key" UNIQUE using index "profiles_username_key"; 24 | 25 | alter table "public"."profiles" add constraint "username_length" CHECK ((char_length(username) >= 3)) not valid; 26 | 27 | alter table "public"."profiles" validate constraint "username_length"; 28 | 29 | set check_function_bodies = off; 30 | 31 | CREATE OR REPLACE FUNCTION public.handle_new_user() 32 | RETURNS trigger 33 | LANGUAGE plpgsql 34 | SECURITY DEFINER 35 | AS $function$ 36 | begin 37 | insert into public.profiles (id, full_name, avatar_url) 38 | values (new.id, new.raw_user_meta_data->>'full_name', new.raw_user_meta_data->>'avatar_url'); 39 | return new; 40 | end; 41 | $function$ 42 | ; 43 | 44 | grant delete on table "public"."profiles" to "anon"; 45 | 46 | grant insert on table "public"."profiles" to "anon"; 47 | 48 | grant references on table "public"."profiles" to "anon"; 49 | 50 | grant select on table "public"."profiles" to "anon"; 51 | 52 | grant trigger on table "public"."profiles" to "anon"; 53 | 54 | grant truncate on table "public"."profiles" to "anon"; 55 | 56 | grant update on table "public"."profiles" to "anon"; 57 | 58 | grant delete on table "public"."profiles" to "authenticated"; 59 | 60 | grant insert on table "public"."profiles" to "authenticated"; 61 | 62 | grant references on table "public"."profiles" to "authenticated"; 63 | 64 | grant select on table "public"."profiles" to "authenticated"; 65 | 66 | grant trigger on table "public"."profiles" to "authenticated"; 67 | 68 | grant truncate on table "public"."profiles" to "authenticated"; 69 | 70 | grant update on table "public"."profiles" to "authenticated"; 71 | 72 | grant delete on table "public"."profiles" to "service_role"; 73 | 74 | grant insert on table "public"."profiles" to "service_role"; 75 | 76 | grant references on table "public"."profiles" to "service_role"; 77 | 78 | grant select on table "public"."profiles" to "service_role"; 79 | 80 | grant trigger on table "public"."profiles" to "service_role"; 81 | 82 | grant truncate on table "public"."profiles" to "service_role"; 83 | 84 | grant update on table "public"."profiles" to "service_role"; 85 | 86 | create policy "Public profiles are viewable by everyone." 87 | on "public"."profiles" 88 | as permissive 89 | for select 90 | to public 91 | using (true); 92 | 93 | 94 | create policy "Users can insert their own profile." 95 | on "public"."profiles" 96 | as permissive 97 | for insert 98 | to public 99 | with check ((auth.uid() = id)); 100 | 101 | 102 | create policy "Users can update own profile." 103 | on "public"."profiles" 104 | as permissive 105 | for update 106 | to public 107 | using ((auth.uid() = id)); 108 | 109 | 110 | 111 | -------------------------------------------------------------------------------- /app/open_aac/windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) 64 | #define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0,0 67 | #endif 68 | 69 | #if defined(FLUTTER_VERSION) 70 | #define VERSION_AS_STRING FLUTTER_VERSION 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "org.learningo" "\0" 93 | VALUE "FileDescription", "open_aac" "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "open_aac" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2024 Learningo. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "open_aac.exe" "\0" 98 | VALUE "ProductName", "open_aac" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /db/README.md: -------------------------------------------------------------------------------- 1 | # OpenAAC DB 2 | 3 | OpenAAC uses a hosted vector database to store and retrieve the vector embeddings of the symbols. 4 | The database is hosted on Pinecone or Supabase and is accessible via the associated dart package. 5 | 6 | ## Setup 7 | You will need AAC symbols in the form of images to upload to the database. Each tile needs to be titled with the word 8 | it depicts followed by the image extension. I.e. `eye.png` where it is an eye icon. 9 | 10 | If you have a set of images arranged in grids, you can use the [imageMagick](https://imagemagick.org/index.php) `convert` and `mogrify` commands to split the images into tiles. 11 | 12 | If you need to remove the top 308 pixels from the image, you can use the following command on all the screenshots in the current directory: 13 | 14 | ```bash 15 | mogrify *.png -crop 2160x1312+0+308 16 | ``` 17 | 18 | For example, with an image that is 2160 × 1312 pixels and is a grid of 19 | 15 x 8 tiles, you can use the following shell script to split all the images into tiles, 20 | each in their own subdirectory: 21 | 22 | ```bash 23 | #!/bin/bash 24 | 25 | for file in *.png 26 | do 27 | # Remove the file extension 28 | base_name=$(basename "$file" .png) 29 | 30 | # Create a subdirectory and copy the file into it 31 | mkdir "$base_name" 32 | cp "$file" "$base_name" 33 | 34 | # Change into the subdirectory and run the ImageMagick command 35 | cd "$base_name" 36 | convert "$file" -crop 15x8@ +repage output%02d.png 37 | 38 | # Change back to the parent directory 39 | cd .. 40 | done 41 | ``` 42 | 43 | In each image's subdirectory, this will create 150 tiles named `output00.png` to `output149.png`. You can then rename them to the correct name. 44 | 45 | The files must be named in the format `word.png` where `word` is the word the tile depicts. Make sure they are all contained in a single folder, for example `\images`. 46 | 47 | ## Upload 48 | To upload the images to the database, you can use the `cli.dart` script. You will need to have an account with 49 | Pinecone or Supabase and OpenAI. Run the following command to upload the images: 50 | 51 | ```bash 52 | dart bin/cli.dart --pinecone_path=images 53 | ``` 54 | 55 | ## Supabase 56 | Inspiration for database and query creation is [here](https://supabase.com/blog/openai-embeddings-postgres-vector). 57 | 58 | ### Table 59 | ```sql 60 | create extension vector; 61 | ``` 62 | 63 | ```sql 64 | create table s4y_images ( 65 | id bigserial primary key, 66 | content text, 67 | path text, 68 | embedding vector(1536) 69 | ); 70 | ``` 71 | 72 | ### Function 73 | ```sql 74 | create or replace function match_images ( 75 | query_embedding vector(1536), 76 | match_threshold float, 77 | match_count int 78 | ) 79 | returns table ( 80 | id bigint, 81 | content text, 82 | path text, 83 | similarity float 84 | ) 85 | language sql stable 86 | as $$ 87 | select 88 | s4y_images.id, 89 | s4y_images.content, 90 | s4y_images.path, 91 | 1 - (s4y_images.embedding <=> query_embedding) as similarity 92 | from s4y_images 93 | where s4y_images.embedding <=> query_embedding < 1 - match_threshold 94 | order by s4y_images.embedding <=> query_embedding 95 | limit match_count; 96 | $$; 97 | ``` 98 | ### Index 99 | Recommended to add index after majority of images are uploaded 100 | ```sql 101 | create index on s4y_images using ivfflat (embedding vector_cosine_ops) 102 | with 103 | (lists = 100); 104 | 105 | ``` 106 | 107 | ## Test 108 | To test the embeddings, you can use the `test.dart` script as follows: 109 | 110 | ```bash 111 | dart bin/cli.dart -t 112 | ``` 113 | 114 | This will split your query into words and search for the embeddings of each word. It will then return the top result for each word. 115 | 116 | ### TODO 117 | - Use an LLM to search for the embeddings as described in [this article](https://docs.pinecone.io/docs/langchain#creating-a-vector-store-and-querying) -------------------------------------------------------------------------------- /app/open_aac/windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates a win32 window with |title| that is positioned and sized using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size this function will scale the inputted width and height as 35 | // as appropriate for the default monitor. The window is invisible until 36 | // |Show| is called. Returns true if the window was created successfully. 37 | bool Create(const std::wstring& title, const Point& origin, const Size& size); 38 | 39 | // Show the current window. Returns true if the window was successfully shown. 40 | bool Show(); 41 | 42 | // Release OS resources associated with window. 43 | void Destroy(); 44 | 45 | // Inserts |content| into the window tree. 46 | void SetChildContent(HWND content); 47 | 48 | // Returns the backing Window handle to enable clients to set icon and other 49 | // window properties. Returns nullptr if the window has been destroyed. 50 | HWND GetHandle(); 51 | 52 | // If true, closing this window will quit the application. 53 | void SetQuitOnClose(bool quit_on_close); 54 | 55 | // Return a RECT representing the bounds of the current client area. 56 | RECT GetClientArea(); 57 | 58 | protected: 59 | // Processes and route salient window messages for mouse handling, 60 | // size change and DPI. Delegates handling of these to member overloads that 61 | // inheriting classes can handle. 62 | virtual LRESULT MessageHandler(HWND window, 63 | UINT const message, 64 | WPARAM const wparam, 65 | LPARAM const lparam) noexcept; 66 | 67 | // Called when CreateAndShow is called, allowing subclass window-related 68 | // setup. Subclasses should return false if setup fails. 69 | virtual bool OnCreate(); 70 | 71 | // Called when Destroy is called. 72 | virtual void OnDestroy(); 73 | 74 | private: 75 | friend class WindowClassRegistrar; 76 | 77 | // OS callback called by message pump. Handles the WM_NCCREATE message which 78 | // is passed when the non-client area is being created and enables automatic 79 | // non-client DPI scaling so that the non-client area automatically 80 | // responds to changes in DPI. All other messages are handled by 81 | // MessageHandler. 82 | static LRESULT CALLBACK WndProc(HWND const window, 83 | UINT const message, 84 | WPARAM const wparam, 85 | LPARAM const lparam) noexcept; 86 | 87 | // Retrieves a class instance pointer for |window| 88 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 89 | 90 | // Update the window frame's theme to match the system theme. 91 | static void UpdateTheme(HWND const window); 92 | 93 | bool quit_on_close_ = false; 94 | 95 | // window handle for top level window. 96 | HWND window_handle_ = nullptr; 97 | 98 | // window handle for hosted content. 99 | HWND child_content_ = nullptr; 100 | }; 101 | 102 | #endif // RUNNER_WIN32_WINDOW_H_ 103 | -------------------------------------------------------------------------------- /app/open_aac/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 | -------------------------------------------------------------------------------- /app/open_aac/macos/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 | -------------------------------------------------------------------------------- /app/open_aac/linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | 10 | struct _MyApplication { 11 | GtkApplication parent_instance; 12 | char** dart_entrypoint_arguments; 13 | }; 14 | 15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 16 | 17 | // Implements GApplication::activate. 18 | static void my_application_activate(GApplication* application) { 19 | MyApplication* self = MY_APPLICATION(application); 20 | GtkWindow* window = 21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 22 | 23 | // Use a header bar when running in GNOME as this is the common style used 24 | // by applications and is the setup most users will be using (e.g. Ubuntu 25 | // desktop). 26 | // If running on X and not using GNOME then just use a traditional title bar 27 | // in case the window manager does more exotic layout, e.g. tiling. 28 | // If running on Wayland assume the header bar will work (may need changing 29 | // if future cases occur). 30 | gboolean use_header_bar = TRUE; 31 | #ifdef GDK_WINDOWING_X11 32 | GdkScreen* screen = gtk_window_get_screen(window); 33 | if (GDK_IS_X11_SCREEN(screen)) { 34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 36 | use_header_bar = FALSE; 37 | } 38 | } 39 | #endif 40 | if (use_header_bar) { 41 | GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 42 | gtk_widget_show(GTK_WIDGET(header_bar)); 43 | gtk_header_bar_set_title(header_bar, "open_aac"); 44 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 46 | } else { 47 | gtk_window_set_title(window, "open_aac"); 48 | } 49 | 50 | gtk_window_set_default_size(window, 1280, 720); 51 | gtk_widget_show(GTK_WIDGET(window)); 52 | 53 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 54 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 55 | 56 | FlView* view = fl_view_new(project); 57 | gtk_widget_show(GTK_WIDGET(view)); 58 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 59 | 60 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 61 | 62 | gtk_widget_grab_focus(GTK_WIDGET(view)); 63 | } 64 | 65 | // Implements GApplication::local_command_line. 66 | static gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) { 67 | MyApplication* self = MY_APPLICATION(application); 68 | // Strip out the first argument as it is the binary name. 69 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 70 | 71 | g_autoptr(GError) error = nullptr; 72 | if (!g_application_register(application, nullptr, &error)) { 73 | g_warning("Failed to register: %s", error->message); 74 | *exit_status = 1; 75 | return TRUE; 76 | } 77 | 78 | g_application_activate(application); 79 | *exit_status = 0; 80 | 81 | return TRUE; 82 | } 83 | 84 | // Implements GObject::dispose. 85 | static void my_application_dispose(GObject* object) { 86 | MyApplication* self = MY_APPLICATION(object); 87 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 88 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 89 | } 90 | 91 | static void my_application_class_init(MyApplicationClass* klass) { 92 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 93 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 94 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 95 | } 96 | 97 | static void my_application_init(MyApplication* self) {} 98 | 99 | MyApplication* my_application_new() { 100 | return MY_APPLICATION(g_object_new(my_application_get_type(), 101 | "application-id", APPLICATION_ID, 102 | "flags", G_APPLICATION_NON_UNIQUE, 103 | nullptr)); 104 | } 105 | -------------------------------------------------------------------------------- /app/open_aac/lib/tts.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io' show Platform; 3 | 4 | import 'package:flutter/foundation.dart' show kIsWeb; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter_tts/flutter_tts.dart'; 7 | 8 | // TTS Functionality based on example code https://github.com/dlutton/flutter_tts/blob/master/example/lib/main.dart 9 | 10 | enum TtsState { playing, stopped, paused, continued } 11 | 12 | class AppTts extends ChangeNotifier { 13 | late FlutterTts flutterTts; 14 | String? language; 15 | String? engine; 16 | double volume = 0.5; 17 | double pitch = 1.0; 18 | double rate = 0.5; 19 | bool isCurrentLanguageInstalled = false; 20 | 21 | String? _newVoiceText; 22 | int? _inputLength; 23 | 24 | TtsState ttsState = TtsState.stopped; 25 | 26 | get isPlaying => ttsState == TtsState.playing; 27 | get isStopped => ttsState == TtsState.stopped; 28 | get isPaused => ttsState == TtsState.paused; 29 | get isContinued => ttsState == TtsState.continued; 30 | 31 | bool get isIOS => !kIsWeb && Platform.isIOS; 32 | bool get isAndroid => !kIsWeb && Platform.isAndroid; 33 | bool get isWindows => !kIsWeb && Platform.isWindows; 34 | bool get isWeb => kIsWeb; 35 | 36 | initTts() { 37 | flutterTts = FlutterTts(); 38 | 39 | _setAwaitOptions(); 40 | 41 | if (isAndroid) { 42 | _getDefaultEngine(); 43 | _getDefaultVoice(); 44 | } 45 | 46 | flutterTts.setStartHandler(() { 47 | print("Playing"); 48 | ttsState = TtsState.playing; 49 | notifyListeners(); 50 | }); 51 | 52 | flutterTts.setCompletionHandler(() { 53 | print("Complete"); 54 | ttsState = TtsState.stopped; 55 | notifyListeners(); 56 | }); 57 | 58 | flutterTts.setCancelHandler(() { 59 | print("Cancel"); 60 | ttsState = TtsState.stopped; 61 | notifyListeners(); 62 | }); 63 | 64 | flutterTts.setPauseHandler(() { 65 | print("Paused"); 66 | ttsState = TtsState.paused; 67 | notifyListeners(); 68 | }); 69 | 70 | flutterTts.setContinueHandler(() { 71 | print("Continued"); 72 | ttsState = TtsState.continued; 73 | notifyListeners(); 74 | }); 75 | 76 | flutterTts.setErrorHandler((msg) { 77 | print("error: $msg"); 78 | ttsState = TtsState.stopped; 79 | notifyListeners(); 80 | }); 81 | } 82 | 83 | Future _getLanguages() async => await flutterTts.getLanguages; 84 | 85 | Future _getEngines() async => await flutterTts.getEngines; 86 | 87 | Future _getDefaultEngine() async { 88 | var engine = await flutterTts.getDefaultEngine; 89 | if (engine != null) { 90 | print(engine); 91 | } 92 | } 93 | 94 | Future _getDefaultVoice() async { 95 | var voice = await flutterTts.getDefaultVoice; 96 | if (voice != null) { 97 | print(voice); 98 | } 99 | } 100 | 101 | Future _speak() async { 102 | await flutterTts.setVolume(volume); 103 | await flutterTts.setSpeechRate(rate); 104 | await flutterTts.setPitch(pitch); 105 | 106 | if (_newVoiceText != null) { 107 | if (_newVoiceText!.isNotEmpty) { 108 | await flutterTts.speak(_newVoiceText!); 109 | } 110 | } 111 | } 112 | 113 | Future _setAwaitOptions() async { 114 | if (isIOS) { 115 | await flutterTts.setSharedInstance(true); 116 | await flutterTts.setIosAudioCategory(IosTextToSpeechAudioCategory.playback, 117 | [ 118 | IosTextToSpeechAudioCategoryOptions.allowBluetooth, 119 | IosTextToSpeechAudioCategoryOptions.allowBluetoothA2DP, 120 | IosTextToSpeechAudioCategoryOptions.mixWithOthers, 121 | IosTextToSpeechAudioCategoryOptions.defaultToSpeaker 122 | ], 123 | IosTextToSpeechAudioMode.defaultMode 124 | ); 125 | } 126 | 127 | await flutterTts.awaitSpeakCompletion(true); 128 | } 129 | 130 | Future stop() async { 131 | var result = await flutterTts.stop(); 132 | if (result == 1) { 133 | ttsState = TtsState.stopped; 134 | notifyListeners(); 135 | } 136 | } 137 | 138 | Future _pause() async { 139 | var result = await flutterTts.pause(); 140 | if (result == 1) { 141 | ttsState = TtsState.paused; 142 | notifyListeners(); 143 | } 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /func/supabase/functions/getImages/index.ts: -------------------------------------------------------------------------------- 1 | // Edge Function to return image path for words matched by vector embedding 2 | 3 | import OpenAI from 'https://deno.land/x/openai@v4.24.0/mod.ts' 4 | 5 | import { createClient } from 'https://cdn.jsdelivr.net/npm/@supabase/supabase-js/+esm' 6 | 7 | import { Redis } from 'https://deno.land/x/upstash_redis@v1.19.3/mod.ts' 8 | import { Ratelimit } from '@upstash/ratelimit' 9 | 10 | export const corsHeaders = { 11 | 'Access-Control-Allow-Origin': '*', 12 | 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', 13 | } 14 | 15 | const openAIEmbeddingsModel = 'text-embedding-3-small' 16 | const vectorMatchThreshold = 0.78 17 | 18 | Deno.serve(async (req) => { 19 | // Search query is passed in request payload 20 | const { words } = await req.json() 21 | console.log("Get Images Call: " + words) 22 | 23 | // Handle CORS if call is from Browser 24 | if (req.method === 'OPTIONS') { 25 | return new Response('ok', { headers: corsHeaders }) 26 | } 27 | 28 | try{ 29 | // Init Supabase client with the Auth context of the logged in user 30 | const authHeader = req.headers.get('Authorization')! 31 | 32 | const supabaseClient = createClient( 33 | Deno.env.get('SUPABASE_URL') ?? '', 34 | Deno.env.get('SUPABASE_ANON_KEY') ?? '', 35 | { global: { headers: { Authorization: authHeader } } } 36 | ) 37 | 38 | // Now we can get the session or user object 39 | const { 40 | data: { user, error }, 41 | } = await supabaseClient.auth.getUser() 42 | 43 | //const { data, error } = await supabaseClient.from('profiles').select('*') 44 | if (error) throw error 45 | 46 | var grantedAccess = false 47 | if (user != null && user['user_metadata'] != null && user['user_metadata']['can_access'] != null) { 48 | grantedAccess = user['user_metadata']['can_access'] 49 | } 50 | 51 | // Limit users without learningo emails or explicit access 52 | if (!user?.email.endsWith("@learningo.org") && !grantedAccess) { 53 | console.log("Anonymous user") 54 | 55 | const data = [] 56 | 57 | return new Response(JSON.stringify({ error: "Not allowed" }), { 58 | headers: { ...corsHeaders, 'Content-Type': 'application/json' }, 59 | status: 401, 60 | }) 61 | } 62 | 63 | // OpenAI recommends replacing newlines with spaces for best results 64 | const input = words.replace(/\n/g, ' ') 65 | 66 | const apiKey = Deno.env.get('OPENAI_API_KEY') 67 | const openai = new OpenAI({ 68 | apiKey: apiKey, 69 | }) 70 | 71 | // Generate embedding for the query itself 72 | const embeddingResponse = await openai.embeddings.create({ 73 | model: openAIEmbeddingsModel, 74 | input, 75 | }) 76 | 77 | if (embeddingResponse.data && embeddingResponse.data.length > 0) { 78 | const responseData = embeddingResponse.data[0]['embedding'] 79 | const { data, error } = await supabaseClient.rpc('match_images', { 80 | match_count: 1, // Choose the number of matches 81 | match_threshold: vectorMatchThreshold, // Choose an appropriate threshold for your data 82 | query_embedding: responseData, 83 | }) 84 | 85 | if (error != null) { 86 | console.log("Error matching images: " + JSON.stringify(error)) 87 | return new Response(JSON.stringify(error), { 88 | headers: { ...corsHeaders, 'Content-Type': 'application/json' }, 89 | status: 500 90 | }) 91 | } else { 92 | // TODO: Figure out why this doesn't work. We can remove auth perm to images if get working 93 | // Create a signed URL of the private image 94 | /*if (data.length == 1) { 95 | const path = data[0]['path'] 96 | console.log("PATH: " + path) 97 | signImage(path) 98 | }*/ 99 | console.log("Successful processing") 100 | return new Response(JSON.stringify(data), { 101 | headers: { ...corsHeaders, 'Content-Type': 'application/json' }, 102 | status: 200 103 | }) 104 | } 105 | } else { 106 | return new Response('ERROR: No embeddings returned', { status: 404, headers: corsHeaders }) 107 | } 108 | } catch (error) { 109 | console.log("ERR: " + error.message) 110 | return new Response(JSON.stringify({ error: error.message }), { 111 | headers: { ...corsHeaders, 'Content-Type': 'application/json' }, 112 | status: 400, 113 | }) 114 | } 115 | }) 116 | -------------------------------------------------------------------------------- /app/open_aac/windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.14) 3 | project(open_aac LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "open_aac") 8 | 9 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 10 | # versions of CMake. 11 | cmake_policy(VERSION 3.14...3.25) 12 | 13 | # Define build configuration option. 14 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 15 | if(IS_MULTICONFIG) 16 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 17 | CACHE STRING "" FORCE) 18 | else() 19 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 20 | set(CMAKE_BUILD_TYPE "Debug" CACHE 21 | STRING "Flutter build mode" FORCE) 22 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 23 | "Debug" "Profile" "Release") 24 | endif() 25 | endif() 26 | # Define settings for the Profile build mode. 27 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 28 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 29 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 30 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 31 | 32 | # Use Unicode for all projects. 33 | add_definitions(-DUNICODE -D_UNICODE) 34 | 35 | # Compilation settings that should be applied to most targets. 36 | # 37 | # Be cautious about adding new options here, as plugins use this function by 38 | # default. In most cases, you should add new options to specific targets instead 39 | # of modifying this function. 40 | function(APPLY_STANDARD_SETTINGS TARGET) 41 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 42 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 43 | target_compile_options(${TARGET} PRIVATE /EHsc) 44 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 45 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 46 | endfunction() 47 | 48 | # Flutter library and tool build rules. 49 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 50 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 51 | 52 | # Application build; see runner/CMakeLists.txt. 53 | add_subdirectory("runner") 54 | 55 | 56 | # Generated plugin build rules, which manage building the plugins and adding 57 | # them to the application. 58 | include(flutter/generated_plugins.cmake) 59 | 60 | 61 | # === Installation === 62 | # Support files are copied into place next to the executable, so that it can 63 | # run in place. This is done instead of making a separate bundle (as on Linux) 64 | # so that building and running from within Visual Studio will work. 65 | set(BUILD_BUNDLE_DIR "$") 66 | # Make the "install" step default, as it's required to run. 67 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 68 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 69 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 70 | endif() 71 | 72 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 73 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 74 | 75 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 76 | COMPONENT Runtime) 77 | 78 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 79 | COMPONENT Runtime) 80 | 81 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 82 | COMPONENT Runtime) 83 | 84 | if(PLUGIN_BUNDLED_LIBRARIES) 85 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 86 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 87 | COMPONENT Runtime) 88 | endif() 89 | 90 | # Copy the native assets provided by the build.dart from all packages. 91 | set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/windows/") 92 | install(DIRECTORY "${NATIVE_ASSETS_DIR}" 93 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 94 | COMPONENT Runtime) 95 | 96 | # Fully re-copy the assets directory on each build to avoid having stale files 97 | # from a previous install. 98 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 99 | install(CODE " 100 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 101 | " COMPONENT Runtime) 102 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 103 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 104 | 105 | # Install the AOT library on non-Debug builds only. 106 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 107 | CONFIGURATIONS Profile;Release 108 | COMPONENT Runtime) 109 | -------------------------------------------------------------------------------- /func/supabase/functions/generateImage/index.ts: -------------------------------------------------------------------------------- 1 | // Generate an image for a word which doesn't have a good match 2 | 3 | import OpenAI from 'https://deno.land/x/openai@v4.24.0/mod.ts' 4 | 5 | import { Redis } from 'https://deno.land/x/upstash_redis@v1.19.3/mod.ts' 6 | import { Ratelimit } from '@upstash/ratelimit' 7 | 8 | import { createClient } from 'https://cdn.jsdelivr.net/npm/@supabase/supabase-js/+esm' 9 | 10 | export const corsHeaders = { 11 | 'Access-Control-Allow-Origin': '*', 12 | 'Access-Control-Allow-Headers': 'authorization, x-client-info, apikey, content-type', 13 | } 14 | 15 | export const imageGenPrompt = `Create a simplified image that is an icon representing "XXXX". 16 | Use only primary colors on a white background. The design is 17 | minimalist with no additional detail. Do not use any character or typography on the image. 18 | This image should resemble the stylistic approach of icons utilized in an 19 | AAC (Augmentative and Alternative Communication) application.` 20 | 21 | export const imageGenModel = "dall-e-2"; 22 | 23 | Deno.serve(async (req) => { 24 | // Search query is passed in request payload 25 | const { word } = await req.json() 26 | console.log("Generate Images Call: " + word) 27 | 28 | // Handle CORS if call is from Browser 29 | if (req.method === 'OPTIONS') { 30 | return new Response('ok', { headers: corsHeaders }) 31 | } 32 | 33 | try{ 34 | // Init Supabase client with the Auth context of the logged in user 35 | const authHeader = req.headers.get('Authorization')! 36 | 37 | const supabaseClient = createClient( 38 | Deno.env.get('SUPABASE_URL') ?? '', 39 | Deno.env.get('SUPABASE_ANON_KEY') ?? '', 40 | { global: { headers: { Authorization: authHeader } } } 41 | ) 42 | 43 | // Now we can get the session or user object 44 | const {data: { user, error }, } = await supabaseClient.auth.getUser() 45 | if (error) throw error 46 | 47 | var grantedAccess = false 48 | if (user != null && user['user_metadata'] != null && user['user_metadata']['can_access'] != null) { 49 | grantedAccess = user['user_metadata']['can_access'] 50 | } 51 | 52 | // Rate limit users without learningo emails or explicit access 53 | if (!user?.email.endsWith("@learningo.org") && !grantedAccess) { 54 | console.log("Anonymous user") 55 | return new Response(JSON.stringify({ error: "Not allowed" }), { 56 | headers: { ...corsHeaders, 'Content-Type': 'application/json' }, 57 | status: 401, 58 | }) 59 | /*const redis = new Redis({ 60 | url: Deno.env.get('UPSTASH_REDIS_REST_URL')!, 61 | token: Deno.env.get('UPSTASH_REDIS_REST_TOKEN')!, 62 | }) 63 | 64 | // Create a new ratelimiter, that allows 10 requests per 10 seconds 65 | const ratelimit = new Ratelimit({ 66 | redis, 67 | limiter: Ratelimit.slidingWindow(10, '10 s'), // TODO: Change for production 68 | analytics: true, 69 | }) 70 | 71 | const anon_access = 'anon_access_gen_img' 72 | const { success } = await ratelimit.limit(anon_access) 73 | 74 | if (!success) { 75 | return new Response(JSON.stringify({ error: "Rate limited" }), { 76 | headers: { ...corsHeaders, 'Content-Type': 'application/json' }, 77 | status: 429, 78 | }) 79 | } 80 | */ 81 | } 82 | 83 | // OpenAI recommends replacing newlines with spaces for best results 84 | const input = word.replace(/\n/g, ' ') 85 | const prompt = imageGenPrompt.replace("XXXX", input) 86 | 87 | const apiKey = Deno.env.get('OPENAI_API_KEY') 88 | const openai = new OpenAI({ 89 | apiKey: apiKey, 90 | }) 91 | 92 | // Generate embedding for the query itself 93 | var imgResponse = await openai.images.generate({ 94 | model: imageGenModel, 95 | prompt: prompt, 96 | n: 1, 97 | size: "512x512", 98 | response_format: "b64_json" 99 | }) 100 | 101 | if (imgResponse.data && imgResponse.data.length > 0) { 102 | const responseData = imgResponse.data[0]['b64_json'] 103 | 104 | return new Response(JSON.stringify(responseData), { 105 | headers: { ...corsHeaders, 'Content-Type': 'application/json' }, 106 | status: 200 107 | }) 108 | } else { 109 | return new Response('ERROR: No image returned', { status: 404, headers: corsHeaders }) 110 | } 111 | } catch (error) { 112 | console.log("ERR: " + error.message) 113 | return new Response(JSON.stringify({ error: error.message }), { 114 | headers: { ...corsHeaders, 'Content-Type': 'application/json' }, 115 | status: 400, 116 | }) 117 | } 118 | }) 119 | -------------------------------------------------------------------------------- /app/open_aac/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: openaac 2 | description: "An application for Open AAC." 3 | # The following line prevents the package from being accidentally published to 4 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 6 | 7 | # The following defines the version and build number for your application. 8 | # A version number is three numbers separated by dots, like 1.2.43 9 | # followed by an optional build number separated by a +. 10 | # Both the version and the builder number may be overridden in flutter 11 | # build by specifying --build-name and --build-number, respectively. 12 | # In Android, build-name is used as versionName while build-number used as versionCode. 13 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 14 | # In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion. 15 | # Read more about iOS versioning at 16 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 17 | # In Windows, build-name is used as the major, minor, and patch parts 18 | # of the product and file versions while build-number is used as the build suffix. 19 | version: 0.0.6+12 20 | 21 | environment: 22 | sdk: '>=3.2.3 <4.0.0' 23 | 24 | # Dependencies specify other packages that your package needs in order to work. 25 | # To automatically upgrade your package dependencies to the latest versions 26 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 27 | # dependencies can be manually updated by changing the version numbers below to 28 | # the latest version available on pub.dev. To see which dependencies have newer 29 | # versions available, run `flutter pub outdated`. 30 | dependencies: 31 | flutter: 32 | sdk: flutter 33 | 34 | 35 | # The following adds the Cupertino Icons font to your application. 36 | # Use with the CupertinoIcons class for iOS style icons. 37 | cupertino_icons: ^1.0.2 38 | provider: ^6.1.1 39 | string_validator: ^1.0.2 40 | image: ^4.1.3 41 | flutter_tts: ^4.0.2 42 | change_app_package_name: ^1.1.0 43 | flutter_launcher_icons: ^0.13.1 44 | supabase_flutter: ^2.3.1 45 | url_launcher: ^6.2.4 46 | image_picker: ^1.0.7 47 | flutter_cache_manager: ^3.3.1 48 | firebase_core: ^2.25.4 49 | firebase_messaging: ^14.7.15 50 | package_info_plus: ^8.0.0 51 | flutter_dotenv: ^5.1.0 52 | 53 | dev_dependencies: 54 | flutter_test: 55 | sdk: flutter 56 | 57 | # The "flutter_lints" package below contains a set of recommended lints to 58 | # encourage good coding practices. The lint set provided by the package is 59 | # activated in the `analysis_options.yaml` file located at the root of your 60 | # package. See that file for information about deactivating specific lint 61 | # rules and activating additional ones. 62 | flutter_lints: ^4.0.0 63 | 64 | # For information on the generic Dart part of this file, see the 65 | # following page: https://dart.dev/tools/pub/pubspec 66 | 67 | # The following section is specific to Flutter packages. 68 | flutter: 69 | 70 | # The following line ensures that the Material Icons font is 71 | # included with your application, so that you can use the icons in 72 | # the material Icons class. 73 | uses-material-design: true 74 | 75 | # To add assets to your application, add an assets section, like this: 76 | # assets: 77 | # - images/a_dot_burr.jpeg 78 | # - images/a_dot_ham.jpeg 79 | assets: 80 | - assets/images/_app/ 81 | - .env 82 | 83 | flutter_launcher_icons: 84 | android: "launcher_icon" 85 | ios: true 86 | image_path: "assets/images/_app/logo.png" 87 | min_sdk_android: 21 # android min sdk min:16, default 21 88 | web: 89 | generate: true 90 | image_path: "assets/images/_app/logo.png" 91 | background_color: "#hexcode" 92 | theme_color: "#hexcode" 93 | windows: 94 | generate: true 95 | image_path: "assets/images/_app/logo.png" 96 | icon_size: 48 # min:48, max:256, default: 48 97 | macos: 98 | generate: true 99 | image_path: "assets/images/_app/logo.png" 100 | 101 | # An image asset can refer to one or more resolution-specific "variants", see 102 | # https://flutter.dev/assets-and-images/#resolution-aware 103 | 104 | # For details regarding adding assets from package dependencies, see 105 | # https://flutter.dev/assets-and-images/#from-packages 106 | 107 | # To add custom fonts to your application, add a fonts section here, 108 | # in this "flutter" section. Each entry in this list should have a 109 | # "family" key with the font family name, and a "fonts" key with a 110 | # list giving the asset and other descriptors for the font. For 111 | # example: 112 | # fonts: 113 | # - family: Schyler 114 | # fonts: 115 | # - asset: fonts/Schyler-Regular.ttf 116 | # - asset: fonts/Schyler-Italic.ttf 117 | # style: italic 118 | # - family: Trajan Pro 119 | # fonts: 120 | # - asset: fonts/TrajanPro.ttf 121 | # - asset: fonts/TrajanPro_Bold.ttf 122 | # weight: 700 123 | # 124 | # For details regarding fonts from package dependencies, 125 | # see https://flutter.dev/custom-fonts/#from-packages 126 | -------------------------------------------------------------------------------- /app/open_aac/linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Project-level configuration. 2 | cmake_minimum_required(VERSION 3.10) 3 | project(runner LANGUAGES CXX) 4 | 5 | # The name of the executable created for the application. Change this to change 6 | # the on-disk name of your application. 7 | set(BINARY_NAME "open_aac") 8 | # The unique GTK application identifier for this application. See: 9 | # https://wiki.gnome.org/HowDoI/ChooseApplicationID 10 | set(APPLICATION_ID "org.learningo.openaac") 11 | 12 | # Explicitly opt in to modern CMake behaviors to avoid warnings with recent 13 | # versions of CMake. 14 | cmake_policy(SET CMP0063 NEW) 15 | 16 | # Load bundled libraries from the lib/ directory relative to the binary. 17 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 18 | 19 | # Root filesystem for cross-building. 20 | if(FLUTTER_TARGET_PLATFORM_SYSROOT) 21 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) 22 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) 23 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 24 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 25 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 26 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 27 | endif() 28 | 29 | # Define build configuration options. 30 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 31 | set(CMAKE_BUILD_TYPE "Debug" CACHE 32 | STRING "Flutter build mode" FORCE) 33 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 34 | "Debug" "Profile" "Release") 35 | endif() 36 | 37 | # Compilation settings that should be applied to most targets. 38 | # 39 | # Be cautious about adding new options here, as plugins use this function by 40 | # default. In most cases, you should add new options to specific targets instead 41 | # of modifying this function. 42 | function(APPLY_STANDARD_SETTINGS TARGET) 43 | target_compile_features(${TARGET} PUBLIC cxx_std_14) 44 | target_compile_options(${TARGET} PRIVATE -Wall -Werror) 45 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") 46 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") 47 | endfunction() 48 | 49 | # Flutter library and tool build rules. 50 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 51 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 52 | 53 | # System-level dependencies. 54 | find_package(PkgConfig REQUIRED) 55 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 56 | 57 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 58 | 59 | # Define the application target. To change its name, change BINARY_NAME above, 60 | # not the value here, or `flutter run` will no longer work. 61 | # 62 | # Any new source files that you add to the application should be added here. 63 | add_executable(${BINARY_NAME} 64 | "main.cc" 65 | "my_application.cc" 66 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 67 | ) 68 | 69 | # Apply the standard set of build settings. This can be removed for applications 70 | # that need different build settings. 71 | apply_standard_settings(${BINARY_NAME}) 72 | 73 | # Add dependency libraries. Add any application-specific dependencies here. 74 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 75 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 76 | 77 | # Run the Flutter tool portions of the build. This must not be removed. 78 | add_dependencies(${BINARY_NAME} flutter_assemble) 79 | 80 | # Only the install-generated bundle's copy of the executable will launch 81 | # correctly, since the resources must in the right relative locations. To avoid 82 | # people trying to run the unbundled copy, put it in a subdirectory instead of 83 | # the default top-level location. 84 | set_target_properties(${BINARY_NAME} 85 | PROPERTIES 86 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" 87 | ) 88 | 89 | 90 | # Generated plugin build rules, which manage building the plugins and adding 91 | # them to the application. 92 | include(flutter/generated_plugins.cmake) 93 | 94 | 95 | # === Installation === 96 | # By default, "installing" just makes a relocatable bundle in the build 97 | # directory. 98 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") 99 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 100 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 101 | endif() 102 | 103 | # Start with a clean build bundle directory every time. 104 | install(CODE " 105 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") 106 | " COMPONENT Runtime) 107 | 108 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 109 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") 110 | 111 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 112 | COMPONENT Runtime) 113 | 114 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 115 | COMPONENT Runtime) 116 | 117 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 118 | COMPONENT Runtime) 119 | 120 | foreach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES}) 121 | install(FILES "${bundled_library}" 122 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 123 | COMPONENT Runtime) 124 | endforeach(bundled_library) 125 | 126 | # Copy the native assets provided by the build.dart from all packages. 127 | set(NATIVE_ASSETS_DIR "${PROJECT_BUILD_DIR}native_assets/linux/") 128 | install(DIRECTORY "${NATIVE_ASSETS_DIR}" 129 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 130 | COMPONENT Runtime) 131 | 132 | # Fully re-copy the assets directory on each build to avoid having stale files 133 | # from a previous install. 134 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 135 | install(CODE " 136 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 137 | " COMPONENT Runtime) 138 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 139 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 140 | 141 | # Install the AOT library on non-Debug builds only. 142 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") 143 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 144 | COMPONENT Runtime) 145 | endif() 146 | -------------------------------------------------------------------------------- /app/open_aac/lib/pages/login_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:firebase_messaging/firebase_messaging.dart'; 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter/services.dart'; 7 | import 'package:supabase_flutter/supabase_flutter.dart'; 8 | import 'package:openaac/main.dart'; 9 | import 'package:url_launcher/url_launcher.dart'; 10 | 11 | class LoginPage extends StatefulWidget { 12 | const LoginPage({super.key}); 13 | 14 | @override 15 | _LoginPageState createState() => _LoginPageState(); 16 | } 17 | 18 | class _LoginPageState extends State { 19 | bool _isLoading = false; 20 | bool _redirecting = false; 21 | late final TextEditingController _emailController = TextEditingController(); 22 | late final StreamSubscription _authStateSubscription; 23 | 24 | Future _signIn() async { 25 | try { 26 | setState(() { 27 | _isLoading = true; 28 | }); 29 | await supabase.auth.signInWithOtp( 30 | email: _emailController.text.trim(), 31 | emailRedirectTo: 32 | kIsWeb ? null : 'io.supabase.flutterquickstart://login-callback/', 33 | ); 34 | if (mounted) { 35 | ScaffoldMessenger.of(context).showSnackBar( 36 | const SnackBar(content: Text('Check your email for a login link!')), 37 | ); 38 | _emailController.clear(); 39 | } 40 | } on AuthException catch (error) { 41 | SnackBar( 42 | content: Text(error.message), 43 | backgroundColor: Theme.of(context).colorScheme.error, 44 | ); 45 | } catch (error) { 46 | SnackBar( 47 | content: const Text('Unexpected error occurred'), 48 | backgroundColor: Theme.of(context).colorScheme.error, 49 | ); 50 | } finally { 51 | if (mounted) { 52 | setState(() { 53 | _isLoading = false; 54 | }); 55 | } 56 | } 57 | } 58 | 59 | 60 | Future _signInAnon() async { 61 | try { 62 | setState(() { 63 | _isLoading = true; 64 | }); 65 | _redirecting = true; 66 | Navigator.of(context).pushReplacementNamed('/home'); 67 | } catch (error) { 68 | SnackBar( 69 | content: const Text('Unexpected error occurred'), 70 | backgroundColor: Theme.of(context).colorScheme.error, 71 | ); 72 | } finally { 73 | if (mounted) { 74 | setState(() { 75 | _isLoading = false; 76 | }); 77 | } 78 | } 79 | } 80 | 81 | @override 82 | void initState() { 83 | _authStateSubscription = supabase.auth.onAuthStateChange.listen((data) async { 84 | if (_redirecting) return; 85 | final session = data.session; 86 | if (data.event == AuthChangeEvent.signedIn) { 87 | 88 | await FirebaseMessaging.instance.requestPermission(); 89 | // Handle potential getAPNSToken() error 90 | try { 91 | await FirebaseMessaging.instance.getAPNSToken(); 92 | 93 | final fcmToken = await FirebaseMessaging.instance.getToken(); 94 | if (fcmToken != null) { 95 | _setFCMToken(fcmToken); 96 | } 97 | } on PlatformException catch (e) { 98 | // Handle the error (e.g., log, display message) 99 | print('Error getting APNS token: $e'); 100 | } 101 | } 102 | if (session != null) { 103 | _redirecting = true; 104 | Navigator.of(context).pushReplacementNamed('/home'); 105 | } 106 | }); 107 | 108 | FirebaseMessaging.instance.onTokenRefresh.listen((fcmToken) { 109 | _setFCMToken(fcmToken); 110 | }); 111 | 112 | FirebaseMessaging.onMessage.listen((payload) { 113 | final notification = payload.notification; 114 | if (notification != null) { 115 | ScaffoldMessenger.of(context).showSnackBar(SnackBar( 116 | content: Text('${notification.title} ${notification.body}'))); 117 | } 118 | }); 119 | 120 | super.initState(); 121 | } 122 | 123 | @override 124 | void dispose() { 125 | _authStateSubscription.cancel(); 126 | _emailController.dispose(); 127 | super.dispose(); 128 | } 129 | 130 | @override 131 | Widget build(BuildContext context) { 132 | return Scaffold( 133 | appBar: AppBar( 134 | title: const Text("Login to Learningo (Optional)"), 135 | leading: Builder( 136 | builder: (BuildContext context) { 137 | return IconButton( 138 | icon: Image.asset('assets/images/_app/logo.png'), 139 | onPressed: () { _launchSite(); }, 140 | tooltip: "Open Learningo Homepage", 141 | ); 142 | }, 143 | ), 144 | ), 145 | body: ListView( 146 | padding: const EdgeInsets.symmetric(vertical: 18, horizontal: 12), 147 | children: [ 148 | const Text('Sign in via the magic link with your email below'), 149 | const SizedBox(height: 18), 150 | TextFormField( 151 | controller: _emailController, 152 | decoration: const InputDecoration(labelText: 'Email'), 153 | ), 154 | const SizedBox(height: 18), 155 | ElevatedButton( 156 | onPressed: _isLoading ? null : _signIn, 157 | child: Text(_isLoading ? 'Loading' : 'Send Magic Link'), 158 | ), 159 | const SizedBox(height: 18), 160 | const Text('Use without providing email'), 161 | const SizedBox(height: 18), 162 | ElevatedButton( 163 | onPressed: _isLoading ? null : _signInAnon, 164 | child: Text(_isLoading ? 'Loading' : 'Sign in Anonymously'), 165 | ), 166 | ], 167 | ), 168 | ); 169 | } 170 | 171 | void _launchSite() async { 172 | final Uri url = Uri.parse('https://learningo.org/app'); 173 | if (!await launchUrl(url)) { 174 | throw Exception('Could not launch $url'); 175 | } 176 | } 177 | 178 | Future _setFCMToken(String fcmToken) async { 179 | final userId = supabase.auth.currentUser?.id; 180 | if (userId != null) { 181 | await supabase.from('profiles').upsert({ 182 | 'id': userId, 183 | 'fcm_token': fcmToken, 184 | }); 185 | } 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /app/open_aac/lib/ai.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:flutter/services.dart'; 4 | import 'package:supabase_flutter/supabase_flutter.dart'; 5 | import 'package:flutter_cache_manager/flutter_cache_manager.dart'; 6 | import 'package:image/image.dart' as img; 7 | 8 | const String modelName = 'text-embedding-ada-002'; 9 | const String pcIndex = 'openaac-embeddings'; 10 | const String namespace = 'openaac-images'; 11 | const String blankTilePath = 'images/_app/blank.png'; 12 | const double vectorMatchThreshold = 0.78; 13 | const String genImagesCachePrefix = "images/openai/"; 14 | const String storedImagesCachePrefix = "images/supabase/"; 15 | 16 | class Mapping { 17 | final String word; 18 | final bool poorMatch; 19 | final String? imagePath; 20 | final Uint8List imageBytes; 21 | 22 | Mapping(this.word, this.imagePath, this.poorMatch, this.imageBytes); 23 | } 24 | 25 | // Perform a lookup on the current text using Supabase 26 | Future> lookupSupabase(String text) async { 27 | List mappings = []; 28 | 29 | if (text.isNotEmpty) { 30 | // Access Supabase client 31 | final sbClient = Supabase.instance.client; 32 | 33 | print("text to lookup: $text"); 34 | // Split the text into a list of words 35 | List words = text.split(' '); 36 | for (var word in words) { 37 | word = word.replaceAll(RegExp(r"[^A-Za-z0-9]"), ""); // Strip out anything not alphanumeric 38 | if (word.isEmpty || word == '') { 39 | continue; 40 | } 41 | try { 42 | // Attempt to retrieve the image from either generated or storage images cache 43 | Mapping? mapping = await _getFileFromCaches(word); 44 | 45 | if (mapping == null) { 46 | // No cached image; proceed with Supabase query 47 | final response = await sbClient.functions.invoke("getImages", body: {'words': word}); 48 | print("word $word => Status: ${response.status} Initial: ${response.data}"); 49 | if (response.status == 200) { 50 | Mapping mapping; 51 | if (response.data.length > 0) { 52 | final match = response.data[0]; 53 | final similarity = match['similarity'].toString(); 54 | if (double.parse(similarity) < vectorMatchThreshold) { 55 | print("poor match for $word. Attempting image generation."); 56 | mapping = await _generateImage(sbClient, word); 57 | } else { 58 | mapping = await _downloadStoredFile(sbClient, match['path'], word); 59 | } 60 | } else { 61 | print("No match for $word. Attempting image generation."); 62 | mapping = await _generateImage(sbClient, word); 63 | } 64 | mappings.add(mapping); 65 | } 66 | } else { 67 | mappings.add(mapping); 68 | } 69 | } on FunctionException catch (err) { 70 | print("Function Exception : ${err.reasonPhrase}"); 71 | rethrow; 72 | } 73 | } 74 | } 75 | return mappings; 76 | } 77 | 78 | // Check only for cached files 79 | Future _getFileFromCaches(String word) async { 80 | // Build the potential image path for generated image 81 | final genImagePath = genImagesCachePrefix + word; 82 | 83 | var genImageCacheFile = await DefaultCacheManager().getFileFromCache(genImagePath); 84 | 85 | if (genImageCacheFile != null) { 86 | // Cache hit! Construct mapping directly 87 | return Mapping(word, genImagePath, true, genImageCacheFile.file.readAsBytesSync()); 88 | } 89 | 90 | // Build the potential image path for a stored image 91 | final storedImagePath = storedImagesCachePrefix + word; 92 | 93 | var storedImageFile = await DefaultCacheManager().getFileFromCache(storedImagePath); 94 | 95 | if (storedImageFile != null) { 96 | // Cache hit! Construct mapping directly 97 | return Mapping(word, storedImagePath, false, storedImageFile.file.readAsBytesSync()); 98 | } 99 | 100 | return null; 101 | } 102 | 103 | Future _downloadStoredFile(SupabaseClient client, String remoteImagePath, String word) async { 104 | try { 105 | Uint8List? imageBytes = await client.storage.from('images').download(remoteImagePath); 106 | 107 | if (imageBytes.isNotEmpty) { 108 | // Put the image file in the cache for the next time. 109 | final storedImagePath = storedImagesCachePrefix + word; 110 | 111 | await DefaultCacheManager().putFile( 112 | storedImagePath, 113 | imageBytes, 114 | fileExtension: "png", 115 | eTag: "real", 116 | key: storedImagePath, 117 | maxAge: Duration(days: 50), 118 | ); 119 | return Mapping(word, storedImagePath, false, imageBytes); 120 | } 121 | } on StorageException catch (err) { 122 | // Not found or some other issue, just return a blank tile 123 | print("Storage exception: ${err.message}"); 124 | } 125 | // blank tile 126 | return await _getBlankMapping(word); 127 | } 128 | 129 | // Use edge function to generate an image as there isn't a close enough vector match. 130 | // Cache generated images in local storage to keep costs down 131 | Future _generateImage(SupabaseClient sbClient, String word) async { 132 | final imagePath = genImagesCachePrefix + word; 133 | final response = await sbClient.functions.invoke("generateImage", body: {'word': word}); 134 | String b64Json = response.data; 135 | 136 | final base64Decoder = base64.decoder; 137 | final decodedBytes = base64Decoder.convert(b64Json); 138 | 139 | Uint8List? resizedData; 140 | if (decodedBytes.isNotEmpty) { 141 | img.Image? rawImage = img.decodeImage(decodedBytes); 142 | img.Image resized = img.copyResize(rawImage!, width: 144, height: 144); 143 | resizedData = img.encodePng(resized); 144 | 145 | // Cache the generated image 146 | await DefaultCacheManager().putFile(imagePath, resizedData, 147 | fileExtension: "png", eTag: "generated", key: imagePath, maxAge: Duration(days: 50), 148 | ); 149 | } 150 | 151 | if (resizedData != null) { 152 | return Mapping(word, null, true, resizedData); 153 | } else { 154 | return _getBlankMapping(word); 155 | } 156 | } 157 | 158 | Future _getBlankMapping(String word) async { 159 | final blankImageData = await rootBundle.load(blankTilePath); 160 | return Mapping(word, blankTilePath, true, blankImageData.buffer.asUint8List()); 161 | } 162 | -------------------------------------------------------------------------------- /app/open_aac/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - app_links (0.0.1): 3 | - Flutter 4 | - Firebase/CoreOnly (10.25.0): 5 | - FirebaseCore (= 10.25.0) 6 | - Firebase/Messaging (10.25.0): 7 | - Firebase/CoreOnly 8 | - FirebaseMessaging (~> 10.25.0) 9 | - firebase_core (2.31.1): 10 | - Firebase/CoreOnly (= 10.25.0) 11 | - Flutter 12 | - firebase_messaging (14.9.3): 13 | - Firebase/Messaging (= 10.25.0) 14 | - firebase_core 15 | - Flutter 16 | - FirebaseCore (10.25.0): 17 | - FirebaseCoreInternal (~> 10.0) 18 | - GoogleUtilities/Environment (~> 7.12) 19 | - GoogleUtilities/Logger (~> 7.12) 20 | - FirebaseCoreInternal (10.25.0): 21 | - "GoogleUtilities/NSData+zlib (~> 7.8)" 22 | - FirebaseInstallations (10.25.0): 23 | - FirebaseCore (~> 10.0) 24 | - GoogleUtilities/Environment (~> 7.8) 25 | - GoogleUtilities/UserDefaults (~> 7.8) 26 | - PromisesObjC (~> 2.1) 27 | - FirebaseMessaging (10.25.0): 28 | - FirebaseCore (~> 10.0) 29 | - FirebaseInstallations (~> 10.0) 30 | - GoogleDataTransport (~> 9.3) 31 | - GoogleUtilities/AppDelegateSwizzler (~> 7.8) 32 | - GoogleUtilities/Environment (~> 7.8) 33 | - GoogleUtilities/Reachability (~> 7.8) 34 | - GoogleUtilities/UserDefaults (~> 7.8) 35 | - nanopb (< 2.30911.0, >= 2.30908.0) 36 | - Flutter (1.0.0) 37 | - flutter_tts (0.0.1): 38 | - Flutter 39 | - GoogleDataTransport (9.4.1): 40 | - GoogleUtilities/Environment (~> 7.7) 41 | - nanopb (< 2.30911.0, >= 2.30908.0) 42 | - PromisesObjC (< 3.0, >= 1.2) 43 | - GoogleUtilities/AppDelegateSwizzler (7.13.3): 44 | - GoogleUtilities/Environment 45 | - GoogleUtilities/Logger 46 | - GoogleUtilities/Network 47 | - GoogleUtilities/Privacy 48 | - GoogleUtilities/Environment (7.13.3): 49 | - GoogleUtilities/Privacy 50 | - PromisesObjC (< 3.0, >= 1.2) 51 | - GoogleUtilities/Logger (7.13.3): 52 | - GoogleUtilities/Environment 53 | - GoogleUtilities/Privacy 54 | - GoogleUtilities/Network (7.13.3): 55 | - GoogleUtilities/Logger 56 | - "GoogleUtilities/NSData+zlib" 57 | - GoogleUtilities/Privacy 58 | - GoogleUtilities/Reachability 59 | - "GoogleUtilities/NSData+zlib (7.13.3)": 60 | - GoogleUtilities/Privacy 61 | - GoogleUtilities/Privacy (7.13.3) 62 | - GoogleUtilities/Reachability (7.13.3): 63 | - GoogleUtilities/Logger 64 | - GoogleUtilities/Privacy 65 | - GoogleUtilities/UserDefaults (7.13.3): 66 | - GoogleUtilities/Logger 67 | - GoogleUtilities/Privacy 68 | - image_picker_ios (0.0.1): 69 | - Flutter 70 | - nanopb (2.30910.0): 71 | - nanopb/decode (= 2.30910.0) 72 | - nanopb/encode (= 2.30910.0) 73 | - nanopb/decode (2.30910.0) 74 | - nanopb/encode (2.30910.0) 75 | - package_info_plus (0.4.5): 76 | - Flutter 77 | - path_provider_foundation (0.0.1): 78 | - Flutter 79 | - FlutterMacOS 80 | - PromisesObjC (2.4.0) 81 | - shared_preferences_foundation (0.0.1): 82 | - Flutter 83 | - FlutterMacOS 84 | - sqflite (0.0.3): 85 | - Flutter 86 | - FlutterMacOS 87 | - url_launcher_ios (0.0.1): 88 | - Flutter 89 | 90 | DEPENDENCIES: 91 | - app_links (from `.symlinks/plugins/app_links/ios`) 92 | - firebase_core (from `.symlinks/plugins/firebase_core/ios`) 93 | - firebase_messaging (from `.symlinks/plugins/firebase_messaging/ios`) 94 | - Flutter (from `Flutter`) 95 | - flutter_tts (from `.symlinks/plugins/flutter_tts/ios`) 96 | - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) 97 | - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) 98 | - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) 99 | - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) 100 | - sqflite (from `.symlinks/plugins/sqflite/darwin`) 101 | - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) 102 | 103 | SPEC REPOS: 104 | trunk: 105 | - Firebase 106 | - FirebaseCore 107 | - FirebaseCoreInternal 108 | - FirebaseInstallations 109 | - FirebaseMessaging 110 | - GoogleDataTransport 111 | - GoogleUtilities 112 | - nanopb 113 | - PromisesObjC 114 | 115 | EXTERNAL SOURCES: 116 | app_links: 117 | :path: ".symlinks/plugins/app_links/ios" 118 | firebase_core: 119 | :path: ".symlinks/plugins/firebase_core/ios" 120 | firebase_messaging: 121 | :path: ".symlinks/plugins/firebase_messaging/ios" 122 | Flutter: 123 | :path: Flutter 124 | flutter_tts: 125 | :path: ".symlinks/plugins/flutter_tts/ios" 126 | image_picker_ios: 127 | :path: ".symlinks/plugins/image_picker_ios/ios" 128 | package_info_plus: 129 | :path: ".symlinks/plugins/package_info_plus/ios" 130 | path_provider_foundation: 131 | :path: ".symlinks/plugins/path_provider_foundation/darwin" 132 | shared_preferences_foundation: 133 | :path: ".symlinks/plugins/shared_preferences_foundation/darwin" 134 | sqflite: 135 | :path: ".symlinks/plugins/sqflite/darwin" 136 | url_launcher_ios: 137 | :path: ".symlinks/plugins/url_launcher_ios/ios" 138 | 139 | SPEC CHECKSUMS: 140 | app_links: e70ca16b4b0f88253b3b3660200d4a10b4ea9795 141 | Firebase: 0312a2352584f782ea56f66d91606891d4607f06 142 | firebase_core: 22e117a2e0dec3cb318c8f53f2dd01c140375617 143 | firebase_messaging: 7c04a2151f0da85796b41e01ad7da870af6a27e2 144 | FirebaseCore: 7ec4d0484817f12c3373955bc87762d96842d483 145 | FirebaseCoreInternal: 910a81992c33715fec9263ca7381d59ab3a750b7 146 | FirebaseInstallations: 91950fe859846fff0fbd296180909dd273103b09 147 | FirebaseMessaging: 88950ba9485052891ebe26f6c43a52bb62248952 148 | Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7 149 | flutter_tts: 0f492aab6accf87059b72354fcb4ba934304771d 150 | GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a 151 | GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15 152 | image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1 153 | nanopb: 438bc412db1928dac798aa6fd75726007be04262 154 | package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c 155 | path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 156 | PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47 157 | shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 158 | sqflite: 673a0e54cc04b7d6dba8d24fb8095b31c3a99eec 159 | url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe 160 | 161 | PODFILE CHECKSUM: 819463e6a0290f5a72f145ba7cde16e8b6ef0796 162 | 163 | COCOAPODS: 1.15.2 164 | -------------------------------------------------------------------------------- /func/supabase/config.toml: -------------------------------------------------------------------------------- 1 | # A string used to distinguish different Supabase projects on the same host. Defaults to the 2 | # working directory name when running `supabase init`. 3 | project_id = "func" 4 | 5 | [api] 6 | enabled = true 7 | # Port to use for the API URL. 8 | port = 54321 9 | # Schemas to expose in your API. Tables, views and stored procedures in this schema will get API 10 | # endpoints. public and storage are always included. 11 | schemas = ["public", "storage", "graphql_public"] 12 | # Extra schemas to add to the search_path of every request. public is always included. 13 | extra_search_path = ["public", "extensions"] 14 | # The maximum number of rows returns from a view, table, or stored procedure. Limits payload size 15 | # for accidental or malicious requests. 16 | max_rows = 1000 17 | 18 | [db] 19 | # Port to use for the local database URL. 20 | port = 54322 21 | # Port used by db diff command to initialize the shadow database. 22 | shadow_port = 54320 23 | # The database major version to use. This has to be the same as your remote database's. Run `SHOW 24 | # server_version;` on the remote database to check. 25 | major_version = 15 26 | 27 | [db.pooler] 28 | enabled = false 29 | # Port to use for the local connection pooler. 30 | port = 54329 31 | # Specifies when a server connection can be reused by other clients. 32 | # Configure one of the supported pooler modes: `transaction`, `session`. 33 | pool_mode = "transaction" 34 | # How many server connections to allow per user/database pair. 35 | default_pool_size = 20 36 | # Maximum number of client connections allowed. 37 | max_client_conn = 100 38 | 39 | [realtime] 40 | enabled = true 41 | # Bind realtime via either IPv4 or IPv6. (default: IPv6) 42 | # ip_version = "IPv6" 43 | # The maximum length in bytes of HTTP request headers. (default: 4096) 44 | # max_header_length = 4096 45 | 46 | [studio] 47 | enabled = true 48 | # Port to use for Supabase Studio. 49 | port = 54323 50 | # External URL of the API server that frontend connects to. 51 | api_url = "http://127.0.0.1" 52 | 53 | # Email testing server. Emails sent with the local dev setup are not actually sent - rather, they 54 | # are monitored, and you can view the emails that would have been sent from the web interface. 55 | [inbucket] 56 | enabled = true 57 | # Port to use for the email testing server web interface. 58 | port = 54324 59 | # Uncomment to expose additional ports for testing user applications that send emails. 60 | # smtp_port = 54325 61 | # pop3_port = 54326 62 | 63 | [storage] 64 | enabled = true 65 | # The maximum file size allowed (e.g. "5MB", "500KB"). 66 | file_size_limit = "50MiB" 67 | 68 | [auth] 69 | enabled = true 70 | # The base URL of your website. Used as an allow-list for redirects and for constructing URLs used 71 | # in emails. 72 | site_url = "http://127.0.0.1:3000" 73 | # A list of *exact* URLs that auth providers are permitted to redirect to post authentication. 74 | additional_redirect_urls = ["https://127.0.0.1:3000"] 75 | # How long tokens are valid for, in seconds. Defaults to 3600 (1 hour), maximum 604,800 (1 week). 76 | jwt_expiry = 3600 77 | # If disabled, the refresh token will never expire. 78 | enable_refresh_token_rotation = true 79 | # Allows refresh tokens to be reused after expiry, up to the specified interval in seconds. 80 | # Requires enable_refresh_token_rotation = true. 81 | refresh_token_reuse_interval = 10 82 | # Allow/disallow new user signups to your project. 83 | enable_signup = true 84 | # Allow/disallow testing manual linking of accounts 85 | enable_manual_linking = false 86 | 87 | [auth.email] 88 | # Allow/disallow new user signups via email to your project. 89 | enable_signup = true 90 | # If enabled, a user will be required to confirm any email change on both the old, and new email 91 | # addresses. If disabled, only the new email is required to confirm. 92 | double_confirm_changes = true 93 | # If enabled, users need to confirm their email address before signing in. 94 | enable_confirmations = false 95 | 96 | # Uncomment to customize email template 97 | # [auth.email.template.invite] 98 | # subject = "You have been invited" 99 | # content_path = "./supabase/templates/invite.html" 100 | 101 | [auth.sms] 102 | # Allow/disallow new user signups via SMS to your project. 103 | enable_signup = true 104 | # If enabled, users need to confirm their phone number before signing in. 105 | enable_confirmations = false 106 | # Template for sending OTP to users 107 | template = "Your code is {{ .Code }} ." 108 | 109 | # Use pre-defined map of phone number to OTP for testing. 110 | [auth.sms.test_otp] 111 | # 4152127777 = "123456" 112 | 113 | # This hook runs before a token is issued and allows you to add additional claims based on the authentication method used. 114 | [auth.hook.custom_access_token] 115 | # enabled = true 116 | # uri = "pg-functions:////" 117 | 118 | 119 | # Configure one of the supported SMS providers: `twilio`, `twilio_verify`, `messagebird`, `textlocal`, `vonage`. 120 | [auth.sms.twilio] 121 | enabled = false 122 | account_sid = "" 123 | message_service_sid = "" 124 | # DO NOT commit your Twilio auth token to git. Use environment variable substitution instead: 125 | auth_token = "env(SUPABASE_AUTH_SMS_TWILIO_AUTH_TOKEN)" 126 | 127 | # Use an external OAuth provider. The full list of providers are: `apple`, `azure`, `bitbucket`, 128 | # `discord`, `facebook`, `github`, `gitlab`, `google`, `keycloak`, `linkedin_oidc`, `notion`, `twitch`, 129 | # `twitter`, `slack`, `spotify`, `workos`, `zoom`. 130 | [auth.external.apple] 131 | enabled = false 132 | client_id = "" 133 | # DO NOT commit your OAuth provider secret to git. Use environment variable substitution instead: 134 | secret = "env(SUPABASE_AUTH_EXTERNAL_APPLE_SECRET)" 135 | # Overrides the default auth redirectUrl. 136 | redirect_uri = "" 137 | # Overrides the default auth provider URL. Used to support self-hosted gitlab, single-tenant Azure, 138 | # or any other third-party OIDC providers. 139 | url = "" 140 | 141 | [analytics] 142 | enabled = false 143 | port = 54327 144 | vector_port = 54328 145 | # Configure one of the supported backends: `postgres`, `bigquery`. 146 | backend = "postgres" 147 | 148 | # Experimental features may be deprecated any time 149 | [experimental] 150 | # Configures Postgres storage engine to use OrioleDB (S3) 151 | orioledb_version = "" 152 | # Configures S3 bucket URL, eg. .s3-.amazonaws.com 153 | s3_host = "env(S3_HOST)" 154 | # Configures S3 bucket region, eg. us-east-1 155 | s3_region = "env(S3_REGION)" 156 | # Configures AWS_ACCESS_KEY_ID for S3 bucket 157 | s3_access_key = "env(S3_ACCESS_KEY)" 158 | # Configures AWS_SECRET_ACCESS_KEY for S3 bucket 159 | s3_secret_key = "env(S3_SECRET_KEY)" 160 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Open Augmentative and Alternative Communication App 2 | 3 | Download on the App Store 4 | 5 | AI can be used to help people communicate! 6 | 7 | This project uses [OpenAI Vector embeddings](https://platform.openai.com/docs/guides/embeddings) to translate a user's text/speech into easy to understand [AAC](https://www.asha.org/public/speech/disorders/aac/) symbols. The OpenAAC app uses OpenAI to match symbols to text to convert natural language to AAC pictograms. 8 | 9 | Currently in trial at [Learningo](https://learningo.org/)! 10 | 11 | **Highlights** 12 | * If there isn't a high match from the Vector DB, OpenAAC will use [OpenAI DALL-E 3](https://openai.com/dall-e-3) to generate a symbol's image. This image data is cached in local storage. 13 | * Tap on icon to hear the associated word using Text-To-Speech engine. 14 | * Tap on "speak" button to read out the entire text using the device's text to speech engine. 15 | 16 | The goal is to assist in communication between neurotypical and nonneurotypical users via mobile devices. 17 | 18 | [![Open AAC Demo](https://img.youtube.com/vi/prk7yOf5zJI/0.jpg)](https://youtu.be/prk7yOf5zJI) 19 | 20 | # App Images 21 | 22 | 23 | 24 | 25 | 26 | # Background 27 | My name is [Ronan O'Driscoll](https://ronanodriscoll.com/). I am a software developer with an autistic son. Through therapy and assisted communication tools, his ability to communicate has improved considerably from when he was largely non-verbal. 28 | 29 | However, it is often a struggle to pass back and forth his iPad to communicate via his AAC app. So I thought I would create a universal mobile app for a secondary user to input his symbols. We use the Speak4Yourself app, but it is only available on iOS. I wanted to create an open source alternative which could be used on any mobile device. 30 | 31 | The OpenAAC App can work with any AAC symbol set, but you need to generate and upload them manually. The `db/` folder in this repo has a number of tools to prepare and upload and convert your images as vector embeddings to the Pinecone online vector database. 32 | 33 | The `app/open_aac` folder contains the Flutter app uses cloud services to match text to symbols. 34 | 35 | AI is a powerful tool. I hope this project can help people communicate better. 36 | 37 | ## AAC Symbol Sets 38 | * [Open Symbols library](https://www.opensymbols.org/) 39 | * [The Noun Project](https://thenounproject.com/) 40 | * [Speak4Yourself](https://speakforyourself.org/) 41 | * Free download of the Speak4Yourself AAC symbols here: https://smartysymbols.com/download/free-speak-for-yourself-printable/ 42 | 43 | ## Architecture 44 | The alpha version of the flutter app connected directly to OpenAI and a Pinecone database. Unfortunately, this approach does not scale to a pool of larger users. The diagram below illustrates how the beta app connects to a vector-enabled Postgres database hosted on [Supabase](https://supabase.com/). Supabase is an exciting platform with a lot of benefits for this project, including serverless Edge functions for connecting to a vector-enabled [Postgres](https://www.postgresql.org/) database and OpenAI image generation. Supabase also provides a robust authentication mechanism, along with S3 storage of icon images. The following illustrates the flow when a user enters their text, to retrieval and presentation of the OpenAAC images. 45 | 46 | ![OpenAAC Image Lookup Flow](docs/OpenAAC_Image_Lookup_Flow.png?raw=true) 47 | 48 | Beginning with the Flutter client app, the details of the flow are as follows: 49 | 50 | 1. The User authenticates with the Supabase Auth service. 51 | 1. An authenticated user calls an Edge function to return Images for each word. 52 | 1. The Edge function converts each word to an embedding using the OpenAI embedding service. 53 | 1. The Edge function takes the embedding vector and call a Postgres function. 54 | 1. This function runs a cosine similarity match against the Images table. 55 | * A separate edge function calls [OpenAI DALL-E 3](https://openai.com/dall-e-3) generation when there isn't a good match for the word. 56 | 1. The app will call Supabase Storage for the images which can be cached locally 57 | 58 | ## Technology Stack 59 | * [Flutter](https://flutter.dev/): Cross platform mobile app framework 60 | * [Supabase](https://supabase.com/): Cloud provider of Postgres database and Serverless Edge functions 61 | * [Upstash Redis](https://upstash.com/): Cloud provider of Redis cache 62 | * [Postgres](https://www.postgresql.org/): Open source database (with [pgvector](https://github.com/pgvector/pgvector/) support) 63 | * [Pinecone](https://pub.dev/packages/pinecone): Pinecone vector database (deprecated) 64 | * [Langchain](https://pub.dev/packages/langchain): LangChain provides a set of ready-to-use components for working with language models and the concept of chains, which allows to "chain" components together to formulate more advanced use cases around LLMs. 65 | * [OpenAI Embeddings](https://platform.openai.com/docs/guides/embeddings): OpenAI’s text embeddings measure the relatedness of text strings 66 | * [OpenAI DALL-E 3](https://openai.com/dall-e-3) 67 | 68 | ## Installation 69 | Details on app availability (Android and iOS) and sign-up will be posted here soon. 70 | 71 | ## Installation (Deprecated) 72 | 1. Install Flutter: https://flutter.dev/docs/get-started/install 73 | 2. Create a Pinecone account: https://www.pinecone.io/. You can use the free tier for this project. 74 | 3. Create an OpenAI API account: https://platform.openai.com/. We will use the "text-embedding-ada-002" model which is priced very cheaply ($0.0001/1K tokens as of January 2024). See [this page](https://openai.com/pricing#language-models) for more details. For image generation, we will use "dall-e-3", which is free of charge (as of January 2024). 75 | 4. Clone this repo 76 | 5. Follow instructions in the `db/` folder to create a Pinecone database and upload your AAC symbols. 77 | 6. Copy the generated icons to the `app/open_aac/assets/images` folder. 78 | 7. Use `flutter` in the `app/open_aac` folder to run the app locally. 79 | 8. Install on your android device by connecting it to the computer using `flutter install`. See [this page](https://docs.flutter.dev/deployment/android#install-an-apk-on-a-device) for more details. 80 | 81 | ## Usage 82 | 1. Open the app 83 | 2. Click on the settings icon in the top right corner.[Deprecated] 84 | 3. Enter your OpenAI and Pinecone API keys, along with Pinecone Project ID and Environment. [Deprecated] 85 | 4. Hit Save [Deprecated] 86 | 5. Return to the main screen 87 | 6. Enter a sentence in the text box 88 | 7. Press the search button 89 | 8. The app will return a list of symbols matching the text 90 | 9. The Clear button next to the text box will clear the text and symbols 91 | 92 | ## Future Goals 93 | * ~~A local cache of words to images to cut down on API calls~~ 94 | * ~~Use a Large Language Model (Dalle-3) to generate symbols when there are low confidence matches in the vector DB.~~ 95 | * ~~Full Supabase integration~~ 96 | * [Autocomplete of previously entered sentences](https://www.dhiwise.com/post/flutter-textfield-autocomplete-for-efficient-user-data-entry) 97 | * Use Supabase Edge Function AI to generate embeddings. See [here](https://supabase.com/docs/guides/ai/quickstarts/generate-text-embeddings) 98 | * Make freely available on the Google Play Store and ~~Apple App Store~~ 99 | * Better integration with other AAC symbol sets 100 | * Allow users to upload their own custom symbols 101 | * Offline mode, so that the app can be used without an internet connection and/or database. Requires local vector database. 102 | * ~~Text to speech option: tap on icons to hear the word.~~ 103 | * ~~A button to read out the text using the device's text to speech engine.~~ This would also highlight the symbols as they are spoken. 104 | --------------------------------------------------------------------------------