v2.36 — Sprint 1: Behebe Issues #1 (Zurück-Button), #5 (Ausgeloggt), #4 (Berechtigungen)

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)
This commit is contained in:
Dustin
2026-08-02 15:48:08 +02:00
parent 15993b1d0a
commit d555a7f146
5 changed files with 34 additions and 5 deletions
+12
View File
@@ -1,12 +1,15 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'dart:io' show Platform;
import 'package:audio_service/audio_service.dart'; import 'package:audio_service/audio_service.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:permission_handler/permission_handler.dart';
import 'database/db_helper.dart'; import 'database/db_helper.dart';
import 'services/favoriten_service.dart'; import 'services/favoriten_service.dart';
import 'services/auth_service.dart'; import 'services/auth_service.dart';
import 'services/cloud_service.dart'; import 'services/cloud_service.dart';
import 'services/melo_logger.dart'; import 'services/melo_logger.dart';
import 'services/audio_handler.dart'; import 'services/audio_handler.dart';
import 'services/musik_scanner.dart';
import 'utils/farb_theme.dart'; import 'utils/farb_theme.dart';
import 'screens/home_screen.dart'; import 'screens/home_screen.dart';
import 'screens/login_screen.dart'; import 'screens/login_screen.dart';
@@ -37,6 +40,15 @@ void main() async {
// Auth initialisieren (Token aus SharedPreferences laden + online validieren) // Auth initialisieren (Token aus SharedPreferences laden + online validieren)
await AuthService().initialisieren(); 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) // Auto-Sync beim Start (wenn eingeloggt und Auto-Sync aktiv)
_starteAutoSyncFallsNoetig(); _starteAutoSyncFallsNoetig();
+9 -2
View File
@@ -13,7 +13,8 @@ import '../services/melo_logger.dart';
/// Playlisten, Favoriten, Auto-Sync, Persistent Login, Benutzerdefinierte Namen /// Playlisten, Favoriten, Auto-Sync, Persistent Login, Benutzerdefinierte Namen
class CloudScreen extends StatefulWidget { class CloudScreen extends StatefulWidget {
final CloudService cloud; final CloudService cloud;
const CloudScreen({super.key, required this.cloud}); final VoidCallback? onZurueck;
const CloudScreen({super.key, required this.cloud, this.onZurueck});
@override @override
State<CloudScreen> createState() => _CloudScreenState(); State<CloudScreen> createState() => _CloudScreenState();
@@ -590,7 +591,13 @@ class _CloudScreenState extends State<CloudScreen> {
backgroundColor: MeloTheme.dunkel1, backgroundColor: MeloTheme.dunkel1,
leading: IconButton( leading: IconButton(
icon: const Icon(Icons.arrow_back, color: Colors.white, size: 22), icon: const Icon(Icons.arrow_back, color: Colors.white, size: 22),
onPressed: () => Navigator.pop(context), onPressed: () {
if (widget.onZurueck != null) {
widget.onZurueck!();
} else {
Navigator.pop(context);
}
},
), ),
title: const Row( title: const Row(
children: [ children: [
+4 -1
View File
@@ -303,7 +303,10 @@ class _MeloHomeState extends State<MeloHome> {
onSongsChanged: _vm.ladeSongs, onSongsChanged: _vm.ladeSongs,
) )
: _aktiverTab == 4 : _aktiverTab == 4
? CloudScreen(cloud: _cloud) ? CloudScreen(
cloud: _cloud,
onZurueck: () => setState(() => _aktiverTab = 0),
)
: Column( : Column(
children: [ children: [
MeloHeader(onDownload: _zeigeDownloadDialog, onSearch: _zeigeSuche, onServer: _zeigeServerBrowser), MeloHeader(onDownload: _zeigeDownloadDialog, onSearch: _zeigeSuche, onServer: _zeigeServerBrowser),
+5 -1
View File
@@ -69,7 +69,11 @@ class AuthService {
headers: authHeader, headers: authHeader,
) )
.timeout(const Duration(seconds: 5)); .timeout(const Duration(seconds: 5));
return response.statusCode == 200; if (response.statusCode == 200) return true;
// Nur bei explizitem 401 (abgelaufen/ungültig) den Token löschen.
// Bei 5xx, anderen Fehlern oder temporären Server-Problemen Token behalten.
if (response.statusCode == 401) return false;
return true;
} catch (_) { } catch (_) {
// Bei Netzwerkfehler: Token lokal akzeptieren (Offline-Modus) // Bei Netzwerkfehler: Token lokal akzeptieren (Offline-Modus)
return true; return true;
+4 -1
View File
@@ -135,8 +135,11 @@ class MusikScanner {
MeloLogger().fehler('scanner_intern_pfad', e); MeloLogger().fehler('scanner_intern_pfad', e);
} }
// 2. Externe System-Ordner (nur Android) // 2. Externe System-Ordner (nur Android, nur wenn Berechtigung vorhanden)
if (Platform.isAndroid) { if (Platform.isAndroid) {
final hatZugriff = await frageSpeicherZugriff();
if (!hatZugriff) return pfade.toList();
final ordner = <String>[ final ordner = <String>[
'/storage/emulated/0/Music', '/storage/emulated/0/Music',
'/storage/emulated/0/Download', '/storage/emulated/0/Download',