fix(alarms): harden native playback and pre-notice actions
This commit is contained in:
@@ -10,17 +10,26 @@ class EventoAlarmaAndroid {
|
||||
required this.alarmaId,
|
||||
required this.titulo,
|
||||
required this.accion,
|
||||
this.triggerAtMillis = 0,
|
||||
this.occurrenceAtMillis = 0,
|
||||
this.snoozeMinutes = 5,
|
||||
});
|
||||
|
||||
final String alarmaId;
|
||||
final String titulo;
|
||||
final String accion;
|
||||
final int triggerAtMillis;
|
||||
final int occurrenceAtMillis;
|
||||
final int snoozeMinutes;
|
||||
|
||||
factory EventoAlarmaAndroid.fromMap(Map<Object?, Object?> map) {
|
||||
return EventoAlarmaAndroid(
|
||||
alarmaId: map['alarmId'] as String? ?? '',
|
||||
titulo: map['alarmTitle'] as String? ?? 'PluriWave',
|
||||
accion: map['alarmAction'] as String? ?? '',
|
||||
triggerAtMillis: (map['triggerAtMillis'] as num?)?.toInt() ?? 0,
|
||||
occurrenceAtMillis: (map['occurrenceAtMillis'] as num?)?.toInt() ?? 0,
|
||||
snoozeMinutes: (map['snoozeMinutes'] as num?)?.toInt() ?? 5,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -29,12 +38,18 @@ class DiagnosticoAlarmasAndroid {
|
||||
const DiagnosticoAlarmasAndroid({
|
||||
required this.puedeProgramarExactas,
|
||||
required this.notificacionesPermitidas,
|
||||
required this.puedeUsarPantallaCompleta,
|
||||
required this.ignoraOptimizacionBateria,
|
||||
required this.alarmasNativasPendientes,
|
||||
required this.fabricante,
|
||||
required this.versionSdk,
|
||||
});
|
||||
|
||||
final bool puedeProgramarExactas;
|
||||
final bool notificacionesPermitidas;
|
||||
final bool puedeUsarPantallaCompleta;
|
||||
final bool ignoraOptimizacionBateria;
|
||||
final int alarmasNativasPendientes;
|
||||
final String fabricante;
|
||||
final int versionSdk;
|
||||
|
||||
@@ -42,13 +57,33 @@ class DiagnosticoAlarmasAndroid {
|
||||
return DiagnosticoAlarmasAndroid(
|
||||
puedeProgramarExactas: map['canScheduleExactAlarms'] as bool? ?? true,
|
||||
notificacionesPermitidas: map['notificationsEnabled'] as bool? ?? true,
|
||||
puedeUsarPantallaCompleta:
|
||||
map['canUseFullScreenIntent'] as bool? ?? true,
|
||||
ignoraOptimizacionBateria:
|
||||
map['isIgnoringBatteryOptimizations'] as bool? ?? true,
|
||||
alarmasNativasPendientes: map['nativePendingAlarmsCount'] as int? ?? 0,
|
||||
fabricante: map['manufacturer'] as String? ?? 'Android',
|
||||
versionSdk: map['sdkInt'] as int? ?? 0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ServicioAlarmasAndroid {
|
||||
abstract class PuertoAlarmasAndroid {
|
||||
Stream<EventoAlarmaAndroid> get eventosAlarma;
|
||||
|
||||
Future<void> programar(AlarmaMusical alarma);
|
||||
Future<void> cancelar(String alarmaId);
|
||||
Future<void> ocultarNotificacionAlarma(String alarmaId);
|
||||
Future<void> detenerSonidoNativo(String alarmaId);
|
||||
Future<bool> solicitarPermisoAlarmasExactas();
|
||||
Future<bool> solicitarPermisoNotificaciones();
|
||||
Future<bool> solicitarPermisoPantallaCompleta();
|
||||
Future<void> confirmarAudioFlutter(String alarmaId);
|
||||
Future<DiagnosticoAlarmasAndroid> diagnostico();
|
||||
Future<EventoAlarmaAndroid?> obtenerEventoInicial();
|
||||
}
|
||||
|
||||
class ServicioAlarmasAndroid implements PuertoAlarmasAndroid {
|
||||
ServicioAlarmasAndroid({
|
||||
MethodChannel channel = const MethodChannel('pluriwave/alarm_scheduler'),
|
||||
}) : _channel = channel {
|
||||
@@ -60,10 +95,12 @@ class ServicioAlarmasAndroid {
|
||||
StreamController<EventoAlarmaAndroid>.broadcast();
|
||||
static bool _handlerInstalado = false;
|
||||
|
||||
@override
|
||||
Stream<EventoAlarmaAndroid> get eventosAlarma => _eventosController.stream;
|
||||
|
||||
@override
|
||||
Future<void> programar(AlarmaMusical alarma) async {
|
||||
final proxima = alarma.proximaEjecucion;
|
||||
final proxima = alarma.proximaProgramable;
|
||||
if (proxima == null || !alarma.activa) {
|
||||
debugPrint(
|
||||
'[PluriWave][alarmas] cancelar por inactiva/sin proxima id=${alarma.id} activa=${alarma.activa} proxima=$proxima',
|
||||
@@ -79,7 +116,20 @@ class ServicioAlarmasAndroid {
|
||||
'title': alarma.nombre,
|
||||
'triggerAtMillis': proxima.millisecondsSinceEpoch,
|
||||
'preNoticeAtMillis':
|
||||
proxima.subtract(const Duration(minutes: 30)).millisecondsSinceEpoch,
|
||||
alarma.snoozeHasta == null
|
||||
? proxima.subtract(const Duration(minutes: 30)).millisecondsSinceEpoch
|
||||
: 0,
|
||||
'hour': alarma.hora,
|
||||
'minute': alarma.minuto,
|
||||
'scheduleType': alarma.tipoProgramacion.name,
|
||||
'weekdays': alarma.diasSemana,
|
||||
'oneShotDateMillis': alarma.fechaUnica?.millisecondsSinceEpoch,
|
||||
'snoozeUntilMillis': alarma.snoozeHasta?.millisecondsSinceEpoch,
|
||||
'snoozeOriginMillis': alarma.snoozeOrigen?.millisecondsSinceEpoch,
|
||||
'snoozeMinutes': alarma.snoozeMinutos,
|
||||
'lastHandledAtMillis':
|
||||
alarma.ultimaEjecucionGestionada?.millisecondsSinceEpoch,
|
||||
'soundOnVacation': alarma.sonarEnVacaciones,
|
||||
'stationName': alarma.emisora?.nombre,
|
||||
'stationUrl': alarma.emisora?.url,
|
||||
'fallbackSound': alarma.sonidoInterno.name,
|
||||
@@ -92,15 +142,23 @@ class ServicioAlarmasAndroid {
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> cancelar(String alarmaId) =>
|
||||
_logAndInvokeVoid('cancelAlarm', {'id': alarmaId});
|
||||
|
||||
@override
|
||||
Future<void> ocultarNotificacionAlarma(String alarmaId) =>
|
||||
_logAndInvokeVoid('dismissAlarmNotification', {'id': alarmaId});
|
||||
|
||||
@override
|
||||
Future<void> detenerSonidoNativo(String alarmaId) =>
|
||||
_logAndInvokeVoid('stopNativeAlarmSound', {'id': alarmaId});
|
||||
|
||||
@override
|
||||
Future<void> confirmarAudioFlutter(String alarmaId) =>
|
||||
_logAndInvokeVoid('confirmFlutterAudio', {'id': alarmaId});
|
||||
|
||||
@override
|
||||
Future<bool> solicitarPermisoAlarmasExactas() async {
|
||||
final abierto = await _channel.invokeMethod<bool>(
|
||||
'requestExactAlarmPermission',
|
||||
@@ -108,6 +166,23 @@ class ServicioAlarmasAndroid {
|
||||
return abierto ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> solicitarPermisoNotificaciones() async {
|
||||
final abierto = await _channel.invokeMethod<bool>(
|
||||
'requestPostNotificationsPermission',
|
||||
);
|
||||
return abierto ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> solicitarPermisoPantallaCompleta() async {
|
||||
final abierto = await _channel.invokeMethod<bool>(
|
||||
'requestFullScreenIntentPermission',
|
||||
);
|
||||
return abierto ?? false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<DiagnosticoAlarmasAndroid> diagnostico() async {
|
||||
debugPrint('[PluriWave][alarmas] diagnostico android');
|
||||
final raw = await _channel.invokeMethod<Map<Object?, Object?>>(
|
||||
@@ -120,6 +195,7 @@ class ServicioAlarmasAndroid {
|
||||
return diag;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<EventoAlarmaAndroid?> obtenerEventoInicial() async {
|
||||
final raw = await _channel.invokeMethod<Map<Object?, Object?>>(
|
||||
'getInitialAlarmIntent',
|
||||
|
||||
Reference in New Issue
Block a user