Skip to content

feat(app): allow users to rename their Omi device#4749

Open
eulicesl wants to merge 13 commits intoBasedHardware:mainfrom
eulicesl:feat/rename-device
Open

feat(app): allow users to rename their Omi device#4749
eulicesl wants to merge 13 commits intoBasedHardware:mainfrom
eulicesl:feat/rename-device

Conversation

@eulicesl
Copy link

@eulicesl eulicesl commented Feb 11, 2026

Closes #2824

Summary

  • Add a rename dialog in Device Settings — tap the device name row to open it, long-press to copy
  • Extract updateDeviceNameOnConnect() helper into SharedPreferencesUtil — centralizes device-switch detection and name preservation, eliminating 5x code duplication
  • Introduce deviceNameDeviceId preference to reliably track which device owns the custom name — fixes race conditions where btDevice.id was read after being overwritten by getDeviceInfo() or btDeviceSet()
  • Show custom name on device home screen header (falls back to hardware name)
  • Preserve custom name on disconnect; clear on unpair/forget (clears both deviceName and deviceNameDeviceId)
  • Add 4 l10n keys: renameDevice, enterDeviceName, deviceNameCannotBeEmpty, deviceNameUpdated

Demo

ScreenRecording_02-11-2026.12-07-27_1.MOV
ScreenRecording_02-11-2026.13.MOV

Changes

  • app/lib/backend/preferences.dartupdateDeviceNameOnConnect() helper + deviceNameDeviceId getter/setter
  • app/lib/providers/device_provider.dart — replace 3 inline blocks with helper call
  • app/lib/providers/onboarding_provider.dart — replace 2 inline blocks with helper call
  • app/lib/pages/settings/device_settings.dart — rename dialog, tap/long-press handler, clear deviceNameDeviceId on unpair
  • app/lib/pages/home/device.dart — show custom name in header, preserve name on disconnect, clear deviceNameDeviceId on unpair
  • app/lib/l10n/app_en.arb + generated localization files

Wrap SharedPreferencesUtil().deviceName writes with checks so
reconnections don't overwrite a user-set custom name. Also detect
device switches by comparing the connecting device ID against the
stored paired device ID — a different device resets the name.
Apply the same custom name preservation logic to onboarding flow.
Detect device switches and only write hardware name when no custom
name exists or when the device ID has changed.
Make the device name row tappable to open a rename dialog. Tap
opens the dialog, long-press copies to clipboard. Preserve custom
name on disconnect (only clear on unpair).
Remove deviceName clearing from the disconnect handler in the
device home page. Custom name should only be cleared on unpair.
Add renameDevice, enterDeviceName, deviceNameCannotBeEmpty, and
deviceNameUpdated keys to ARB and regenerate localization files.
Copilot AI review requested due to automatic review settings February 11, 2026 15:24
Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a useful feature for renaming devices. The implementation of the rename dialog and the logic to preserve the custom name on reconnection are well done. However, I've identified a couple of issues that should be addressed:

  1. Critical Bug: The 'Forget Device' functionality does not clear the custom device name, which is contrary to the feature's description and user expectations. This violates the rule about ensuring UI element actions are consistent with user expectations.
  2. Code Duplication: The logic for updating the device name on connection is repeated in five different places. This should be refactored into a single helper method to improve maintainability, aligning with the rule to prefer existing helper functions over inlining logic.

My detailed comments provide suggestions on how to fix these issues.

