Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"version": 1,
"images": [
{
"file": "001-mobile-ios-sign-in-film.png",
"platform": "mobile-ios",
"description": "Sign-in over the launch film, no Get started page (local test build shows one local sign-in button where store builds show Apple/Google)",
"captured_by": "agent",
"source": "manual"
},
{
"file": "002-mobile-ios-data-privacy-film.png",
"platform": "mobile-ios",
"description": "Data & Privacy over the launch film",
"captured_by": "agent",
"source": "manual"
},
{
"file": "003-mobile-ios-home-tasks-dot.png",
"platform": "mobile-ios",
"description": "Home after onboarding: the first conversation and a dot on Tasks",
"captured_by": "agent",
"source": "manual"
},
{
"file": "004-mobile-ios-home-live-pill.png",
"platform": "mobile-ios",
"description": "Bottom-bar live pill while the phone records (local server without transcription, so Reconnecting also shows)",
"captured_by": "agent",
"source": "manual"
},
{
"file": "005-mobile-ios-chat-welcome.png",
"platform": "mobile-ios",
"description": "Omi's first chat message in an empty chat",
"captured_by": "agent",
"source": "manual"
},
{
"file": "006-mobile-ios-settings-upgrade.png",
"platform": "mobile-ios",
"description": "The paywall experiment's arm opening in place (Settings > Upgrade; the app-open re-ask opens the same paywall). Outline arm forced",
"captured_by": "agent",
"source": "manual"
}
]
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
19 changes: 19 additions & 0 deletions .agent-artifacts/ui-evidence/yinka-settings-rework/evidence.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"version": 1,
"images": [
{
"file": "001-mobile-ios-settings-before-light.png",
"platform": "mobile-ios",
"description": "Settings sheet before (Free plan, widget-test render of #21711's head): full-width Upgrade, Referral with a NEW tag beside Plan, one long group of eight rows",
"captured_by": "agent",
"source": "manual"
},
{
"file": "002-mobile-ios-settings-after-light.png",
"platform": "mobile-ios",
"description": "Settings sheet after, iOS simulator (card arm, Free plan, no device): no handle band, plan card with the plan name large and a full-width Upgrade, Account initial, Device (Disconnected) / Recording / Notifications / Privacy first",
"captured_by": "agent",
"source": "manual"
}
]
}
Binary file added app/assets/videos/onboarding_launch_loop.mp4
Binary file not shown.
3 changes: 3 additions & 0 deletions app/changelog/unreleased/20261015-home-redesign-upgrade.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"change": "A calmer Home: recording lives in the bottom bar, new conversations read as they form, and upgrading opens right where you are"
}
3 changes: 3 additions & 0 deletions app/changelog/unreleased/20261015-settings-order.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"change": "Settings puts what you use most first: Device (with battery), Recording & Transcription, Notifications and Privacy"
}
2 changes: 0 additions & 2 deletions app/contracts/addressability/unkeyed-baseline.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,10 +98,8 @@
"app/lib/pages/onboarding/apple_watch_permission_page.dart": 9,
"app/lib/pages/onboarding/auth.dart": 6,
"app/lib/pages/onboarding/complete_screen.dart": 2,
"app/lib/pages/onboarding/device_selection.dart": 2,
"app/lib/pages/onboarding/find_device/found_devices.dart": 1,
"app/lib/pages/onboarding/find_device/page.dart": 2,
"app/lib/pages/onboarding/found_omi/found_omi_widget.dart": 4,
"app/lib/pages/onboarding/interactive_device_onboarding/interactive_device_onboarding_wrapper.dart": 1,
"app/lib/pages/onboarding/interactive_device_onboarding/steps/double_press_config_step.dart": 1,
"app/lib/pages/onboarding/interactive_device_onboarding/widgets/onboarding_intro_screen.dart": 2,
Expand Down
7 changes: 7 additions & 0 deletions app/docs/ux-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ There are exactly two ways out, and they mean different things.
widget. It owns the top radius (`OmiRadius.xl`), the 36×4 drag handle, the optional title row
with a trailing `OmiCloseButton`, safe-area and keyboard insets, and `isScrollControlled`. Never
a raw `showModalBottomSheet` (`raw-bottom-sheet`) and never a hand-drawn handle.
- The Settings sheet drops the handle band (`showDragHandle: false`): its own header, with the close X,
is the top of the sheet, and it still swipes down to close.
- A sheet nothing may dismiss (a required update) passes `isDismissible: false, enableDrag: false`
and blocks back with `PopScope`, and has no handle that promises a swipe: `showOmiSheet` drops it
itself; `showOmiSurfaceSheet` takes `showDragHandle: false`.
Expand Down Expand Up @@ -307,6 +309,11 @@ guidance.
Open Settings) — then the system prompt. A screen's Continue asks, in row order, for every
permission still askable (`requestMissingOnboardingPermissions`), then moves on whatever the
reader chose.
- First-run exception (`pages/onboarding/permissions/permissions_widget.dart`): one switch card per
permission; toggling a card shows its system prompt. Continue unlocks once every card is
**answered**, never only when granted: a "no" turns the card orange (notifications red with
"Tap to fix", which opens Settings) and the reader moves on. Bluetooth is asked as the app
opens, so its card appears only when it was not allowed.
- Permanently denied → the row says so and offers **Open Settings** (`l10n.openSettings`).
- Ask when a feature needs the permission ("Always" location only when a feature requires it).

