feat(alarms): native reliability fixes and end-to-end snooze
- Use mediaPlayback|systemExempted FGS type with FOREGROUND_SERVICE_SYSTEM_EXEMPTED so alarms fire on Android 14+ (FOREGROUND_SERVICE_ALARM does not exist in the SDK) - Deduplicate fire notifications: the foreground service FSI notification is the single owner; receiver path removed - Notification channel v2 with alarm sound URI and USAGE_ALARM attributes, one-time guarded migration from legacy channels - Pass fallback station through the MethodChannel (NativeAlarmSpec schemaVersion 3) with a three-stage audio chain: primary -> fallback station -> bundled WAV - Native fade-in volume ramp honoring fadeInSegundos when the app is killed - Request battery-optimization exemption once, tracked with a persisted asked-once flag - Fix snooze end-to-end: native ACTION_SNOOZE now reports back to Flutter (snoozed event + cold-start sync), snooze anchor unified to occurrence+minutes on both sides, periodic recalc no longer erases an active snooze - Add snooze buttons (3/5/10/custom) to the ringing screen with shared audio teardown - Redesign ringing screen on PluriWaveScaffold with reduced-motion-aware entry animation (new PluriAnimate helper) - Alarm editor: live next-trigger preview, searchable station pickers (primary and fallback), configurable snooze duration, volume floor down to 0 - New alarm strings localized across all 13 locales - New unit/widget tests for the snooze flow, alarm bridge payloads, ringing screen and editor (77 tests green) - SDD artifacts for the app-quality-and-native-alarms change (explore, proposal, spec, design, tasks, apply progress)
This commit is contained in:
@@ -1510,6 +1510,36 @@ abstract class AppLocalizations {
|
||||
/// **'Sin emisora: usar sonido interno'**
|
||||
String get noStationUseInternalSound;
|
||||
|
||||
/// No description provided for @alarmFallbackStationLabel.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Emisora de respaldo'**
|
||||
String get alarmFallbackStationLabel;
|
||||
|
||||
/// No description provided for @alarmStationPickerSearchHint.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Buscá una emisora por nombre'**
|
||||
String get alarmStationPickerSearchHint;
|
||||
|
||||
/// No description provided for @alarmSnoozeDurationTitle.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Duración de la posposición'**
|
||||
String get alarmSnoozeDurationTitle;
|
||||
|
||||
/// No description provided for @snoozeAction.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'Posponer'**
|
||||
String get snoozeAction;
|
||||
|
||||
/// No description provided for @alarmSnoozeOptionLabel.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
/// **'{minutes} min'**
|
||||
String alarmSnoozeOptionLabel(int minutes);
|
||||
|
||||
/// No description provided for @saveFavoritesAlarmHint.
|
||||
///
|
||||
/// In es, this message translates to:
|
||||
|
||||
@@ -798,6 +798,23 @@ class AppLocalizationsAr extends AppLocalizations {
|
||||
@override
|
||||
String get noStationUseInternalSound => 'بدون محطة: استخدام الصوت الداخلي';
|
||||
|
||||
@override
|
||||
String get alarmFallbackStationLabel => 'محطة احتياطية';
|
||||
|
||||
@override
|
||||
String get alarmStationPickerSearchHint => 'ابحث عن محطة بالاسم';
|
||||
|
||||
@override
|
||||
String get alarmSnoozeDurationTitle => 'مدة الغفوة';
|
||||
|
||||
@override
|
||||
String get snoozeAction => 'غفوة';
|
||||
|
||||
@override
|
||||
String alarmSnoozeOptionLabel(int minutes) {
|
||||
return '$minutes د';
|
||||
}
|
||||
|
||||
@override
|
||||
String get saveFavoritesAlarmHint =>
|
||||
'احفظ محطات في المفضلة لاستخدامها كمنبه موسيقي.';
|
||||
|
||||
@@ -804,6 +804,23 @@ class AppLocalizationsBn extends AppLocalizations {
|
||||
String get noStationUseInternalSound =>
|
||||
'স্টেশন নেই: অভ্যন্তরীণ শব্দ ব্যবহার করুন';
|
||||
|
||||
@override
|
||||
String get alarmFallbackStationLabel => 'ব্যাকআপ স্টেশন';
|
||||
|
||||
@override
|
||||
String get alarmStationPickerSearchHint => 'নাম দিয়ে স্টেশন খুঁজুন';
|
||||
|
||||
@override
|
||||
String get alarmSnoozeDurationTitle => 'স্নুজ সময়কাল';
|
||||
|
||||
@override
|
||||
String get snoozeAction => 'স্নুজ';
|
||||
|
||||
@override
|
||||
String alarmSnoozeOptionLabel(int minutes) {
|
||||
return '$minutes মিনিট';
|
||||
}
|
||||
|
||||
@override
|
||||
String get saveFavoritesAlarmHint =>
|
||||
'সুরেলা অ্যালার্ম হিসেবে ব্যবহার করতে প্রিয়তে স্টেশন সংরক্ষণ করুন।';
|
||||
|
||||
@@ -806,6 +806,23 @@ class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get noStationUseInternalSound => 'Kein Sender: internen Ton verwenden';
|
||||
|
||||
@override
|
||||
String get alarmFallbackStationLabel => 'Ersatzsender';
|
||||
|
||||
@override
|
||||
String get alarmStationPickerSearchHint => 'Sender nach Name suchen';
|
||||
|
||||
@override
|
||||
String get alarmSnoozeDurationTitle => 'Schlummerdauer';
|
||||
|
||||
@override
|
||||
String get snoozeAction => 'Schlummern';
|
||||
|
||||
@override
|
||||
String alarmSnoozeOptionLabel(int minutes) {
|
||||
return '$minutes Min.';
|
||||
}
|
||||
|
||||
@override
|
||||
String get saveFavoritesAlarmHint =>
|
||||
'Speichere Sender in Favoriten, um sie als musikalischen Alarm zu verwenden.';
|
||||
|
||||
@@ -801,6 +801,23 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get noStationUseInternalSound => 'No station: use internal sound';
|
||||
|
||||
@override
|
||||
String get alarmFallbackStationLabel => 'Backup station';
|
||||
|
||||
@override
|
||||
String get alarmStationPickerSearchHint => 'Search for a station by name';
|
||||
|
||||
@override
|
||||
String get alarmSnoozeDurationTitle => 'Snooze duration';
|
||||
|
||||
@override
|
||||
String get snoozeAction => 'Snooze';
|
||||
|
||||
@override
|
||||
String alarmSnoozeOptionLabel(int minutes) {
|
||||
return '$minutes min';
|
||||
}
|
||||
|
||||
@override
|
||||
String get saveFavoritesAlarmHint =>
|
||||
'Save stations in Favorites to use them as a music alarm.';
|
||||
|
||||
@@ -804,6 +804,23 @@ class AppLocalizationsEs extends AppLocalizations {
|
||||
@override
|
||||
String get noStationUseInternalSound => 'Sin emisora: usar sonido interno';
|
||||
|
||||
@override
|
||||
String get alarmFallbackStationLabel => 'Emisora de respaldo';
|
||||
|
||||
@override
|
||||
String get alarmStationPickerSearchHint => 'Buscá una emisora por nombre';
|
||||
|
||||
@override
|
||||
String get alarmSnoozeDurationTitle => 'Duración de la posposición';
|
||||
|
||||
@override
|
||||
String get snoozeAction => 'Posponer';
|
||||
|
||||
@override
|
||||
String alarmSnoozeOptionLabel(int minutes) {
|
||||
return '$minutes min';
|
||||
}
|
||||
|
||||
@override
|
||||
String get saveFavoritesAlarmHint =>
|
||||
'Guardá emisoras en Favoritos para usarlas como alarma musical.';
|
||||
|
||||
@@ -809,6 +809,23 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
String get noStationUseInternalSound =>
|
||||
'Aucune station : utiliser le son interne';
|
||||
|
||||
@override
|
||||
String get alarmFallbackStationLabel => 'Station de secours';
|
||||
|
||||
@override
|
||||
String get alarmStationPickerSearchHint => 'Rechercher une station par nom';
|
||||
|
||||
@override
|
||||
String get alarmSnoozeDurationTitle => 'Durée de répétition';
|
||||
|
||||
@override
|
||||
String get snoozeAction => 'Répéter';
|
||||
|
||||
@override
|
||||
String alarmSnoozeOptionLabel(int minutes) {
|
||||
return '$minutes min';
|
||||
}
|
||||
|
||||
@override
|
||||
String get saveFavoritesAlarmHint =>
|
||||
'Enregistrez des stations dans les Favoris pour les utiliser comme alarme musicale.';
|
||||
|
||||
@@ -801,6 +801,23 @@ class AppLocalizationsHi extends AppLocalizations {
|
||||
String get noStationUseInternalSound =>
|
||||
'कोई स्टेशन नहीं: आंतरिक ध्वनि इस्तेमाल करें';
|
||||
|
||||
@override
|
||||
String get alarmFallbackStationLabel => 'बैकअप स्टेशन';
|
||||
|
||||
@override
|
||||
String get alarmStationPickerSearchHint => 'नाम से स्टेशन खोजें';
|
||||
|
||||
@override
|
||||
String get alarmSnoozeDurationTitle => 'स्नूज़ अवधि';
|
||||
|
||||
@override
|
||||
String get snoozeAction => 'स्नूज़';
|
||||
|
||||
@override
|
||||
String alarmSnoozeOptionLabel(int minutes) {
|
||||
return '$minutes मिनट';
|
||||
}
|
||||
|
||||
@override
|
||||
String get saveFavoritesAlarmHint =>
|
||||
'उन्हें संगीतमय अलार्म के रूप में इस्तेमाल करने के लिए स्टेशन पसंदीदा में सहेजें।';
|
||||
|
||||
@@ -804,6 +804,23 @@ class AppLocalizationsId extends AppLocalizations {
|
||||
String get noStationUseInternalSound =>
|
||||
'Tanpa stasiun: gunakan suara internal';
|
||||
|
||||
@override
|
||||
String get alarmFallbackStationLabel => 'Stasiun cadangan';
|
||||
|
||||
@override
|
||||
String get alarmStationPickerSearchHint => 'Cari stasiun berdasarkan nama';
|
||||
|
||||
@override
|
||||
String get alarmSnoozeDurationTitle => 'Durasi tunda';
|
||||
|
||||
@override
|
||||
String get snoozeAction => 'Tunda';
|
||||
|
||||
@override
|
||||
String alarmSnoozeOptionLabel(int minutes) {
|
||||
return '$minutes mnt';
|
||||
}
|
||||
|
||||
@override
|
||||
String get saveFavoritesAlarmHint =>
|
||||
'Simpan stasiun ke Favorit untuk digunakan sebagai alarm musik.';
|
||||
|
||||
@@ -805,6 +805,23 @@ class AppLocalizationsIt extends AppLocalizations {
|
||||
String get noStationUseInternalSound =>
|
||||
'Nessuna emittente: usa suono interno';
|
||||
|
||||
@override
|
||||
String get alarmFallbackStationLabel => 'Emittente di riserva';
|
||||
|
||||
@override
|
||||
String get alarmStationPickerSearchHint => 'Cerca un\'emittente per nome';
|
||||
|
||||
@override
|
||||
String get alarmSnoozeDurationTitle => 'Durata posticipo';
|
||||
|
||||
@override
|
||||
String get snoozeAction => 'Posticipa';
|
||||
|
||||
@override
|
||||
String alarmSnoozeOptionLabel(int minutes) {
|
||||
return '$minutes min';
|
||||
}
|
||||
|
||||
@override
|
||||
String get saveFavoritesAlarmHint =>
|
||||
'Salva emittenti nei Preferiti per usarle come sveglia musicale.';
|
||||
|
||||
@@ -777,6 +777,23 @@ class AppLocalizationsJa extends AppLocalizations {
|
||||
@override
|
||||
String get noStationUseInternalSound => '局なし: 内部音を使用';
|
||||
|
||||
@override
|
||||
String get alarmFallbackStationLabel => '予備の局';
|
||||
|
||||
@override
|
||||
String get alarmStationPickerSearchHint => '局名で検索';
|
||||
|
||||
@override
|
||||
String get alarmSnoozeDurationTitle => 'スヌーズ時間';
|
||||
|
||||
@override
|
||||
String get snoozeAction => 'スヌーズ';
|
||||
|
||||
@override
|
||||
String alarmSnoozeOptionLabel(int minutes) {
|
||||
return '$minutes分';
|
||||
}
|
||||
|
||||
@override
|
||||
String get saveFavoritesAlarmHint => '音楽アラームとして使うには、局をお気に入りに保存してください。';
|
||||
|
||||
|
||||
@@ -803,6 +803,23 @@ class AppLocalizationsPt extends AppLocalizations {
|
||||
@override
|
||||
String get noStationUseInternalSound => 'Sem estação: usar som interno';
|
||||
|
||||
@override
|
||||
String get alarmFallbackStationLabel => 'Estação reserva';
|
||||
|
||||
@override
|
||||
String get alarmStationPickerSearchHint => 'Buscar estação pelo nome';
|
||||
|
||||
@override
|
||||
String get alarmSnoozeDurationTitle => 'Duração da soneca';
|
||||
|
||||
@override
|
||||
String get snoozeAction => 'Soneca';
|
||||
|
||||
@override
|
||||
String alarmSnoozeOptionLabel(int minutes) {
|
||||
return '$minutes min';
|
||||
}
|
||||
|
||||
@override
|
||||
String get saveFavoritesAlarmHint =>
|
||||
'Salve estações nos Favoritos para usá-las como alarme musical.';
|
||||
|
||||
@@ -805,6 +805,23 @@ class AppLocalizationsRu extends AppLocalizations {
|
||||
String get noStationUseInternalSound =>
|
||||
'Без станции: использовать внутренний звук';
|
||||
|
||||
@override
|
||||
String get alarmFallbackStationLabel => 'Резервная станция';
|
||||
|
||||
@override
|
||||
String get alarmStationPickerSearchHint => 'Поиск станции по названию';
|
||||
|
||||
@override
|
||||
String get alarmSnoozeDurationTitle => 'Интервал повтора';
|
||||
|
||||
@override
|
||||
String get snoozeAction => 'Отложить';
|
||||
|
||||
@override
|
||||
String alarmSnoozeOptionLabel(int minutes) {
|
||||
return '$minutes мин';
|
||||
}
|
||||
|
||||
@override
|
||||
String get saveFavoritesAlarmHint =>
|
||||
'Сохраните станции в избранное, чтобы использовать их как музыкальный будильник.';
|
||||
|
||||
@@ -773,6 +773,23 @@ class AppLocalizationsZh extends AppLocalizations {
|
||||
@override
|
||||
String get noStationUseInternalSound => '无电台:使用内部声音';
|
||||
|
||||
@override
|
||||
String get alarmFallbackStationLabel => '备用电台';
|
||||
|
||||
@override
|
||||
String get alarmStationPickerSearchHint => '按名称搜索电台';
|
||||
|
||||
@override
|
||||
String get alarmSnoozeDurationTitle => '贪睡时长';
|
||||
|
||||
@override
|
||||
String get snoozeAction => '贪睡';
|
||||
|
||||
@override
|
||||
String alarmSnoozeOptionLabel(int minutes) {
|
||||
return '$minutes 分钟';
|
||||
}
|
||||
|
||||
@override
|
||||
String get saveFavoritesAlarmHint => '将电台保存到收藏,即可把它们用作音乐闹钟。';
|
||||
|
||||
|
||||
Reference in New Issue
Block a user