fix: permission screen - location tap opens settings when permanently denied, battery dialog re-checks on done
This commit is contained in:
parent
650a6efeca
commit
6cbb7a2070
3 changed files with 83 additions and 38 deletions
|
|
@ -90,6 +90,9 @@ class BridgeModule : FlutterPlugin, MethodChannel.MethodCallHandler {
|
|||
"openBatteryOptimizationSettings" -> {
|
||||
openBatteryOptimizationSettings(result)
|
||||
}
|
||||
"isBatteryOptimizationDisabled" -> {
|
||||
result.success(isBatteryOptimizationDisabled())
|
||||
}
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
|
@ -215,4 +218,14 @@ class BridgeModule : FlutterPlugin, MethodChannel.MethodCallHandler {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isBatteryOptimizationDisabled(): Boolean {
|
||||
return try {
|
||||
val powerManager = context?.getSystemService(android.content.Context.POWER_SERVICE) as? android.os.PowerManager
|
||||
val packageName = context?.packageName ?: ""
|
||||
powerManager?.isIgnoringBatteryOptimizations(packageName) == true
|
||||
} catch (e: Exception) {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -104,4 +104,16 @@ class LocationBridge {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static Future<bool> isBatteryOptimizationDisabled() async {
|
||||
try {
|
||||
final result = await _methodChannel.invokeMethod<bool>(
|
||||
'isBatteryOptimizationDisabled',
|
||||
);
|
||||
return result ?? false;
|
||||
} on PlatformException catch (e) {
|
||||
debugPrint('Failed to check battery optimization: ${e.message}');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ class _PermissionScreenState extends State<PermissionScreen> {
|
|||
Future<void> _checkPermissions() async {
|
||||
final location = await Permission.locationAlways.status;
|
||||
final notification = await Permission.notification.status;
|
||||
// Battery optimization check is Android-only
|
||||
final batteryOptOut = await _isBatteryOptimizationDisabled();
|
||||
|
||||
if (mounted) {
|
||||
|
|
@ -45,10 +44,12 @@ class _PermissionScreenState extends State<PermissionScreen> {
|
|||
}
|
||||
|
||||
Future<bool> _isBatteryOptimizationDisabled() async {
|
||||
// On Android, check if battery optimization is disabled for the app
|
||||
// We approximate this - in production you'd use a native method
|
||||
// For now, assume not granted until user says so
|
||||
return false;
|
||||
try {
|
||||
final result = await LocationBridge.isBatteryOptimizationDisabled();
|
||||
return result == true;
|
||||
} catch (_) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool get _allGranted =>
|
||||
|
|
@ -56,32 +57,39 @@ class _PermissionScreenState extends State<PermissionScreen> {
|
|||
|
||||
Future<void> _requestLocation() async {
|
||||
final status = await Permission.locationAlways.request();
|
||||
if (!status.isGranted && status.isPermanentlyDenied) {
|
||||
// Permission permanently denied - open app settings
|
||||
await openAppSettings();
|
||||
}
|
||||
if (mounted) {
|
||||
setState(() => _locationGranted = status.isGranted);
|
||||
final newStatus = await Permission.locationAlways.status;
|
||||
setState(() => _locationGranted = newStatus.isGranted);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _requestNotification() async {
|
||||
final status = await Permission.notification.request();
|
||||
if (!status.isGranted && status.isPermanentlyDenied) {
|
||||
await openAppSettings();
|
||||
}
|
||||
if (mounted) {
|
||||
setState(() => _notificationGranted = status.isGranted);
|
||||
final newStatus = await Permission.notification.status;
|
||||
setState(() => _notificationGranted = newStatus.isGranted);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _requestBatteryOptOut() async {
|
||||
final success = await LocationBridge.openBatteryOptimizationSettings();
|
||||
if (success) {
|
||||
// Show dialog asking user to confirm after disabling battery opt
|
||||
if (mounted) _showBatteryOptDialog();
|
||||
} else {
|
||||
// Fallback to manual instructions dialog
|
||||
if (mounted) _showBatteryOptDialog();
|
||||
final opened = await LocationBridge.openBatteryOptimizationSettings();
|
||||
if (mounted) {
|
||||
// Show dialog after the settings screen opens
|
||||
_showBatteryOptDialog(opened);
|
||||
}
|
||||
}
|
||||
|
||||
void _showBatteryOptDialog() {
|
||||
void _showBatteryOptDialog(bool settingsOpened) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (ctx) => AlertDialog(
|
||||
backgroundColor: const Color(0xFF1a1a1a),
|
||||
shape: RoundedRectangleBorder(
|
||||
|
|
@ -98,10 +106,12 @@ class _PermissionScreenState extends State<PermissionScreen> {
|
|||
color: Color(0xFFe0e0e0),
|
||||
),
|
||||
),
|
||||
content: const Text(
|
||||
'Please disable battery optimization for this app to ensure reliable background tracking.\n\n'
|
||||
'In the next screen, find "Traccar Client" and set it to "Don\'t optimize" or "Unrestricted".',
|
||||
style: TextStyle(
|
||||
content: Text(
|
||||
settingsOpened
|
||||
? 'A settings screen has opened.\n\n'
|
||||
'Find "Traccar Client" and set it to "Don\'t optimize" or "Unrestricted", then come back and tap DONE below.'
|
||||
: 'Open Settings → Apps → Traccar Client → Battery and select "Don\'t optimize" or "Unrestricted", then come back and tap DONE below.',
|
||||
style: const TextStyle(
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 12,
|
||||
color: Color(0xFF9e9e9e),
|
||||
|
|
@ -109,9 +119,13 @@ class _PermissionScreenState extends State<PermissionScreen> {
|
|||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(ctx),
|
||||
onPressed: () {
|
||||
Navigator.pop(ctx);
|
||||
// Open app details settings as fallback
|
||||
openAppSettings();
|
||||
},
|
||||
child: const Text(
|
||||
'OPEN SETTINGS',
|
||||
'OPEN APP SETTINGS',
|
||||
style: TextStyle(
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 11,
|
||||
|
|
@ -122,12 +136,16 @@ class _PermissionScreenState extends State<PermissionScreen> {
|
|||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
onPressed: () async {
|
||||
Navigator.pop(ctx);
|
||||
setState(() => _batteryOptOut = true);
|
||||
// Re-check battery optimization state via native
|
||||
final disabled = await _isBatteryOptimizationDisabled();
|
||||
if (mounted) {
|
||||
setState(() => _batteryOptOut = disabled);
|
||||
}
|
||||
},
|
||||
child: const Text(
|
||||
'DONE',
|
||||
'CHECK & DONE',
|
||||
style: TextStyle(
|
||||
fontFamily: 'monospace',
|
||||
fontSize: 11,
|
||||
|
|
@ -213,11 +231,11 @@ class _PermissionScreenState extends State<PermissionScreen> {
|
|||
color: Color(0xFF161616),
|
||||
border: Border(bottom: BorderSide(color: Color(0xFF2a2a2a), width: 1)),
|
||||
),
|
||||
child: Row(
|
||||
child: const Row(
|
||||
children: [
|
||||
const Icon(Icons.shield, color: Color(0xFF00bcd4), size: 20),
|
||||
const SizedBox(width: 12),
|
||||
const Text(
|
||||
Icon(Icons.shield, color: Color(0xFF00bcd4), size: 20),
|
||||
SizedBox(width: 12),
|
||||
Text(
|
||||
'PERMISSIONS',
|
||||
style: TextStyle(
|
||||
fontFamily: 'monospace',
|
||||
|
|
@ -245,18 +263,18 @@ class _PermissionScreenState extends State<PermissionScreen> {
|
|||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF00bcd4).withOpacity(0.15),
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
decoration: const BoxDecoration(
|
||||
color: Color(0xFF00bcd4),
|
||||
borderRadius: BorderRadius.all(Radius.circular(4)),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.info_outline,
|
||||
color: Color(0xFF00bcd4),
|
||||
color: Colors.white,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 14),
|
||||
const Expanded(
|
||||
SizedBox(width: 14),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
|
|
@ -420,7 +438,7 @@ class _PermissionScreenState extends State<PermissionScreen> {
|
|||
_notificationGranted,
|
||||
_batteryOptOut,
|
||||
].where((x) => x).length;
|
||||
final total = 3;
|
||||
const total = 3;
|
||||
final progress = granted / total;
|
||||
|
||||
return Column(
|
||||
|
|
@ -428,9 +446,11 @@ class _PermissionScreenState extends State<PermissionScreen> {
|
|||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.check_circle_outline,
|
||||
color: Color(0xFF00e676),
|
||||
Icon(
|
||||
granted == total
|
||||
? Icons.check_circle
|
||||
: Icons.check_circle_outline,
|
||||
color: const Color(0xFF00e676),
|
||||
size: 14,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue