diff --git a/client/android/app/build.gradle b/client/android/app/build.gradle index 492e1ae2cf..a90a330dd1 100644 --- a/client/android/app/build.gradle +++ b/client/android/app/build.gradle @@ -25,7 +25,7 @@ if (flutterVersionName == null) { android { namespace "com.appveyor.flet" compileSdkVersion flutter.compileSdkVersion - ndkVersion flutter.ndkVersion + ndkVersion "25.1.8937393" compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 diff --git a/client/pubspec.lock b/client/pubspec.lock index 89a8168fb9..b8539abdf4 100644 --- a/client/pubspec.lock +++ b/client/pubspec.lock @@ -260,6 +260,13 @@ packages: relative: true source: path version: "0.23.0" + flet_permission_handler: + dependency: "direct main" + description: + path: "../packages/flet_permission_handler" + relative: true + source: path + version: "0.23.0" flet_rive: dependency: "direct main" description: diff --git a/packages/flet_permission_handler/lib/src/permission_handler.dart b/packages/flet_permission_handler/lib/src/permission_handler.dart index 55568f1ce2..9ee427e632 100644 --- a/packages/flet_permission_handler/lib/src/permission_handler.dart +++ b/packages/flet_permission_handler/lib/src/permission_handler.dart @@ -1,5 +1,7 @@ import 'package:flet/flet.dart'; import 'package:flutter/widgets.dart'; +import 'package:permission_handler/permission_handler.dart'; + import 'utils/permission_handler.dart'; class PermissionHandlerControl extends StatefulWidget { @@ -43,15 +45,42 @@ class _PermissionHandlerControlState extends State Widget build(BuildContext context) { debugPrint( "PermissionHandler build: ${widget.control.id} (${widget.control.hashCode})"); - + () async { widget.backend.subscribeMethods(widget.control.id, (methodName, args) async { switch (methodName) { - case "checkPermission": - return await checkPermission(args['permissionOf']!); - case "requestPermission": - return await requestPermission(args['permissionOf']!); + case "check_permission": + bool? isGranted = await parsePermission(args['of'])?.isGranted; + bool? isDenied = await parsePermission(args['of'])?.isDenied; + bool? isPermanentlyDenied = + await parsePermission(args['of'])?.isPermanentlyDenied; + bool? isLimited = await parsePermission(args['of'])?.isLimited; + bool? isProvisional = + await parsePermission(args['of'])?.isProvisional; + bool? isRestricted = + await parsePermission(args['of'])?.isRestricted; + + if (isGranted == true) { + return "granted"; + } else if (isDenied == true) { + return "denied"; + } else if (isPermanentlyDenied == true) { + return "permanentlyDenied"; + } else if (isLimited == true) { + return "limited"; + } else if (isProvisional == true) { + return "provisional"; + } else if (isRestricted == true) { + return "restricted"; + } + return null; + case "request_permission": + Future permissionStatus = + parsePermission(args['of'])!.request(); + return permissionStatus.then((value) async { + return value.name; + }); } return null; }); @@ -59,4 +88,4 @@ class _PermissionHandlerControlState extends State return const SizedBox.shrink(); } -} \ No newline at end of file +} diff --git a/packages/flet_permission_handler/lib/src/utils/permission_handler.dart b/packages/flet_permission_handler/lib/src/utils/permission_handler.dart index 1a312dfb90..89c82cb868 100644 --- a/packages/flet_permission_handler/lib/src/utils/permission_handler.dart +++ b/packages/flet_permission_handler/lib/src/utils/permission_handler.dart @@ -1,117 +1,14 @@ -import "dart:async"; -import "dart:convert"; - +import "package:collection/collection.dart"; import "package:permission_handler/permission_handler.dart"; -Permission? parsePermissionInstance(String permissionOf) { - switch (permissionOf) { - case "accessMediaLocation": - return Permission.accessMediaLocation; - case "accessNotificationPolicy": - return Permission.accessNotificationPolicy; - case "activityRecognition": - return Permission.activityRecognition; - case "appTrackingTransparency": - return Permission.appTrackingTransparency; - case "assistant": - return Permission.assistant; - case "audio": - return Permission.audio; - case "backgroundRefresh": - return Permission.backgroundRefresh; - case "bluetooth": - return Permission.bluetooth; - case "bluetoothAdvertise": - return Permission.bluetoothAdvertise; - case "bluetoothConnect": - return Permission.bluetoothConnect; - case "bluetoothScan": - return Permission.bluetoothScan; - case "calendarFullAccess": - return Permission.calendarFullAccess; - case "calendarWriteOnly": - return Permission.calendarWriteOnly; - case "camera": - return Permission.camera; - case "contacts": - return Permission.contacts; - case "criticalAlerts": - return Permission.criticalAlerts; - case "ignoreBatteryOptimizations": - return Permission.ignoreBatteryOptimizations; - case "location": - return Permission.location; - case "locationAlways": - return Permission.locationAlways; - case "locationWhenInUse": - return Permission.locationWhenInUse; - case "manageExternalStorage": - return Permission.manageExternalStorage; - case "mediaLibrary": - return Permission.mediaLibrary; - case "microphone": - return Permission.microphone; - case "nearbyWifiDevices": - return Permission.nearbyWifiDevices; - case "notification": - return Permission.notification; - case "phone": - return Permission.phone; - case "photos": - return Permission.photos; - case "photosAddOnly": - return Permission.photosAddOnly; - case "reminders": - return Permission.reminders; - case "requestInstallPackages": - return Permission.requestInstallPackages; - case "scheduleExactAlarm": - return Permission.scheduleExactAlarm; - case "sensors": - return Permission.sensors; - case "sensorsAlways": - return Permission.sensorsAlways; - case "sms": - return Permission.sms; - case "speech": - return Permission.speech; - case "storage": - return Permission.storage; - case "systemAlertWindow": - return Permission.systemAlertWindow; - case "unknown": - return Permission.unknown; - case "videos": - return Permission.videos; - default: - return null; +Permission? parsePermission(String? permission, + [Permission? defaultPermission]) { + if (permission == null) { + return defaultPermission; } + return Permission.values.firstWhereOrNull( + (Permission p) => + p.toString().toLowerCase() == permission.toLowerCase(), + ) ?? + defaultPermission; } - -Future checkPermission(String permissionOf) async { - bool isGranted = await parsePermissionInstance(permissionOf)!.isGranted; - bool isDenied = await parsePermissionInstance(permissionOf)!.isDenied; - bool isPermanentlyDenied = - await parsePermissionInstance(permissionOf)!.isPermanentlyDenied; - bool isLimited = await parsePermissionInstance(permissionOf)!.isLimited; - bool isProvisional = - await parsePermissionInstance(permissionOf)!.isProvisional; - bool isRestricted = await parsePermissionInstance(permissionOf)!.isRestricted; - - return json.encode({ - "is_granted": isGranted, - "is_denied": isDenied, - "is_permanently_denied": isPermanentlyDenied, - "is_limited": isLimited, - "is_provisional": isProvisional, - "is_restricted": isRestricted - }); -} - -Future requestPermission(String permissionOf) async { - Future permissionStatus = - parsePermissionInstance(permissionOf)!.request(); - return permissionStatus.then((value) async { - return await checkPermission(permissionOf); - }); -} \ No newline at end of file diff --git a/sdk/python/packages/flet-core/src/flet_core/__init__.py b/sdk/python/packages/flet-core/src/flet_core/__init__.py index 3d5263e9e8..3e2c586a75 100644 --- a/sdk/python/packages/flet-core/src/flet_core/__init__.py +++ b/sdk/python/packages/flet-core/src/flet_core/__init__.py @@ -206,7 +206,11 @@ PaintSweepGradient, StrokeJoin, ) -from flet_core.permission_handler import PermissionHandler +from flet_core.permission_handler import ( + PermissionHandler, + PermissionType, + PermissionStatus, +) from flet_core.popup_menu_button import ( PopupMenuButton, PopupMenuItem, diff --git a/sdk/python/packages/flet-core/src/flet_core/permission_handler.py b/sdk/python/packages/flet-core/src/flet_core/permission_handler.py index 3df216baf2..ec1dbec271 100644 --- a/sdk/python/packages/flet-core/src/flet_core/permission_handler.py +++ b/sdk/python/packages/flet-core/src/flet_core/permission_handler.py @@ -1,88 +1,69 @@ from enum import Enum -import json from typing import Any, Optional -from dataclasses import dataclass, field + from flet_core.control import Control from flet_core.ref import Ref -@dataclass -class PermissionStatus: - is_granted: Optional[bool] = field(default=None) - is_denied: Optional[bool] = field(default=None) - is_permanently_denied: Optional[bool] = field(default=None) - is_limited: Optional[bool] = field(default=None) - is_provisional: Optional[bool] = field(default=None) - is_restricted: Optional[bool] = field(default=None) - - -class PermissionTemplate(Control): - def __init__(self, permission_of, invoke_method, invoke_method_async): - - self.__permission_of = permission_of - self.invoke_method = invoke_method - self.invoke_method_async = invoke_method_async - - def check_permission(self, wait_timeout: Optional[float] = 5) -> PermissionStatus: - permission = self.invoke_method( - "checkPermission", - {"permissionOf": self.__permission_of}, - wait_for_result=True, - wait_timeout=wait_timeout, - ) - if permission != "null": - return PermissionStatus(**json.loads(permission)) - else: - return PermissionStatus() - - async def check_permission_async(self, wait_timeout: Optional[float] = 5) -> PermissionStatus: - permission = await self.invoke_method_async( - "checkPermission", - {"permissionOf": self.__permission_of}, - wait_for_result=True, - wait_timeout=wait_timeout, - ) - if permission != "null": - return PermissionStatus(**json.loads(permission)) - else: - return PermissionStatus() - - def request_permission(self, wait_timeout: Optional[float] = 25) -> PermissionStatus: - permission = self.invoke_method( - "requestPermission", - {"permissionOf": self.__permission_of}, - wait_for_result=True, - wait_timeout=wait_timeout, - ) - if permission != "null": - return PermissionStatus(**json.loads(permission)) - else: - return PermissionStatus() - - async def request_permission_async( - self, wait_timeout: Optional[float] = 25 - ) -> PermissionStatus: - permission = await self.invoke_method_async( - "requestPermission", - {"permissionOf": self.__permission_of}, - wait_for_result=True, - wait_timeout=wait_timeout, - ) - if permission != "null": - return PermissionStatus(**json.loads(permission)) - else: - return PermissionStatus() +class PermissionStatus(Enum): + GRANTED = "granted" + DENIED = "denied" + PERMANENTLY_DENIED = "permanentlyDenied" + LIMITED = "limited" + PROVISIONAL = "provisional" + RESTRICTED = "restricted" + + +class PermissionType(Enum): + ACCESS_MEDIA_LOCATION = "accessMediaLocation" + ACCESS_NOTIFICATION_POLICY = "accessNotificationPolicy" + ACTIVITY_RECOGNITION = "activityRecognition" + APP_TRACKING_TRANSPARENCY = "appTrackingTransparency" + ASSISTANT = "assistant" + AUDIO = "audio" + BACKGROUND_REFRESH = "backgroundRefresh" + BLUETOOTH = "bluetooth" + BLUETOOTH_ADVERTISE = "bluetoothAdvertise" + BLUETOOTH_CONNECT = "bluetoothConnect" + BLUETOOTH_SCAN = "bluetoothScan" + CALENDAR_FULL_ACCESS = "calendarFullAccess" + CALENDAR_WRITE_ONLY = "calendarWriteOnly" + CAMERA = "camera" + CONTACTS = "contacts" + CRITICAL_ALERTS = "criticalAlerts" + IGNORE_BATTERY_OPTIMIZATIONS = "ignoreBatteryOptimizations" + LOCATION = "location" + LOCATION_ALWAYS = "locationAlways" + LOCATION_WHEN_IN_USE = "locationWhenInUse" + MANAGE_EXTERNAL_STORAGE = "manageExternalStorage" + MEDIA_LIBRARY = "mediaLibrary" + MICROPHONE = "microphone" + NEARBY_WIFI_DEVICES = "nearbyWifiDevices" + NOTIFICATION = "notification" + PHONE = "phone" + PHOTOS = "photos" + PHOTOS_ADD_ONLY = "photosAddOnly" + REMINDERS = "reminders" + REQUEST_INSTALL_PACKAGES = "requestInstallPackages" + SCHEDULE_EXACT_ALARM = "scheduleExactAlarm" + SENSORS = "sensors" + SENSORS_ALWAYS = "sensorsAlways" + SMS = "sms" + SPEECH = "speech" + STORAGE = "storage" + SYSTEM_ALERT_WINDOW = "systemAlertWindow" + UNKNOWN = "unknown" + VIDEOS = "videos" class PermissionHandler(Control): """ A control that allows you check and request permission from your device. - This control is non-visual and should be added to `page.overlay` list - + This control is non-visual and should be added to `page.overlay` list. ----- - Online docs: https://flet.dev/docs/controls/flet_permission_handler + Online docs: https://flet.dev/docs/controls/permissionhandler """ def __init__( @@ -97,123 +78,58 @@ def __init__( ref=ref, data=data, ) - self.accessMediaLocation = PermissionTemplate( - "accessMediaLocation", self.invoke_method, self.invoke_method_async - ) - self.accessNotificationPolicy = PermissionTemplate( - "accessNotificationPolicy", self.invoke_method, self.invoke_method_async - ) - self.activityRecognition = PermissionTemplate( - "activityRecognition", self.invoke_method, self.invoke_method_async - ) - self.appTrackingTransparency = PermissionTemplate( - "appTrackingTransparency", self.invoke_method, self.invoke_method_async - ) - self.assistant = PermissionTemplate( - "assistant", self.invoke_method, self.invoke_method_async - ) - self.audio = PermissionTemplate( - "audio", self.invoke_method, self.invoke_method_async - ) - self.backgroundRefresh = PermissionTemplate( - "backgroundRefresh", self.invoke_method, self.invoke_method_async - ) - self.bluetooth = PermissionTemplate( - "bluetooth", self.invoke_method, self.invoke_method_async - ) - self.bluetoothAdvertise = PermissionTemplate( - "bluetoothAdvertise", self.invoke_method, self.invoke_method_async - ) - self.bluetoothConnect = PermissionTemplate( - "bluetoothConnect", self.invoke_method, self.invoke_method_async - ) - self.bluetoothScan = PermissionTemplate( - "bluetoothScan", self.invoke_method, self.invoke_method_async - ) - self.calendarFullAccess = PermissionTemplate( - "calendarFullAccess", self.invoke_method, self.invoke_method_async - ) - self.calendarWriteOnly = PermissionTemplate( - "calendarWriteOnly", self.invoke_method, self.invoke_method_async - ) - self.camera = PermissionTemplate( - "camera", self.invoke_method, self.invoke_method_async - ) - self.contacts = PermissionTemplate( - "contacts", self.invoke_method, self.invoke_method_async - ) - self.criticalAlerts = PermissionTemplate( - "criticalAlerts", self.invoke_method, self.invoke_method_async - ) - self.ignoreBatteryOptimizations = PermissionTemplate( - "ignoreBatteryOptimizations", self.invoke_method, self.invoke_method_async - ) - self.location = PermissionTemplate( - "location", self.invoke_method, self.invoke_method_async - ) - self.locationAlways = PermissionTemplate( - "locationAlways", self.invoke_method, self.invoke_method_async - ) - self.locationWhenInUse = PermissionTemplate( - "locationWhenInUse", self.invoke_method, self.invoke_method_async - ) - self.manageExternalStorage = PermissionTemplate( - "manageExternalStorage", self.invoke_method, self.invoke_method_async - ) - self.mediaLibrary = PermissionTemplate( - "mediaLibrary", self.invoke_method, self.invoke_method_async - ) - self.microphone = PermissionTemplate( - "microphone", self.invoke_method, self.invoke_method_async - ) - self.nearbyWifiDevices = PermissionTemplate( - "nearbyWifiDevices", self.invoke_method, self.invoke_method_async - ) - self.notification = PermissionTemplate( - "notification", self.invoke_method, self.invoke_method_async - ) - self.phone = PermissionTemplate( - "phone", self.invoke_method, self.invoke_method_async - ) - self.photos = PermissionTemplate( - "photos", self.invoke_method, self.invoke_method_async - ) - self.photosAddOnly = PermissionTemplate( - "photosAddOnly", self.invoke_method, self.invoke_method_async - ) - self.reminders = PermissionTemplate( - "reminders", self.invoke_method, self.invoke_method_async - ) - self.requestInstallPackages = PermissionTemplate( - "requestInstallPackages", self.invoke_method, self.invoke_method_async - ) - self.scheduleExactAlarm = PermissionTemplate( - "scheduleExactAlarm", self.invoke_method, self.invoke_method_async - ) - self.sensors = PermissionTemplate( - "sensors", self.invoke_method, self.invoke_method_async - ) - self.sensorsAlways = PermissionTemplate( - "sensorsAlways", self.invoke_method, self.invoke_method_async - ) - self.sms = PermissionTemplate( - "sms", self.invoke_method, self.invoke_method_async - ) - self.speech = PermissionTemplate( - "speech", self.invoke_method, self.invoke_method_async - ) - self.storage = PermissionTemplate( - "storage", self.invoke_method, self.invoke_method_async - ) - self.systemAlertWindow = PermissionTemplate( - "systemAlertWindow", self.invoke_method, self.invoke_method_async + + def _get_control_name(self): + return "permission_handler" + + def check_permission( + self, of: PermissionType, wait_timeout: Optional[float] = 25 + ) -> Optional[PermissionStatus]: + out = self.invoke_method( + "check_permission", + {"of": of.value if isinstance(of, PermissionType) else of}, + wait_for_result=True, + wait_timeout=wait_timeout, ) - self.unknown = PermissionTemplate( - "unknown", self.invoke_method, self.invoke_method_async + if out == "null": + return None + return PermissionStatus(out) + + async def check_permission_async( + self, of: PermissionType, wait_timeout: Optional[float] = 25 + ) -> Optional[PermissionStatus]: + out = await self.invoke_method_async( + "check_permission", + {"of": of.value if isinstance(of, PermissionType) else of}, + wait_for_result=True, + wait_timeout=wait_timeout, ) - self.videos = PermissionTemplate( - "videos", self.invoke_method, self.invoke_method_async + if out == "null": + return None + return PermissionStatus(out) + + def request_permission( + self, of: PermissionType, wait_timeout: Optional[float] = 25 + ) -> Optional[PermissionStatus]: + out = self.invoke_method( + "request_permission", + {"of": of.value if isinstance(of, PermissionType) else of}, + wait_for_result=True, + wait_timeout=wait_timeout, ) + if out == "null": + return None + return PermissionStatus(out) - def _get_control_name(self): - return "permission_handler" + async def request_permission_async( + self, of: PermissionType, wait_timeout: Optional[float] = 25 + ) -> Optional[PermissionStatus]: + out = await self.invoke_method_async( + "request_permission", + {"of": of.value if isinstance(of, PermissionType) else of}, + wait_for_result=True, + wait_timeout=wait_timeout, + ) + if out == "null": + return None + return PermissionStatus(out)