merge: make audio diagnostics visible in release builds
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer' as developer;
|
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
@@ -94,16 +93,18 @@ StreamSubscription<Object> observarErroresAudio(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Default [observarErroresAudio] logger: one `[PluriWave]`-prefixed
|
/// Default [observarErroresAudio] logger: one line per swallowed plugin
|
||||||
/// `developer.log` line per swallowed plugin exception, at the same
|
/// exception.
|
||||||
/// `level: 900` (SEVERE) that `servicio_audio.dart`'s existing error lines
|
///
|
||||||
/// use, so a single logcat/DevTools filter catches both.
|
/// Uses [debugPrint], NOT `dart:developer`'s `log`. That distinction is the
|
||||||
|
/// whole reason this channel existed for weeks without ever producing a
|
||||||
|
/// single line of evidence: `log()` writes to the VM service, which a
|
||||||
|
/// RELEASE build does not have, so every exception this was built to catch
|
||||||
|
/// was still being thrown away — just one layer further down than before.
|
||||||
|
/// `debugPrint` reaches logcat in release, which is the only build that ever
|
||||||
|
/// runs in the car.
|
||||||
void registrarErrorAudioService(Object error) {
|
void registrarErrorAudioService(Object error) {
|
||||||
developer.log(
|
debugPrint('[PluriWave][ArranqueAudio] AudioService.asyncError: $error');
|
||||||
'[PluriWave] AudioService.asyncError: $error',
|
|
||||||
name: 'ArranqueAudio',
|
|
||||||
level: 900,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Minimal branded bootstrap widget for the degraded path (Design "still
|
/// Minimal branded bootstrap widget for the degraded path (Design "still
|
||||||
|
|||||||
@@ -97,7 +97,5 @@ DecisionAvanceCola decidirAvanceCola({
|
|||||||
/// distinct `ColaLocal` during the async URI-resolve gap is correctly
|
/// distinct `ColaLocal` during the async URI-resolve gap is correctly
|
||||||
/// detected as stale and aborts the advance, instead of silently racing an
|
/// detected as stale and aborts the advance, instead of silently racing an
|
||||||
/// external play/stop.
|
/// external play/stop.
|
||||||
bool avanceEsValido(
|
bool avanceEsValido(ColaLocal? colaLocalActual, ColaLocal? siguienteEsperado) =>
|
||||||
ColaLocal? colaLocalActual,
|
identical(colaLocalActual, siguienteEsperado);
|
||||||
ColaLocal? siguienteEsperado,
|
|
||||||
) => identical(colaLocalActual, siguienteEsperado);
|
|
||||||
|
|||||||
@@ -61,9 +61,10 @@ String nombreCarpetaDesdeUri(String treeUri, {required String nombreGenerico}) {
|
|||||||
segmento = documentId.substring(ultimaBarra + 1);
|
segmento = documentId.substring(ultimaBarra + 1);
|
||||||
} else {
|
} else {
|
||||||
final ultimosDosPuntos = documentId.lastIndexOf(':');
|
final ultimosDosPuntos = documentId.lastIndexOf(':');
|
||||||
segmento = ultimosDosPuntos >= 0
|
segmento =
|
||||||
? documentId.substring(ultimosDosPuntos + 1)
|
ultimosDosPuntos >= 0
|
||||||
: documentId;
|
? documentId.substring(ultimosDosPuntos + 1)
|
||||||
|
: documentId;
|
||||||
}
|
}
|
||||||
|
|
||||||
final recortado = segmento.trim();
|
final recortado = segmento.trim();
|
||||||
@@ -200,10 +201,9 @@ class FuenteMusicaLocalAutoImpl implements FuenteMusicaLocalAuto {
|
|||||||
try {
|
try {
|
||||||
final uri = await _uriPersistida();
|
final uri = await _uriPersistida();
|
||||||
if (uri == null || uri.isEmpty) return false;
|
if (uri == null || uri.isEmpty) return false;
|
||||||
final valido = await _canal.invokeMethod<bool>(
|
final valido = await _canal.invokeMethod<bool>('hasPersistedPermission', {
|
||||||
'hasPersistedPermission',
|
'treeUri': uri,
|
||||||
{'treeUri': uri},
|
});
|
||||||
);
|
|
||||||
return valido ?? false;
|
return valido ?? false;
|
||||||
} catch (_) {
|
} catch (_) {
|
||||||
// Cold-start / revoked-permission safety (Spec "Permission revoked or
|
// Cold-start / revoked-permission safety (Spec "Permission revoked or
|
||||||
|
|||||||
@@ -1,9 +1,8 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer' as developer;
|
|
||||||
import 'dart:ui' show Locale;
|
import 'dart:ui' show Locale;
|
||||||
|
|
||||||
import 'package:audio_service/audio_service.dart';
|
import 'package:audio_service/audio_service.dart';
|
||||||
import 'package:flutter/foundation.dart' show visibleForTesting;
|
import 'package:flutter/foundation.dart' show debugPrint, visibleForTesting;
|
||||||
import 'package:just_audio/just_audio.dart';
|
import 'package:just_audio/just_audio.dart';
|
||||||
|
|
||||||
import '../l10n/display_names.dart';
|
import '../l10n/display_names.dart';
|
||||||
@@ -836,7 +835,14 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
/// `eqDisponible` rides along because the equalizer custom action is gated
|
/// `eqDisponible` rides along because the equalizer custom action is gated
|
||||||
/// on it and the flag is otherwise unobservable — one car session answers
|
/// on it and the flag is otherwise unobservable — one car session answers
|
||||||
/// both questions at once:
|
/// both questions at once:
|
||||||
/// adb logcat -s ServicioAudio
|
///
|
||||||
|
/// adb logcat | grep PluriWave
|
||||||
|
///
|
||||||
|
/// It uses [debugPrint] and NOT `dart:developer`'s `log`, and that is not a
|
||||||
|
/// style choice. `log()` writes to the VM service, which a RELEASE build
|
||||||
|
/// does not have — so this trace, and every error line in this file, was
|
||||||
|
/// invisible in the only build that ever runs in a car. Weeks of "no
|
||||||
|
/// evidence" were this, not a quiet app. Do not convert these back.
|
||||||
void _trazarEstadoPublicado() {
|
void _trazarEstadoPublicado() {
|
||||||
final s = playbackState.value;
|
final s = playbackState.value;
|
||||||
final traza =
|
final traza =
|
||||||
@@ -846,7 +852,7 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
'controles=${s.controls.length}';
|
'controles=${s.controls.length}';
|
||||||
if (traza == _ultimaTrazaEstado) return;
|
if (traza == _ultimaTrazaEstado) return;
|
||||||
_ultimaTrazaEstado = traza;
|
_ultimaTrazaEstado = traza;
|
||||||
developer.log(traza, name: 'ServicioAudio');
|
debugPrint('[PluriWave][ServicioAudio] estado $traza');
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Binds [construirControlesTransporte] — which holds the whole contract,
|
/// Binds [construirControlesTransporte] — which holds the whole contract,
|
||||||
@@ -909,11 +915,7 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
mensaje = _textos.audioErrorGeneric;
|
mensaje = _textos.audioErrorGeneric;
|
||||||
}
|
}
|
||||||
|
|
||||||
developer.log(
|
debugPrint('[PluriWave][ServicioAudio] Error reproducción: $codigoLog');
|
||||||
'[PluriWave] Error reproducción: $codigoLog',
|
|
||||||
name: 'ServicioAudio',
|
|
||||||
level: 900,
|
|
||||||
);
|
|
||||||
|
|
||||||
_detenerReconexion();
|
_detenerReconexion();
|
||||||
playbackState.add(
|
playbackState.add(
|
||||||
@@ -956,12 +958,10 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
_reconectando = true;
|
_reconectando = true;
|
||||||
developer.log(
|
debugPrint(
|
||||||
'[PluriWave] Stall de red, reintento ${_reconexion.intentos}/'
|
'[PluriWave][ServicioAudio] Stall de red, reintento ${_reconexion.intentos}/'
|
||||||
'${_reconexion.maxReintentos} en '
|
'${_reconexion.maxReintentos} en '
|
||||||
'${_reconexion.retrasoParaIntento(_reconexion.intentos).inSeconds}s',
|
'${_reconexion.retrasoParaIntento(_reconexion.intentos).inSeconds}s',
|
||||||
name: 'ServicioAudio',
|
|
||||||
level: 800,
|
|
||||||
);
|
);
|
||||||
playbackState.add(
|
playbackState.add(
|
||||||
playbackState.value.copyWith(
|
playbackState.value.copyWith(
|
||||||
@@ -1207,11 +1207,8 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
rethrow;
|
rethrow;
|
||||||
} on Exception catch (e, stackTrace) {
|
} on Exception catch (e, stackTrace) {
|
||||||
_cambiandoFuente = false;
|
_cambiandoFuente = false;
|
||||||
developer.log(
|
debugPrint(
|
||||||
'[PluriWave] Error inesperado en playMediaItem: $e',
|
'[PluriWave][ServicioAudio] Error inesperado en playMediaItem: $e',
|
||||||
name: 'ServicioAudio',
|
|
||||||
level: 900,
|
|
||||||
stackTrace: stackTrace,
|
|
||||||
);
|
);
|
||||||
if (revision == _revisionFuente) {
|
if (revision == _revisionFuente) {
|
||||||
playbackState.add(
|
playbackState.add(
|
||||||
@@ -1264,11 +1261,8 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
void _iniciarPlaySinBloquear(MediaItem mediaItem, int revision) {
|
void _iniciarPlaySinBloquear(MediaItem mediaItem, int revision) {
|
||||||
unawaited(
|
unawaited(
|
||||||
_player.play().catchError((Object error, StackTrace stackTrace) {
|
_player.play().catchError((Object error, StackTrace stackTrace) {
|
||||||
developer.log(
|
debugPrint(
|
||||||
'[PluriWave] Error al iniciar ${mediaItem.title}: $error',
|
'[PluriWave][ServicioAudio] Error al iniciar ${mediaItem.title}: $error',
|
||||||
name: 'ServicioAudio',
|
|
||||||
level: 900,
|
|
||||||
stackTrace: stackTrace,
|
|
||||||
);
|
);
|
||||||
if (revision == _revisionFuente) {
|
if (revision == _revisionFuente) {
|
||||||
_gestionarErrorReproduccion(error);
|
_gestionarErrorReproduccion(error);
|
||||||
@@ -1553,11 +1547,7 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
if (destino == null) return;
|
if (destino == null) return;
|
||||||
await playMediaItem(mediaItemParaEmisora(destino, l10n: _textos));
|
await playMediaItem(mediaItemParaEmisora(destino, l10n: _textos));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
developer.log(
|
debugPrint('[PluriWave][ServicioAudio] Error saltando de emisora: $e');
|
||||||
'[PluriWave] Error saltando de emisora: $e',
|
|
||||||
name: 'ServicioAudio',
|
|
||||||
level: 900,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1720,10 +1710,8 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
if (emisora == null) return;
|
if (emisora == null) return;
|
||||||
await playMediaItem(mediaItemParaEmisora(emisora, l10n: _textos));
|
await playMediaItem(mediaItemParaEmisora(emisora, l10n: _textos));
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
developer.log(
|
debugPrint(
|
||||||
'[PluriWave] Error en playFromSearch($query): $e',
|
'[PluriWave][ServicioAudio] Error en playFromSearch($query): $e',
|
||||||
name: 'ServicioAudio',
|
|
||||||
level: 900,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1793,10 +1781,8 @@ class PluriWaveAudioHandler extends BaseAudioHandler
|
|||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Spec "Unknown or stale media id": never propagate from the handler.
|
// Spec "Unknown or stale media id": never propagate from the handler.
|
||||||
developer.log(
|
debugPrint(
|
||||||
'[PluriWave] Error en playFromMediaId($mediaId): $e',
|
'[PluriWave][ServicioAudio] Error en playFromMediaId($mediaId): $e',
|
||||||
name: 'ServicioAudio',
|
|
||||||
level: 900,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:developer' as developer;
|
|
||||||
|
|
||||||
import 'package:audio_session/audio_session.dart';
|
import 'package:audio_session/audio_session.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@@ -89,10 +88,8 @@ class ServicioAudioSession {
|
|||||||
(_) => unawaited(manejarDesconexionSalida()),
|
(_) => unawaited(manejarDesconexionSalida()),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
developer.log(
|
debugPrint(
|
||||||
'[PluriWave] No se pudo configurar la sesion de audio: $e',
|
'[PluriWave][ServicioAudioSession] No se pudo configurar la sesion de audio: $e',
|
||||||
name: 'ServicioAudioSession',
|
|
||||||
level: 900,
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -157,7 +157,9 @@ class ServicioDispositivoAudioReal extends ServicioDispositivoAudio {
|
|||||||
@override
|
@override
|
||||||
Future<Map<String, String>> obtenerNombresEmparejados() async {
|
Future<Map<String, String>> obtenerNombresEmparejados() async {
|
||||||
try {
|
try {
|
||||||
final raw = await _methodChannel.invokeMethod<Map>('getBondedDeviceNames');
|
final raw = await _methodChannel.invokeMethod<Map>(
|
||||||
|
'getBondedDeviceNames',
|
||||||
|
);
|
||||||
if (raw == null) return const {};
|
if (raw == null) return const {};
|
||||||
return {
|
return {
|
||||||
for (final entry in raw.entries)
|
for (final entry in raw.entries)
|
||||||
|
|||||||
@@ -70,7 +70,10 @@ class ServicioEcualizador {
|
|||||||
final porEmisora = _leerPresetsPorEmisora(prefs);
|
final porEmisora = _leerPresetsPorEmisora(prefs);
|
||||||
final presetsDispositivo = _leerMapa(prefs, _keyPresetsPorDispositivo);
|
final presetsDispositivo = _leerMapa(prefs, _keyPresetsPorDispositivo);
|
||||||
final presetsMatriz = _leerMapa(prefs, _keyPresetsMatriz);
|
final presetsMatriz = _leerMapa(prefs, _keyPresetsMatriz);
|
||||||
final nombresDispositivos = _leerMapaStrings(prefs, _keyNombresDispositivos);
|
final nombresDispositivos = _leerMapaStrings(
|
||||||
|
prefs,
|
||||||
|
_keyNombresDispositivos,
|
||||||
|
);
|
||||||
return ConfiguracionEcualizador(
|
return ConfiguracionEcualizador(
|
||||||
principal: principal,
|
principal: principal,
|
||||||
porEmisora: porEmisora,
|
porEmisora: porEmisora,
|
||||||
@@ -124,21 +127,22 @@ class ServicioEcualizador {
|
|||||||
String prefijo,
|
String prefijo,
|
||||||
) async {
|
) async {
|
||||||
final presetsPorDispositivo = _leerMapa(prefs, _keyPresetsPorDispositivo);
|
final presetsPorDispositivo = _leerMapa(prefs, _keyPresetsPorDispositivo);
|
||||||
final dispositivos = presetsPorDispositivo.keys
|
final dispositivos =
|
||||||
.where((clave) => clave.startsWith(prefijo))
|
presetsPorDispositivo.keys
|
||||||
.toList();
|
.where((clave) => clave.startsWith(prefijo))
|
||||||
|
.toList();
|
||||||
|
|
||||||
final nombres = _leerMapaStrings(prefs, _keyNombresDispositivos);
|
final nombres = _leerMapaStrings(prefs, _keyNombresDispositivos);
|
||||||
final nombresAPurgar = nombres.keys
|
final nombresAPurgar =
|
||||||
.where((clave) => clave.startsWith(prefijo))
|
nombres.keys.where((clave) => clave.startsWith(prefijo)).toList();
|
||||||
.toList();
|
|
||||||
|
|
||||||
final matriz = _leerMapa(prefs, _keyPresetsMatriz);
|
final matriz = _leerMapa(prefs, _keyPresetsMatriz);
|
||||||
final matrizAPurgar = matriz.keys.where((clave) {
|
final matrizAPurgar =
|
||||||
final separador = clave.indexOf(':');
|
matriz.keys.where((clave) {
|
||||||
if (separador == -1) return false;
|
final separador = clave.indexOf(':');
|
||||||
return clave.substring(separador + 1).startsWith(prefijo);
|
if (separador == -1) return false;
|
||||||
}).toList();
|
return clave.substring(separador + 1).startsWith(prefijo);
|
||||||
|
}).toList();
|
||||||
|
|
||||||
for (final clave in dispositivos) {
|
for (final clave in dispositivos) {
|
||||||
presetsPorDispositivo.remove(clave);
|
presetsPorDispositivo.remove(clave);
|
||||||
@@ -183,11 +187,12 @@ class ServicioEcualizador {
|
|||||||
// (station UUIDs are RFC4122 and contain no colons — multi-device-eq
|
// (station UUIDs are RFC4122 and contain no colons — multi-device-eq
|
||||||
// ADR-3), since deviceId itself may contain colons (e.g. a MAC-based id).
|
// ADR-3), since deviceId itself may contain colons (e.g. a MAC-based id).
|
||||||
final presetsMatriz = _leerMapa(prefs, _keyPresetsMatriz);
|
final presetsMatriz = _leerMapa(prefs, _keyPresetsMatriz);
|
||||||
final clavesMatrizAPurgar = presetsMatriz.keys.where((clave) {
|
final clavesMatrizAPurgar =
|
||||||
final separador = clave.indexOf(':');
|
presetsMatriz.keys.where((clave) {
|
||||||
if (separador == -1) return false;
|
final separador = clave.indexOf(':');
|
||||||
return clave.substring(separador + 1) == deviceId;
|
if (separador == -1) return false;
|
||||||
}).toList();
|
return clave.substring(separador + 1) == deviceId;
|
||||||
|
}).toList();
|
||||||
if (clavesMatrizAPurgar.isNotEmpty) {
|
if (clavesMatrizAPurgar.isNotEmpty) {
|
||||||
for (final clave in clavesMatrizAPurgar) {
|
for (final clave in clavesMatrizAPurgar) {
|
||||||
presetsMatriz.remove(clave);
|
presetsMatriz.remove(clave);
|
||||||
|
|||||||
Reference in New Issue
Block a user