Skip to content

fix/feat: misc app fixes, cancel flow, permissions, sync & firmware#6204

Merged
mdmohsin7 merged 20 commits into
mainfrom
fix/misc-app-fixes
Mar 31, 2026
Merged

fix/feat: misc app fixes, cancel flow, permissions, sync & firmware#6204
mdmohsin7 merged 20 commits into
mainfrom
fix/misc-app-fixes

Conversation

@mdmohsin7
Copy link
Copy Markdown
Member

@mdmohsin7 mdmohsin7 commented Mar 31, 2026

Summary

  • Permissions interstitial on Android: On fresh install, users signing in through OnboardingWrapper were routed directly to HomePageWrapper, bypassing the permissions gate. Now checks permissions before routing.
  • NotifyOnKill service: The notificationsEnabled SharedPreferences flag was only set during onboarding, so users who granted notification permission via Settings > Permissions never had the notifyOnKill service registered. Now checks actual system permission state.
  • Auto sync page: Added swipe-to-delete on individual WAL items and a "Manage Storage" bottom sheet (clear synced/pending/all) to match the offline sync page.
  • Developer settings build fix: _buildManualFirmwareFlash expected a DeviceProvider but was passed DeveloperModeProvider. Now reads DeviceProvider from context.
  • Subscription cancel flow: 3-step full-screen cancellation flow (reason → dynamic feedback → consequences) replacing the old confirmation dialog. Stores reason in Firestore and Mixpanel. Full funnel tracking (started/reason/confirmed/kept/abandoned).
  • Manual firmware flash fix: startLegacyDfu was ignoring the user-picked zip file path, always looking at the app documents directory. Now passes zipFilePath through to both legacy and MCU DFU paths.

Test plan

  • Fresh install on Android → sign in → verify permissions interstitial shows
  • Enable notifications via Settings > Permissions → kill app → verify "Omi disconnected" notification appears
  • Auto sync page → verify swipe-to-delete and "Manage Storage" button work
  • Developer settings with device connected → verify no build errors
  • Cancel subscription → verify 3-step flow with dynamic feedback titles
  • Cancel subscription → verify reason stored in Firestore and Mixpanel
  • Manual firmware flash → pick ZIP → verify flash starts without "firmware not found" error

🤖 Generated with Claude Code

…stall

On Android, Firebase auth is cleared on uninstall so users sign in
through OnboardingWrapper. The auth callback and initState both routed
directly to HomePageWrapper, bypassing the _PermissionsGate. This adds
a permissions check before routing so the interstitial page is shown
when permissions haven't been granted.
The notificationsEnabled SharedPreferences flag was only set during
onboarding. Users who granted notification permission via Settings >
Permissions never had the flag set, so the notifyOnKill service was
never registered and no notification was shown on app kill.

Now checks Permission.notification.isGranted directly and syncs the
pref flag, so the service is registered regardless of how the user
granted permission.
Add swipe-to-delete on individual WAL items and a "Manage Storage"
button with options to clear synced files, pending files, or all files,
matching the functionality already present in the offline sync page.
_buildManualFirmwareFlash expects a DeviceProvider but was being passed
the DeveloperModeProvider from the Consumer. Now reads DeviceProvider
from context via watch<DeviceProvider>().
Adds CancelSubscriptionRequest with optional reason and reason_details.
Stores feedback on the user document in Firestore before Stripe cancel.
Fully backwards compatible.
Tracks flow started, reason selected, confirmed, kept plan, and
abandoned with step number and reason for full funnel analysis.
Full-screen page flow replacing the old confirmation dialog:
1. Reason selection (7 options with icons)
2. Dynamic feedback (title/subtitle change based on selected reason)
3. Consequences + billing info + confirm

Stores reason in Firestore and Mixpanel. Backend receives the reason
key (e.g. too_expensive) while UI shows localized labels.
Adds reason labels, dynamic feedback titles/subtitles, consequence
texts, and billing info strings. Regenerated localizations.
startLegacyDfu was hardcoding the firmware path to the app documents
directory, ignoring the user-picked file. Since isLegacySecureDFU
defaults to true and getLatestVersion is never called for manual flash,
it always took the legacy path — causing "DFU firmware not found".
Manual flash always provides a modern firmware ZIP (manifest.json +
image files). NordicDfu (legacy) cannot parse this format, causing
"could not dfu zip file". Now calls startMCUDfu directly.
@mdmohsin7 mdmohsin7 marked this pull request as ready for review March 31, 2026 17:40
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 31, 2026

Greptile Summary

This PR delivers six targeted bug fixes: Android permissions gate on sign-in, NotifyOnKill service registration, auto-sync WAL management UI, developer settings build error, subscription cancellation flow (replaced with a new 3-step full-screen flow with Mixpanel/Firestore tracking), and manual firmware flash ignoring user-picked ZIP files.

Key changes:

  • onboarding/wrapper.dart: Adds _routeWithPermissionsCheck to check actual permission state before routing to HomePageWrapper; second call site in the onSignIn callback is missing await (the callback is not async)
  • cancel_subscription_sheet.dart (new): 3-step full-screen cancellation flow; PopScope(canPop: !_isCancelling) causes the system back button to close the entire flow from pages 1 and 2 rather than navigating back a step — the onPopInvokedWithResult guard condition can never fire in the scenario it is meant to handle
  • firmware_mixin.dart: zipFilePath now forwarded to startLegacyDfu; manual flash page hard-coded to MCU DFU
  • home/page.dart: Live Permission.notification.isGranted check replaces potentially stale SharedPreferences flag
  • developer.dart: Builder widget reads DeviceProvider from context instead of receiving the wrong DeveloperModeProvider
  • Backend: Optional cancellation reason stored in Firestore and Mixpanel; reason_details: str = None type hint should be Optional[str] = None

Confidence Score: 4/5

Mostly safe to merge; one P1 UX bug in the new cancellation flow lets the system back button skip to close instead of going back a step.

A P1 logic bug in cancel_subscription_sheet.dart means PopScope(canPop: !_isCancelling) closes the entire cancellation flow when the user is on pages 1 or 2 and presses the system back button. The onPopInvokedWithResult callback is effectively dead code due to the invariant between canPop and !_isCancelling. All other fixes are correct and well-guarded.

app/lib/pages/settings/widgets/cancel_subscription_sheet.dart (PopScope canPop logic) and app/lib/pages/onboarding/wrapper.dart (unawaited async call in onSignIn).

Important Files Changed

Filename Overview
app/lib/pages/settings/widgets/cancel_subscription_sheet.dart New 3-step cancellation flow widget; PopScope(canPop: !_isCancelling) causes system back to close the entire flow from pages 1 and 2 instead of going back a step — P1 logic bug.
app/lib/pages/onboarding/wrapper.dart Adds _routeWithPermissionsCheck to gate sign-in via the permissions interstitial; second call site in onSignIn callback is not awaited (callback is not async), leaving the Future unhandled.
app/lib/pages/home/firmware_mixin.dart Passes zipFilePath through to startLegacyDfu so user-picked ZIP files are no longer ignored — straightforward and correct fix.
app/lib/pages/home/page.dart Replaces SharedPreferences flag check with live Permission.notification.isGranted to correctly register NotifyOnKill service for users who granted permissions post-onboarding.
app/lib/pages/settings/developer.dart Fixes _buildManualFirmwareFlash to read DeviceProvider from context via a Builder instead of receiving the wrong DeveloperModeProvider; also switches manual flash to always use MCU DFU.
backend/routers/payment.py Adds optional CancelSubscriptionRequest body to the DELETE subscription endpoint; stores cancellation reason before proceeding — correct and clean.
backend/database/users.py Adds set_user_cancellation_feedback to write reason+details to Firestore; reason_details: str = None should be Optional[str] = None per PEP 484.
app/lib/pages/conversations/auto_sync_page.dart Adds swipe-to-delete on WAL items and a Manage Storage bottom sheet (clear synced/pending/all) with proper confirmation dialogs and mounted checks — well-implemented.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[User taps Cancel Subscription] --> B[CancelSubscriptionFlow.show]
    B --> C[Step 1: Pick Reason]
    C -->|Reason selected + Continue| D[MixpanelManager subscriptionCancelReasonSelected]
    D --> E[Step 2: Feedback]
    E -->|Continue or Skip| F[Step 3: Confirm]
    F -->|Keep My Plan| G[MixpanelManager subscriptionCancelKeptPlan]
    G --> H[pop false]
    F -->|Confirm and Cancel| I[_confirmCancel]
    I --> J[MixpanelManager subscriptionCancelConfirmed]
    J --> K[UsageProvider cancelUserSubscription]
    K --> L[DELETE /v1/payments/subscription with JSON body]
    L --> M[backend: store cancellation_feedback in Firestore]
    M --> N[fetchSubscription + loadAvailablePlans]
    N --> O[pop true]
    C -->|AppBar back on page 0| P[MixpanelManager subscriptionCancelAbandoned]
    P --> Q[pop false]
    C -->|System back P1 bug| R[Entire flow closes immediately - pages 1 and 2 also affected]
