├── .dockerignore ├── .github └── workflows │ └── docker-image.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── alembic.ini ├── alembic ├── README ├── env.py ├── script.py.mako └── versions │ ├── 33bddba74d25_add_suggested_links.py │ ├── 9f91a67f25f2_add_capture_images.py │ └── cc799b49fec7_initial_migration.py ├── clients ├── android │ ├── .gitignore │ ├── .idea │ │ ├── .gitignore │ │ ├── .name │ │ ├── compiler.xml │ │ ├── gradle.xml │ │ ├── kotlinc.xml │ │ ├── misc.xml │ │ └── vcs.xml │ ├── README.md │ ├── app │ │ ├── .gitignore │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── androidTest │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── owl │ │ │ │ └── Owl │ │ │ │ └── ExampleInstrumentedTest.kt │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── com │ │ │ │ │ └── owl │ │ │ │ │ └── Owl │ │ │ │ │ ├── ApiServiceSingleton.kt │ │ │ │ │ ├── AppConstants.kt │ │ │ │ │ ├── AudioStreamer.kt │ │ │ │ │ ├── CameraHandler.kt │ │ │ │ │ ├── ConversationApiService.kt │ │ │ │ │ ├── ConversationsScreen.kt │ │ │ │ │ ├── ConversationsViewModel.kt │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ ├── Models.kt │ │ │ │ │ └── ui │ │ │ │ │ └── theme │ │ │ │ │ ├── Color.kt │ │ │ │ │ ├── Theme.kt │ │ │ │ │ └── Type.kt │ │ │ └── res │ │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ │ ├── drawable │ │ │ │ └── ic_launcher_background.xml │ │ │ │ ├── layout │ │ │ │ └── activity_main.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.webp │ │ │ │ └── ic_launcher_round.webp │ │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── strings.xml │ │ │ │ └── themes.xml │ │ │ │ └── xml │ │ │ │ ├── backup_rules.xml │ │ │ │ └── data_extraction_rules.xml │ │ │ └── test │ │ │ └── java │ │ │ └── com │ │ │ └── owl │ │ │ └── Owl │ │ │ └── ExampleUnitTest.kt │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ └── settings.gradle ├── ios │ ├── Owl Watch App │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ └── Owl_Logo_1024.png │ │ │ └── Contents.json │ │ ├── OwlWatchApp.swift │ │ ├── Preview Content │ │ │ └── Preview Assets.xcassets │ │ │ │ └── Contents.json │ │ ├── Services │ │ │ ├── CaptureManager.swift │ │ │ ├── NetworkManager.swift │ │ │ └── WatchConnectivityManager.swift │ │ └── Views │ │ │ ├── ContentView.swift │ │ │ └── SettingsView.swift │ ├── Owl Watch AppTests │ │ └── Owl_Watch_AppTests.swift │ ├── Owl Watch AppUITests │ │ ├── Owl_Watch_AppUITests.swift │ │ └── Owl_Watch_AppUITestsLaunchTests.swift │ ├── Owl Watch Extension │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ └── Owl_Logo_1024.png │ │ │ ├── Contents.json │ │ │ └── WidgetBackground.colorset │ │ │ │ └── Contents.json │ │ ├── Info.plist │ │ └── Owl_Watch_Extension.swift │ ├── Owl-Watch-App-Info.plist │ ├── Owl.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── Owl Watch App.xcscheme │ │ │ ├── Owl Watch ExtensionExtension.xcscheme │ │ │ └── Owl.xcscheme │ ├── Owl │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ └── Owl_Logo_1024.png │ │ │ └── Contents.json │ │ ├── ContentView.swift │ │ ├── Extensions │ │ │ └── JSONDecoder+Extensions.swift │ │ ├── Info.plist │ │ ├── Models │ │ │ ├── Capture.swift │ │ │ ├── Conversation.swift │ │ │ ├── Location.swift │ │ │ └── Transcript.swift │ │ ├── OwlApp.swift │ │ ├── Preview Content │ │ │ └── Preview Assets.xcassets │ │ │ │ └── Contents.json │ │ ├── Services │ │ │ ├── API.swift │ │ │ ├── BLEManager.swift │ │ │ ├── CaptureManager.swift │ │ │ ├── LocationManager.swift │ │ │ ├── SocketManager.swift │ │ │ └── WatchConnectivityManager.swift │ │ ├── Util │ │ │ └── FrameSequencer.swift │ │ ├── ViewModels │ │ │ └── ConversationsViewModel.swift │ │ └── Views │ │ │ ├── ConversationDetailView.swift │ │ │ ├── ConversationsView.swift │ │ │ ├── DeviceBannerView.swift │ │ │ └── LinkMetadataView.swift │ ├── OwlTests │ │ └── OwlTests.swift │ ├── OwlUITests │ │ ├── OwlUITests.swift │ │ └── OwlUITestsLaunchTests.swift │ └── Shared │ │ ├── AppConstants.swift │ │ ├── Extensions │ │ ├── URL+Extensions.swift │ │ ├── URLRequest+Extensions.swift │ │ └── UUID+Extensions.swift │ │ ├── Files │ │ ├── AudioFileWriter.swift │ │ └── FileUploadTask.swift │ │ └── Util │ │ └── MultipartForm.swift ├── sony_spresense │ └── firmware │ │ ├── README.md │ │ └── spresense │ │ └── spresense.ino ├── web │ ├── .dockerignore │ ├── .gitignore │ ├── Dockerfile │ ├── README.md │ ├── jsconfig.json │ ├── next.config.mjs │ ├── package-lock.json │ ├── package.json │ ├── postcss.config.js │ ├── public │ │ ├── next.svg │ │ └── vercel.svg │ ├── server.js │ ├── src │ │ └── app │ │ │ ├── api │ │ │ └── [...all] │ │ │ │ └── route.js │ │ │ ├── components │ │ │ ├── CaptureComponent.js │ │ │ └── CountUpTimer.js │ │ │ ├── conversations │ │ │ └── [id] │ │ │ │ └── page.js │ │ │ ├── favicon.ico │ │ │ ├── globals.css │ │ │ ├── hooks │ │ │ ├── useAudioRecorder.js │ │ │ ├── useBluetoothAudioStreamer.js │ │ │ ├── useLocationTracker.js │ │ │ └── useSocket.js │ │ │ ├── layout.js │ │ │ ├── page.js │ │ │ ├── socket.js │ │ │ └── utils │ │ │ └── frameSequencer.js │ ├── tailwind.config.js │ └── yarn.lock └── xiao-esp32s3-sense │ ├── .gitignore │ ├── .vscode │ └── extensions.json │ └── firmware │ ├── .gitignore │ ├── .vscode │ ├── extensions.json │ └── settings.json │ ├── include │ └── README │ ├── lib │ ├── README │ └── espressif_esp_audio_codec_1.0.1 │ │ ├── CMakeLists.txt │ │ ├── LICENSE │ │ ├── README.md │ │ ├── idf_component.yml │ │ ├── include │ │ ├── esp_aac_enc.h │ │ ├── esp_adpcm_enc.h │ │ ├── esp_amrnb_enc.h │ │ ├── esp_amrwb_enc.h │ │ ├── esp_audio_codec_version.h │ │ ├── esp_audio_def.h │ │ ├── esp_audio_enc.h │ │ ├── esp_audio_enc_def.h │ │ ├── esp_g711_enc.h │ │ ├── esp_opus_enc.h │ │ └── esp_pcm_enc.h │ │ └── lib │ │ ├── esp32 │ │ └── libesp_audio_codec.a │ │ ├── esp32c3 │ │ └── libesp_audio_codec.a │ │ ├── esp32s2 │ │ └── libesp_audio_codec.a │ │ └── esp32s3 │ │ └── libesp_audio_codec.a │ ├── platformio.ini │ ├── src │ └── main.cpp │ └── test │ └── README ├── docker-compose.yml ├── docs ├── apple_watch_setup.md ├── development_boards.md ├── docker_setup.md ├── images │ ├── apple_watch │ │ ├── complication_1.jpg │ │ ├── complication_2.jpg │ │ ├── complication_3.jpg │ │ ├── complication_4.jpg │ │ ├── complication_5.jpg │ │ ├── content_view.png │ │ └── settings_view.png │ ├── bee │ │ └── bee.png │ ├── capture_storage │ │ ├── captures_today.png │ │ └── conversations.png │ ├── featured │ │ ├── apple_watch.jpg │ │ ├── devices.jpg │ │ ├── ios_conversation_example_ces.png │ │ ├── ios_conversation_example_home_depot.png │ │ ├── ios_conversations.png │ │ └── pendant_wearable.jpg │ ├── windows │ │ ├── windows_env_vars_1.png │ │ ├── windows_env_vars_2.png │ │ ├── windows_env_vars_3.png │ │ └── windows_env_vars_4.png │ ├── xcode │ │ ├── developer_mode_iphone.png │ │ ├── developer_mode_watch.png │ │ ├── xcode_app_constants.png │ │ ├── xcode_device.png │ │ ├── xcode_issue_navigator.png │ │ ├── xcode_scheme.png │ │ ├── xcode_signing_and_capabilities.png │ │ └── xcode_team_selection.png │ └── xiao_esp32s3_sense │ │ ├── antenna_installation.gif │ │ ├── battery_eemb_1200mah.jpg │ │ ├── daughterboard_installation.gif │ │ ├── ios_conversation_completed.png │ │ ├── ios_conversation_details.png │ │ ├── ios_conversation_in_progress.png │ │ ├── soldered_connectors.jpg │ │ ├── vscode_extensions.png │ │ ├── vscode_platformio.png │ │ ├── vscode_platformio_build_button.png │ │ ├── vscode_platformio_build_success.png │ │ ├── vscode_platformio_serial_port_button.png │ │ ├── vscode_platformio_upload_button.png │ │ ├── vscode_platformio_upload_success.png │ │ ├── xiao_bottom_pads.png │ │ ├── xiao_esp32s3_sense_board.jpg │ │ ├── xiao_pi_case_1.jpg │ │ └── xiao_pi_case_2.jpg ├── ios_instructions.md ├── macos_and_linux_setup.md ├── server_configuration.md ├── sony_spresense_setup.md ├── windows_setup.md └── xiao_esp32s3_sense_setup.md ├── owl ├── __init__.py ├── core │ ├── __init__.py │ ├── cli.py │ ├── config.py │ └── utils │ │ ├── __init__.py │ │ ├── async_multiprocessing_queue.py │ │ ├── hexdump.py │ │ └── suppress_output.py ├── database │ ├── __init__.py │ ├── crud.py │ └── database.py ├── devices │ ├── __init__.py │ └── device_type.py ├── files │ ├── __init__.py │ ├── aac_frame_sequencer.py │ ├── capture_directory.py │ ├── realtime_audio_converter.py │ └── wav_file.py ├── models │ ├── __init__.py │ ├── bing.py │ ├── datetime_serialization.py │ └── schemas.py ├── prompts │ ├── __init__.py │ ├── suggestion.py │ └── summarization.py ├── sample_config.yaml ├── server │ ├── __init__.py │ ├── app_state.py │ ├── capture_socket.py │ ├── main.py │ ├── routes │ │ ├── capture.py │ │ └── conversations.py │ ├── streaming_capture_handler.py │ ├── task.py │ └── udp_capture_socket.py └── services │ ├── __init__.py │ ├── capture │ └── capture_service.py │ ├── conversation │ ├── __init__.py │ ├── conversation_service.py │ └── transcript_summarizer.py │ ├── endpointing │ ├── chunking │ │ ├── conversation_detection_service.py │ │ └── conversation_endpoint_detector.py │ └── streaming │ │ ├── __init__.py │ │ ├── abstract_streaming_endpointing_service.py │ │ └── streaming_endpointing_service.py │ ├── llm │ └── llm_service.py │ ├── notification │ ├── __init__.py │ └── notification_service.py │ ├── stt │ ├── __init__.py │ ├── asynchronous │ │ ├── __init__.py │ │ ├── abstract_async_transcription_service.py │ │ ├── async_deepgram_transcription_service.py │ │ ├── async_transcription_service_factory.py │ │ ├── async_whisper │ │ │ ├── __init__.py │ │ │ └── async_whisper_transcription_server.py │ │ └── async_whisper_transcription_service.py │ └── streaming │ │ ├── __init__.py │ │ ├── abstract_streaming_transcription_service.py │ │ ├── streaming_deepgram_transcription_service.py │ │ ├── streaming_transcription_service_factory.py │ │ ├── streaming_whisper │ │ ├── __init__.py │ │ ├── audio_to_text_recorder.py │ │ └── streaming_whisper_server.py │ │ └── streaming_whisper_transcription_service.py │ ├── vad │ ├── time_segment.py │ └── vad.py │ └── web_search │ ├── __init__.py │ └── bing_search_service.py ├── poetry.lock ├── pyproject.toml ├── requirements-windows.txt └── tests ├── data └── audio │ ├── test_session.wav │ └── test_speaker.m4a └── test_whisper_transcription_service.py /.dockerignore: -------------------------------------------------------------------------------- 1 | .git/ 2 | /captures/ 3 | /clients/ 4 | /docs/ 5 | local.yaml 6 | db.sqlite3 7 | 8 | # Byte-compiled / optimized / DLL files 9 | __pycache__/ 10 | *.py[cod] 11 | *$py.class 12 | 13 | # C extensions 14 | *.so 15 | 16 | # Distribution / packaging 17 | .Python 18 | build/ 19 | develop-eggs/ 20 | dist/ 21 | downloads/ 22 | eggs/ 23 | .eggs/ 24 | lib/ 25 | lib64/ 26 | parts/ 27 | sdist/ 28 | var/ 29 | wheels/ 30 | pip-wheel-metadata/ 31 | share/python-wheels/ 32 | *.egg-info/ 33 | .installed.cfg 34 | *.egg 35 | 36 | # PyInstaller 37 | # Usually these files are written by a python script from a template 38 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 39 | *.manifest 40 | *.spec 41 | 42 | # Installer logs 43 | pip-log.txt 44 | pip-delete-this-directory.txt 45 | 46 | # Unit test / coverage reports 47 | htmlcov/ 48 | .tox/ 49 | .nox/ 50 | .coverage 51 | .coverage.* 52 | .cache 53 | nosetests.xml 54 | coverage.xml 55 | *.cover 56 | .hypothesis/ 57 | .pytest_cache/ 58 | cover/ 59 | 60 | # Translations 61 | *.mo 62 | *.pot 63 | 64 | # Django stuff: 65 | *.log 66 | local_settings.py 67 | db.sqlite3 68 | db.sqlite3-journal 69 | 70 | # Flask stuff: 71 | instance/ 72 | .webassets-cache 73 | 74 | # Scrapy stuff: 75 | .scrapy 76 | 77 | # Sphinx documentation 78 | docs/_build/ 79 | 80 | # PyBuilder 81 | target/ 82 | 83 | # Jupyter Notebook 84 | .ipynb_checkpoints 85 | 86 | # IPython 87 | profile_default/ 88 | ipython_config.py 89 | 90 | # pyenv 91 | .python-version 92 | 93 | # poetry 94 | poetry.lock 95 | .pyproject.toml 96 | __pypackages__/ 97 | 98 | # MyPy 99 | .mypy_cache/ 100 | 101 | # Pytest 102 | .pytest_cache/ 103 | 104 | # VS Code 105 | .vscode/ 106 | 107 | # PyCharm 108 | .idea/ 109 | 110 | # Environments 111 | .env 112 | .venv 113 | env/ 114 | venv/ 115 | ENV/ 116 | env.bak/ 117 | venv.bak/ 118 | 119 | # Spyder project settings 120 | .spyderproject 121 | .spyproject 122 | 123 | # Rope project settings 124 | .ropeproject 125 | 126 | # mkdocs documentation 127 | /site 128 | 129 | # Others 130 | .DS_Store 131 | *.swp 132 | *.bak 133 | *.tmp 134 | *~ 135 | 136 | # Exclude the Dockerfile and .dockerignore to prevent potential leaks 137 | Dockerfile 138 | .dockerignore -------------------------------------------------------------------------------- /.github/workflows/docker-image.yml: -------------------------------------------------------------------------------- 1 | name: Docker Images 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - 'v*' 9 | 10 | jobs: 11 | build-and-push: 12 | runs-on: ubuntu-latest 13 | permissions: 14 | contents: read 15 | packages: write 16 | steps: 17 | - name: Delete huge unnecessary tools folder 18 | run: sudo rm -rf /opt/hostedtoolcache 19 | 20 | - name: Remove unnecessary files 21 | run: | 22 | sudo rm -rf "$AGENT_TOOLSDIRECTORY" 23 | sudo rm -rf /usr/share/dotnet 24 | sudo rm -rf /opt/ghc 25 | sudo rm -rf "/usr/local/share/boost" 26 | 27 | - name: Check out the repo 28 | uses: actions/checkout@v2 29 | 30 | - name: Set up Docker Buildx 31 | uses: docker/setup-buildx-action@v1 32 | 33 | - name: Log in to Docker Hub 34 | uses: docker/login-action@v1 35 | with: 36 | username: ${{ secrets.DOCKER_USERNAME }} 37 | password: ${{ secrets.DOCKER_PASSWORD }} 38 | 39 | - name: Build and push backend image 40 | uses: docker/build-push-action@v2 41 | with: 42 | context: . 43 | file: ./Dockerfile 44 | push: true 45 | tags: etown/owl:latest 46 | platforms: linux/amd64,linux/arm64 47 | 48 | - name: Build and push web image 49 | uses: docker/build-push-action@v2 50 | with: 51 | context: ./clients/web 52 | file: ./clients/web/Dockerfile 53 | push: true 54 | tags: etown/owl-web:latest 55 | platforms: linux/amd64,linux/arm64 56 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local configuration files 2 | /*.yaml 3 | 4 | # Local database 5 | *.sqlite3 6 | 7 | # Local audio 8 | audio_cache/ 9 | *.wav 10 | *.mp3 11 | *.m4a 12 | *.mp4 13 | *.aac 14 | 15 | # vscode 16 | .vscode/launch.json 17 | 18 | 19 | # Local voice samples 20 | voice_samples/ 21 | 22 | # Local models 23 | pretrained_models/ 24 | 25 | # Captures 26 | captures/ 27 | 28 | # APN certificates 29 | *.p12 30 | *.pem 31 | *.p8 32 | 33 | # Byte-compiled / optimized / DLL files 34 | __pycache__/ 35 | *.py[cod] 36 | *$py.class 37 | 38 | # MacOS 39 | .DS_Store 40 | 41 | # Windows 42 | *.bat 43 | 44 | # Xcode 45 | ## User settings 46 | xcuserdata/ 47 | project.xcworkspace/xcuserdata/ 48 | IDEWorkspaceChecks.plist 49 | workspace.xcworkspace/ 50 | 51 | ## Build generated 52 | build/ 53 | DerivedData/ 54 | 55 | ## Various settings 56 | *.pbxuser 57 | !default.pbxuser 58 | *.mode1v3 59 | !default.mode1v3 60 | *.mode2v3 61 | !default.mode2v3 62 | *.perspectivev3 63 | !default.perspectivev3 64 | xcuserdata/ 65 | 66 | ## Other 67 | *.moved-aside 68 | *.xccheckout 69 | *.xcscmblueprint 70 | 71 | # CocoaPods 72 | Pods/ 73 | *.xcworkspace 74 | 75 | # Carthage 76 | Carthage/Build 77 | 78 | # Fastlane 79 | fastlane/report.xml 80 | fastlane/Preview.html 81 | fastlane/screenshots 82 | fastlane/test_output 83 | 84 | # Code Injection 85 | *.dynamic 86 | *.dylib 87 | inject_derived 88 | 89 | # Thumbnails 90 | ._* 91 | 92 | # Files that might appear in the root of a volume 93 | .DocumentRevisions-V100 94 | .fseventsd 95 | .Spotlight-V100 96 | .TemporaryItems 97 | .Trashes 98 | .VolumeIcon.icns 99 | .com.apple.timemachine.donotpresent 100 | 101 | # Directories potentially created on remote AFP share 102 | .AppleDB 103 | .AppleDesktop 104 | Network Trash Folder 105 | Temporary Items 106 | .apdisk -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.11-slim as base 2 | 3 | ARG DEBIAN_FRONTEND=noninteractive 4 | 5 | RUN apt-get update && apt-get install -y \ 6 | curl \ 7 | git \ 8 | ffmpeg \ 9 | pkg-config \ 10 | libcairo2-dev \ 11 | portaudio19-dev \ 12 | libsndfile1 \ 13 | libgomp1 \ 14 | libjpeg-dev \ 15 | libpng-dev \ 16 | cmake && \ 17 | apt-get clean && \ 18 | rm -rf /var/lib/apt/lists/* 19 | 20 | RUN pip install --no-cache-dir --upgrade pip && \ 21 | pip install --no-cache-dir poetry && \ 22 | poetry --version 23 | 24 | ENV PATH="/root/.local/bin:$PATH" 25 | 26 | WORKDIR /app 27 | 28 | COPY . /app 29 | 30 | RUN poetry config virtualenvs.create false && \ 31 | poetry install -vvv && \ 32 | rm -rf /root/.cache/pypoetry /root/.cache/pip 33 | 34 | RUN echo '#!/bin/sh\n\ 35 | if [ -z "$CONFIG_FILE" ]; then\n\ 36 | poetry run owl serve --host 0.0.0.0\n\ 37 | else\n\ 38 | poetry run owl serve --host 0.0.0.0 --config "$CONFIG_FILE"\n\ 39 | fi' > /entrypoint.sh && \ 40 | chmod +x /entrypoint.sh 41 | 42 | ENTRYPOINT ["/entrypoint.sh"] 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ethan Sutin and Bart Trzynadlowski 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /alembic/README: -------------------------------------------------------------------------------- 1 | Generic single-database configuration. -------------------------------------------------------------------------------- /alembic/env.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | from sqlalchemy import engine_from_config 4 | from sqlalchemy import pool 5 | 6 | from sqlmodel import SQLModel 7 | from owl.models.schemas import * 8 | 9 | from alembic import context 10 | 11 | # this is the Alembic Config object, which provides 12 | # access to the values within the .ini file in use. 13 | config = context.config 14 | 15 | # Interpret the config file for Python logging. 16 | # This line sets up loggers basically. 17 | if config.config_file_name: 18 | logging.getLogger('sqlalchemy.engine').setLevel(logging.WARN) 19 | logging.getLogger('alembic').setLevel(logging.INFO) 20 | 21 | # add your model's MetaData object here 22 | # for 'autogenerate' support 23 | # from myapp import mymodel 24 | # target_metadata = mymodel.Base.metadata 25 | 26 | target_metadata = SQLModel.metadata 27 | 28 | # other values from the config, defined by the needs of env.py, 29 | # can be acquired: 30 | # my_important_option = config.get_main_option("my_important_option") 31 | # ... etc. 32 | 33 | 34 | def run_migrations_offline() -> None: 35 | """Run migrations in 'offline' mode. 36 | 37 | This configures the context with just a URL 38 | and not an Engine, though an Engine is acceptable 39 | here as well. By skipping the Engine creation 40 | we don't even need a DBAPI to be available. 41 | 42 | Calls to context.execute() here emit the given string to the 43 | script output. 44 | 45 | """ 46 | url = config.get_main_option("sqlalchemy.url") 47 | context.configure( 48 | url=url, 49 | target_metadata=target_metadata, 50 | literal_binds=True, 51 | dialect_opts={"paramstyle": "named"}, 52 | ) 53 | 54 | with context.begin_transaction(): 55 | context.run_migrations() 56 | 57 | 58 | def run_migrations_online() -> None: 59 | """Run migrations in 'online' mode. 60 | 61 | In this scenario we need to create an Engine 62 | and associate a connection with the context. 63 | 64 | """ 65 | connectable = engine_from_config( 66 | config.get_section(config.config_ini_section, {}), 67 | prefix="sqlalchemy.", 68 | poolclass=pool.NullPool, 69 | ) 70 | 71 | with connectable.connect() as connection: 72 | context.configure( 73 | connection=connection, target_metadata=target_metadata 74 | ) 75 | 76 | with context.begin_transaction(): 77 | context.run_migrations() 78 | 79 | 80 | if context.is_offline_mode(): 81 | run_migrations_offline() 82 | else: 83 | run_migrations_online() 84 | -------------------------------------------------------------------------------- /alembic/script.py.mako: -------------------------------------------------------------------------------- 1 | """${message} 2 | 3 | Revision ID: ${up_revision} 4 | Revises: ${down_revision | comma,n} 5 | Create Date: ${create_date} 6 | 7 | """ 8 | from typing import Sequence, Union 9 | 10 | from alembic import op 11 | import sqlalchemy as sa 12 | import sqlmodel 13 | ${imports if imports else ""} 14 | 15 | # revision identifiers, used by Alembic. 16 | revision: str = ${repr(up_revision)} 17 | down_revision: Union[str, None] = ${repr(down_revision)} 18 | branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} 19 | depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} 20 | 21 | 22 | def upgrade() -> None: 23 | ${upgrades if upgrades else "pass"} 24 | 25 | 26 | def downgrade() -> None: 27 | ${downgrades if downgrades else "pass"} 28 | -------------------------------------------------------------------------------- /alembic/versions/33bddba74d25_add_suggested_links.py: -------------------------------------------------------------------------------- 1 | """Add suggested links 2 | 3 | Revision ID: 33bddba74d25 4 | Revises: cc799b49fec7 5 | Create Date: 2024-02-21 15:50:58.673704 6 | 7 | """ 8 | from typing import Sequence, Union 9 | 10 | from alembic import op 11 | import sqlalchemy as sa 12 | import sqlmodel 13 | 14 | 15 | # revision identifiers, used by Alembic. 16 | revision: str = '33bddba74d25' 17 | down_revision: Union[str, None] = 'cc799b49fec7' 18 | branch_labels: Union[str, Sequence[str], None] = None 19 | depends_on: Union[str, Sequence[str], None] = None 20 | 21 | 22 | def upgrade() -> None: 23 | # ### commands auto generated by Alembic - please adjust! ### 24 | op.create_table('suggestedlink', 25 | sa.Column('created_at', sa.DateTime(), nullable=False), 26 | sa.Column('updated_at', sa.DateTime(), nullable=False), 27 | sa.Column('id', sa.Integer(), nullable=False), 28 | sa.Column('conversation_id', sa.Integer(), nullable=True), 29 | sa.Column('url', sqlmodel.sql.sqltypes.AutoString(), nullable=False), 30 | sa.ForeignKeyConstraint(['conversation_id'], ['conversation.id'], ), 31 | sa.PrimaryKeyConstraint('id') 32 | ) 33 | # ### end Alembic commands ### 34 | 35 | 36 | def downgrade() -> None: 37 | # ### commands auto generated by Alembic - please adjust! ### 38 | op.drop_table('suggestedlink') 39 | # ### end Alembic commands ### 40 | -------------------------------------------------------------------------------- /alembic/versions/9f91a67f25f2_add_capture_images.py: -------------------------------------------------------------------------------- 1 | """Add capture images 2 | 3 | Revision ID: 9f91a67f25f2 4 | Revises: 33bddba74d25 5 | Create Date: 2024-02-28 10:59:19.873949 6 | 7 | """ 8 | from typing import Sequence, Union 9 | 10 | from alembic import op 11 | import sqlalchemy as sa 12 | import sqlmodel 13 | 14 | 15 | # revision identifiers, used by Alembic. 16 | revision: str = '9f91a67f25f2' 17 | down_revision: Union[str, None] = '33bddba74d25' 18 | branch_labels: Union[str, Sequence[str], None] = None 19 | depends_on: Union[str, Sequence[str], None] = None 20 | 21 | 22 | def upgrade() -> None: 23 | # ### commands auto generated by Alembic - please adjust! ### 24 | op.create_table('image', 25 | sa.Column('created_at', sa.DateTime(), nullable=False), 26 | sa.Column('updated_at', sa.DateTime(), nullable=False), 27 | sa.Column('id', sa.Integer(), nullable=False), 28 | sa.Column('filepath', sqlmodel.sql.sqltypes.AutoString(), nullable=False), 29 | sa.Column('captured_at', sa.DateTime(), nullable=False), 30 | sa.Column('conversation_uuid', sqlmodel.sql.sqltypes.AutoString(), nullable=False), 31 | sa.Column('source_capture_id', sa.Integer(), nullable=False), 32 | sa.Column('conversation_id', sa.Integer(), nullable=True), 33 | sa.ForeignKeyConstraint(['conversation_id'], ['conversation.id'], ), 34 | sa.ForeignKeyConstraint(['source_capture_id'], ['capture.id'], ), 35 | sa.PrimaryKeyConstraint('id') 36 | ) 37 | # ### end Alembic commands ### 38 | 39 | 40 | def downgrade() -> None: 41 | # ### commands auto generated by Alembic - please adjust! ### 42 | op.drop_table('image') 43 | # ### end Alembic commands ### 44 | -------------------------------------------------------------------------------- /clients/android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/caches 5 | /.idea/libraries 6 | /.idea/modules.xml 7 | /.idea/workspace.xml 8 | /.idea/navEditor.xml 9 | /.idea/assetWizardSettings.xml 10 | .DS_Store 11 | /build 12 | /captures 13 | .externalNativeBuild 14 | .cxx 15 | local.properties 16 | -------------------------------------------------------------------------------- /clients/android/.idea/.gitignore: -------------------------------------------------------------------------------- 1 | # Default ignored files 2 | /shelf/ 3 | /workspace.xml 4 | -------------------------------------------------------------------------------- /clients/android/.idea/.name: -------------------------------------------------------------------------------- 1 | Owl -------------------------------------------------------------------------------- /clients/android/.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /clients/android/.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 18 | 19 | -------------------------------------------------------------------------------- /clients/android/.idea/kotlinc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /clients/android/.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | -------------------------------------------------------------------------------- /clients/android/.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /clients/android/README.md: -------------------------------------------------------------------------------- 1 | # Native Android Version Coming Soon 2 | 3 | *Update 2:* There is now a skeleton for displaying conversations. Full companion UI and Bluetooth support coming soon. 4 | 5 | *Update:* This project now contains an implementation of Android as a capture device itself. This means it uses the android devices camera and microphone, primarily targeting non phone android devices as wearable/semi-wearable multimodal capture devices. See this [Demo](https://x.com/EthanSutin/status/1763646900056826153?s=20). 6 | 7 | 8 | Native Android version with full companion app and Bluetooth capture coming soon. Please [join our Discord](https://discord.gg/TwrBFG9Z) to help! 9 | 10 | In the meantime, you can use the web version on your Android device. WebBluetooth works very well on Android. 11 | [![WebBluetooth Demo](http://img.youtube.com/vi/y4bqPLv-EHo/0.jpg)](https://youtube.com/shorts/y4bqPLv-EHo "Owl Tutorial") 12 | -------------------------------------------------------------------------------- /clients/android/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /clients/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /clients/android/app/src/androidTest/java/com/owl/Owl/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.owl.Owl 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.owl.Owl", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /clients/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 20 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /clients/android/app/src/main/java/com/owl/Owl/ApiServiceSingleton.kt: -------------------------------------------------------------------------------- 1 | package com.owl.Owl 2 | 3 | object ApiServiceSingleton { 4 | val apiService: ConversationApiService by lazy { 5 | ConversationApiService() 6 | } 7 | } -------------------------------------------------------------------------------- /clients/android/app/src/main/java/com/owl/Owl/AppConstants.kt: -------------------------------------------------------------------------------- 1 | package com.owl.Owl 2 | 3 | object AppConstants { 4 | const val apiBaseURL = "" 5 | const val clientToken = "" 6 | } -------------------------------------------------------------------------------- /clients/android/app/src/main/java/com/owl/Owl/ConversationApiService.kt: -------------------------------------------------------------------------------- 1 | package com.owl.Owl 2 | 3 | import com.google.gson.Gson 4 | import com.google.gson.reflect.TypeToken 5 | import kotlinx.coroutines.Dispatchers 6 | import kotlinx.coroutines.withContext 7 | import okhttp3.OkHttpClient 8 | import okhttp3.Request 9 | 10 | class ConversationApiService { 11 | 12 | private val client = OkHttpClient() 13 | private val gson = Gson() 14 | 15 | suspend fun fetchConversations(): List = withContext(Dispatchers.IO) { 16 | try { 17 | val request = Request.Builder() 18 | .url("${AppConstants.apiBaseURL}/conversations") 19 | .addHeader("Authorization", "Bearer ${AppConstants.clientToken}") 20 | .build() 21 | 22 | client.newCall(request).execute().use { response -> 23 | if (!response.isSuccessful) throw Exception("Server responded with code $response") 24 | 25 | val responseBody = response.body?.string() ?: throw Exception("Null Response Body") 26 | val conversationsResponse = gson.fromJson(responseBody, ConversationsResponse::class.java) 27 | conversationsResponse.conversations 28 | } 29 | } catch (e: Exception) { 30 | e.printStackTrace() 31 | emptyList() 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /clients/android/app/src/main/java/com/owl/Owl/ConversationsScreen.kt: -------------------------------------------------------------------------------- 1 | package com.owl.Owl 2 | 3 | import androidx.compose.foundation.layout.Column 4 | import androidx.compose.foundation.layout.PaddingValues 5 | import androidx.compose.foundation.layout.padding 6 | import androidx.compose.foundation.lazy.LazyColumn 7 | import androidx.compose.foundation.lazy.items 8 | import androidx.compose.material3.Text 9 | import androidx.compose.runtime.Composable 10 | import androidx.compose.runtime.collectAsState 11 | import androidx.compose.ui.Modifier 12 | import androidx.compose.ui.text.style.TextOverflow 13 | import androidx.compose.ui.unit.dp 14 | import androidx.lifecycle.viewmodel.compose.viewModel 15 | 16 | @Composable 17 | fun ConversationsScreen(viewModel: ConversationsViewModel = viewModel()) { 18 | val conversations = viewModel.conversations.collectAsState(initial = emptyList()) 19 | 20 | LazyColumn( 21 | contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp) 22 | ) { 23 | items(conversations.value, key = { it.id }) { conversation -> 24 | ConversationItem(conversation = conversation) 25 | } 26 | } 27 | } 28 | 29 | @Composable 30 | fun ConversationItem(conversation: Conversation) { 31 | Column(modifier = Modifier.padding(vertical = 8.dp)) { 32 | Text( 33 | text = "Start Time: ${conversation.startTime}", 34 | modifier = Modifier.padding(bottom = 4.dp) 35 | ) 36 | Text( 37 | text = "State: ${conversation.state}", 38 | modifier = Modifier.padding(bottom = 4.dp) 39 | ) 40 | Text( 41 | text = "Summary: ${conversation.shortSummary ?: conversation.summary ?: "No summary"}", 42 | maxLines = 2, 43 | overflow = TextOverflow.Ellipsis 44 | ) 45 | } 46 | } -------------------------------------------------------------------------------- /clients/android/app/src/main/java/com/owl/Owl/ConversationsViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.owl.Owl 2 | 3 | import androidx.lifecycle.ViewModel 4 | import androidx.lifecycle.viewModelScope 5 | import kotlinx.coroutines.flow.MutableStateFlow 6 | import kotlinx.coroutines.flow.asStateFlow 7 | import kotlinx.coroutines.launch 8 | 9 | class ConversationsViewModel : ViewModel() { 10 | 11 | private val apiService = ApiServiceSingleton.apiService 12 | 13 | private val _conversations = MutableStateFlow>(emptyList()) 14 | val conversations = _conversations.asStateFlow() 15 | 16 | init { 17 | fetchConversations() 18 | } 19 | 20 | fun fetchConversations() { 21 | viewModelScope.launch { 22 | _conversations.value = apiService.fetchConversations() 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /clients/android/app/src/main/java/com/owl/Owl/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package com.owl.Owl.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | val Purple40 = Color(0xFF6650a4) 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) -------------------------------------------------------------------------------- /clients/android/app/src/main/java/com/owl/Owl/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package com.owl.Owl.ui.theme 2 | 3 | import android.app.Activity 4 | import android.os.Build 5 | import androidx.compose.foundation.isSystemInDarkTheme 6 | import androidx.compose.material3.MaterialTheme 7 | import androidx.compose.material3.darkColorScheme 8 | import androidx.compose.material3.dynamicDarkColorScheme 9 | import androidx.compose.material3.dynamicLightColorScheme 10 | import androidx.compose.material3.lightColorScheme 11 | import androidx.compose.runtime.Composable 12 | import androidx.compose.runtime.SideEffect 13 | import androidx.compose.ui.graphics.toArgb 14 | import androidx.compose.ui.platform.LocalContext 15 | import androidx.compose.ui.platform.LocalView 16 | import androidx.core.view.WindowCompat 17 | 18 | private val DarkColorScheme = darkColorScheme( 19 | primary = Purple80, 20 | secondary = PurpleGrey80, 21 | tertiary = Pink80 22 | ) 23 | 24 | private val LightColorScheme = lightColorScheme( 25 | primary = Purple40, 26 | secondary = PurpleGrey40, 27 | tertiary = Pink40 28 | 29 | /* Other default colors to override 30 | background = Color(0xFFFFFBFE), 31 | surface = Color(0xFFFFFBFE), 32 | onPrimary = Color.White, 33 | onSecondary = Color.White, 34 | onTertiary = Color.White, 35 | onBackground = Color(0xFF1C1B1F), 36 | onSurface = Color(0xFF1C1B1F), 37 | */ 38 | ) 39 | 40 | @Composable 41 | fun OwlTheme( 42 | darkTheme: Boolean = isSystemInDarkTheme(), 43 | // Dynamic color is available on Android 12+ 44 | dynamicColor: Boolean = true, 45 | content: @Composable () -> Unit 46 | ) { 47 | val colorScheme = when { 48 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { 49 | val context = LocalContext.current 50 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 51 | } 52 | 53 | darkTheme -> DarkColorScheme 54 | else -> LightColorScheme 55 | } 56 | val view = LocalView.current 57 | if (!view.isInEditMode) { 58 | SideEffect { 59 | val window = (view.context as Activity).window 60 | window.statusBarColor = colorScheme.primary.toArgb() 61 | WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme 62 | } 63 | } 64 | 65 | MaterialTheme( 66 | colorScheme = colorScheme, 67 | typography = Typography, 68 | content = content 69 | ) 70 | } -------------------------------------------------------------------------------- /clients/android/app/src/main/java/com/owl/Owl/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package com.owl.Owl.ui.theme 2 | 3 | import androidx.compose.material3.Typography 4 | import androidx.compose.ui.text.TextStyle 5 | import androidx.compose.ui.text.font.FontFamily 6 | import androidx.compose.ui.text.font.FontWeight 7 | import androidx.compose.ui.unit.sp 8 | 9 | // Set of Material typography styles to start with 10 | val Typography = Typography( 11 | bodyLarge = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp, 15 | lineHeight = 24.sp, 16 | letterSpacing = 0.5.sp 17 | ) 18 | /* Other default text styles to override 19 | titleLarge = TextStyle( 20 | fontFamily = FontFamily.Default, 21 | fontWeight = FontWeight.Normal, 22 | fontSize = 22.sp, 23 | lineHeight = 28.sp, 24 | letterSpacing = 0.sp 25 | ), 26 | labelSmall = TextStyle( 27 | fontFamily = FontFamily.Default, 28 | fontWeight = FontWeight.Medium, 29 | fontSize = 11.sp, 30 | lineHeight = 16.sp, 31 | letterSpacing = 0.5.sp 32 | ) 33 | */ 34 | ) -------------------------------------------------------------------------------- /clients/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /clients/android/app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 16 | 17 | -------------------------------------------------------------------------------- /clients/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /clients/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /clients/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwlAIProject/Owl/91922655c3cbf0e5f3282c22806b5ac0cf30063e/clients/android/app/src/main/res/mipmap-hdpi/ic_launcher.webp -------------------------------------------------------------------------------- /clients/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwlAIProject/Owl/91922655c3cbf0e5f3282c22806b5ac0cf30063e/clients/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /clients/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwlAIProject/Owl/91922655c3cbf0e5f3282c22806b5ac0cf30063e/clients/android/app/src/main/res/mipmap-mdpi/ic_launcher.webp -------------------------------------------------------------------------------- /clients/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwlAIProject/Owl/91922655c3cbf0e5f3282c22806b5ac0cf30063e/clients/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /clients/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwlAIProject/Owl/91922655c3cbf0e5f3282c22806b5ac0cf30063e/clients/android/app/src/main/res/mipmap-xhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /clients/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwlAIProject/Owl/91922655c3cbf0e5f3282c22806b5ac0cf30063e/clients/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /clients/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwlAIProject/Owl/91922655c3cbf0e5f3282c22806b5ac0cf30063e/clients/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /clients/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwlAIProject/Owl/91922655c3cbf0e5f3282c22806b5ac0cf30063e/clients/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /clients/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwlAIProject/Owl/91922655c3cbf0e5f3282c22806b5ac0cf30063e/clients/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp -------------------------------------------------------------------------------- /clients/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OwlAIProject/Owl/91922655c3cbf0e5f3282c22806b5ac0cf30063e/clients/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp -------------------------------------------------------------------------------- /clients/android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | -------------------------------------------------------------------------------- /clients/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Owl 3 | -------------------------------------------------------------------------------- /clients/android/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |