You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
265 lines
10 KiB
Dart
265 lines
10 KiB
Dart
import 'dart:async';
|
|
import 'dart:developer';
|
|
import 'package:flutter/material.dart';
|
|
import 'package:permission_handler/permission_handler.dart';
|
|
import 'package:test_sa/modules/cx_module/chat/call/call_error_handler.dart';
|
|
|
|
/// Production-ready permission helper for audio/video calls
|
|
/// Handles all permission requests with proper error handling and user feedback
|
|
class CallPermissionHelper {
|
|
CallPermissionHelper._();
|
|
|
|
/// Check and request microphone permission for audio calls
|
|
/// Returns true if permission is granted, false otherwise
|
|
static Future<bool> requestMicrophonePermission(BuildContext context) async {
|
|
try {
|
|
log('🎤 [PERMISSION] Checking microphone permission...', name: 'CallPermissionHelper');
|
|
|
|
// Check current status
|
|
final status = await Permission.microphone.status;
|
|
log(' Current status: ${status.name}', name: 'CallPermissionHelper');
|
|
|
|
if (status.isGranted) {
|
|
log('✅ [PERMISSION] Microphone already granted', name: 'CallPermissionHelper');
|
|
return true;
|
|
}
|
|
|
|
// If denied permanently, show settings dialog
|
|
if (status.isPermanentlyDenied) {
|
|
log('⚠️ [PERMISSION] Microphone permanently denied - showing settings dialog', name: 'CallPermissionHelper');
|
|
if (context.mounted) {
|
|
await _showPermissionDeniedDialog(
|
|
context,
|
|
'Microphone Permission Required',
|
|
'This app needs microphone access to make calls. Please enable it in settings.',
|
|
Permission.microphone,
|
|
);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Request permission
|
|
log('📤 [PERMISSION] Requesting microphone permission...', name: 'CallPermissionHelper');
|
|
final result = await Permission.microphone.request();
|
|
log(' Result: ${result.name}', name: 'CallPermissionHelper');
|
|
|
|
if (result.isGranted) {
|
|
log('✅ [PERMISSION] Microphone permission granted', name: 'CallPermissionHelper');
|
|
return true;
|
|
}
|
|
|
|
if (result.isPermanentlyDenied) {
|
|
log('❌ [PERMISSION] Microphone permanently denied', name: 'CallPermissionHelper');
|
|
if (context.mounted) {
|
|
await _showPermissionDeniedDialog(
|
|
context,
|
|
'Microphone Permission Required',
|
|
'This app needs microphone access to make calls. Please enable it in settings.',
|
|
Permission.microphone,
|
|
);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// User denied
|
|
log('❌ [PERMISSION] Microphone permission denied by user', name: 'CallPermissionHelper');
|
|
if (context.mounted) {
|
|
CallErrorHandler.showMicrophonePermissionDenied(context);
|
|
}
|
|
return false;
|
|
} catch (e, stackTrace) {
|
|
log('❌ [PERMISSION] Error requesting microphone permission: $e\nStack: $stackTrace',
|
|
name: 'CallPermissionHelper');
|
|
if (context.mounted) {
|
|
CallErrorHandler.showGenericError(context, 'Failed to request microphone permission');
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// Check and request camera permission for video calls
|
|
/// Returns true if permission is granted, false otherwise
|
|
static Future<bool> requestCameraPermission(BuildContext context) async {
|
|
try {
|
|
log('📹 [PERMISSION] Checking camera permission...', name: 'CallPermissionHelper');
|
|
|
|
// Check current status
|
|
final status = await Permission.camera.status;
|
|
log(' Current status: ${status.name}', name: 'CallPermissionHelper');
|
|
|
|
if (status.isGranted) {
|
|
log('✅ [PERMISSION] Camera already granted', name: 'CallPermissionHelper');
|
|
return true;
|
|
}
|
|
|
|
// If denied permanently, show settings dialog
|
|
if (status.isPermanentlyDenied) {
|
|
log('⚠️ [PERMISSION] Camera permanently denied - showing settings dialog', name: 'CallPermissionHelper');
|
|
if (context.mounted) {
|
|
await _showPermissionDeniedDialog(
|
|
context,
|
|
'Camera Permission Required',
|
|
'This app needs camera access for video calls. Please enable it in settings.',
|
|
Permission.camera,
|
|
);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// Request permission
|
|
log('📤 [PERMISSION] Requesting camera permission...', name: 'CallPermissionHelper');
|
|
final result = await Permission.camera.request();
|
|
log(' Result: ${result.name}', name: 'CallPermissionHelper');
|
|
|
|
if (result.isGranted) {
|
|
log('✅ [PERMISSION] Camera permission granted', name: 'CallPermissionHelper');
|
|
return true;
|
|
}
|
|
|
|
if (result.isPermanentlyDenied) {
|
|
log('❌ [PERMISSION] Camera permanently denied', name: 'CallPermissionHelper');
|
|
if (context.mounted) {
|
|
await _showPermissionDeniedDialog(
|
|
context,
|
|
'Camera Permission Required',
|
|
'This app needs camera access for video calls. Please enable it in settings.',
|
|
Permission.camera,
|
|
);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// User denied
|
|
log('❌ [PERMISSION] Camera permission denied by user', name: 'CallPermissionHelper');
|
|
if (context.mounted) {
|
|
CallErrorHandler.showCameraPermissionDenied(context);
|
|
}
|
|
return false;
|
|
} catch (e, stackTrace) {
|
|
log('❌ [PERMISSION] Error requesting camera permission: $e\nStack: $stackTrace',
|
|
name: 'CallPermissionHelper');
|
|
if (context.mounted) {
|
|
CallErrorHandler.showGenericError(context, 'Failed to request camera permission');
|
|
}
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/// Request all permissions required for audio calls (microphone only)
|
|
/// Returns true if all permissions are granted, false otherwise
|
|
static Future<bool> requestAudioCallPermissions(BuildContext context) async {
|
|
log('═══════════════════════════════════════════', name: 'CallPermissionHelper');
|
|
log('🎵 [PERMISSION] Requesting audio call permissions...', name: 'CallPermissionHelper');
|
|
|
|
final micGranted = await requestMicrophonePermission(context);
|
|
|
|
if (!micGranted) {
|
|
log('❌ [PERMISSION] Audio call permissions denied', name: 'CallPermissionHelper');
|
|
log('═══════════════════════════════════════════', name: 'CallPermissionHelper');
|
|
return false;
|
|
}
|
|
|
|
log('✅ [PERMISSION] All audio call permissions granted', name: 'CallPermissionHelper');
|
|
log('═══════════════════════════════════════════', name: 'CallPermissionHelper');
|
|
return true;
|
|
}
|
|
|
|
/// Request all permissions required for video calls (microphone + camera)
|
|
/// Returns true if all permissions are granted, false otherwise
|
|
static Future<bool> requestVideoCallPermissions(BuildContext context) async {
|
|
log('═══════════════════════════════════════════', name: 'CallPermissionHelper');
|
|
log('📹 [PERMISSION] Requesting video call permissions...', name: 'CallPermissionHelper');
|
|
|
|
// Request both permissions
|
|
final micGranted = await requestMicrophonePermission(context);
|
|
if (!micGranted) {
|
|
log('❌ [PERMISSION] Video call permissions denied (microphone)', name: 'CallPermissionHelper');
|
|
log('═══════════════════════════════════════════', name: 'CallPermissionHelper');
|
|
return false;
|
|
}
|
|
|
|
final cameraGranted = await requestCameraPermission(context);
|
|
if (!cameraGranted) {
|
|
log('❌ [PERMISSION] Video call permissions denied (camera)', name: 'CallPermissionHelper');
|
|
log('═══════════════════════════════════════════', name: 'CallPermissionHelper');
|
|
return false;
|
|
}
|
|
|
|
log('✅ [PERMISSION] All video call permissions granted', name: 'CallPermissionHelper');
|
|
log('═══════════════════════════════════════════', name: 'CallPermissionHelper');
|
|
return true;
|
|
}
|
|
|
|
/// Check if microphone permission is granted (without requesting)
|
|
static Future<bool> isMicrophoneGranted() async {
|
|
final status = await Permission.microphone.status;
|
|
return status.isGranted;
|
|
}
|
|
|
|
/// Check if camera permission is granted (without requesting)
|
|
static Future<bool> isCameraGranted() async {
|
|
final status = await Permission.camera.status;
|
|
return status.isGranted;
|
|
}
|
|
|
|
/// Show dialog when permission is permanently denied, with option to open settings
|
|
static Future<void> _showPermissionDeniedDialog(
|
|
BuildContext context,
|
|
String title,
|
|
String message,
|
|
Permission permission,
|
|
) async {
|
|
if (!context.mounted) return;
|
|
|
|
return showDialog(
|
|
context: context,
|
|
barrierDismissible: false,
|
|
builder: (BuildContext context) {
|
|
return AlertDialog(
|
|
title: Text(title),
|
|
content: Text(message),
|
|
actions: [
|
|
TextButton(
|
|
onPressed: () => Navigator.of(context).pop(),
|
|
child: const Text('Cancel'),
|
|
),
|
|
TextButton(
|
|
onPressed: () async {
|
|
Navigator.of(context).pop();
|
|
await openAppSettings();
|
|
},
|
|
child: const Text('Open Settings'),
|
|
),
|
|
],
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
/// Handle permission denial during an active call
|
|
/// Shows error and optionally ends the call
|
|
static Future<void> handlePermissionDeniedDuringCall(
|
|
BuildContext context,
|
|
String permissionName, {
|
|
required Function() onEndCall,
|
|
int delaySeconds = 2,
|
|
}) async {
|
|
if (!context.mounted) return;
|
|
|
|
log('❌ [PERMISSION] $permissionName denied during active call', name: 'CallPermissionHelper');
|
|
log(' Showing error and ending call in ${delaySeconds}s...', name: 'CallPermissionHelper');
|
|
|
|
CallErrorHandler.showGenericError(
|
|
context,
|
|
'$permissionName permission is required for this call.',
|
|
);
|
|
|
|
// Give user time to see the error
|
|
await Future.delayed(Duration(seconds: delaySeconds));
|
|
|
|
if (context.mounted) {
|
|
onEndCall();
|
|
}
|
|
}
|
|
}
|