This repository has been archived on 2026-06-02. You can view files and clone it. You cannot open issues or pull requests or push a commit.
bvandeusen d441dcf954 fix(voice): fix silent STT failure — use AAC/M4A, store onError, guard NaN amplitude
Three bugs causing silent STT failure on Android:

1. AudioEncoder.opus produces an OGG container on Android but the file
   was named .webm — faster-whisper rejected it due to format mismatch.
   Changed to AudioEncoder.aacLc + .m4a (reliable on all Android versions).
   Updated voice_api.dart to send audio/mp4 MIME type accordingly.

2. onError callback was never stored, so errors in _startListening() and
   _handleSilence() were silently swallowed. Now stored as _onError and
   called before exitVoiceMode() on any failure.

3. Amplitude stream can emit NaN or ±Infinity on some Android devices
   during recorder initialisation. NaN < -40.0 is false, so silence was
   never detected. Now treated explicitly as silence.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-06 17:39:21 -04:00
2026-02-28 21:28:53 -05:00

Fabled — Android App

Native Android client for FabledAssistant, a self-hosted AI second-brain and productivity assistant.

Features

  • Daily Briefing — the primary screen; opens on launch. Shows your AI-compiled morning digest (tasks, calendar, weather, RSS) with a full conversation you can reply to inline. Refresh manually or browse past briefings from the overflow menu.
  • Quick Capture — always-visible input bar above all tabs. Type a note or task and submit; multiple captures queue sequentially so the input is never blocked. Falls back to offline persistence when the server is unreachable.
  • Knowledge — unified browsable feed of notes, people, places, lists, and tasks across six filter tabs. Tag filters, inline search (debounced), and two-tier pagination (ID list → batch hydration). Tasks load from /api/tasks directly and are fully integrated with the knowledge feed.
  • Chat — streaming AI conversations with real-time SSE display, including live tool-use status notifications (e.g. "Calling create_note…"). Tap + to start a new conversation or open an existing one.
  • Calendar — month strip + daily agenda view backed by the server's internal event store. Full event CRUD with a modal form: title, all-day toggle, start/end date+time pickers, repeat (None/Daily/Weekly/Monthly/Yearly), description, location, and colour chips. Custom RRULE strings are preserved read-only.
  • News — RSS article feed with per-feed filtering and reactions. Tap "Discuss" to open any article in a new chat conversation.
  • Projects — browse and edit projects; tap a project to see its milestone-grouped task list.
  • Voice I/O — tap the microphone in the capture bar or chat to dictate. Server-side STT transcribes audio; TTS reads assistant replies aloud in voice mode.
  • Note & task editing — full Markdown editor for notes, task editor with due date, priority, project, and milestone assignment.
  • OAuth / SSO — authenticates via your server's OIDC provider; local username/password login also supported if enabled server-side.
  • Session persistence — stays logged in across restarts via a persistent cookie jar.
  • Auto-refresh — data refreshes automatically when the app returns to the foreground (throttled to once per 5 minutes) and when switching between shell tabs.
  • Auto-update — checks your Forgejo releases on launch and prompts to download and install new APKs in-app.

Requirements

  • A running FabledAssistant server (self-hosted)
  • Android 5.0+ (API 21)

Getting Started

Prerequisites

  • Flutter 3.x SDK
  • Android Studio (for Android SDK and emulator)
  • JDK 17

Setup

flutter pub get
flutter run

On first launch, enter your FabledAssistant server URL (e.g. https://fabled.example.com) and sign in.

Architecture

lib/
├── main.dart                        # Entry point; resolves async deps before runApp
├── app.dart                         # GoRouter + auth guards + 3-tab shell + auto-refresh
├── core/
│   ├── constants.dart               # Route name constants
│   ├── exceptions.dart              # AppException hierarchy
│   └── theme.dart                   # Custom slate-indigo ColorScheme + Fraunces typography
├── data/
│   ├── api/                         # Dio HTTP layer (one class per resource)
│   │   ├── chat_api.dart            # SSE streaming; typed ChatStreamEvent (text/status)
│   │   ├── events_api.dart          # Calendar event CRUD
│   │   ├── knowledge_api.dart       # Two-tier paginated knowledge feed
│   │   ├── news_api.dart            # RSS feed + reactions
│   │   └── voice_api.dart           # STT + TTS endpoints
│   ├── models/                      # Plain Dart models with fromJson/toJson
│   │   ├── calendar_event.dart      # CalendarEvent + dateOnly() helper
│   │   ├── knowledge_item.dart      # KnowledgeItem (notes/tasks unified)
│   │   └── message.dart             # Chat message with streaming status
│   └── repositories/                # Thin wrappers over API classes
├── providers/                       # Riverpod providers (state + dependency wiring)
│   ├── briefing_provider.dart       # Today's briefing — optimistic UI, SSE, polling
│   ├── calendar_provider.dart       # CalendarNotifier: month navigation + event CRUD
│   ├── chat_provider.dart           # Conversations + streaming messages + status
│   ├── knowledge_provider.dart      # Two-tier pagination; delegates tasks to TasksApi
│   ├── news_provider.dart           # NewsNotifier + FeedsNotifier
│   └── capture_work_queue_provider.dart  # Sequential in-memory capture queue
├── screens/
│   ├── briefing/                    # BriefingScreen + BriefingHistoryScreen
│   ├── calendar/                    # CalendarScreen (TableCalendar) + EventFormSheet
│   ├── chat/                        # ConversationsTabScreen + ChatScreen
│   ├── knowledge/                   # KnowledgeScreen (6-tab feed + search + tag filters)
│   ├── library/                     # ProjectTasksScreen (milestone-grouped task list)
│   ├── news/                        # NewsScreen (feed filter + reactions + discuss)
│   ├── notes/                       # NoteDetailScreen + NoteEditScreen
│   ├── projects/                    # ProjectsScreen + ProjectEditScreen
│   ├── tasks/                       # TaskEditScreen
│   └── settings/ auth/ setup/ splash/
└── widgets/
    ├── chat_message_bubble.dart     # Shared bubble (Chat + Briefing); shows tool status
    ├── knowledge_item_card.dart     # Card for notes, people, places, lists, tasks
    ├── news_card.dart               # RSS article card with reactions
    └── voice_mic_button.dart        # Animated mic button (capture bar + chat)

Key packages: flutter_riverpod, go_router, dio + cookie_jar, google_fonts, flutter_markdown_plus, table_calendar, record, just_audio

Building a Release APK

flutter build apk --release

The APK will be at build/app/outputs/flutter-apk/app-release.apk.

CI / CD

CI and build are defined in a single workflow (.forgejo/workflows/ci.yml) running on the shared py3.12-node22 act runner with the ghcr.io/cirruslabs/flutter:stable container.

Trigger Analyze + Test Build APK Release
PR
Push dev ✓ (artifact fabledapp-dev-<sha>)
Push main
Tag v* ✓ (artifact fabledapp-<sha>) ✓ Forgejo Release + APK attached

The build job has needs: [analyze] — a failed analyze or test blocks the APK build.

To cut a release:

git tag v26.04.06 && git push origin v26.04.06

Requires a RELEASE_TOKEN secret (Forgejo PAT with write:repository scope) set in repo Settings → Secrets → Actions.

S
Description
mobile app to connect to the fabled assistant
Readme 2.6 MiB
Languages
Dart 87.9%
C++ 5.7%
CMake 4.2%
Swift 0.7%
Shell 0.5%
Other 1%