Loading

Reviews (1): Last reviewed commit: "feat: add translations for cancel flow k..." | Re-trigger Greptile

Comment thread app/lib/pages/settings/widgets/cancel_subscription_sheet.dart
Comment thread app/lib/pages/onboarding/wrapper.dart Outdated
Comment thread backend/database/users.py Outdated
Use datetime.now(timezone.utc) instead of time.time() so Firestore
stores it as a proper Timestamp type, consistent with other timestamp
fields like speaker_embedding_updated_at.
…tails

- Fix canPop logic so system back navigates to previous cancel flow
  step instead of closing the entire flow
- Await _routeWithPermissionsCheck in onSignIn callback
- Add Optional type annotation to reason_details parameter
- Store empty string instead of null when reason_details not provided
Shows battery optimization exemption row on Android, matching the
onboarding and interstitial permission flows.
@mdmohsin7 mdmohsin7 changed the title fix: misc app fixes fix/feat: misc app fixes, cancel flow, permissions, sync & firmware Mar 31, 2026
@mdmohsin7 mdmohsin7 merged commit f83c832 into main Mar 31, 2026
2 checks passed
@mdmohsin7 mdmohsin7 deleted the fix/misc-app-fixes branch March 31, 2026 18:33
Glucksberg pushed a commit to Glucksberg/omi-local that referenced this pull request Apr 28, 2026
…asedHardware#6204)

## Summary
- **Permissions interstitial on Android**: On fresh install, users
signing in through OnboardingWrapper were routed directly to
HomePageWrapper, bypassing the permissions gate. Now checks permissions
before routing.
- **NotifyOnKill service**: The `notificationsEnabled` SharedPreferences
flag was only set during onboarding, so users who granted notification
permission via Settings > Permissions never had the notifyOnKill service
registered. Now checks actual system permission state.
- **Auto sync page**: Added swipe-to-delete on individual WAL items and
a "Manage Storage" bottom sheet (clear synced/pending/all) to match the
offline sync page.
- **Developer settings build fix**: `_buildManualFirmwareFlash` expected
a `DeviceProvider` but was passed `DeveloperModeProvider`. Now reads
`DeviceProvider` from context.
- **Subscription cancel flow**: 3-step full-screen cancellation flow
(reason → dynamic feedback → consequences) replacing the old
confirmation dialog. Stores reason in Firestore and Mixpanel. Full
funnel tracking (started/reason/confirmed/kept/abandoned).
- **Manual firmware flash fix**: `startLegacyDfu` was ignoring the
user-picked zip file path, always looking at the app documents
directory. Now passes `zipFilePath` through to both legacy and MCU DFU
paths.

## Test plan
- [ ] Fresh install on Android → sign in → verify permissions
interstitial shows
- [x] Enable notifications via Settings > Permissions → kill app →
verify "Omi disconnected" notification appears
- [x] Auto sync page → verify swipe-to-delete and "Manage Storage"
button work
- [x] Developer settings with device connected → verify no build errors
- [x] Cancel subscription → verify 3-step flow with dynamic feedback
titles
- [x] Cancel subscription → verify reason stored in Firestore and
Mixpanel
- [x] Manual firmware flash → pick ZIP → verify flash starts without
"firmware not found" error

🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant