feat(#509): hidden 7-tap Experimental unlock + settings section

7 taps on the About version row (rolling ~3s window) reveal a neutral
'Experimental' settings category; persisted on AppSettings, survives
restart, re-hidden from a switch inside the section. Empty of toggles
for now (Fast Sync #118, CoreScope #524 land into it later).

Countdown snackbars after tap 4; already-unlocked feedback. Version-text
tap is absorbed so it counts without opening the About dialog.

Agent: QuietSnow (session 31eaba02)
chore/542-assets2
Strycher 2 months ago
parent 8724e2a796
commit 567745db18

@ -215,6 +215,21 @@
"settings_rebootDeviceSubtitle": "Restart the MeshCore device",
"settings_rebootDeviceConfirm": "Are you sure you want to reboot the device? You will be disconnected.",
"settings_debug": "Debug",
"settings_experimental": "Experimental",
"settings_experimentalSubtitle": "Advanced and in-development features",
"settings_experimentalDescription": "These features are experimental and may change or be removed at any time. They default to off.",
"settings_experimentalHide": "Hide this section",
"settings_experimentalHideSubtitle": "Re-hide Experimental. Unlock again with 7 taps on the version row.",
"settings_experimentalUnlocked": "Experimental features unlocked",
"settings_experimentalAlreadyUnlocked": "Experimental features are already unlocked",
"settings_experimentalCountdown": "{count} more taps to unlock experimental features",
"@settings_experimentalCountdown": {
"placeholders": {
"count": {
"type": "int"
}
}
},
"settings_companionDebugLog": "Companion Debug Log",
"settings_companionDebugLogSubtitle": "BLE/TCP/USB commands, responses, and raw data",
"settings_appDebugLog": "App Debug Log",

@ -1090,6 +1090,54 @@ abstract class AppLocalizations {
/// **'Debug'**
String get settings_debug;
/// No description provided for @settings_experimental.
///
/// In en, this message translates to:
/// **'Experimental'**
String get settings_experimental;
/// No description provided for @settings_experimentalSubtitle.
///
/// In en, this message translates to:
/// **'Advanced and in-development features'**
String get settings_experimentalSubtitle;
/// No description provided for @settings_experimentalDescription.
///
/// In en, this message translates to:
/// **'These features are experimental and may change or be removed at any time. They default to off.'**
String get settings_experimentalDescription;
/// No description provided for @settings_experimentalHide.
///
/// In en, this message translates to:
/// **'Hide this section'**
String get settings_experimentalHide;
/// No description provided for @settings_experimentalHideSubtitle.
///
/// In en, this message translates to:
/// **'Re-hide Experimental. Unlock again with 7 taps on the version row.'**
String get settings_experimentalHideSubtitle;
/// No description provided for @settings_experimentalUnlocked.
///
/// In en, this message translates to:
/// **'Experimental features unlocked'**
String get settings_experimentalUnlocked;
/// No description provided for @settings_experimentalAlreadyUnlocked.
///
/// In en, this message translates to:
/// **'Experimental features are already unlocked'**
String get settings_experimentalAlreadyUnlocked;
/// No description provided for @settings_experimentalCountdown.
///
/// In en, this message translates to:
/// **'{count} more taps to unlock experimental features'**
String settings_experimentalCountdown(int count);
/// No description provided for @settings_companionDebugLog.
///
/// In en, this message translates to:

@ -540,6 +540,36 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get settings_debug => 'Отстрани';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog =>
'Лог за отстраняване на грешки (за съпътстваща програма)';

@ -537,6 +537,36 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get settings_debug => 'Fehlerbehebung';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog =>
'Debug-Protokoll für die Begleitsoftware';

@ -528,6 +528,36 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get settings_debug => 'Debug';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog => 'Companion Debug Log';

@ -538,6 +538,36 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get settings_debug => 'Depurar';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog => 'Registro de depuración asociado';

@ -542,6 +542,36 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get settings_debug => 'Déboguer';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog => 'Journal de débogage associé';

@ -540,6 +540,36 @@ class AppLocalizationsHu extends AppLocalizations {
@override
String get settings_debug => 'Hibakeresés';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog => 'Párhuzamos hibakeresési napló';

@ -540,6 +540,36 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get settings_debug => 'Risoluzione dei problemi';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog => 'Registro di debug per il supporto';

@ -514,6 +514,36 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get settings_debug => 'デバッグ';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog => '同伴デバッグログ';

@ -514,6 +514,36 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get settings_debug => '디버깅';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog => '동반 디버깅 로그';

@ -534,6 +534,36 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get settings_debug => 'Foutopsporing';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog =>
'Debuglog voor aanvullende informatie';

@ -542,6 +542,36 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get settings_debug => 'Naprawianie błędów';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog =>
'Log debugowania (dla pomocy w rozwiązywaniu problemów)';

@ -540,6 +540,36 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get settings_debug => 'Depurar';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog => 'Registro de depuração auxiliar';

@ -539,6 +539,36 @@ class AppLocalizationsRu extends AppLocalizations {
@override
String get settings_debug => 'Отладка';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog =>
'Журнал отладки (для сопутствующего приложения)';

@ -534,6 +534,36 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get settings_debug => 'Ladenie';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog =>
'Logovanie pre ladenie (sprievodný log)';

@ -532,6 +532,36 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get settings_debug => 'Odpravljanje napak';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog => 'Log zapis za odpravljanje napak';

@ -529,6 +529,36 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get settings_debug => 'Felsök';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog => 'Följande felsökningslogg';

@ -536,6 +536,36 @@ class AppLocalizationsUk extends AppLocalizations {
@override
String get settings_debug => 'Налагодження';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog =>
'Журнал відлачування (для супутника)';

@ -507,6 +507,36 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get settings_debug => '调试';
@override
String get settings_experimental => 'Experimental';
@override
String get settings_experimentalSubtitle =>
'Advanced and in-development features';
@override
String get settings_experimentalDescription =>
'These features are experimental and may change or be removed at any time. They default to off.';
@override
String get settings_experimentalHide => 'Hide this section';
@override
String get settings_experimentalHideSubtitle =>
'Re-hide Experimental. Unlock again with 7 taps on the version row.';
@override
String get settings_experimentalUnlocked => 'Experimental features unlocked';
@override
String get settings_experimentalAlreadyUnlocked =>
'Experimental features are already unlocked';
@override
String settings_experimentalCountdown(int count) {
return '$count more taps to unlock experimental features';
}
@override
String get settings_companionDebugLog => '调试日志';

@ -139,6 +139,11 @@ class AppSettings {
final String themeMode;
final String? languageOverride; // null = system default
final bool appDebugLogEnabled;
/// Owner-only Experimental settings section, revealed by a 7-tap gesture on
/// the version row (#509). Not a security boundary — just keeps advanced/
/// me-centric toggles out of the default UI. Persists across restarts.
final bool experimentalUnlocked;
final Map<String, String> batteryChemistryByDeviceId;
final Map<String, String> batteryChemistryByRepeaterId;
final UnitSystem unitSystem;
@ -217,6 +222,7 @@ class AppSettings {
this.themeMode = 'system',
this.languageOverride,
this.appDebugLogEnabled = false,
this.experimentalUnlocked = false,
Map<String, String>? batteryChemistryByDeviceId,
Map<String, String>? batteryChemistryByRepeaterId,
this.unitSystem = UnitSystem.metric,
@ -283,6 +289,7 @@ class AppSettings {
'theme_mode': themeMode,
'language_override': languageOverride,
'app_debug_log_enabled': appDebugLogEnabled,
'experimental_unlocked': experimentalUnlocked,
'battery_chemistry_by_device_id': batteryChemistryByDeviceId,
'battery_chemistry_by_repeater_id': batteryChemistryByRepeaterId,
'unit_system': unitSystem.value,
@ -380,6 +387,7 @@ class AppSettings {
themeMode: json['theme_mode'] as String? ?? 'system',
languageOverride: json['language_override'] as String?,
appDebugLogEnabled: json['app_debug_log_enabled'] as bool? ?? false,
experimentalUnlocked: json['experimental_unlocked'] as bool? ?? false,
batteryChemistryByDeviceId:
(json['battery_chemistry_by_device_id'] as Map?)?.map(
(key, value) => MapEntry(key.toString(), value.toString()),
@ -497,6 +505,7 @@ class AppSettings {
String? themeMode,
Object? languageOverride = _unset,
bool? appDebugLogEnabled,
bool? experimentalUnlocked,
Map<String, String>? batteryChemistryByDeviceId,
Map<String, String>? batteryChemistryByRepeaterId,
UnitSystem? unitSystem,
@ -556,6 +565,7 @@ class AppSettings {
? this.languageOverride
: languageOverride as String?,
appDebugLogEnabled: appDebugLogEnabled ?? this.appDebugLogEnabled,
experimentalUnlocked: experimentalUnlocked ?? this.experimentalUnlocked,
batteryChemistryByDeviceId:
batteryChemistryByDeviceId ?? this.batteryChemistryByDeviceId,
batteryChemistryByRepeaterId:

@ -55,6 +55,13 @@ class _SettingsScreenState extends State<SettingsScreen> {
bool _showBatteryVoltage = false;
String _appVersion = '';
// #509 hidden unlock: 7 taps on the version row within a rolling window
// reveals the Experimental settings section.
static const int _experimentalUnlockTaps = 7;
static const Duration _experimentalTapWindow = Duration(seconds: 3);
int _versionTapCount = 0;
DateTime? _lastVersionTap;
@override
void initState() {
super.initState();
@ -69,6 +76,50 @@ class _SettingsScreenState extends State<SettingsScreen> {
});
}
void _handleVersionTap() {
final l10n = context.l10n;
final settingsService = context.read<AppSettingsService>();
if (settingsService.settings.experimentalUnlocked) {
_versionTapCount = 0;
_showVersionSnack(l10n.settings_experimentalAlreadyUnlocked);
return;
}
final now = DateTime.now();
if (_lastVersionTap == null ||
now.difference(_lastVersionTap!) > _experimentalTapWindow) {
_versionTapCount = 0;
}
_lastVersionTap = now;
_versionTapCount++;
final remaining = _experimentalUnlockTaps - _versionTapCount;
if (remaining <= 0) {
_versionTapCount = 0;
settingsService.setExperimentalUnlocked(true);
_showVersionSnack(l10n.settings_experimentalUnlocked);
} else if (remaining <= 3) {
_showVersionSnack(
l10n.settings_experimentalCountdown(remaining),
brief: true,
);
}
}
void _showVersionSnack(String message, {bool brief = false}) {
ScaffoldMessenger.of(context)
..hideCurrentSnackBar()
..showSnackBar(
SnackBar(
content: Text(message),
duration: brief
? const Duration(milliseconds: 900)
: const Duration(seconds: 2),
),
);
}
@override
Widget build(BuildContext context) {
final l10n = context.l10n;
@ -90,6 +141,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
offbandCaps: c.offbandCaps ?? 0,
),
);
// Owner-only Experimental section (#509), revealed by the 7-tap gesture on
// the About version row. Selects narrowly so the category list only rebuilds
// when the flag flips, not on every connector update (same reason as above).
final experimentalUnlocked = context.select<AppSettingsService, bool>(
(s) => s.settings.experimentalUnlocked,
);
return [
SettingsCategory(
icon: Icons.badge_outlined,
@ -155,6 +212,13 @@ class _SettingsScreenState extends State<SettingsScreen> {
title: l10n.settings_actions,
builder: _actionsPane,
),
if (experimentalUnlocked)
SettingsCategory(
icon: Icons.science_outlined,
title: l10n.settings_experimental,
subtitle: l10n.settings_experimentalSubtitle,
builder: _experimentalPane,
),
SettingsCategory(
icon: Icons.build_outlined,
title: l10n.settings_debug,
@ -163,6 +227,38 @@ class _SettingsScreenState extends State<SettingsScreen> {
];
}
Widget _experimentalPane(BuildContext context) {
final l10n = context.l10n;
final settingsService = context.watch<AppSettingsService>();
return ListView(
padding: const EdgeInsets.all(16),
children: [
Card(
child: Padding(
padding: const EdgeInsets.all(16),
child: Text(l10n.settings_experimentalDescription),
),
),
const SizedBox(height: 16),
// Future me-centric/experimental toggles land here (e.g. Fast Sync
// #118, CoreScope repeats #524). Empty of features until they arrive.
Card(
child: SwitchListTile(
secondary: const Icon(Icons.visibility_off_outlined),
title: Text(l10n.settings_experimentalHide),
subtitle: Text(l10n.settings_experimentalHideSubtitle),
value: settingsService.settings.experimentalUnlocked,
onChanged: (value) {
if (!value) {
settingsService.setExperimentalUnlocked(false);
}
},
),
),
],
);
}
Widget _observerPane(BuildContext context) => const ObserverSettingsView();
Widget _blockedPane(BuildContext context) => const BlockedView();
@ -637,9 +733,15 @@ class _SettingsScreenState extends State<SettingsScreen> {
child: ListTile(
leading: const Icon(Icons.info_outline),
title: Text(l10n.settings_about),
subtitle: Text(
l10n.settings_aboutVersion(
_appVersion.isEmpty ? l10n.common_loading : _appVersion,
// Tapping the version number counts toward the #509 unlock and is
// absorbed here; tapping elsewhere on the row still opens the dialog.
subtitle: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: _handleVersionTap,
child: Text(
l10n.settings_aboutVersion(
_appVersion.isEmpty ? l10n.common_loading : _appVersion,
),
),
),
onTap: () => _showAbout(context),

@ -182,6 +182,10 @@ class AppSettingsService extends ChangeNotifier {
appLogger.setEnabled(value);
}
Future<void> setExperimentalUnlocked(bool value) async {
await updateSettings(_settings.copyWith(experimentalUnlocked: value));
}
Future<void> setMapShowDiscoveryContacts(bool value) async {
await updateSettings(_settings.copyWith(mapShowDiscoveryContacts: value));
}

@ -278,6 +278,20 @@ void main() {
expect(settings.maxMessageRetries, equals(5));
});
test('experimentalUnlocked defaults off and round-trips (#509)', () {
expect(AppSettings().experimentalUnlocked, isFalse);
final unlocked = AppSettings().copyWith(experimentalUnlocked: true);
expect(unlocked.experimentalUnlocked, isTrue);
expect(unlocked.toJson()['experimental_unlocked'], isTrue);
final restored = AppSettings.fromJson(unlocked.toJson());
expect(restored.experimentalUnlocked, isTrue);
// Missing key (older persisted settings) defaults to locked.
expect(AppSettings.fromJson({}).experimentalUnlocked, isFalse);
});
test('toJson includes all new fields', () {
final settings = AppSettings();
final json = settings.toJson();

@ -12,6 +12,14 @@
"settings_gpsStatusTime",
"settings_gpsStatusTapRefresh",
"settings_gpsStatusNoResponse",
"settings_experimental",
"settings_experimentalSubtitle",
"settings_experimentalDescription",
"settings_experimentalHide",
"settings_experimentalHideSubtitle",
"settings_experimentalUnlocked",
"settings_experimentalAlreadyUnlocked",
"settings_experimentalCountdown",
"settings_infoFirmware",
"settings_infoModel",
"settings_infoOffbandCaps",
@ -30,11 +38,25 @@
"channels_notifyAll",
"channels_notifyMentionsOnly",
"channels_notifyOff",
"chat_sendingAgain",
"message_sendAgain",
"chat_replyWithRoute",
"debugLog_shareLog",
"debugLog_saveLog",
"debugLog_downloadLog",
"debugLog_shareSubject",
"debugLog_logSaved",
"debugLog_logUnavailable",
"channel_heardTooltip",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
"channelPath_snrValue",
"channelPath_rssiLabel",
"channelPath_rssiValue",
"channels_shareQr",
"channels_shareQrTitle",
"channels_shareQrInstructions",
@ -63,7 +85,14 @@
"block_namesHint",
"block_offloadActive",
"block_offloadStoreFull",
"block_composerNotice"
"block_composerNotice",
"storageUnavailableTitle",
"storageUnavailableBody",
"batteryOptimizationTitle",
"batteryOptimizationBody",
"batteryOptimizationOpenSettings",
"batteryOptimizationDismiss",
"batteryOptimizationOpenFailed"
],
"de": [
@ -79,6 +108,14 @@
"settings_gpsStatusTime",
"settings_gpsStatusTapRefresh",
"settings_gpsStatusNoResponse",
"settings_experimental",
"settings_experimentalSubtitle",
"settings_experimentalDescription",
"settings_experimentalHide",
"settings_experimentalHideSubtitle",
"settings_experimentalUnlocked",
"settings_experimentalAlreadyUnlocked",
"settings_experimentalCountdown",
"settings_infoFirmware",
"settings_infoModel",
"settings_infoOffbandCaps",
@ -97,11 +134,25 @@
"channels_notifyAll",
"channels_notifyMentionsOnly",
"channels_notifyOff",
"chat_sendingAgain",
"message_sendAgain",
"chat_replyWithRoute",
"debugLog_shareLog",
"debugLog_saveLog",
"debugLog_downloadLog",
"debugLog_shareSubject",
"debugLog_logSaved",
"debugLog_logUnavailable",
"channel_heardTooltip",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
"channelPath_snrValue",
"channelPath_rssiLabel",
"channelPath_rssiValue",
"channels_shareQr",
"channels_shareQrTitle",
"channels_shareQrInstructions",
@ -130,7 +181,14 @@
"block_namesHint",
"block_offloadActive",
"block_offloadStoreFull",
"block_composerNotice"
"block_composerNotice",
"storageUnavailableTitle",
"storageUnavailableBody",
"batteryOptimizationTitle",
"batteryOptimizationBody",
"batteryOptimizationOpenSettings",
"batteryOptimizationDismiss",
"batteryOptimizationOpenFailed"
],
"es": [
@ -146,6 +204,14 @@
"settings_gpsStatusTime",
"settings_gpsStatusTapRefresh",
"settings_gpsStatusNoResponse",
"settings_experimental",
"settings_experimentalSubtitle",
"settings_experimentalDescription",
"settings_experimentalHide",
"settings_experimentalHideSubtitle",
"settings_experimentalUnlocked",
"settings_experimentalAlreadyUnlocked",
"settings_experimentalCountdown",
"settings_infoFirmware",
"settings_infoModel",
"settings_infoOffbandCaps",
@ -164,11 +230,25 @@
"channels_notifyAll",
"channels_notifyMentionsOnly",
"channels_notifyOff",
"chat_sendingAgain",
"message_sendAgain",
"chat_replyWithRoute",
"debugLog_shareLog",
"debugLog_saveLog",
"debugLog_downloadLog",
"debugLog_shareSubject",
"debugLog_logSaved",
"debugLog_logUnavailable",
"channel_heardTooltip",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
"channelPath_snrValue",
"channelPath_rssiLabel",
"channelPath_rssiValue",
"channels_shareQr",
"channels_shareQrTitle",
"channels_shareQrInstructions",
@ -197,7 +277,14 @@
"block_namesHint",
"block_offloadActive",
"block_offloadStoreFull",
"block_composerNotice"
"block_composerNotice",
"storageUnavailableTitle",
"storageUnavailableBody",
"batteryOptimizationTitle",
"batteryOptimizationBody",
"batteryOptimizationOpenSettings",
"batteryOptimizationDismiss",
"batteryOptimizationOpenFailed"
],
"fr": [
@ -213,6 +300,14 @@
"settings_gpsStatusTime",
"settings_gpsStatusTapRefresh",
"settings_gpsStatusNoResponse",
"settings_experimental",
"settings_experimentalSubtitle",
"settings_experimentalDescription",
"settings_experimentalHide",
"settings_experimentalHideSubtitle",
"settings_experimentalUnlocked",
"settings_experimentalAlreadyUnlocked",
"settings_experimentalCountdown",
"settings_infoFirmware",
"settings_infoModel",
"settings_infoOffbandCaps",
@ -231,11 +326,25 @@
"channels_notifyAll",
"channels_notifyMentionsOnly",
"channels_notifyOff",
"chat_sendingAgain",
"message_sendAgain",
"chat_replyWithRoute",
"debugLog_shareLog",
"debugLog_saveLog",
"debugLog_downloadLog",
"debugLog_shareSubject",
"debugLog_logSaved",
"debugLog_logUnavailable",
"channel_heardTooltip",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
"channelPath_snrValue",
"channelPath_rssiLabel",
"channelPath_rssiValue",
"channels_shareQr",
"channels_shareQrTitle",
"channels_shareQrInstructions",
@ -264,7 +373,14 @@
"block_namesHint",
"block_offloadActive",
"block_offloadStoreFull",
"block_composerNotice"
"block_composerNotice",
"storageUnavailableTitle",
"storageUnavailableBody",
"batteryOptimizationTitle",
"batteryOptimizationBody",
"batteryOptimizationOpenSettings",
"batteryOptimizationDismiss",
"batteryOptimizationOpenFailed"
],
"hu": [
@ -280,6 +396,14 @@
"settings_gpsStatusTime",
"settings_gpsStatusTapRefresh",
"settings_gpsStatusNoResponse",
"settings_experimental",
"settings_experimentalSubtitle",
"settings_experimentalDescription",
"settings_experimentalHide",
"settings_experimentalHideSubtitle",
"settings_experimentalUnlocked",
"settings_experimentalAlreadyUnlocked",
"settings_experimentalCountdown",
"settings_infoFirmware",
"settings_infoModel",
"settings_infoOffbandCaps",
@ -298,11 +422,25 @@
"channels_notifyAll",
"channels_notifyMentionsOnly",
"channels_notifyOff",
"chat_sendingAgain",
"message_sendAgain",
"chat_replyWithRoute",
"debugLog_shareLog",
"debugLog_saveLog",
"debugLog_downloadLog",
"debugLog_shareSubject",
"debugLog_logSaved",
"debugLog_logUnavailable",
"channel_heardTooltip",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
"channelPath_snrValue",
"channelPath_rssiLabel",
"channelPath_rssiValue",
"channels_shareQr",
"channels_shareQrTitle",
"channels_shareQrInstructions",
@ -331,7 +469,14 @@
"block_namesHint",
"block_offloadActive",
"block_offloadStoreFull",
"block_composerNotice"
"block_composerNotice",
"storageUnavailableTitle",
"storageUnavailableBody",
"batteryOptimizationTitle",
"batteryOptimizationBody",
"batteryOptimizationOpenSettings",
"batteryOptimizationDismiss",
"batteryOptimizationOpenFailed"
],
"it": [
@ -347,6 +492,14 @@
"settings_gpsStatusTime",
"settings_gpsStatusTapRefresh",
"settings_gpsStatusNoResponse",
"settings_experimental",
"settings_experimentalSubtitle",
"settings_experimentalDescription",
"settings_experimentalHide",
"settings_experimentalHideSubtitle",
"settings_experimentalUnlocked",
"settings_experimentalAlreadyUnlocked",
"settings_experimentalCountdown",
"settings_infoFirmware",
"settings_infoModel",
"settings_infoOffbandCaps",
@ -365,11 +518,25 @@
"channels_notifyAll",
"channels_notifyMentionsOnly",
"channels_notifyOff",
"chat_sendingAgain",
"message_sendAgain",
"chat_replyWithRoute",
"debugLog_shareLog",
"debugLog_saveLog",
"debugLog_downloadLog",
"debugLog_shareSubject",
"debugLog_logSaved",
"debugLog_logUnavailable",
"channel_heardTooltip",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
"channelPath_snrValue",
"channelPath_rssiLabel",
"channelPath_rssiValue",
"channels_shareQr",
"channels_shareQrTitle",
"channels_shareQrInstructions",
@ -398,7 +565,14 @@
"block_namesHint",
"block_offloadActive",
"block_offloadStoreFull",
"block_composerNotice"
"block_composerNotice",
"storageUnavailableTitle",
"storageUnavailableBody",
"batteryOptimizationTitle",
"batteryOptimizationBody",
"batteryOptimizationOpenSettings",
"batteryOptimizationDismiss",
"batteryOptimizationOpenFailed"
],
"ja": [
@ -414,6 +588,14 @@
"settings_gpsStatusTime",
"settings_gpsStatusTapRefresh",
"settings_gpsStatusNoResponse",
"settings_experimental",
"settings_experimentalSubtitle",
"settings_experimentalDescription",
"settings_experimentalHide",
"settings_experimentalHideSubtitle",
"settings_experimentalUnlocked",
"settings_experimentalAlreadyUnlocked",
"settings_experimentalCountdown",
"settings_infoFirmware",
"settings_infoModel",
"settings_infoOffbandCaps",
@ -432,11 +614,25 @@
"channels_notifyAll",
"channels_notifyMentionsOnly",
"channels_notifyOff",
"chat_sendingAgain",
"message_sendAgain",
"chat_replyWithRoute",
"debugLog_shareLog",
"debugLog_saveLog",
"debugLog_downloadLog",
"debugLog_shareSubject",
"debugLog_logSaved",
"debugLog_logUnavailable",
"channel_heardTooltip",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
"channelPath_snrValue",
"channelPath_rssiLabel",
"channelPath_rssiValue",
"channels_shareQr",
"channels_shareQrTitle",
"channels_shareQrInstructions",
@ -465,7 +661,14 @@
"block_namesHint",
"block_offloadActive",
"block_offloadStoreFull",
"block_composerNotice"
"block_composerNotice",
"storageUnavailableTitle",
"storageUnavailableBody",
"batteryOptimizationTitle",
"batteryOptimizationBody",
"batteryOptimizationOpenSettings",
"batteryOptimizationDismiss",
"batteryOptimizationOpenFailed"
],
"ko": [
@ -481,6 +684,14 @@
"settings_gpsStatusTime",
"settings_gpsStatusTapRefresh",
"settings_gpsStatusNoResponse",
"settings_experimental",
"settings_experimentalSubtitle",
"settings_experimentalDescription",
"settings_experimentalHide",
"settings_experimentalHideSubtitle",
"settings_experimentalUnlocked",
"settings_experimentalAlreadyUnlocked",
"settings_experimentalCountdown",
"settings_infoFirmware",
"settings_infoModel",
"settings_infoOffbandCaps",
@ -499,11 +710,25 @@
"channels_notifyAll",
"channels_notifyMentionsOnly",
"channels_notifyOff",
"chat_sendingAgain",
"message_sendAgain",
"chat_replyWithRoute",
"debugLog_shareLog",
"debugLog_saveLog",
"debugLog_downloadLog",
"debugLog_shareSubject",
"debugLog_logSaved",
"debugLog_logUnavailable",
"channel_heardTooltip",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
"channelPath_snrValue",
"channelPath_rssiLabel",
"channelPath_rssiValue",
"channels_shareQr",
"channels_shareQrTitle",
"channels_shareQrInstructions",
@ -532,7 +757,14 @@
"block_namesHint",
"block_offloadActive",
"block_offloadStoreFull",
"block_composerNotice"
"block_composerNotice",
"storageUnavailableTitle",
"storageUnavailableBody",
"batteryOptimizationTitle",
"batteryOptimizationBody",
"batteryOptimizationOpenSettings",
"batteryOptimizationDismiss",
"batteryOptimizationOpenFailed"
],
"nl": [
@ -548,6 +780,14 @@
"settings_gpsStatusTime",
"settings_gpsStatusTapRefresh",
"settings_gpsStatusNoResponse",
"settings_experimental",
"settings_experimentalSubtitle",
"settings_experimentalDescription",
"settings_experimentalHide",
"settings_experimentalHideSubtitle",
"settings_experimentalUnlocked",
"settings_experimentalAlreadyUnlocked",
"settings_experimentalCountdown",
"settings_infoFirmware",
"settings_infoModel",
"settings_infoOffbandCaps",
@ -566,11 +806,25 @@
"channels_notifyAll",
"channels_notifyMentionsOnly",
"channels_notifyOff",
"chat_sendingAgain",
"message_sendAgain",
"chat_replyWithRoute",
"debugLog_shareLog",
"debugLog_saveLog",
"debugLog_downloadLog",
"debugLog_shareSubject",
"debugLog_logSaved",
"debugLog_logUnavailable",
"channel_heardTooltip",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
"channelPath_snrValue",
"channelPath_rssiLabel",
"channelPath_rssiValue",
"channels_shareQr",
"channels_shareQrTitle",
"channels_shareQrInstructions",
@ -599,7 +853,14 @@
"block_namesHint",
"block_offloadActive",
"block_offloadStoreFull",
"block_composerNotice"
"block_composerNotice",
"storageUnavailableTitle",
"storageUnavailableBody",
"batteryOptimizationTitle",
"batteryOptimizationBody",
"batteryOptimizationOpenSettings",
"batteryOptimizationDismiss",
"batteryOptimizationOpenFailed"
],
"pl": [
@ -615,6 +876,14 @@
"settings_gpsStatusTime",
"settings_gpsStatusTapRefresh",
"settings_gpsStatusNoResponse",
"settings_experimental",
"settings_experimentalSubtitle",
"settings_experimentalDescription",
"settings_experimentalHide",
"settings_experimentalHideSubtitle",
"settings_experimentalUnlocked",
"settings_experimentalAlreadyUnlocked",
"settings_experimentalCountdown",
"settings_infoFirmware",
"settings_infoModel",
"settings_infoOffbandCaps",
@ -633,11 +902,25 @@
"channels_notifyAll",
"channels_notifyMentionsOnly",
"channels_notifyOff",
"chat_sendingAgain",
"message_sendAgain",
"chat_replyWithRoute",
"debugLog_shareLog",
"debugLog_saveLog",
"debugLog_downloadLog",
"debugLog_shareSubject",
"debugLog_logSaved",
"debugLog_logUnavailable",
"channel_heardTooltip",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
"channelPath_snrValue",
"channelPath_rssiLabel",
"channelPath_rssiValue",
"channels_shareQr",
"channels_shareQrTitle",
"channels_shareQrInstructions",
@ -666,7 +949,14 @@
"block_namesHint",
"block_offloadActive",
"block_offloadStoreFull",
"block_composerNotice"
"block_composerNotice",
"storageUnavailableTitle",
"storageUnavailableBody",
"batteryOptimizationTitle",
"batteryOptimizationBody",
"batteryOptimizationOpenSettings",
"batteryOptimizationDismiss",
"batteryOptimizationOpenFailed"
],
"pt": [
@ -682,6 +972,14 @@
"settings_gpsStatusTime",
"settings_gpsStatusTapRefresh",
"settings_gpsStatusNoResponse",
"settings_experimental",
"settings_experimentalSubtitle",
"settings_experimentalDescription",
"settings_experimentalHide",
"settings_experimentalHideSubtitle",
"settings_experimentalUnlocked",
"settings_experimentalAlreadyUnlocked",
"settings_experimentalCountdown",
"settings_infoFirmware",
"settings_infoModel",
"settings_infoOffbandCaps",
@ -700,11 +998,25 @@
"channels_notifyAll",
"channels_notifyMentionsOnly",
"channels_notifyOff",
"chat_sendingAgain",
"message_sendAgain",
"chat_replyWithRoute",
"debugLog_shareLog",
"debugLog_saveLog",
"debugLog_downloadLog",
"debugLog_shareSubject",
"debugLog_logSaved",
"debugLog_logUnavailable",
"channel_heardTooltip",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
"channelPath_snrValue",
"channelPath_rssiLabel",
"channelPath_rssiValue",
"channels_shareQr",
"channels_shareQrTitle",
"channels_shareQrInstructions",
@ -733,7 +1045,14 @@
"block_namesHint",
"block_offloadActive",
"block_offloadStoreFull",
"block_composerNotice"
"block_composerNotice",
"storageUnavailableTitle",
"storageUnavailableBody",
"batteryOptimizationTitle",
"batteryOptimizationBody",
"batteryOptimizationOpenSettings",
"batteryOptimizationDismiss",
"batteryOptimizationOpenFailed"
],
"ru": [
@ -749,6 +1068,14 @@
"settings_gpsStatusTime",
"settings_gpsStatusTapRefresh",
"settings_gpsStatusNoResponse",
"settings_experimental",
"settings_experimentalSubtitle",
"settings_experimentalDescription",
"settings_experimentalHide",
"settings_experimentalHideSubtitle",
"settings_experimentalUnlocked",
"settings_experimentalAlreadyUnlocked",
"settings_experimentalCountdown",
"settings_infoFirmware",
"settings_infoModel",
"settings_infoOffbandCaps",
@ -767,11 +1094,25 @@
"channels_notifyAll",
"channels_notifyMentionsOnly",
"channels_notifyOff",
"chat_sendingAgain",
"message_sendAgain",
"chat_replyWithRoute",
"debugLog_shareLog",
"debugLog_saveLog",
"debugLog_downloadLog",
"debugLog_shareSubject",
"debugLog_logSaved",
"debugLog_logUnavailable",
"channel_heardTooltip",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
"channelPath_snrValue",
"channelPath_rssiLabel",
"channelPath_rssiValue",
"channels_shareQr",
"channels_shareQrTitle",
"channels_shareQrInstructions",
@ -800,7 +1141,14 @@
"block_namesHint",
"block_offloadActive",
"block_offloadStoreFull",
"block_composerNotice"
"block_composerNotice",
"storageUnavailableTitle",
"storageUnavailableBody",
"batteryOptimizationTitle",
"batteryOptimizationBody",
"batteryOptimizationOpenSettings",
"batteryOptimizationDismiss",
"batteryOptimizationOpenFailed"
],
"sk": [
@ -816,6 +1164,14 @@
"settings_gpsStatusTime",
"settings_gpsStatusTapRefresh",
"settings_gpsStatusNoResponse",
"settings_experimental",
"settings_experimentalSubtitle",
"settings_experimentalDescription",
"settings_experimentalHide",
"settings_experimentalHideSubtitle",
"settings_experimentalUnlocked",
"settings_experimentalAlreadyUnlocked",
"settings_experimentalCountdown",
"settings_infoFirmware",
"settings_infoModel",
"settings_infoOffbandCaps",
@ -834,11 +1190,25 @@
"channels_notifyAll",
"channels_notifyMentionsOnly",
"channels_notifyOff",
"chat_sendingAgain",
"message_sendAgain",
"chat_replyWithRoute",
"debugLog_shareLog",
"debugLog_saveLog",
"debugLog_downloadLog",
"debugLog_shareSubject",
"debugLog_logSaved",
"debugLog_logUnavailable",
"channel_heardTooltip",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
"channelPath_snrValue",
"channelPath_rssiLabel",
"channelPath_rssiValue",
"channels_shareQr",
"channels_shareQrTitle",
"channels_shareQrInstructions",
@ -867,7 +1237,14 @@
"block_namesHint",
"block_offloadActive",
"block_offloadStoreFull",
"block_composerNotice"
"block_composerNotice",
"storageUnavailableTitle",
"storageUnavailableBody",
"batteryOptimizationTitle",
"batteryOptimizationBody",
"batteryOptimizationOpenSettings",
"batteryOptimizationDismiss",
"batteryOptimizationOpenFailed"
],
"sl": [
@ -883,6 +1260,14 @@
"settings_gpsStatusTime",
"settings_gpsStatusTapRefresh",
"settings_gpsStatusNoResponse",
"settings_experimental",
"settings_experimentalSubtitle",
"settings_experimentalDescription",
"settings_experimentalHide",
"settings_experimentalHideSubtitle",
"settings_experimentalUnlocked",
"settings_experimentalAlreadyUnlocked",
"settings_experimentalCountdown",
"settings_infoFirmware",
"settings_infoModel",
"settings_infoOffbandCaps",
@ -901,11 +1286,25 @@
"channels_notifyAll",
"channels_notifyMentionsOnly",
"channels_notifyOff",
"chat_sendingAgain",
"message_sendAgain",
"chat_replyWithRoute",
"debugLog_shareLog",
"debugLog_saveLog",
"debugLog_downloadLog",
"debugLog_shareSubject",
"debugLog_logSaved",
"debugLog_logUnavailable",
"channel_heardTooltip",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
"channelPath_snrValue",
"channelPath_rssiLabel",
"channelPath_rssiValue",
"channels_shareQr",
"channels_shareQrTitle",
"channels_shareQrInstructions",
@ -934,7 +1333,14 @@
"block_namesHint",
"block_offloadActive",
"block_offloadStoreFull",
"block_composerNotice"
"block_composerNotice",
"storageUnavailableTitle",
"storageUnavailableBody",
"batteryOptimizationTitle",
"batteryOptimizationBody",
"batteryOptimizationOpenSettings",
"batteryOptimizationDismiss",
"batteryOptimizationOpenFailed"
],
"sv": [
@ -950,6 +1356,14 @@
"settings_gpsStatusTime",
"settings_gpsStatusTapRefresh",
"settings_gpsStatusNoResponse",
"settings_experimental",
"settings_experimentalSubtitle",
"settings_experimentalDescription",
"settings_experimentalHide",
"settings_experimentalHideSubtitle",
"settings_experimentalUnlocked",
"settings_experimentalAlreadyUnlocked",
"settings_experimentalCountdown",
"settings_infoFirmware",
"settings_infoModel",
"settings_infoOffbandCaps",
@ -968,11 +1382,25 @@
"channels_notifyAll",
"channels_notifyMentionsOnly",
"channels_notifyOff",
"chat_sendingAgain",
"message_sendAgain",
"chat_replyWithRoute",
"debugLog_shareLog",
"debugLog_saveLog",
"debugLog_downloadLog",
"debugLog_shareSubject",
"debugLog_logSaved",
"debugLog_logUnavailable",
"channel_heardTooltip",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
"channelPath_snrValue",
"channelPath_rssiLabel",
"channelPath_rssiValue",
"channels_shareQr",
"channels_shareQrTitle",
"channels_shareQrInstructions",
@ -1001,7 +1429,14 @@
"block_namesHint",
"block_offloadActive",
"block_offloadStoreFull",
"block_composerNotice"
"block_composerNotice",
"storageUnavailableTitle",
"storageUnavailableBody",
"batteryOptimizationTitle",
"batteryOptimizationBody",
"batteryOptimizationOpenSettings",
"batteryOptimizationDismiss",
"batteryOptimizationOpenFailed"
],
"uk": [
@ -1017,6 +1452,14 @@
"settings_gpsStatusTime",
"settings_gpsStatusTapRefresh",
"settings_gpsStatusNoResponse",
"settings_experimental",
"settings_experimentalSubtitle",
"settings_experimentalDescription",
"settings_experimentalHide",
"settings_experimentalHideSubtitle",
"settings_experimentalUnlocked",
"settings_experimentalAlreadyUnlocked",
"settings_experimentalCountdown",
"settings_infoFirmware",
"settings_infoModel",
"settings_infoOffbandCaps",
@ -1035,11 +1478,25 @@
"channels_notifyAll",
"channels_notifyMentionsOnly",
"channels_notifyOff",
"chat_sendingAgain",
"message_sendAgain",
"chat_replyWithRoute",
"debugLog_shareLog",
"debugLog_saveLog",
"debugLog_downloadLog",
"debugLog_shareSubject",
"debugLog_logSaved",
"debugLog_logUnavailable",
"channel_heardTooltip",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
"channelPath_snrValue",
"channelPath_rssiLabel",
"channelPath_rssiValue",
"channels_shareQr",
"channels_shareQrTitle",
"channels_shareQrInstructions",
@ -1068,7 +1525,14 @@
"block_namesHint",
"block_offloadActive",
"block_offloadStoreFull",
"block_composerNotice"
"block_composerNotice",
"storageUnavailableTitle",
"storageUnavailableBody",
"batteryOptimizationTitle",
"batteryOptimizationBody",
"batteryOptimizationOpenSettings",
"batteryOptimizationDismiss",
"batteryOptimizationOpenFailed"
],
"zh": [
@ -1084,6 +1548,14 @@
"settings_gpsStatusTime",
"settings_gpsStatusTapRefresh",
"settings_gpsStatusNoResponse",
"settings_experimental",
"settings_experimentalSubtitle",
"settings_experimentalDescription",
"settings_experimentalHide",
"settings_experimentalHideSubtitle",
"settings_experimentalUnlocked",
"settings_experimentalAlreadyUnlocked",
"settings_experimentalCountdown",
"settings_infoFirmware",
"settings_infoModel",
"settings_infoOffbandCaps",
@ -1102,11 +1574,25 @@
"channels_notifyAll",
"channels_notifyMentionsOnly",
"channels_notifyOff",
"chat_sendingAgain",
"message_sendAgain",
"chat_replyWithRoute",
"debugLog_shareLog",
"debugLog_saveLog",
"debugLog_downloadLog",
"debugLog_shareSubject",
"debugLog_logSaved",
"debugLog_logUnavailable",
"channel_heardTooltip",
"map_alwaysShowNames",
"path_suggestedRoute",
"path_searchRepeaters",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
"channelPath_snrValue",
"channelPath_rssiLabel",
"channelPath_rssiValue",
"channels_shareQr",
"channels_shareQrTitle",
"channels_shareQrInstructions",
@ -1135,6 +1621,13 @@
"block_namesHint",
"block_offloadActive",
"block_offloadStoreFull",
"block_composerNotice"
"block_composerNotice",
"storageUnavailableTitle",
"storageUnavailableBody",
"batteryOptimizationTitle",
"batteryOptimizationBody",
"batteryOptimizationOpenSettings",
"batteryOptimizationDismiss",
"batteryOptimizationOpenFailed"
]
}

Loading…
Cancel
Save

Powered by TurnKey Linux.