🐛 Fix: INTERNET-Permission fehlte im Release-Manifest

ROOT CAUSE für die fehlenden Logs — und für den fehlschlagenden
Navidrome-Login: Flutter injiziert android.permission.INTERNET nur in
debug/ und profile/AndroidManifest.xml. In main/AndroidManifest.xml
stand sie nicht, das Release-APK hatte damit gar keinen Netzzugriff.
Jede http-Anfrage schlug sofort mit SocketException fehl.

- INTERNET + ACCESS_NETWORK_STATE in main/AndroidManifest.xml ergänzt
- Login-Versuch/-Erfolg/-Fehlschlag im Settings-Dialog explizit geloggt
  (URL + User + ob das Passwortfeld leer war; das Passwort selbst nicht)
- Batch-Schwelle 20 -> 5, damit INFO-Logs auch ohne vorherigen Fehler
  hochgeladen werden

Nicht geaendert: der Sofort-Upload bei ERROR war bereits implementiert
(error() ruft _uploadIfError -> _uploadLogs direkt auf) und war nicht
die Ursache.

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XJzQjUtnvYUtHdnTs3iCru
This commit is contained in:
Hermes (Server)
2026-08-19 22:28:36 +02:00
co-authored by Claude Haiku 4.5
parent db0152e3fa
commit 323a67e3a2
3 changed files with 15 additions and 1 deletions
+5
View File
@@ -1,4 +1,9 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"> <manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Netzwerk: Flutter injiziert INTERNET nur in Debug-/Profile-Builds.
Ohne diesen Eintrag hat das Release-APK keinerlei Netzzugriff
(kein Navidrome-Login, kein Log-Upload). -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/> <uses-permission android:name="android.permission.WAKE_LOCK"/>
+1 -1
View File
@@ -34,7 +34,7 @@ class LogEntry {
/// Centralized Logging Service mit Auto-Upload zu Server /// Centralized Logging Service mit Auto-Upload zu Server
class LoggerService extends ChangeNotifier { class LoggerService extends ChangeNotifier {
static const String _logServerUrl = 'https://logs.baka-net.de/api/logs'; static const String _logServerUrl = 'https://logs.baka-net.de/api/logs';
static const int _batchSize = 20; static const int _batchSize = 5;
final List<LogEntry> _logs = []; final List<LogEntry> _logs = [];
late final String _deviceModel; late final String _deviceModel;
+9
View File
@@ -5,6 +5,7 @@ import '../library/database.dart';
import '../library/permissions.dart'; import '../library/permissions.dart';
import '../library/playlist_service.dart'; import '../library/playlist_service.dart';
import '../services/cache_manager.dart'; import '../services/cache_manager.dart';
import '../services/logger_service.dart';
import '../services/navidrome_service.dart'; import '../services/navidrome_service.dart';
import '../services/offline_mode.dart'; import '../services/offline_mode.dart';
import 'library_stats.dart'; import 'library_stats.dart';
@@ -261,16 +262,24 @@ class _SettingsScreenState extends State<SettingsScreen> {
? null ? null
: () async { : () async {
setDialogState(() => verbindet = true); setDialogState(() => verbindet = true);
logger.info(
'Login-Versuch: url=${urlCtrl.text} user=$selectedUser '
'pwLeer=${passCtrl.text.isEmpty}');
_navidrome.setCredentials( _navidrome.setCredentials(
urlCtrl.text, selectedUser, passCtrl.text); urlCtrl.text, selectedUser, passCtrl.text);
final ok = await _navidrome.ping(); final ok = await _navidrome.ping();
setDialogState(() => verbindet = false); setDialogState(() => verbindet = false);
if (ok && ctx.mounted) { if (ok && ctx.mounted) {
logger.info(
'Login erfolgreich: url=${urlCtrl.text} user=$selectedUser');
await _navidrome.speichereZugangsdaten( await _navidrome.speichereZugangsdaten(
urlCtrl.text, selectedUser, passCtrl.text); urlCtrl.text, selectedUser, passCtrl.text);
if (ctx.mounted) Navigator.pop(ctx); if (ctx.mounted) Navigator.pop(ctx);
if (mounted) setState(() {}); if (mounted) setState(() {});
} else if (ctx.mounted) { } else if (ctx.mounted) {
logger.error(
'Login fehlgeschlagen: url=${urlCtrl.text} '
'user=$selectedUser pwLeer=${passCtrl.text.isEmpty}');
setDialogState(() => setDialogState(() =>
fehler = '❌ Login fehlgeschlagen'); fehler = '❌ Login fehlgeschlagen');
} }