Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
86f0113
feat(app): Home redesign + upgrade entry points (from #21445, rebased…
Oct 15, 2026
cb30527
feat(app): Tasks dot after the first conversation's to-dos, starter t…
Oct 15, 2026
873c149
test(app): Tasks save tests wait for the tick-and-fold; locked memory…
Oct 15, 2026
02ff6d8
chore(app): note why the chat welcome's word reveal uses a short timer
Oct 15, 2026
991a1f0
Merge remote-tracking branch 'origin/main' into yinka/app-redesign-up…
Oct 15, 2026
dd3e9f0
feat(app): sign-in and data & privacy over the launch film (replaces …
Oct 15, 2026
74a8e30
feat(app): a phone recording reconnecting on its own shows in the bot…
Oct 15, 2026
1f8169a
feat(app): locked conversations and memories say Upgrade, matching wh…
Oct 15, 2026
23eab70
feat(app): Home's recording pill is the one place a live recording sh…
Oct 15, 2026
09a93d5
Merge remote-tracking branch 'origin/main' into yinka/app-redesign-up…
Oct 15, 2026
0e341db
chore(app): keep unrelated test files and the found-Omi baseline as o…
Oct 15, 2026
93e4960
fix(app): review fixes for the Home redesign
Oct 15, 2026
765d31f
Merge origin/main (incl. #21689 Home declutter) into yinka/app-redesi…
Oct 15, 2026
f56b6ff
feat(app): Today's rows split by stage — the pending row is audio not…
Oct 15, 2026
078349e
feat(app): home-redesign PostHog flag — test shows the redesigned Hom…
Oct 15, 2026
2b7a037
test(app): home-redesign — redesign tests run with the flag on; off-p…
Oct 15, 2026
abe91a6
refactor(app): move the paywall re-ask and the sign-in launch film ou…
Oct 15, 2026
135bbdb
chore(app): mark the control arm's verbatim literals in the legacy Ho…
Oct 15, 2026
0b6d517
chore: record the home-redesign PostHog flag (963799) and draft exper…
Oct 15, 2026
a4c9f0f
Merge origin/main into yinka/app-redesign-upgrade
YinkaAdepoju Oct 16, 2026
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.
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,12 @@
{
"version": 1,
"images": [
{"file": "001-mobile-ios-home-idle-new-recording.png", "platform": "mobile-ios", "description": "Redesigned Home (home-redesign test): no card above the list; Ask Omi beside the + New Recording pill", "captured_by": "agent", "source": "manual"},
{"file": "002-mobile-ios-home-pill-listening.png", "platform": "mobile-ios", "description": "Recording with the phone: the pill says Listening with a red dot and Stop; still no card above the list", "captured_by": "agent", "source": "manual"},
{"file": "003-mobile-ios-home-pill-paused.png", "platform": "mobile-ios", "description": "Paused: grey dot and the pill's one control is Resume", "captured_by": "agent", "source": "manual"},
{"file": "004-mobile-ios-home-pill-sheet.png", "platform": "mobile-ios", "description": "Tapping a paused pill opens the details sheet: Resume, Live Transcript, Stop Recording", "captured_by": "agent", "source": "manual"},
{"file": "005-mobile-ios-home-tasks-dot.png", "platform": "mobile-ios", "description": "Home after onboarding: the first conversation and a dot on Tasks (captured on the #21445 branch; locked row shown in its older look)", "captured_by": "agent", "source": "manual"},
{"file": "006-mobile-ios-chat-welcome.png", "platform": "mobile-ios", "description": "Omi's first chat message in an empty chat", "captured_by": "agent", "source": "manual"},
{"file": "007-mobile-ios-upgrade-in-place.png", "platform": "mobile-ios", "description": "An upgrade entry point opening the paywall experiment's arm in place (Outline arm forced)", "captured_by": "agent", "source": "manual"}
]
}
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"
}
5 changes: 5 additions & 0 deletions app/docs/ux-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,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 @@ -16,7 +16,9 @@ import 'package:omi/pages/activity/activity_bell_button.dart';
import 'package:omi/pages/activity/activity_seen.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 @@ -222,7 +224,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 @@ -231,40 +244,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 @@ -403,11 +393,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 @@ -434,9 +424,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
8 changes: 7 additions & 1 deletion app/lib/l10n/app_ar.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1516,7 +1516,7 @@
"durationSeconds": "المدة: {seconds} ثانية",
"additionalSpeechSampleRemoved": "تمت إزالة عينة الكلام الإضافية",
"consentDataMessage": "بالمتابعة، سيتم تخزين محادثاتك وتسجيلاتك ومعلوماتك الشخصية بشكل آمن على خوادمنا. تتم معالجة تسجيلاتك الصوتية ونصوصك بواسطة خدمات ذكاء اصطناعي تابعة لجهات خارجية (بما في ذلك Deepgram للنسخ و OpenAI للتحليل) لتزويدك برؤى مدعومة بالذكاء الاصطناعي وتمكين جميع ميزات التطبيق.",
"tasksEmptyStateMessage": "ابدأ محادثة لإنشاء مهمة.",
"tasksEmptyStateMessage": "ستظهر هنا المهام من محادثاتك.",
"clearChatAction": "مسح المحادثة",
"enableApps": "تفعيل التطبيقات",
"omiAppName": "Omi",
Expand Down Expand Up @@ -4129,6 +4129,12 @@
"onboardingOmiNotOrdered": "لم أطلبه بعد",
"onboardingOmiOrdered": "لقد طلبته",
"onboardingPermissionTapToFix": "اضغط للإصلاح",
"voiceProfileCardDismissed": "يمكنك إعداد بصمة صوتك في أي وقت من الإعدادات.",
"homeNewRecordingButton": "تسجيل جديد",
"chatWelcomeMessage": "مرحبًا، أنا Omi. اسألني عن أي شيء تحدثت عنه: ما قاله شخص ما، أو ما وعدت به، أو ملخص يومك. يمكنني أيضًا إضافة مهام وتذكّر الأشياء لك.",
"onboardingStarterTaskAsk": "اسأل Omi عن يومك",
"onboardingStarterTaskRecord": "سجّل محادثة مع Omi",
"onboardingStarterTaskCheck": "ضع علامة على هذه المهمة لإنهائها",
"homePendingTitle": "محادثات في الطريق",
"homePendingCatchingUp": "جارٍ اللحاق بالصوت السابق",
"onTheWayTitle": "في الطريق",
Expand Down
8 changes: 7 additions & 1 deletion app/lib/l10n/app_be.arb
Original file line number Diff line number Diff line change
Expand Up @@ -5786,7 +5786,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 @@ -11552,6 +11552,12 @@
"onboardingOmiNotOrdered": "Яшчэ не замовіў",
"onboardingOmiOrdered": "Я ўжо замовіў",
"onboardingPermissionTapToFix": "Націсніце, каб выправіць",
"voiceProfileCardDismissed": "Профіль голасу заўсёды можна наладзіць у Наладах.",
"homeNewRecordingButton": "Новы запіс",
"chatWelcomeMessage": "Прывітанне, я Omi. Пытайцеся пра ўсё, пра што вы размаўлялі: што хтосьці сказаў, што вы паабяцалі, ці як прайшоў ваш дзень. Я таксама магу дадаваць задачы і запамінаць рэчы за вас.",
"onboardingStarterTaskAsk": "Спытайце Omi пра свой дзень",
"onboardingStarterTaskRecord": "Запішыце размову з Omi",
"onboardingStarterTaskCheck": "Адзначце гэтую задачу, каб завяршыць яе",
"homePendingTitle": "Размовы ў дарозе",
"homePendingCatchingUp": "Апрацоўваем ранейшае аўдыя",
"onTheWayTitle": "У дарозе",
Expand Down
8 changes: 7 additions & 1 deletion app/lib/l10n/app_bg.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1505,7 +1505,7 @@
"durationSeconds": "Продължителност: {seconds} секунди",
"additionalSpeechSampleRemoved": "Допълнителната гласова проба е премахната",
"consentDataMessage": "С продължаването вашите разговори, записи и лична информация ще бъдат сигурно съхранени на нашите сървъри. Вашите аудиозаписи и транскрипции се обработват от AI услуги на трети страни (включително Deepgram за транскрипция и OpenAI за анализ), за да ви предоставят прозрения, базирани на AI, и да активират всички функции на приложението.",
"tasksEmptyStateMessage": "Започнете разговор, за да създадете задача.",
"tasksEmptyStateMessage": "Задачите от разговорите ви ще се появят тук.",
"clearChatAction": "Изчисти чата",
"enableApps": "Активиране на приложения",
"omiAppName": "Omi",
Expand Down Expand Up @@ -4129,6 +4129,12 @@
"onboardingOmiNotOrdered": "Още не съм поръчал",
"onboardingOmiOrdered": "Поръчал съм го",
"onboardingPermissionTapToFix": "Докоснете, за да поправите",
"voiceProfileCardDismissed": "Можете да настроите гласовия профил по всяко време от Настройки.",
"homeNewRecordingButton": "Нов запис",
"chatWelcomeMessage": "Здравей, аз съм Omi. Питай ме за всичко, за което си говорил: какво е казал някой, какво си обещал или обобщение на деня ти. Мога и да добавям задачи и да запомням неща вместо теб.",
"onboardingStarterTaskAsk": "Попитай Omi за деня си",
"onboardingStarterTaskRecord": "Запиши разговор с Omi",
"onboardingStarterTaskCheck": "Отметни тази задача, за да я завършиш",
"homePendingTitle": "Разговори на път",
"homePendingCatchingUp": "Наваксваме по-ранното аудио",
"onTheWayTitle": "На път",
Expand Down
8 changes: 7 additions & 1 deletion app/lib/l10n/app_bn.arb
Original file line number Diff line number Diff line change
Expand Up @@ -5786,7 +5786,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 @@ -11552,6 +11552,12 @@
"onboardingOmiNotOrdered": "এখনও অর্ডার করিনি",
"onboardingOmiOrdered": "আমি অর্ডার করেছি",
"onboardingPermissionTapToFix": "ঠিক করতে ট্যাপ করুন",
"voiceProfileCardDismissed": "সেটিংসে যেকোনো সময় ভয়েস প্রোফাইল সেট করতে পারেন।",
"homeNewRecordingButton": "নতুন রেকর্ডিং",
"chatWelcomeMessage": "হাই, আমি Omi। আপনি যা নিয়ে কথা বলেছেন তার যেকোনো কিছু জিজ্ঞেস করুন: কেউ কী বলেছে, আপনি কী প্রতিশ্রুতি দিয়েছেন, বা আপনার দিনের সারাংশ। আমি কাজ যোগ করতে এবং আপনার জন্য জিনিস মনে রাখতেও পারি।",
"onboardingStarterTaskAsk": "Omi-কে আপনার দিন সম্পর্কে জিজ্ঞেস করুন",
"onboardingStarterTaskRecord": "Omi দিয়ে একটি কথোপকথন রেকর্ড করুন",
"onboardingStarterTaskCheck": "শেষ করতে এই কাজটিতে টিক দিন",
"homePendingTitle": "কথোপকথন আসছে",
"homePendingCatchingUp": "আগের অডিও প্রক্রিয়া করা হচ্ছে",
"onTheWayTitle": "আসছে",
Expand Down
8 changes: 7 additions & 1 deletion app/lib/l10n/app_bs.arb
Original file line number Diff line number Diff line change
Expand Up @@ -5786,7 +5786,7 @@
"@consentDataMessage": {
"description": "Consent message explaining how user data will be stored and used"
},
"tasksEmptyStateMessage": "Započnite razgovor da kreirate zadatak.",
"tasksEmptyStateMessage": "Zadaci iz vaših razgovora pojavit će se ovdje.",
"@tasksEmptyStateMessage": {
"description": "Empty state message shown when there are no tasks, with instruction to tap + button"
},
Expand Down Expand Up @@ -11552,6 +11552,12 @@
"onboardingOmiNotOrdered": "Još nisam naručio/la",
"onboardingOmiOrdered": "Naručio/la sam ga",
"onboardingPermissionTapToFix": "Dodirnite da popravite",
"voiceProfileCardDismissed": "Glasovni profil možete podesiti bilo kada u Postavkama.",
"homeNewRecordingButton": "Novo snimanje",
"chatWelcomeMessage": "Zdravo, ja sam Omi. Pitaj me bilo šta o čemu si pričao: šta je neko rekao, šta si obećao ili sažetak tvog dana. Mogu i dodavati zadatke i pamtiti stvari za tebe.",
"onboardingStarterTaskAsk": "Pitaj Omi o svom danu",
"onboardingStarterTaskRecord": "Snimi razgovor uz Omi",
"onboardingStarterTaskCheck": "Označi ovaj zadatak da ga završiš",
"homePendingTitle": "Razgovori stižu",
"homePendingCatchingUp": "Obrađujemo raniji zvuk",
"onTheWayTitle": "Na putu",
Expand Down
Loading