#120 warn user if system file picker is disabled
This commit is contained in:
parent
8aacd5064f
commit
25311c5fcb
9 changed files with 68 additions and 12 deletions
|
@ -1,6 +1,7 @@
|
|||
package deckers.thibault.aves.channel.calls
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.Resources
|
||||
import android.os.Build
|
||||
import androidx.core.content.pm.ShortcutManagerCompat
|
||||
|
@ -18,6 +19,7 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
|
|||
"getDefaultTimeZone" -> safe(call, result, ::getDefaultTimeZone)
|
||||
"getLocales" -> safe(call, result, ::getLocales)
|
||||
"getPerformanceClass" -> safe(call, result, ::getPerformanceClass)
|
||||
"isSystemFilePickerEnabled" -> safe(call, result, ::isSystemFilePickerEnabled)
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +36,6 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
|
|||
// but using hybrid composition would make it usable on API 19 too,
|
||||
// cf https://github.com/flutter/flutter/issues/23728
|
||||
"canRenderGoogleMaps" to (sdkInt >= Build.VERSION_CODES.KITKAT_WATCH),
|
||||
"hasFilePicker" to (sdkInt >= Build.VERSION_CODES.KITKAT),
|
||||
"showPinShortcutFeedback" to (sdkInt >= Build.VERSION_CODES.O),
|
||||
"supportEdgeToEdgeUIMode" to (sdkInt >= Build.VERSION_CODES.Q),
|
||||
)
|
||||
|
@ -82,6 +83,15 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
|
|||
result.success(Build.VERSION.SDK_INT)
|
||||
}
|
||||
|
||||
private fun isSystemFilePickerEnabled(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||
val enabled = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).resolveActivity(context.packageManager) != null
|
||||
} else {
|
||||
false
|
||||
}
|
||||
result.success(enabled)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CHANNEL = "deckers.thibault/aves/device"
|
||||
}
|
||||
|
|
|
@ -170,7 +170,7 @@ class StorageAccessStreamHandler(private val activity: Activity, arguments: Any?
|
|||
MainActivity.pendingStorageAccessResultHandlers[MainActivity.OPEN_FILE_REQUEST] = PendingStorageAccessResultHandler(null, ::onGranted, ::onDenied)
|
||||
activity.startActivityForResult(intent, MainActivity.OPEN_FILE_REQUEST)
|
||||
} else {
|
||||
MainActivity.notifyError("failed to resolve activity for intent=$intent")
|
||||
MainActivity.notifyError("failed to resolve activity for intent=$intent extras=${intent.extras}")
|
||||
onDenied()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,7 @@ object PermissionManager {
|
|||
MainActivity.pendingStorageAccessResultHandlers[MainActivity.DOCUMENT_TREE_ACCESS_REQUEST] = PendingStorageAccessResultHandler(path, onGranted, onDenied)
|
||||
activity.startActivityForResult(intent, MainActivity.DOCUMENT_TREE_ACCESS_REQUEST)
|
||||
} else {
|
||||
MainActivity.notifyError("failed to resolve activity for intent=$intent")
|
||||
MainActivity.notifyError("failed to resolve activity for intent=$intent extras=${intent.extras}")
|
||||
onDenied()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -204,6 +204,8 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"missingSystemFilePickerDialogTitle": "Missing System File Picker",
|
||||
"missingSystemFilePickerDialogMessage": "The system file picker is missing or disabled. Please enable it and try again.",
|
||||
|
||||
"unsupportedTypeDialogTitle": "Unsupported Types",
|
||||
"unsupportedTypeDialogMessage": "{count, plural, =1{This operation is not supported for items of the following type: {types}.} other{This operation is not supported for items of the following types: {types}.}}",
|
||||
|
|
|
@ -6,7 +6,7 @@ final Device device = Device._private();
|
|||
class Device {
|
||||
late final String _userAgent;
|
||||
late final bool _canGrantDirectoryAccess, _canPinShortcut, _canPrint, _canRenderFlagEmojis, _canRenderGoogleMaps;
|
||||
late final bool _hasFilePicker, _showPinShortcutFeedback, _supportEdgeToEdgeUIMode;
|
||||
late final bool _showPinShortcutFeedback, _supportEdgeToEdgeUIMode;
|
||||
|
||||
String get userAgent => _userAgent;
|
||||
|
||||
|
@ -20,8 +20,6 @@ class Device {
|
|||
|
||||
bool get canRenderGoogleMaps => _canRenderGoogleMaps;
|
||||
|
||||
bool get hasFilePicker => _hasFilePicker;
|
||||
|
||||
bool get showPinShortcutFeedback => _showPinShortcutFeedback;
|
||||
|
||||
bool get supportEdgeToEdgeUIMode => _supportEdgeToEdgeUIMode;
|
||||
|
@ -38,7 +36,6 @@ class Device {
|
|||
_canPrint = capabilities['canPrint'] ?? false;
|
||||
_canRenderFlagEmojis = capabilities['canRenderFlagEmojis'] ?? false;
|
||||
_canRenderGoogleMaps = capabilities['canRenderGoogleMaps'] ?? false;
|
||||
_hasFilePicker = capabilities['hasFilePicker'] ?? false;
|
||||
_showPinShortcutFeedback = capabilities['showPinShortcutFeedback'] ?? false;
|
||||
_supportEdgeToEdgeUIMode = capabilities['supportEdgeToEdgeUIMode'] ?? false;
|
||||
}
|
||||
|
|
|
@ -11,6 +11,8 @@ abstract class DeviceService {
|
|||
Future<List<Locale>> getLocales();
|
||||
|
||||
Future<int> getPerformanceClass();
|
||||
|
||||
Future<bool> isSystemFilePickerEnabled();
|
||||
}
|
||||
|
||||
class PlatformDeviceService implements DeviceService {
|
||||
|
@ -60,7 +62,6 @@ class PlatformDeviceService implements DeviceService {
|
|||
@override
|
||||
Future<int> getPerformanceClass() async {
|
||||
try {
|
||||
await platform.invokeMethod('getPerformanceClass');
|
||||
final result = await platform.invokeMethod('getPerformanceClass');
|
||||
if (result != null) return result as int;
|
||||
} on PlatformException catch (e, stack) {
|
||||
|
@ -68,4 +69,15 @@ class PlatformDeviceService implements DeviceService {
|
|||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isSystemFilePickerEnabled() async {
|
||||
try {
|
||||
final result = await platform.invokeMethod('isSystemFilePickerEnabled');
|
||||
if (result != null) return result as bool;
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,11 +47,12 @@ mixin PermissionAwareMixin {
|
|||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
final directory = dir.relativeDir.isEmpty ? context.l10n.rootDirectoryDescription : context.l10n.otherDirectoryDescription(dir.relativeDir);
|
||||
final l10n = context.l10n;
|
||||
final directory = dir.relativeDir.isEmpty ? l10n.rootDirectoryDescription : l10n.otherDirectoryDescription(dir.relativeDir);
|
||||
final volume = dir.getVolumeDescription(context);
|
||||
return AvesDialog(
|
||||
title: context.l10n.storageAccessDialogTitle,
|
||||
content: Text(context.l10n.storageAccessDialogMessage(directory, volume)),
|
||||
title: l10n.storageAccessDialogTitle,
|
||||
content: Text(l10n.storageAccessDialogMessage(directory, volume)),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
|
@ -68,6 +69,26 @@ mixin PermissionAwareMixin {
|
|||
// abort if the user cancels in Flutter
|
||||
if (confirmed == null || !confirmed) return false;
|
||||
|
||||
if (!await deviceService.isSystemFilePickerEnabled()) {
|
||||
await showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
final l10n = context.l10n;
|
||||
return AvesDialog(
|
||||
title: l10n.missingSystemFilePickerDialogTitle,
|
||||
content: Text(l10n.missingSystemFilePickerDialogMessage),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () => Navigator.pop(context),
|
||||
child: Text(MaterialLocalizations.of(context).okButtonLabel),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
final granted = await storageService.requestDirectoryAccess(dir.volumePath);
|
||||
if (!granted) {
|
||||
// abort if the user denies access from the native dialog
|
||||
|
|
|
@ -51,7 +51,7 @@ class _DebugAndroidAppSectionState extends State<DebugAndroidAppSection> with Au
|
|||
return ValueListenableBuilder<String>(
|
||||
valueListenable: _queryNotifier,
|
||||
builder: (context, query, child) {
|
||||
if ({package.packageName, ...package.potentialDirs}.none((v) => v.contains(query))) {
|
||||
if ({package.packageName, ...package.potentialDirs}.none((v) => v.toLowerCase().contains(query.toLowerCase()))) {
|
||||
return const SizedBox();
|
||||
}
|
||||
return Text.rich(
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
"de": [
|
||||
"filterRatingUnratedLabel",
|
||||
"filterRatingRejectedLabel",
|
||||
"missingSystemFilePickerDialogTitle",
|
||||
"missingSystemFilePickerDialogMessage",
|
||||
"editEntryDateDialogSourceFieldLabel",
|
||||
"editEntryDateDialogSourceCustomDate",
|
||||
"editEntryDateDialogSourceTitle",
|
||||
|
@ -12,9 +14,21 @@
|
|||
"settingsThumbnailShowRating"
|
||||
],
|
||||
|
||||
"fr": [
|
||||
"missingSystemFilePickerDialogTitle",
|
||||
"missingSystemFilePickerDialogMessage"
|
||||
],
|
||||
|
||||
"ko": [
|
||||
"missingSystemFilePickerDialogTitle",
|
||||
"missingSystemFilePickerDialogMessage"
|
||||
],
|
||||
|
||||
"ru": [
|
||||
"filterRatingUnratedLabel",
|
||||
"filterRatingRejectedLabel",
|
||||
"missingSystemFilePickerDialogTitle",
|
||||
"missingSystemFilePickerDialogMessage",
|
||||
"editEntryDateDialogSourceFieldLabel",
|
||||
"editEntryDateDialogSourceCustomDate",
|
||||
"editEntryDateDialogSourceTitle",
|
||||
|
|
Loading…
Reference in a new issue