feat: Murattal player enhancements & prayer schedule auto-scroll
- Murattal: Spotify-style 5-button controls [Shuffle, Prev, Play, Next, Playlist] - Murattal: Animated 7-bar equalizer visualization in player circle - Murattal: Unsplash API background with frosted glass player overlay - Murattal: Transparent AppBar with backdrop blur - Murattal: Surah playlist bottom sheet with full 114 Surah list - Murattal: Auto-play disabled on screen open, enabled on navigation - Murattal: Shuffle mode for random Surah playback - Murattal: Photographer attribution per Unsplash guidelines - Dashboard: Auto-scroll prayer schedule to next active prayer - Fix: setState lifecycle errors on Reading & Murattal screens - Setup: flutter_dotenv, cached_network_image, url_launcher deps
This commit is contained in:
86
lib/data/services/location_service.dart
Normal file
86
lib/data/services/location_service.dart
Normal file
@@ -0,0 +1,86 @@
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:geocoding/geocoding.dart' as geocoding;
|
||||
import 'package:hive_flutter/hive_flutter.dart';
|
||||
import '../local/hive_boxes.dart';
|
||||
import '../local/models/app_settings.dart';
|
||||
|
||||
/// Location service with GPS + fallback to last known location.
|
||||
class LocationService {
|
||||
LocationService._();
|
||||
static final LocationService instance = LocationService._();
|
||||
|
||||
/// Request permission and get current GPS location.
|
||||
Future<Position?> getCurrentLocation() async {
|
||||
bool serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) return null;
|
||||
|
||||
LocationPermission permission = await Geolocator.checkPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
if (permission == LocationPermission.denied) return null;
|
||||
}
|
||||
|
||||
if (permission == LocationPermission.deniedForever) return null;
|
||||
|
||||
try {
|
||||
final position = await Geolocator.getCurrentPosition(
|
||||
locationSettings: const LocationSettings(
|
||||
accuracy: LocationAccuracy.medium,
|
||||
timeLimit: Duration(seconds: 10),
|
||||
),
|
||||
);
|
||||
|
||||
// Save to settings for fallback
|
||||
await _saveLastKnown(position.latitude, position.longitude);
|
||||
return position;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// Get last known location from Hive settings.
|
||||
({double lat, double lng, String? cityName})? getLastKnownLocation() {
|
||||
final settingsBox = Hive.box<AppSettings>(HiveBoxes.settings);
|
||||
final settings = settingsBox.get('default');
|
||||
if (settings?.lastLat != null && settings?.lastLng != null) {
|
||||
return (
|
||||
lat: settings!.lastLat!,
|
||||
lng: settings.lastLng!,
|
||||
cityName: settings.lastCityName,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Reverse geocode to get city name from coordinates.
|
||||
Future<String> getCityName(double lat, double lng) async {
|
||||
try {
|
||||
final placemarks = await geocoding.placemarkFromCoordinates(lat, lng);
|
||||
if (placemarks.isNotEmpty) {
|
||||
final place = placemarks.first;
|
||||
final city = place.locality ?? place.subAdministrativeArea ?? 'Unknown';
|
||||
final country = place.country ?? '';
|
||||
return '$city, $country';
|
||||
}
|
||||
} catch (_) {
|
||||
// Geocoding may fail offline — return coords
|
||||
}
|
||||
return '${lat.toStringAsFixed(2)}, ${lng.toStringAsFixed(2)}';
|
||||
}
|
||||
|
||||
/// Save last known position to Hive.
|
||||
Future<void> _saveLastKnown(double lat, double lng) async {
|
||||
final settingsBox = Hive.box<AppSettings>(HiveBoxes.settings);
|
||||
final settings = settingsBox.get('default');
|
||||
if (settings != null) {
|
||||
settings.lastLat = lat;
|
||||
settings.lastLng = lng;
|
||||
try {
|
||||
settings.lastCityName = await getCityName(lat, lng);
|
||||
} catch (_) {
|
||||
// Ignore geocoding errors
|
||||
}
|
||||
await settings.save();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user