Issue #1: CloudScreen Zurück-Button — onZurueck Callback statt Navigator.pop() - CloudScreen erhält VoidCallback? onZurueck Parameter - Bei onZurueck != null → Callback (Tab-Wechsel in home_screen) - Fallback: Navigator.pop(context) für Standalone-Nutzung Issue #5: Token wird nur bei HTTP 401 gelöscht - _tokenOnlinePruefen(): nur statusCode == 401 → false - 5xx, andere Fehler, Netzwerkfehler → true (Token behalten) - Verhindert Ausloggen bei temporären Server-Problemen Issue #4: Android 13+ Berechtigungen beim App-Start - main.dart: frageSpeicherZugriff() + POST_NOTIFICATIONS nach Auth-Init - musik_scanner.dart: Permission-Check vor externen Ordnern in _sammleMusikPfade() Build: ⏭️ (no APK)
119 lines
3.5 KiB
Dart
119 lines
3.5 KiB
Dart
import 'package:flutter/material.dart';
|
||
import 'dart:io' show Platform;
|
||
import 'package:audio_service/audio_service.dart';
|
||
import 'package:shared_preferences/shared_preferences.dart';
|
||
import 'package:permission_handler/permission_handler.dart';
|
||
import 'database/db_helper.dart';
|
||
import 'services/favoriten_service.dart';
|
||
import 'services/auth_service.dart';
|
||
import 'services/cloud_service.dart';
|
||
import 'services/melo_logger.dart';
|
||
import 'services/audio_handler.dart';
|
||
import 'services/musik_scanner.dart';
|
||
import 'utils/farb_theme.dart';
|
||
import 'screens/home_screen.dart';
|
||
import 'screens/login_screen.dart';
|
||
|
||
void main() async {
|
||
WidgetsFlutterBinding.ensureInitialized();
|
||
|
||
// Logger startet sofort – zeichnet ALLES auf
|
||
MeloLogger().init('2.35');
|
||
|
||
try {
|
||
await DbHelper().db;
|
||
await FavoritenService().init();
|
||
// Hintergrund-Audio-Service starten
|
||
await AudioService.init(
|
||
builder: () => MeloAudioHandler(),
|
||
config: const AudioServiceConfig(
|
||
androidNotificationChannelId: 'de.baka.melo.audio',
|
||
androidNotificationChannelName: 'Melo Wiedergabe',
|
||
androidNotificationOngoing: true,
|
||
),
|
||
);
|
||
MeloLogger().zustand('start_ok', {'db': 'ok', 'audio': 'ok'});
|
||
} catch (e, stack) {
|
||
MeloLogger().fehler('App-Start', e, stack);
|
||
}
|
||
|
||
// Auth initialisieren (Token aus SharedPreferences laden + online validieren)
|
||
await AuthService().initialisieren();
|
||
|
||
// Android-Berechtigungen beim Start abfragen (Issue #4)
|
||
// Speicherzugriff für Musik-Scan (READ_MEDIA_AUDIO / Storage)
|
||
await MusikScanner().frageSpeicherZugriff();
|
||
|
||
// POST_NOTIFICATIONS für Android 13+ (Notifications für Downloads, Player)
|
||
if (Platform.isAndroid) {
|
||
await Permission.notification.request();
|
||
}
|
||
|
||
// Auto-Sync beim Start (wenn eingeloggt und Auto-Sync aktiv)
|
||
_starteAutoSyncFallsNoetig();
|
||
|
||
runApp(const MeloApp());
|
||
}
|
||
|
||
/// Führt Cloud-Auto-Sync beim App-Start aus, falls konfiguriert
|
||
Future<void> _starteAutoSyncFallsNoetig() async {
|
||
try {
|
||
final auth = AuthService();
|
||
if (!auth.istEingeloggt) return;
|
||
|
||
// Prüfen, ob Auto-Sync aktiviert ist
|
||
final SharedPreferences prefs =
|
||
await SharedPreferences.getInstance();
|
||
final autoSync = prefs.getBool('cloud_auto') ?? true;
|
||
if (!autoSync) return;
|
||
|
||
final cloud = CloudService();
|
||
final ok = await cloud.login(auth.benutzer);
|
||
if (!ok) return;
|
||
|
||
// Letzten Sync prüfen — nur syncen wenn nötig
|
||
final lastSync = prefs.getString('cloud_last_sync_ts');
|
||
final now = DateTime.now();
|
||
|
||
if (lastSync != null) {
|
||
final last = DateTime.tryParse(lastSync);
|
||
if (last != null) {
|
||
final interval = prefs.getInt('cloud_interval') ?? 6;
|
||
if (now.difference(last).inHours < interval) {
|
||
return; // Noch nicht fällig
|
||
}
|
||
}
|
||
}
|
||
|
||
// Sync ausführen
|
||
MeloLogger().aktion('auto_sync_start', {});
|
||
final st = await cloud.status();
|
||
if (st != null) {
|
||
final serverSongs = await cloud.listSongs();
|
||
MeloLogger().aktion('auto_sync_done',
|
||
{'songs': serverSongs.length});
|
||
}
|
||
await prefs.setString('cloud_last_sync_ts', now.toIso8601String());
|
||
} catch (e) {
|
||
MeloLogger().fehler('auto_sync_init', e);
|
||
}
|
||
}
|
||
|
||
class MeloApp extends StatelessWidget {
|
||
const MeloApp({super.key});
|
||
|
||
@override
|
||
Widget build(BuildContext context) {
|
||
final auth = AuthService();
|
||
|
||
return MaterialApp(
|
||
title: 'Melo',
|
||
debugShowCheckedModeBanner: false,
|
||
theme: MeloTheme.theme,
|
||
home: auth.istEingeloggt
|
||
? const Scaffold(body: MeloHome())
|
||
: const LoginScreen(),
|
||
);
|
||
}
|
||
}
|