Expand Down
10 changes: 5 additions & 5 deletions app/integration_test/app_performance_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ void main() {
debugPrint('[4/7] Navigating to CHAT screen...');

// Find and tap the "Ask Omi" button
final askOmiButton = find.text('Ask Omi');
final askOmiButton = find.byKey(const ValueKey('home_ask_omi_button'));
if (askOmiButton.evaluate().isNotEmpty) {
await tester.tap(askOmiButton);
await _pumpFor(tester, 2000);
Expand Down Expand Up @@ -185,7 +185,7 @@ Future<void> _handleOnboardingIfNeeded(WidgetTester tester) async {
debugPrint('[2/7] Checking for onboarding...');

// Check if we're already on home screen
final askOmi = find.text('Ask Omi');
final askOmi = find.byKey(const ValueKey('home_ask_omi_button'));
if (askOmi.evaluate().isNotEmpty) {
debugPrint(' Already logged in - on home screen');
return;
Expand Down Expand Up @@ -275,7 +275,7 @@ Future<void> _handleAuthFlow(WidgetTester tester) async {
await tester.pump(const Duration(milliseconds: 500));

// Check if we've reached home screen
final askOmi = find.text('Ask Omi');
final askOmi = find.byKey(const ValueKey('home_ask_omi_button'));
if (askOmi.evaluate().isNotEmpty) {
debugPrint(' Sign-in complete - reached home screen');
return;
Expand Down Expand Up @@ -377,7 +377,7 @@ Future<void> _handleUserReviewScreen(WidgetTester tester) async {
}

// Check if we've already passed to home screen
final askOmi = find.text('Ask Omi');
final askOmi = find.byKey(const ValueKey('home_ask_omi_button'));
if (askOmi.evaluate().isNotEmpty) {
debugPrint(' Already on home screen - skipping user review');
return;
Expand Down Expand Up @@ -484,7 +484,7 @@ Future<void> _handleSpeechProfileScreen(WidgetTester tester) async {
await _pumpFor(tester, 500);

// Check if we're already on home screen
final askOmi = find.text('Ask Omi');
final askOmi = find.byKey(const ValueKey('home_ask_omi_button'));
if (askOmi.evaluate().isNotEmpty) {
debugPrint(' Onboarding complete - reached home screen');
return;
Expand Down
64 changes: 27 additions & 37 deletions app/integration_test/visual_audit/scenarios/capture.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Home's capture surfaces where the app really draws them: the live card at the top of Home, the
// record button to the right of the floating Ask Omi bar, the device chip in the header,
// the sheets they open, and the live page. The frame mirrors HomePage's layout; its Ask Omi bar
// is a copy of HomePage._buildChatBar (private there).
// record pill beside the Ask Omi button at the bottom ("+ New", "Stop", or the capturing pendant),
// the device chip in the header, the sheets they open, and the live page. The frame mirrors
// HomePage's layout.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:font_awesome_flutter/font_awesome_flutter.dart';
Expand All @@ -14,7 +14,9 @@ import 'package:omi/backend/schema/phone_call.dart';
import 'package:omi/backend/schema/transcript_segment.dart';
import 'package:omi/pages/conversation_capturing/page.dart';
import 'package:omi/pages/home/home_content.dart';
import 'package:omi/pages/action_items/widgets/tasks_more_menu.dart';
import 'package:omi/pages/home/widgets/battery_info_widget.dart';
import 'package:omi/pages/home/widgets/home_bottom_controls.dart';
import 'package:omi/pages/home/widgets/home_daily_recaps.dart';
import 'package:omi/pages/home/widgets/home_tab_switcher.dart';
import 'package:omi/backend/schema/daily_summary.dart';
Expand Down Expand Up @@ -209,7 +211,18 @@ class HomeFrame extends StatelessWidget {
),
body: Stack(children: [
Column(children: [
HomeTabSwitcher(onTabTap: (_, __) {}),
// The selected page's one action beside the switcher, as HomePage draws it.
HomeTabSwitcher(
onTabTap: (_, __) {},
trailing: tasks != null
? const TasksMoreMenu()
: OmiIconButton(
icon: const FaIcon(FontAwesomeIcons.calendar, size: 18),
label: 'Filter by date',
color: OmiColors.textSecondary,
onPressed: () {},
),
),
Expanded(child: tasks ?? HomeContentPage(loadRecaps: recaps)),
]),
if (tasks == null) const HomeChatBarBackdrop(),
Expand All @@ -218,40 +231,17 @@ class HomeFrame extends StatelessWidget {
left: 16,
right: 16,
bottom: homeChatBarOffset(context),
child: const Row(children: [Expanded(child: _AskOmiBar()), SizedBox(width: 10), HomeRecordButton()]),
child: Row(children: [
HomeChatButton(onTap: () {}, onLongPress: () {}),
const SizedBox(width: 10),
const Expanded(child: HomeRecordButton()),
]),
),
]),
);
}
}

/// Copy of HomePage._buildChatBar's look.
class _AskOmiBar extends StatelessWidget {
const _AskOmiBar();

@override
Widget build(BuildContext context) => Container(
height: kHomeChatBarHeight,
decoration: BoxDecoration(
color: OmiColors.surface1,
borderRadius: OmiRadius.pillAll,
border: Border.all(color: OmiColors.border, width: 1),
),
child: Row(children: [
const SizedBox(width: 18),
Expanded(child: Text('Ask Omi', style: OmiType.subhead.copyWith(color: OmiColors.textTertiary))),
Container(
width: 42,
height: 42,
margin: const EdgeInsets.only(right: 6),
alignment: Alignment.center,
decoration: BoxDecoration(color: OmiColors.accent, shape: BoxShape.circle),
child: FaIcon(FontAwesomeIcons.microphone, size: 15, color: OmiColors.onAccent),
),
]),
);
}

Future<void> _runHome(
AuditRun a,
AuditLive live, {
Expand Down Expand Up @@ -390,11 +380,11 @@ final captureScenarios = <AuditScenario>[
),
AuditScenario(
id: 'home-capture-pendant-tap',
title: 'Tap the record button while the pendant records',
title: 'Hold the pendant pill while the pendant records',
page: _home,
state: 'The pendant streams; the record button is tapped',
state: 'The pendant streams; its pill (in place of + New) is held',
run: (a) => _runHome(a, AuditLive.pendant,
pendantConnected: true, tap: find.byType(HomeRecordButton), action: 'Tap the record button'),
pendantConnected: true, longPress: find.byType(HomeRecordButton), action: 'Hold the pendant pill'),
),
AuditScenario(
id: 'home-capture-phone-after-pendant',
Expand All @@ -421,9 +411,9 @@ final captureScenarios = <AuditScenario>[
id: 'home-capture-options',
title: 'Other ways to record',
page: _home,
state: 'Nothing recording; the record button is held (the ⌄ badge opens the same sheet)',
state: 'Nothing recording; + New is held',
run: (a) async {
await _runHome(a, AuditLive.idle, longPress: find.byType(HomeRecordButton), action: 'Hold the record button');
await _runHome(a, AuditLive.idle, longPress: find.byType(HomeRecordButton), action: 'Hold + New');
},
),
AuditScenario(
Expand Down
5 changes: 5 additions & 0 deletions app/lib/backend/preferences.dart
Original file line number Diff line number Diff line change
Expand Up @@ -996,6 +996,11 @@ class SharedPreferencesUtil {

set hasSpeakerProfile(bool value) => saveBool('hasSpeakerProfile', value);

/// The reader closed Home's "Teach Omi your voice" card; Settings > Voice Profile still has it.
bool get voiceProfileCardDismissed => getBool('voiceProfileCardDismissed');

set voiceProfileCardDismissed(bool value) => saveBool('voiceProfileCardDismissed', value);

bool get showDiscardedMemories => getBool('showDiscardedMemories', defaultValue: false);

set showDiscardedMemories(bool value) => saveBool('showDiscardedMemories', value);
Expand Down
17 changes: 12 additions & 5 deletions app/lib/gen/assets.gen.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 10 additions & 2 deletions app/lib/l10n/app_ar.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1512,7 +1512,7 @@
"durationSeconds": "المدة: {seconds} ثانية",
"additionalSpeechSampleRemoved": "تمت إزالة عينة الكلام الإضافية",
"consentDataMessage": "بالمتابعة، سيتم تخزين محادثاتك وتسجيلاتك ومعلوماتك الشخصية بشكل آمن على خوادمنا. تتم معالجة تسجيلاتك الصوتية ونصوصك بواسطة خدمات ذكاء اصطناعي تابعة لجهات خارجية (بما في ذلك Deepgram للنسخ و OpenAI للتحليل) لتزويدك برؤى مدعومة بالذكاء الاصطناعي وتمكين جميع ميزات التطبيق.",
"tasksEmptyStateMessage": "ابدأ محادثة لإنشاء مهمة.",
"tasksEmptyStateMessage": "ستظهر هنا المهام من محادثاتك.",
"clearChatAction": "مسح المحادثة",
"enableApps": "تفعيل التطبيقات",
"omiAppName": "Omi",
Expand Down Expand Up @@ -4115,5 +4115,13 @@
"onboardingNoOmiYet": "ليس لديك Omi بعد؟",
"onboardingOmiNotOrdered": "لم أطلبه بعد",
"onboardingOmiOrdered": "لقد طلبته",
"onboardingPermissionTapToFix": "اضغط للإصلاح"
"onboardingPermissionTapToFix": "اضغط للإصلاح",
"consentShortMessage": "يستخدم Omi الذكاء الاصطناعي لتحويل محادثاتك إلى ملاحظات. يمكنك حذفها في أي وقت.",
"consentAiProviders": "تتم المعالجة بواسطة {providers}.",
"voiceProfileCardDismissed": "يمكنك إعداد بصمة صوتك في أي وقت من الإعدادات.",
"homeNewRecordingButton": "تسجيل جديد",
"chatWelcomeMessage": "مرحبًا، أنا Omi. اسألني عن أي شيء تحدثت عنه: ما قاله شخص ما، أو ما وعدت به، أو ملخص يومك. يمكنني أيضًا إضافة مهام وتذكّر الأشياء لك.",
"onboardingStarterTaskAsk": "اسأل Omi عن يومك",
"onboardingStarterTaskRecord": "سجّل محادثة مع Omi",
"onboardingStarterTaskCheck": "ضع علامة على هذه المهمة لإنهائها"
}
12 changes: 10 additions & 2 deletions app/lib/l10n/app_be.arb
Original file line number Diff line number Diff line change
Expand Up @@ -5782,7 +5782,7 @@
"@consentDataMessage": {
"description": "Consent message explaining how user data will be stored and used"
},
"tasksEmptyStateMessage": "Пачніце размову, каб стварыць задачу.",
"tasksEmptyStateMessage": "Справы з вашых размоў з'явяцца тут.",
"@tasksEmptyStateMessage": {
"description": "Empty state message shown when there are no tasks, with instruction to tap + button"
},
Expand Down Expand Up @@ -11541,5 +11541,13 @@
"onboardingNoOmiYet": "Яшчэ няма Omi?",
"onboardingOmiNotOrdered": "Яшчэ не замовіў",
"onboardingOmiOrdered": "Я ўжо замовіў",
"onboardingPermissionTapToFix": "Націсніце, каб выправіць"
"onboardingPermissionTapToFix": "Націсніце, каб выправіць",
"consentShortMessage": "Omi з дапамогай ШІ ператварае вашы размовы ў нататкі. Іх можна выдаліць у любы момант.",
"consentAiProviders": "Апрацоўка: {providers}.",
"voiceProfileCardDismissed": "Профіль голасу заўсёды можна наладзіць у Наладах.",
"homeNewRecordingButton": "Новы запіс",
"chatWelcomeMessage": "Прывітанне, я Omi. Пытайцеся пра ўсё, пра што вы размаўлялі: што хтосьці сказаў, што вы паабяцалі, ці як прайшоў ваш дзень. Я таксама магу дадаваць задачы і запамінаць рэчы за вас.",
"onboardingStarterTaskAsk": "Спытайце Omi пра свой дзень",
"onboardingStarterTaskRecord": "Запішыце размову з Omi",
"onboardingStarterTaskCheck": "Адзначце гэтую задачу, каб завяршыць яе"
}
Loading
Loading