#1160 secure view: prevent access to export actions
This commit is contained in:
parent
31f4737d3c
commit
f9bfbd5bea
12 changed files with 65 additions and 33 deletions
|
@ -4,6 +4,7 @@ import android.annotation.SuppressLint
|
||||||
import android.app.SearchManager
|
import android.app.SearchManager
|
||||||
import android.appwidget.AppWidgetManager
|
import android.appwidget.AppWidgetManager
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
|
@ -317,6 +318,13 @@ open class MainActivity : FlutterFragmentActivity() {
|
||||||
INTENT_DATA_KEY_URI to uri.toString(),
|
INTENT_DATA_KEY_URI to uri.toString(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as android.app.KeyguardManager
|
||||||
|
val isLocked = keyguardManager.isKeyguardLocked
|
||||||
|
if (isLocked) {
|
||||||
|
// device is locked, so access to content is limited to intent URI by default
|
||||||
|
fields[INTENT_DATA_KEY_SECURE_URIS] = listOf(uri.toString())
|
||||||
|
}
|
||||||
|
|
||||||
if (action == MediaStore.ACTION_REVIEW_SECURE) {
|
if (action == MediaStore.ACTION_REVIEW_SECURE) {
|
||||||
val uris = ArrayList<String>()
|
val uris = ArrayList<String>()
|
||||||
intent.clipData?.let { clipData ->
|
intent.clipData?.let { clipData ->
|
||||||
|
@ -324,8 +332,10 @@ open class MainActivity : FlutterFragmentActivity() {
|
||||||
clipData.getItemAt(i).uri?.let { uris.add(it.toString()) }
|
clipData.getItemAt(i).uri?.let { uris.add(it.toString()) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (uris.isNotEmpty()) {
|
||||||
fields[INTENT_DATA_KEY_SECURE_URIS] = uris
|
fields[INTENT_DATA_KEY_SECURE_URIS] = uris
|
||||||
}
|
}
|
||||||
|
}
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && intent.hasExtra(MediaStore.EXTRA_BRIGHTNESS)) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && intent.hasExtra(MediaStore.EXTRA_BRIGHTNESS)) {
|
||||||
fields[INTENT_DATA_KEY_BRIGHTNESS] = intent.getFloatExtra(MediaStore.EXTRA_BRIGHTNESS, 0f)
|
fields[INTENT_DATA_KEY_BRIGHTNESS] = intent.getFloatExtra(MediaStore.EXTRA_BRIGHTNESS, 0f)
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,6 +33,7 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
|
||||||
"getDefaultTimeZoneRawOffsetMillis" -> safe(call, result, ::getDefaultTimeZoneRawOffsetMillis)
|
"getDefaultTimeZoneRawOffsetMillis" -> safe(call, result, ::getDefaultTimeZoneRawOffsetMillis)
|
||||||
"getLocales" -> safe(call, result, ::getLocales)
|
"getLocales" -> safe(call, result, ::getLocales)
|
||||||
"getPerformanceClass" -> safe(call, result, ::getPerformanceClass)
|
"getPerformanceClass" -> safe(call, result, ::getPerformanceClass)
|
||||||
|
"isLocked" -> safe(call, result, ::isLocked)
|
||||||
"isSystemFilePickerEnabled" -> safe(call, result, ::isSystemFilePickerEnabled)
|
"isSystemFilePickerEnabled" -> safe(call, result, ::isSystemFilePickerEnabled)
|
||||||
"requestMediaManagePermission" -> safe(call, result, ::requestMediaManagePermission)
|
"requestMediaManagePermission" -> safe(call, result, ::requestMediaManagePermission)
|
||||||
"getAvailableHeapSize" -> safe(call, result, ::getAvailableHeapSize)
|
"getAvailableHeapSize" -> safe(call, result, ::getAvailableHeapSize)
|
||||||
|
@ -49,13 +50,11 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
|
||||||
val sdkInt = Build.VERSION.SDK_INT
|
val sdkInt = Build.VERSION.SDK_INT
|
||||||
result.success(
|
result.success(
|
||||||
hashMapOf(
|
hashMapOf(
|
||||||
"canGrantDirectoryAccess" to (sdkInt >= Build.VERSION_CODES.LOLLIPOP),
|
|
||||||
"canPinShortcut" to ShortcutManagerCompat.isRequestPinShortcutSupported(context),
|
"canPinShortcut" to ShortcutManagerCompat.isRequestPinShortcutSupported(context),
|
||||||
"canRenderFlagEmojis" to (sdkInt >= Build.VERSION_CODES.M),
|
"canRenderFlagEmojis" to (sdkInt >= Build.VERSION_CODES.M),
|
||||||
"canRenderSubdivisionFlagEmojis" to (sdkInt >= Build.VERSION_CODES.O),
|
"canRenderSubdivisionFlagEmojis" to (sdkInt >= Build.VERSION_CODES.O),
|
||||||
"canRequestManageMedia" to (sdkInt >= Build.VERSION_CODES.S),
|
"canRequestManageMedia" to (sdkInt >= Build.VERSION_CODES.S),
|
||||||
"canSetLockScreenWallpaper" to (sdkInt >= Build.VERSION_CODES.N),
|
"canSetLockScreenWallpaper" to (sdkInt >= Build.VERSION_CODES.N),
|
||||||
"canUseCrypto" to (sdkInt >= Build.VERSION_CODES.LOLLIPOP),
|
|
||||||
"hasGeocoder" to Geocoder.isPresent(),
|
"hasGeocoder" to Geocoder.isPresent(),
|
||||||
"isDynamicColorAvailable" to DynamicColors.isDynamicColorAvailable(),
|
"isDynamicColorAvailable" to DynamicColors.isDynamicColorAvailable(),
|
||||||
"showPinShortcutFeedback" to (sdkInt >= Build.VERSION_CODES.O),
|
"showPinShortcutFeedback" to (sdkInt >= Build.VERSION_CODES.O),
|
||||||
|
@ -100,6 +99,12 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
|
||||||
result.success(Build.VERSION.SDK_INT)
|
result.success(Build.VERSION.SDK_INT)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun isLocked(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||||
|
val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as android.app.KeyguardManager
|
||||||
|
val isLocked = keyguardManager.isKeyguardLocked
|
||||||
|
result.success(isLocked)
|
||||||
|
}
|
||||||
|
|
||||||
private fun isSystemFilePickerEnabled(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
private fun isSystemFilePickerEnabled(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||||
val enabled = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).resolveActivity(context.packageManager) != null
|
val enabled = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).resolveActivity(context.packageManager) != null
|
||||||
result.success(enabled)
|
result.success(enabled)
|
||||||
|
|
|
@ -6,8 +6,12 @@ import 'package:connectivity_plus/connectivity_plus.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
abstract class AvesAvailability {
|
abstract class AvesAvailability {
|
||||||
|
Future<void> onNewIntent();
|
||||||
|
|
||||||
void onResume();
|
void onResume();
|
||||||
|
|
||||||
|
bool get isLocked;
|
||||||
|
|
||||||
Future<bool> get isConnected;
|
Future<bool> get isConnected;
|
||||||
|
|
||||||
Future<bool> get canLocatePlaces;
|
Future<bool> get canLocatePlaces;
|
||||||
|
@ -16,15 +20,24 @@ abstract class AvesAvailability {
|
||||||
}
|
}
|
||||||
|
|
||||||
class LiveAvesAvailability implements AvesAvailability {
|
class LiveAvesAvailability implements AvesAvailability {
|
||||||
bool? _isConnected;
|
bool? _isConnected, _isLocked;
|
||||||
|
|
||||||
LiveAvesAvailability() {
|
LiveAvesAvailability() {
|
||||||
Connectivity().onConnectivityChanged.listen(_updateConnectivityFromResult);
|
Connectivity().onConnectivityChanged.listen(_updateConnectivityFromResult);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<void> onNewIntent() async {
|
||||||
|
_isLocked = await deviceService.isLocked();
|
||||||
|
debugPrint('Device is locked=$_isLocked');
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onResume() => _isConnected = null;
|
void onResume() => _isConnected = null;
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get isLocked => _isLocked ?? false;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> get isConnected async {
|
Future<bool> get isConnected async {
|
||||||
if (_isConnected != null) return SynchronousFuture(_isConnected!);
|
if (_isConnected != null) return SynchronousFuture(_isConnected!);
|
||||||
|
|
|
@ -9,8 +9,8 @@ final Device device = Device._private();
|
||||||
|
|
||||||
class Device {
|
class Device {
|
||||||
late final String _packageName, _packageVersion, _userAgent;
|
late final String _packageName, _packageVersion, _userAgent;
|
||||||
late final bool _canAuthenticateUser, _canGrantDirectoryAccess, _canPinShortcut;
|
late final bool _canAuthenticateUser, _canPinShortcut;
|
||||||
late final bool _canRenderFlagEmojis, _canRenderSubdivisionFlagEmojis, _canRequestManageMedia, _canSetLockScreenWallpaper, _canUseCrypto;
|
late final bool _canRenderFlagEmojis, _canRenderSubdivisionFlagEmojis, _canRequestManageMedia, _canSetLockScreenWallpaper;
|
||||||
late final bool _hasGeocoder, _isDynamicColorAvailable, _isTelevision, _showPinShortcutFeedback, _supportEdgeToEdgeUIMode, _supportPictureInPicture;
|
late final bool _hasGeocoder, _isDynamicColorAvailable, _isTelevision, _showPinShortcutFeedback, _supportEdgeToEdgeUIMode, _supportPictureInPicture;
|
||||||
|
|
||||||
String get packageName => _packageName;
|
String get packageName => _packageName;
|
||||||
|
@ -21,8 +21,6 @@ class Device {
|
||||||
|
|
||||||
bool get canAuthenticateUser => _canAuthenticateUser;
|
bool get canAuthenticateUser => _canAuthenticateUser;
|
||||||
|
|
||||||
bool get canGrantDirectoryAccess => _canGrantDirectoryAccess;
|
|
||||||
|
|
||||||
bool get canPinShortcut => _canPinShortcut;
|
bool get canPinShortcut => _canPinShortcut;
|
||||||
|
|
||||||
bool get canRenderFlagEmojis => _canRenderFlagEmojis;
|
bool get canRenderFlagEmojis => _canRenderFlagEmojis;
|
||||||
|
@ -33,10 +31,6 @@ class Device {
|
||||||
|
|
||||||
bool get canSetLockScreenWallpaper => _canSetLockScreenWallpaper;
|
bool get canSetLockScreenWallpaper => _canSetLockScreenWallpaper;
|
||||||
|
|
||||||
bool get canUseCrypto => _canUseCrypto;
|
|
||||||
|
|
||||||
bool get canUseVaults => canAuthenticateUser || canUseCrypto;
|
|
||||||
|
|
||||||
bool get hasGeocoder => _hasGeocoder;
|
bool get hasGeocoder => _hasGeocoder;
|
||||||
|
|
||||||
bool get isDynamicColorAvailable => _isDynamicColorAvailable;
|
bool get isDynamicColorAvailable => _isDynamicColorAvailable;
|
||||||
|
@ -71,13 +65,11 @@ class Device {
|
||||||
}
|
}
|
||||||
|
|
||||||
final capabilities = await deviceService.getCapabilities();
|
final capabilities = await deviceService.getCapabilities();
|
||||||
_canGrantDirectoryAccess = capabilities['canGrantDirectoryAccess'] ?? false;
|
|
||||||
_canPinShortcut = capabilities['canPinShortcut'] ?? false;
|
_canPinShortcut = capabilities['canPinShortcut'] ?? false;
|
||||||
_canRenderFlagEmojis = capabilities['canRenderFlagEmojis'] ?? false;
|
_canRenderFlagEmojis = capabilities['canRenderFlagEmojis'] ?? false;
|
||||||
_canRenderSubdivisionFlagEmojis = capabilities['canRenderSubdivisionFlagEmojis'] ?? false;
|
_canRenderSubdivisionFlagEmojis = capabilities['canRenderSubdivisionFlagEmojis'] ?? false;
|
||||||
_canRequestManageMedia = capabilities['canRequestManageMedia'] ?? false;
|
_canRequestManageMedia = capabilities['canRequestManageMedia'] ?? false;
|
||||||
_canSetLockScreenWallpaper = capabilities['canSetLockScreenWallpaper'] ?? false;
|
_canSetLockScreenWallpaper = capabilities['canSetLockScreenWallpaper'] ?? false;
|
||||||
_canUseCrypto = capabilities['canUseCrypto'] ?? false;
|
|
||||||
_hasGeocoder = capabilities['hasGeocoder'] ?? false;
|
_hasGeocoder = capabilities['hasGeocoder'] ?? false;
|
||||||
_isDynamicColorAvailable = capabilities['isDynamicColorAvailable'] ?? false;
|
_isDynamicColorAvailable = capabilities['isDynamicColorAvailable'] ?? false;
|
||||||
_showPinShortcutFeedback = capabilities['showPinShortcutFeedback'] ?? false;
|
_showPinShortcutFeedback = capabilities['showPinShortcutFeedback'] ?? false;
|
||||||
|
|
|
@ -14,6 +14,8 @@ abstract class DeviceService {
|
||||||
|
|
||||||
Future<int> getPerformanceClass();
|
Future<int> getPerformanceClass();
|
||||||
|
|
||||||
|
Future<bool> isLocked();
|
||||||
|
|
||||||
Future<bool> isSystemFilePickerEnabled();
|
Future<bool> isSystemFilePickerEnabled();
|
||||||
|
|
||||||
Future<void> requestMediaManagePermission();
|
Future<void> requestMediaManagePermission();
|
||||||
|
@ -89,6 +91,17 @@ class PlatformDeviceService implements DeviceService {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<bool> isLocked() async {
|
||||||
|
try {
|
||||||
|
final result = await _platform.invokeMethod('isLocked');
|
||||||
|
if (result != null) return result as bool;
|
||||||
|
} on PlatformException catch (e, stack) {
|
||||||
|
await reportService.recordError(e, stack);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<bool> isSystemFilePickerEnabled() async {
|
Future<bool> isSystemFilePickerEnabled() async {
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -25,14 +25,11 @@ class _DebugDeviceSectionState extends State<DebugDeviceSection> with AutomaticK
|
||||||
'packageVersion': device.packageVersion,
|
'packageVersion': device.packageVersion,
|
||||||
'userAgent': device.userAgent,
|
'userAgent': device.userAgent,
|
||||||
'canAuthenticateUser': '${device.canAuthenticateUser}',
|
'canAuthenticateUser': '${device.canAuthenticateUser}',
|
||||||
'canGrantDirectoryAccess': '${device.canGrantDirectoryAccess}',
|
|
||||||
'canPinShortcut': '${device.canPinShortcut}',
|
'canPinShortcut': '${device.canPinShortcut}',
|
||||||
'canRenderFlagEmojis': '${device.canRenderFlagEmojis}',
|
'canRenderFlagEmojis': '${device.canRenderFlagEmojis}',
|
||||||
'canRenderSubdivisionFlagEmojis': '${device.canRenderSubdivisionFlagEmojis}',
|
'canRenderSubdivisionFlagEmojis': '${device.canRenderSubdivisionFlagEmojis}',
|
||||||
'canRequestManageMedia': '${device.canRequestManageMedia}',
|
'canRequestManageMedia': '${device.canRequestManageMedia}',
|
||||||
'canSetLockScreenWallpaper': '${device.canSetLockScreenWallpaper}',
|
'canSetLockScreenWallpaper': '${device.canSetLockScreenWallpaper}',
|
||||||
'canUseCrypto': '${device.canUseCrypto}',
|
|
||||||
'canUseVaults': '${device.canUseVaults}',
|
|
||||||
'hasGeocoder': '${device.hasGeocoder}',
|
'hasGeocoder': '${device.hasGeocoder}',
|
||||||
'isDynamicColorAvailable': '${device.isDynamicColorAvailable}',
|
'isDynamicColorAvailable': '${device.isDynamicColorAvailable}',
|
||||||
'isTelevision': '${device.isTelevision}',
|
'isTelevision': '${device.isTelevision}',
|
||||||
|
|
|
@ -42,11 +42,9 @@ class _EditVaultDialogState extends State<EditVaultDialog> with FeedbackMixin, V
|
||||||
|
|
||||||
final List<VaultLockType> _lockTypeOptions = [
|
final List<VaultLockType> _lockTypeOptions = [
|
||||||
if (device.canAuthenticateUser) VaultLockType.system,
|
if (device.canAuthenticateUser) VaultLockType.system,
|
||||||
if (device.canUseCrypto) ...[
|
|
||||||
VaultLockType.pattern,
|
VaultLockType.pattern,
|
||||||
VaultLockType.pin,
|
VaultLockType.pin,
|
||||||
VaultLockType.password,
|
VaultLockType.password,
|
||||||
],
|
|
||||||
];
|
];
|
||||||
|
|
||||||
VaultDetails? get initialDetails => widget.initialDetails;
|
VaultDetails? get initialDetails => widget.initialDetails;
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:aves/app_mode.dart';
|
import 'package:aves/app_mode.dart';
|
||||||
import 'package:aves/model/device.dart';
|
|
||||||
import 'package:aves/model/entry/entry.dart';
|
import 'package:aves/model/entry/entry.dart';
|
||||||
import 'package:aves/model/filters/album.dart';
|
import 'package:aves/model/filters/album.dart';
|
||||||
import 'package:aves/model/filters/filters.dart';
|
import 'package:aves/model/filters/filters.dart';
|
||||||
|
@ -78,12 +77,10 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> with
|
||||||
final selectedSingleItem = selectedFilters.length == 1;
|
final selectedSingleItem = selectedFilters.length == 1;
|
||||||
final isMain = appMode == AppMode.main;
|
final isMain = appMode == AppMode.main;
|
||||||
|
|
||||||
final canCreate = !settings.isReadOnly && appMode.canCreateFilter && !isSelecting;
|
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case ChipSetAction.createAlbum:
|
case ChipSetAction.createAlbum:
|
||||||
return canCreate;
|
|
||||||
case ChipSetAction.createVault:
|
case ChipSetAction.createVault:
|
||||||
return canCreate && device.canUseVaults;
|
return !settings.isReadOnly && appMode.canCreateFilter && !isSelecting;
|
||||||
case ChipSetAction.delete:
|
case ChipSetAction.delete:
|
||||||
case ChipSetAction.rename:
|
case ChipSetAction.rename:
|
||||||
return isMain && isSelecting && !settings.isReadOnly;
|
return isMain && isSelecting && !settings.isReadOnly;
|
||||||
|
|
|
@ -98,6 +98,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
_initialExplorerPath = null;
|
_initialExplorerPath = null;
|
||||||
_secureUris = null;
|
_secureUris = null;
|
||||||
|
|
||||||
|
await availability.onNewIntent();
|
||||||
await androidFileUtils.init();
|
await androidFileUtils.init();
|
||||||
if (!{
|
if (!{
|
||||||
IntentActions.edit,
|
IntentActions.edit,
|
||||||
|
|
|
@ -43,7 +43,7 @@ class PrivacySection extends SettingsSection {
|
||||||
SettingsTilePrivacySaveSearchHistory(),
|
SettingsTilePrivacySaveSearchHistory(),
|
||||||
if (!settings.useTvLayout) SettingsTilePrivacyEnableBin(),
|
if (!settings.useTvLayout) SettingsTilePrivacyEnableBin(),
|
||||||
SettingsTilePrivacyHiddenItems(),
|
SettingsTilePrivacyHiddenItems(),
|
||||||
if (!settings.useTvLayout && device.canGrantDirectoryAccess) SettingsTilePrivacyStorageAccess(),
|
if (!settings.useTvLayout) SettingsTilePrivacyStorageAccess(),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -159,6 +159,10 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
||||||
case EntryAction.convertMotionPhotoToStillImage:
|
case EntryAction.convertMotionPhotoToStillImage:
|
||||||
case EntryAction.viewMotionPhotoVideo:
|
case EntryAction.viewMotionPhotoVideo:
|
||||||
return _metadataActionDelegate.canApply(targetEntry, action);
|
return _metadataActionDelegate.canApply(targetEntry, action);
|
||||||
|
case EntryAction.convert:
|
||||||
|
case EntryAction.copy:
|
||||||
|
case EntryAction.move:
|
||||||
|
return !availability.isLocked;
|
||||||
default:
|
default:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import 'package:aves/model/entry/extensions/props.dart';
|
||||||
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
||||||
import 'package:aves/model/settings/settings.dart';
|
import 'package:aves/model/settings/settings.dart';
|
||||||
import 'package:aves/model/source/collection_lens.dart';
|
import 'package:aves/model/source/collection_lens.dart';
|
||||||
|
import 'package:aves/services/common/services.dart';
|
||||||
import 'package:aves/theme/icons.dart';
|
import 'package:aves/theme/icons.dart';
|
||||||
import 'package:aves/view/view.dart';
|
import 'package:aves/view/view.dart';
|
||||||
import 'package:aves/widgets/common/action_controls/quick_choosers/move_button.dart';
|
import 'package:aves/widgets/common/action_controls/quick_choosers/move_button.dart';
|
||||||
|
@ -278,6 +279,7 @@ class _ViewerButtonRowContentState extends State<ViewerButtonRowContent> {
|
||||||
...topLevelActions.map((action) => _buildPopupMenuItem(context, action, videoController)),
|
...topLevelActions.map((action) => _buildPopupMenuItem(context, action, videoController)),
|
||||||
if (exportActions.isNotEmpty)
|
if (exportActions.isNotEmpty)
|
||||||
PopupMenuExpansionPanel<EntryAction>(
|
PopupMenuExpansionPanel<EntryAction>(
|
||||||
|
enabled: !availability.isLocked,
|
||||||
value: 'export',
|
value: 'export',
|
||||||
expandedNotifier: _popupExpandedNotifier,
|
expandedNotifier: _popupExpandedNotifier,
|
||||||
icon: AIcons.export,
|
icon: AIcons.export,
|
||||||
|
@ -345,18 +347,18 @@ class _ViewerButtonRowContentState extends State<ViewerButtonRowContent> {
|
||||||
}
|
}
|
||||||
|
|
||||||
PopupMenuItem<EntryAction> _buildPopupMenuItem(BuildContext context, EntryAction action, AvesVideoController? videoController) {
|
PopupMenuItem<EntryAction> _buildPopupMenuItem(BuildContext context, EntryAction action, AvesVideoController? videoController) {
|
||||||
late final bool enabled;
|
var enabled = widget.actionDelegate.canApply(action);
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case EntryAction.videoCaptureFrame:
|
case EntryAction.videoCaptureFrame:
|
||||||
enabled = videoController?.canCaptureFrameNotifier.value ?? false;
|
enabled &= videoController?.canCaptureFrameNotifier.value ?? false;
|
||||||
case EntryAction.videoToggleMute:
|
case EntryAction.videoToggleMute:
|
||||||
enabled = videoController?.canMuteNotifier.value ?? false;
|
enabled &= videoController?.canMuteNotifier.value ?? false;
|
||||||
case EntryAction.videoSelectStreams:
|
case EntryAction.videoSelectStreams:
|
||||||
enabled = videoController?.canSelectStreamNotifier.value ?? false;
|
enabled &= videoController?.canSelectStreamNotifier.value ?? false;
|
||||||
case EntryAction.videoSetSpeed:
|
case EntryAction.videoSetSpeed:
|
||||||
enabled = videoController?.canSetSpeedNotifier.value ?? false;
|
enabled &= videoController?.canSetSpeedNotifier.value ?? false;
|
||||||
default:
|
default:
|
||||||
enabled = true;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget? child;
|
Widget? child;
|
||||||
|
|
Loading…
Reference in a new issue