Comment on lines 887 to 890
await SharedPreferencesUtil().btDeviceSet(BtDevice(id: '', name: '', type: DeviceType.omi, rssi: 0));
SharedPreferencesUtil().deviceName = '';
if (provider.connectedDevice != null) {
await _bleDisconnectDevice(provider.connectedDevice!);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

critical

When a user forgets a device, their custom name for it should also be cleared. The PR description states that the name should be cleared on unpair/forget, but this change prevents that by removing the line that clears the device name. This goes against user expectations for a 'forget device' action. Please re-add the line to clear the name.

Suggested change
await SharedPreferencesUtil().btDeviceSet(BtDevice(id: '', name: '', type: DeviceType.omi, rssi: 0));
SharedPreferencesUtil().deviceName = '';
if (provider.connectedDevice != null) {
await _bleDisconnectDevice(provider.connectedDevice!);
}
await SharedPreferencesUtil().btDeviceSet(BtDevice(id: '', name: '', type: DeviceType.omi, rssi: 0));
SharedPreferencesUtil().deviceName = '';
if (provider.connectedDevice != null) {
await _bleDisconnectDevice(provider.connectedDevice!);
}
References
  1. Ensure a UI element's action is consistent with its label to meet user expectations. For instance, a button labeled 'Get App' should open the download link directly rather than triggering a share action.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the disconnect handler, not the unpair/forget handler. The unpair handler (line ~920) correctly clears both deviceName and deviceNameDeviceId.

Disconnect preserves the custom name intentionally — users expect to reconnect and see their custom name. The stale-name-on-different-device concern is now handled by deviceNameDeviceId (see 5e18237), which tracks which device ID owns the custom name independently of btDevice.id.

Comment on lines 287 to 295
var previousDeviceId = SharedPreferencesUtil().btDevice.id;
if (previousDeviceId.isNotEmpty && previousDeviceId != connectedDevice!.id) {
SharedPreferencesUtil().deviceName = connectedDevice!.name;
} else {
var storedName = SharedPreferencesUtil().deviceName;
if (storedName.isEmpty) {
SharedPreferencesUtil().deviceName = connectedDevice!.name;
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

This logic for updating the device name upon connection is duplicated in 5 places across DeviceProvider and OnboardingProvider (3 times in this file, 2 in onboarding_provider.dart). This creates maintenance challenges and increases the risk of introducing bugs if the logic needs to be updated in the future. Please refactor this into a single helper method, for example, within SharedPreferencesUtil.

Here's a suggested implementation for the helper method:

// In a utility class like SharedPreferencesUtil
void updateDeviceNameOnConnect(BtDevice newDevice) {
  final previousDeviceId = btDevice.id;
  if (previousDeviceId.isNotEmpty && previousDeviceId != newDevice.id) {
    // Switched to a new device, reset the name to hardware name.
    deviceName = newDevice.name;
  } else if (deviceName.isEmpty) {
    // Same device or first time connecting, and no custom name is set.
    // Set to hardware name.
    deviceName = newDevice.name;
  }
  // If a custom name exists for the same device, do nothing to preserve it.
}

You can then replace the duplicated blocks with a single call: SharedPreferencesUtil().updateDeviceNameOnConnect(connectedDevice!);

References
  1. Prefer using existing helper functions over inlining their logic, especially when they handle complex cases like fallbacks or error handling. Inlining can introduce subtle bugs by missing parts of the original logic.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Addressed in `5e18237`. Extracted `updateDeviceNameOnConnect(newDeviceId, hardwareName)` into `SharedPreferencesUtil` with a dedicated `deviceNameDeviceId` preference. All 5 call sites now use the single helper — see `56a4dc6` and `4c3a46a`.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds end-user device renaming support and attempts to prevent automatic reconnections from overwriting a user-defined device name, with localization updates for the new UI strings.

Changes:

  • Add a rename dialog in Device Settings (tap to rename, long-press to copy).
  • Guard writes to SharedPreferencesUtil().deviceName in DeviceProvider/OnboardingProvider to preserve custom names.
  • Add new localization keys and regenerate localization outputs.

Reviewed changes

Copilot reviewed 40 out of 40 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
app/lib/providers/onboarding_provider.dart Adds device-switch detection logic to avoid overwriting a stored custom device name during onboarding connections.
app/lib/providers/device_provider.dart Adds guarded deviceName writes intended to preserve user-defined names across reconnects and reset on device switch.
app/lib/pages/settings/device_settings.dart Adds rename dialog + tap/long-press handling; adjusts disconnect flow to avoid clearing deviceName.
app/lib/pages/home/device.dart Adjusts disconnect flow to avoid clearing deviceName.
app/lib/l10n/app_en.arb Adds 4 new English localization keys for the rename UI.
app/lib/l10n/app_localizations.dart Adds new abstract localization getters and documentation for rename UI strings.
app/lib/l10n/app_localizations_ar.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_bg.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_ca.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_cs.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_da.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_de.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_el.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_en.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_es.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_et.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_fi.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_fr.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_hi.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_hu.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_id.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_it.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_ja.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_ko.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_lt.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_lv.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_ms.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_nl.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_no.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_pl.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_pt.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_ro.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_ru.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_sk.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_sv.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_th.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_tr.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_uk.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_vi.dart Regenerated localization output including new keys.
app/lib/l10n/app_localizations_zh.dart Regenerated localization output including new keys.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +9876 to +9892
"audioPlaybackFailed": "Unable to play audio. The file may be corrupted or missing.",
"renameDevice": "Rename Device",
"@renameDevice": {
"description": "Title for rename device dialog"
},
"enterDeviceName": "Enter device name",
"@enterDeviceName": {
"description": "Hint text for device name input field"
},
"deviceNameCannotBeEmpty": "Device name cannot be empty",
"@deviceNameCannotBeEmpty": {
"description": "Error when device name is empty"
},
"deviceNameUpdated": "Device name updated",
"@deviceNameUpdated": {
"description": "Snackbar message after device name is updated"
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

New l10n keys are added only to app_en.arb. The other locale ARB files (e.g., app_zh.arb, app_vi.arb, etc.) don’t contain these keys, which results in untranslated strings (currently falling back to English in generated localization files). Add the new keys to each app_<locale>.arb (with appropriate translations or explicit fallback) to keep localization sources complete and avoid gen-l10n drift.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This follows the existing project pattern — all other l10n keys are English-only in `app_en.arb` and the generated `app_localizations_*.dart` files fall back to English. The project doesn't maintain per-locale ARB files for translations; `flutter gen-l10n` handles the fallback automatically.

Comment on lines 287 to 295
var previousDeviceId = SharedPreferencesUtil().btDevice.id;
if (previousDeviceId.isNotEmpty && previousDeviceId != connectedDevice!.id) {
SharedPreferencesUtil().deviceName = connectedDevice!.name;
} else {
var storedName = SharedPreferencesUtil().deviceName;
if (storedName.isEmpty) {
SharedPreferencesUtil().deviceName = connectedDevice!.name;
}
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DeviceProvider has existing unit tests, but the new device-name persistence/switch-detection behavior isn’t covered. Please add tests that verify: (1) a non-empty stored deviceName is not overwritten on reconnect to the same device, and (2) connecting a different device ID resets deviceName back to the hardware name (or whatever the intended behavior is).

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unit tests exist locally and pass (6 tests covering: store hardware name when empty, preserve custom name on reconnect, allow custom name override, reset on disconnect, reset on device switch, preserve on same-device reconnect). Kept out of the PR per project contribution guidelines but happy to include if reviewers want them.

Comment on lines 311 to 321
setConnectedDevice(cDevice);
setisDeviceStorageSupport();
SharedPreferencesUtil().deviceName = cDevice.name;
var previousDeviceId2 = SharedPreferencesUtil().btDevice.id;
if (previousDeviceId2.isNotEmpty && previousDeviceId2 != cDevice.id) {
SharedPreferencesUtil().deviceName = cDevice.name;
} else {
var storedName2 = SharedPreferencesUtil().deviceName;
if (storedName2.isEmpty) {
SharedPreferencesUtil().deviceName = cDevice.name;
}
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Potential race condition: setConnectedDevice(cDevice) kicks off async work that updates SharedPreferencesUtil().btDevice (via getDeviceInfo()), but previousDeviceId2 is read immediately afterwards. Depending on timing, the stored ID may be the old or the new device, making the “device switch” detection nondeterministic. Capture the stored device ID before calling setConnectedDevice(...) (or restructure so the preference update and comparison happen in a well-defined order).

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in `5e18237`. The helper now uses a dedicated `deviceNameDeviceId` preference instead of reading `btDevice.id`, so it's immune to race conditions with `setConnectedDevice()`, `getDeviceInfo()`, or `btDeviceSet()`. The comparison happens against `deviceNameDeviceId` which is only written by the helper itself.

Comment on lines 885 to 890
GestureDetector(
onTap: () async {
await SharedPreferencesUtil().btDeviceSet(BtDevice(id: '', name: '', type: DeviceType.omi, rssi: 0));
SharedPreferencesUtil().deviceName = '';
if (provider.connectedDevice != null) {
await _bleDisconnectDevice(provider.connectedDevice!);
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This disconnect handler clears btDevice (effectively “forgetting” the paired device) but now leaves SharedPreferencesUtil().deviceName intact. That can cause the app to show a stale custom name when the user later connects a different device (especially since the providers’ switch-detection relies on btDevice.id, which is now empty). Either avoid clearing btDevice for a simple disconnect, or clear deviceName when you clear btDevice, or persist an explicit deviceNameDeviceId to safely associate custom names with hardware.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The stale-name concern is now resolved by `deviceNameDeviceId` (see `5e18237`). When a different device connects, `updateDeviceNameOnConnect()` compares the new device ID against `deviceNameDeviceId` (not `btDevice.id`), so it correctly detects the switch even after disconnect cleared `btDevice`. The custom name is intentionally preserved on disconnect for same-device reconnect.

Comment on lines 359 to 364
GestureDetector(
onTap: () async {
await SharedPreferencesUtil().btDeviceSet(BtDevice(id: '', name: '', type: DeviceType.omi, rssi: 0));
SharedPreferencesUtil().deviceName = '';
if (provider.connectedDevice != null) {
await _bleDisconnectDevice(provider.connectedDevice!);
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same issue as in Device Settings: this “disconnect” action clears the stored btDevice but no longer clears the stored deviceName. After this, connecting a different device can inherit the previous device’s custom name because switch-detection is based on btDevice.id (now empty). Consider keeping btDevice on disconnect, or clearing deviceName alongside btDevice, or storing a deviceNameDeviceId mapping.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same fix — `deviceNameDeviceId` tracks the association independently of `btDevice.id`. See reply above and commit `5e18237`.

Comment on lines 493 to 500
var previousId = SharedPreferencesUtil().btDevice.id;
if (previousId.isNotEmpty && previousId != cDevice.id) {
SharedPreferencesUtil().deviceName = cDevice.name;
} else {
var storedName = SharedPreferencesUtil().deviceName;
if (storedName.isEmpty) {
SharedPreferencesUtil().deviceName = cDevice.name;
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

previousId is read from SharedPreferencesUtil().btDevice.id after btDeviceSet(device) has already overwritten the stored device. This makes the “device switch” check ineffective in onboarding (it will almost always compare the new device ID to itself), so a custom deviceName may not reset when the user connects a different device. Capture the prior stored device ID before calling btDeviceSet(...) and use that value for the comparison(s).

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in `4c3a46a`. Both inline blocks replaced with `SharedPreferencesUtil().updateDeviceNameOnConnect()` which uses `deviceNameDeviceId` — a separate preference that isn't affected by `btDeviceSet()` writes.

Comment on lines 512 to 521
var previousId2 = SharedPreferencesUtil().btDevice.id;
SharedPreferencesUtil().btDevice = connectedDevice!;
SharedPreferencesUtil().deviceName = connectedDevice.name;
if (previousId2.isNotEmpty && previousId2 != connectedDevice.id) {
SharedPreferencesUtil().deviceName = connectedDevice.name;
} else {
var storedName2 = SharedPreferencesUtil().deviceName;
if (storedName2.isEmpty) {
SharedPreferencesUtil().deviceName = connectedDevice.name;
}
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This second “device switch” guard still relies on SharedPreferencesUtil().btDevice.id, but in this flow the stored btDevice was already set earlier in handleTap. As a result, previousId2 is likely the newly selected device ID and won’t detect switching from a previously paired device. Consider carrying a single previousDeviceId captured once at the start of handleTap (before any btDevice writes) and reuse it here.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same fix — replaced with helper call in `4c3a46a`. No longer reads `btDevice.id`.

Comment on lines 431 to 440
await getDeviceInfo();
SharedPreferencesUtil().deviceName = device.name;
var previousDeviceId3 = SharedPreferencesUtil().btDevice.id;
if (previousDeviceId3.isNotEmpty && previousDeviceId3 != device.id) {
SharedPreferencesUtil().deviceName = device.name;
} else {
var storedDeviceName = SharedPreferencesUtil().deviceName;
if (storedDeviceName.isEmpty) {
SharedPreferencesUtil().deviceName = device.name;
}
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In _onDeviceConnected, getDeviceInfo() writes SharedPreferencesUtil().btDevice = pairedDevice! (see getDeviceInfo), so reading previousDeviceId3 afterwards will usually return the current device ID. That prevents this logic from ever treating a connection as a “device switch”, so a previous custom deviceName may be incorrectly preserved. Read/capture the stored device ID before getDeviceInfo() updates it (or store a separate “deviceId associated with deviceName” preference).

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in `56a4dc6`. Replaced with `SharedPreferencesUtil().updateDeviceNameOnConnect()` which uses `deviceNameDeviceId` instead of `btDevice.id`, eliminating the race with `getDeviceInfo()`.

Comment on lines 287 to 295
var previousDeviceId = SharedPreferencesUtil().btDevice.id;
if (previousDeviceId.isNotEmpty && previousDeviceId != connectedDevice!.id) {
SharedPreferencesUtil().deviceName = connectedDevice!.name;
} else {
var storedName = SharedPreferencesUtil().deviceName;
if (storedName.isEmpty) {
SharedPreferencesUtil().deviceName = connectedDevice!.name;
}
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new “only overwrite deviceName when empty or device changed” logic is duplicated in several places in this file. To reduce the chance of future inconsistencies/bugs, consider extracting it into a small helper (e.g., a private method on DeviceProvider or a SharedPreferencesUtil helper) and reuse it at each call site.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in `5e18237` — extracted `updateDeviceNameOnConnect()` into `SharedPreferencesUtil`. All 5 call sites replaced in `56a4dc6` and `4c3a46a`.

Display the user's custom name in the device page header instead
of the hardware BLE name. Falls back to hardware name when no
custom name is set.
…eferencesUtil

Add deviceNameDeviceId preference to track which device owns the
custom name. Centralizes device-switch detection and name
preservation into a single method, eliminating 5x code duplication
and fixing race conditions where btDevice.id was read after being
overwritten.
Replace 3 inline device-name guard blocks with single helper call.
…ider

Replace 2 inline device-name guard blocks with single helper call.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 41 out of 41 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 89 to 93
if (previousId.isNotEmpty && previousId != newDeviceId) {
deviceName = hardwareName;
} else if (deviceName.isEmpty) {
deviceName = hardwareName;
}
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updateDeviceNameOnConnect will not update deviceName when deviceNameDeviceId is empty but deviceName is already set (common for existing installs where deviceName was previously persisted as the last connected hardware name). This can leave a stale name when connecting to a different device for the first time after upgrading. Consider treating an empty deviceNameDeviceId as "no ownership information" and resetting deviceName to hardwareName (or otherwise explicitly handling this migration case) before setting deviceNameDeviceId.

Suggested change
if (previousId.isNotEmpty && previousId != newDeviceId) {
deviceName = hardwareName;
} else if (deviceName.isEmpty) {
deviceName = hardwareName;
}
final currentName = deviceName;
// Migration/legacy case: we have a name but no associated device ID yet.
// Treat this as "no ownership information" and reset to the hardware name
// for the first device that connects after upgrade.
if (previousId.isEmpty && currentName.isNotEmpty) {
deviceName = hardwareName;
} else if (previousId.isNotEmpty && previousId != newDeviceId) {
// Switching to a different device: reset to that device's hardware name.
deviceName = hardwareName;
} else if (currentName.isEmpty) {
// No name set yet: initialize it from the hardware name.
deviceName = hardwareName;
}

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch. Fixed in `228fb83` — when `deviceNameDeviceId` is empty (migration/legacy), we now reset `deviceName` to hardware name before recording ownership. This prevents stale names from carrying over on first connect after upgrade.

Comment on lines 150 to 160
onTap: () {
final newName = controller.text.trim();
if (newName.isEmpty) {
AppSnackbar.showSnackbarError(context.l10n.deviceNameCannotBeEmpty);
return;
}
SharedPreferencesUtil().deviceName = newName;
Navigator.of(ctx).pop();
setState(() {});
AppSnackbar.showSnackbar(context.l10n.deviceNameUpdated);
},
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the rename dialog, saving only sets deviceName but never sets deviceNameDeviceId. If a user renames while connected and deviceNameDeviceId is still empty (e.g., existing install prior to this preference being introduced), the custom name can incorrectly carry over to the next device because ownership isn’t recorded. Set deviceNameDeviceId to the current device id when saving (and consider guarding setState with mounted since this callback runs after popping the dialog).

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in `f946663`. Rename dialog now sets `deviceNameDeviceId` to the current device ID on save, and `setState` is guarded with `mounted`.

Comment on lines 285 to 288
if (connectedDevice == null) {
connectedDevice = await _getConnectedDevice();
SharedPreferencesUtil().deviceName = connectedDevice!.name;
SharedPreferencesUtil().updateDeviceNameOnConnect(connectedDevice!.id, connectedDevice!.name);
MixpanelManager().deviceConnected();
Copy link

Copilot AI Feb 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are existing unit tests for DeviceProvider (see app/test/providers/device_provider_test.dart), but the new connect-time name persistence logic is now routed through SharedPreferencesUtil.updateDeviceNameOnConnect. Adding a focused test that covers (a) first connect sets hardware name, (b) user-custom name persists for same device, and (c) switching device ids resets to hardware name would help prevent regressions.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tests covering all three scenarios exist locally and pass. Kept out of this PR per project contribution guidelines but can include if requested.

When deviceNameDeviceId is empty (existing install upgrade), reset
deviceName to hardware name instead of preserving a potentially
stale name from a previous device.
@eulicesl
Copy link
Author

eulicesl commented Feb 11, 2026

@aaravgarg videos uploaded to show feature working
@beastoin any feedback?

@eulicesl eulicesl changed the title feat(app): allow users to rename their Omi device feat(app): allow users to rename their Omi device #2824 Feb 11, 2026
@eulicesl eulicesl changed the title feat(app): allow users to rename their Omi device #2824 feat(app): allow users to rename their Omi device Feb 11, 2026
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.

rename my Omi device ($300)

1 participant