All checks were successful
/ build (push) Successful in 5m17s
- Bumped Android minSdk to 30 (Android 11) - Fixed permission service not handling Android SDK 33 correctly - Fixed permission service not being started during application start
149 lines
4.5 KiB
Dart
149 lines
4.5 KiB
Dart
import 'dart:async';
|
|
import 'dart:io' show Platform;
|
|
|
|
import 'package:device_info_plus/device_info_plus.dart';
|
|
import 'package:logger/logger.dart';
|
|
import 'package:permission_handler/permission_handler.dart';
|
|
|
|
import '../../constants.dart';
|
|
import '../../core/services/stoppable_service.dart';
|
|
import '../../core/util/logger.dart';
|
|
|
|
class PermissionService extends StoppableService {
|
|
final Logger _logger = getLogger();
|
|
|
|
Timer? _serviceCheckTimer;
|
|
|
|
bool _devicePermissionDialogActive = false;
|
|
|
|
bool _deviceInformationInitialized = false;
|
|
bool _useStoragePermission = true;
|
|
|
|
PermissionService();
|
|
|
|
Future checkEnabledAndPermission() async {
|
|
if (_devicePermissionDialogActive) {
|
|
_logger.d('Device permission dialog active, skipping');
|
|
return;
|
|
}
|
|
|
|
bool allGranted = false;
|
|
bool anyPermanentlyDenied = false;
|
|
|
|
// Since Android compileSdk >= 33, "storage" is deprecated
|
|
// Instead, request access to all of
|
|
// - Permission.photos
|
|
// - Permission.videos
|
|
// - Permission.audio
|
|
//
|
|
// For iOS and Android < 33, keep using "storage"
|
|
if (_useStoragePermission) {
|
|
PermissionStatus storagePermission = await Permission.storage.status;
|
|
allGranted = PermissionStatus.granted == storagePermission;
|
|
anyPermanentlyDenied =
|
|
PermissionStatus.permanentlyDenied == storagePermission;
|
|
} else {
|
|
PermissionStatus photosPermission = await Permission.photos.status;
|
|
PermissionStatus videosPermission = await Permission.videos.status;
|
|
PermissionStatus audioPermission = await Permission.audio.status;
|
|
|
|
allGranted = PermissionStatus.granted == photosPermission &&
|
|
PermissionStatus.granted == videosPermission &&
|
|
PermissionStatus.granted == audioPermission;
|
|
anyPermanentlyDenied =
|
|
PermissionStatus.permanentlyDenied == photosPermission ||
|
|
PermissionStatus.permanentlyDenied == videosPermission ||
|
|
PermissionStatus.permanentlyDenied == audioPermission;
|
|
}
|
|
|
|
// show warning to user to manually handle, don't enforce it over and over again
|
|
if (anyPermanentlyDenied) {
|
|
_logger.w(
|
|
"At least one required permission has been denied permanently, stopping service");
|
|
stop();
|
|
return;
|
|
}
|
|
|
|
// all good, stop the permission service
|
|
if (allGranted) {
|
|
_logger.d("All permissions have been granted, stopping service");
|
|
stop();
|
|
return;
|
|
}
|
|
|
|
// not all have been granted, show OS dialog
|
|
_logger.d(
|
|
"Not all permissions have been granted yet, initializing permission dialog");
|
|
_devicePermissionDialogActive = true;
|
|
|
|
if (_useStoragePermission) {
|
|
await [Permission.storage].request().whenComplete(() {
|
|
_logger.d('Device request permission finished');
|
|
_devicePermissionDialogActive = false;
|
|
});
|
|
} else {
|
|
await [Permission.photos, Permission.videos, Permission.audio]
|
|
.request()
|
|
.whenComplete(() {
|
|
_logger.d('Device request permission finished');
|
|
_devicePermissionDialogActive = false;
|
|
});
|
|
}
|
|
}
|
|
|
|
@override
|
|
Future start() async {
|
|
super.start();
|
|
await _determineDeviceInfo();
|
|
await checkEnabledAndPermission();
|
|
|
|
_serviceCheckTimer = Timer.periodic(
|
|
const Duration(milliseconds: Constants.mediaPermissionCheckInterval),
|
|
(serviceTimer) async {
|
|
if (!super.serviceStopped) {
|
|
await checkEnabledAndPermission();
|
|
} else {
|
|
serviceTimer.cancel();
|
|
}
|
|
});
|
|
_logger.d('PermissionService started');
|
|
}
|
|
|
|
@override
|
|
void stop() {
|
|
_removeServiceCheckTimer();
|
|
super.stop();
|
|
_logger.d('PermissionService stopped');
|
|
}
|
|
|
|
Future _determineDeviceInfo() async {
|
|
if (_deviceInformationInitialized) {
|
|
_logger.d('Device information already initialized, skipping');
|
|
return;
|
|
}
|
|
|
|
DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();
|
|
if (Platform.isAndroid) {
|
|
final androidInfo = await deviceInfoPlugin.androidInfo;
|
|
if (androidInfo.version.sdkInt >= 33) {
|
|
_useStoragePermission = false;
|
|
}
|
|
}
|
|
|
|
if (_useStoragePermission) {
|
|
_logger.d('Device requires [storage] permission');
|
|
} else {
|
|
_logger.d('Device requires [photos,videos,audio] permission');
|
|
}
|
|
|
|
_deviceInformationInitialized = true;
|
|
}
|
|
|
|
void _removeServiceCheckTimer() {
|
|
if (_serviceCheckTimer != null) {
|
|
_serviceCheckTimer!.cancel();
|
|
_serviceCheckTimer = null;
|
|
_logger.d('Removed service check timer');
|
|
}
|
|
}
|
|
}
|