viewer: action to rotate screen when device has locked rotation
This commit is contained in:
parent
c294343f07
commit
adc41bf3cd
16 changed files with 255 additions and 26 deletions
|
@ -22,7 +22,8 @@ import io.flutter.plugin.common.MethodCall
|
|||
import io.flutter.plugin.common.MethodChannel
|
||||
|
||||
class MainActivity : FlutterActivity() {
|
||||
private lateinit var contentStreamHandler: ContentChangeStreamHandler
|
||||
private lateinit var mediaStoreChangeStreamHandler: MediaStoreChangeStreamHandler
|
||||
private lateinit var settingsChangeStreamHandler: SettingsChangeStreamHandler
|
||||
private lateinit var intentStreamHandler: IntentStreamHandler
|
||||
private lateinit var intentDataMap: MutableMap<String, Any?>
|
||||
|
||||
|
@ -50,8 +51,11 @@ class MainActivity : FlutterActivity() {
|
|||
StreamsChannel(messenger, StorageAccessStreamHandler.CHANNEL).setStreamHandlerFactory { args -> StorageAccessStreamHandler(this, args) }
|
||||
|
||||
// Media Store change monitoring
|
||||
contentStreamHandler = ContentChangeStreamHandler(this).apply {
|
||||
EventChannel(messenger, ContentChangeStreamHandler.CHANNEL).setStreamHandler(this)
|
||||
mediaStoreChangeStreamHandler = MediaStoreChangeStreamHandler(this).apply {
|
||||
EventChannel(messenger, MediaStoreChangeStreamHandler.CHANNEL).setStreamHandler(this)
|
||||
}
|
||||
settingsChangeStreamHandler = SettingsChangeStreamHandler(this).apply {
|
||||
EventChannel(messenger, SettingsChangeStreamHandler.CHANNEL).setStreamHandler(this)
|
||||
}
|
||||
|
||||
// intent handling
|
||||
|
@ -75,7 +79,8 @@ class MainActivity : FlutterActivity() {
|
|||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
contentStreamHandler.dispose()
|
||||
mediaStoreChangeStreamHandler.dispose()
|
||||
settingsChangeStreamHandler.dispose()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
package deckers.thibault.aves.channel.calls
|
||||
|
||||
import android.app.Activity
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import android.view.WindowManager
|
||||
import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||
|
@ -11,6 +14,8 @@ class WindowHandler(private val activity: Activity) : MethodCallHandler {
|
|||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||
when (call.method) {
|
||||
"keepScreenOn" -> safe(call, result, ::keepScreenOn)
|
||||
"isRotationLocked" -> safe(call, result, ::isRotationLocked)
|
||||
"requestOrientation" -> safe(call, result, ::requestOrientation)
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
@ -32,7 +37,28 @@ class WindowHandler(private val activity: Activity) : MethodCallHandler {
|
|||
result.success(null)
|
||||
}
|
||||
|
||||
private fun isRotationLocked(@Suppress("UNUSED_PARAMETER") call: MethodCall, result: MethodChannel.Result) {
|
||||
var locked = false
|
||||
try {
|
||||
locked = Settings.System.getInt(activity.contentResolver, Settings.System.ACCELEROMETER_ROTATION) == 0
|
||||
} catch (e: Exception) {
|
||||
Log.w(LOG_TAG, "failed to get settings", e)
|
||||
}
|
||||
result.success(locked)
|
||||
}
|
||||
|
||||
private fun requestOrientation(call: MethodCall, result: MethodChannel.Result) {
|
||||
val orientation = call.argument<Int>("orientation")
|
||||
if (orientation == null) {
|
||||
result.error("requestOrientation-args", "failed because of missing arguments", null)
|
||||
return
|
||||
}
|
||||
activity.requestedOrientation = orientation
|
||||
result.success(true)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG = LogUtils.createTag<WindowHandler>()
|
||||
const val CHANNEL = "deckers.thibault/aves/window"
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ import deckers.thibault.aves.utils.LogUtils
|
|||
import io.flutter.plugin.common.EventChannel
|
||||
import io.flutter.plugin.common.EventChannel.EventSink
|
||||
|
||||
class ContentChangeStreamHandler(private val context: Context) : EventChannel.StreamHandler {
|
||||
class MediaStoreChangeStreamHandler(private val context: Context) : EventChannel.StreamHandler {
|
||||
// cannot use `lateinit` because we cannot guarantee
|
||||
// its initialization in `onListen` at the right time
|
||||
private var eventSink: EventSink? = null
|
||||
|
@ -58,7 +58,7 @@ class ContentChangeStreamHandler(private val context: Context) : EventChannel.St
|
|||
}
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG = LogUtils.createTag<ContentChangeStreamHandler>()
|
||||
const val CHANNEL = "deckers.thibault/aves/contentchange"
|
||||
private val LOG_TAG = LogUtils.createTag<MediaStoreChangeStreamHandler>()
|
||||
const val CHANNEL = "deckers.thibault/aves/mediastorechange"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
package deckers.thibault.aves.channel.streams
|
||||
|
||||
import android.content.Context
|
||||
import android.database.ContentObserver
|
||||
import android.net.Uri
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
import io.flutter.plugin.common.EventChannel
|
||||
import io.flutter.plugin.common.EventChannel.EventSink
|
||||
|
||||
class SettingsChangeStreamHandler(private val context: Context) : EventChannel.StreamHandler {
|
||||
// cannot use `lateinit` because we cannot guarantee
|
||||
// its initialization in `onListen` at the right time
|
||||
private var eventSink: EventSink? = null
|
||||
private var handler: Handler? = null
|
||||
|
||||
private val contentObserver = object : ContentObserver(null) {
|
||||
private var accelerometerRotation: Int = 0
|
||||
|
||||
init {
|
||||
update()
|
||||
}
|
||||
|
||||
override fun onChange(selfChange: Boolean) {
|
||||
this.onChange(selfChange, null)
|
||||
}
|
||||
|
||||
override fun onChange(selfChange: Boolean, uri: Uri?) {
|
||||
if (update()) {
|
||||
success(
|
||||
hashMapOf(
|
||||
Settings.System.ACCELEROMETER_ROTATION to accelerometerRotation
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun update(): Boolean {
|
||||
var changed = false
|
||||
try {
|
||||
val newAccelerometerRotation = Settings.System.getInt(context.contentResolver, Settings.System.ACCELEROMETER_ROTATION)
|
||||
if (accelerometerRotation != newAccelerometerRotation) {
|
||||
accelerometerRotation = newAccelerometerRotation
|
||||
changed = true
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.w(LOG_TAG, "failed to get settings", e)
|
||||
}
|
||||
return changed
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
context.contentResolver.apply {
|
||||
registerContentObserver(Settings.System.CONTENT_URI, true, contentObserver)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onListen(arguments: Any?, eventSink: EventSink) {
|
||||
this.eventSink = eventSink
|
||||
handler = Handler(Looper.getMainLooper())
|
||||
}
|
||||
|
||||
override fun onCancel(arguments: Any?) {}
|
||||
|
||||
fun dispose() {
|
||||
context.contentResolver.unregisterContentObserver(contentObserver)
|
||||
}
|
||||
|
||||
private fun success(settings: FieldMap) {
|
||||
handler?.post {
|
||||
try {
|
||||
eventSink?.success(settings)
|
||||
} catch (e: Exception) {
|
||||
Log.w(LOG_TAG, "failed to use event sink", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG = LogUtils.createTag<SettingsChangeStreamHandler>()
|
||||
const val CHANNEL = "deckers.thibault/aves/settingschange"
|
||||
}
|
||||
}
|
|
@ -88,6 +88,8 @@
|
|||
"@entryActionSetAs": {},
|
||||
"entryActionOpenMap": "Show on map…",
|
||||
"@entryActionOpenMap": {},
|
||||
"entryActionRotateScreen": "Rotate screen",
|
||||
"@entryActionRotateScreen": {},
|
||||
"entryActionAddFavourite": "Add to favourites",
|
||||
"@entryActionAddFavourite": {},
|
||||
"entryActionRemoveFavourite": "Remove from favourites",
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
"entryActionOpen": "다른 앱에서 열기…",
|
||||
"entryActionSetAs": "다음 용도로 사용…",
|
||||
"entryActionOpenMap": "지도에서 보기…",
|
||||
"entryActionRotateScreen": "화면 회전",
|
||||
"entryActionAddFavourite": "즐겨찾기에 추가",
|
||||
"entryActionRemoveFavourite": "즐겨찾기에서 삭제",
|
||||
|
||||
|
|
|
@ -21,6 +21,8 @@ enum EntryAction {
|
|||
open,
|
||||
openMap,
|
||||
setAs,
|
||||
// platform
|
||||
rotateScreen,
|
||||
// debug
|
||||
debug,
|
||||
}
|
||||
|
@ -40,6 +42,7 @@ class EntryActions {
|
|||
EntryAction.export,
|
||||
EntryAction.print,
|
||||
EntryAction.viewSource,
|
||||
EntryAction.rotateScreen,
|
||||
];
|
||||
|
||||
static const externalApp = [
|
||||
|
@ -93,6 +96,9 @@ extension ExtraEntryAction on EntryAction {
|
|||
return context.l10n.entryActionSetAs;
|
||||
case EntryAction.openMap:
|
||||
return context.l10n.entryActionOpenMap;
|
||||
// platform
|
||||
case EntryAction.rotateScreen:
|
||||
return context.l10n.entryActionRotateScreen;
|
||||
// debug
|
||||
case EntryAction.debug:
|
||||
return 'Debug';
|
||||
|
@ -132,6 +138,9 @@ extension ExtraEntryAction on EntryAction {
|
|||
case EntryAction.setAs:
|
||||
case EntryAction.openMap:
|
||||
return null;
|
||||
// platform
|
||||
case EntryAction.rotateScreen:
|
||||
return AIcons.rotateScreen;
|
||||
// debug
|
||||
case EntryAction.debug:
|
||||
return AIcons.debug;
|
||||
|
|
|
@ -4,20 +4,26 @@ import 'package:aves/model/filters/filters.dart';
|
|||
import 'package:aves/model/settings/enums.dart';
|
||||
import 'package:aves/model/settings/screen_on.dart';
|
||||
import 'package:aves/model/source/enums.dart';
|
||||
import 'package:aves/services/window_service.dart';
|
||||
import 'package:aves/utils/pedantic.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_crashlytics/firebase_crashlytics.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
final Settings settings = Settings._private();
|
||||
|
||||
class Settings extends ChangeNotifier {
|
||||
final EventChannel _platformSettingsChangeChannel = const EventChannel('deckers.thibault/aves/settingschange');
|
||||
|
||||
static SharedPreferences? _prefs;
|
||||
|
||||
Settings._private();
|
||||
Settings._private() {
|
||||
_platformSettingsChangeChannel.receiveBroadcastStream().listen((event) => _onPlatformSettingsChange(event as Map?));
|
||||
}
|
||||
|
||||
// app
|
||||
static const hasAcceptedTermsKey = 'has_accepted_terms';
|
||||
|
@ -84,6 +90,7 @@ class Settings extends ChangeNotifier {
|
|||
static const viewerQuickActionsDefault = [
|
||||
EntryAction.toggleFavourite,
|
||||
EntryAction.share,
|
||||
EntryAction.rotateScreen,
|
||||
];
|
||||
static const videoQuickActionsDefault = [
|
||||
VideoAction.replay10,
|
||||
|
@ -92,6 +99,7 @@ class Settings extends ChangeNotifier {
|
|||
|
||||
Future<void> init() async {
|
||||
_prefs = await SharedPreferences.getInstance();
|
||||
_isRotationLocked = await WindowService.isRotationLocked();
|
||||
}
|
||||
|
||||
// Crashlytics initialization is separated from the main settings initialization
|
||||
|
@ -364,4 +372,30 @@ class Settings extends ChangeNotifier {
|
|||
notifyListeners();
|
||||
}
|
||||
}
|
||||
|
||||
// platform settings
|
||||
|
||||
void _onPlatformSettingsChange(Map? fields) {
|
||||
fields?.forEach((key, value) {
|
||||
switch (key) {
|
||||
// cf Android `Settings.System.ACCELEROMETER_ROTATION`
|
||||
case 'accelerometer_rotation':
|
||||
if (value is int) {
|
||||
final newValue = value == 0;
|
||||
if (_isRotationLocked != newValue) {
|
||||
_isRotationLocked = newValue;
|
||||
if (!_isRotationLocked) {
|
||||
WindowService.requestOrientation();
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
bool _isRotationLocked = false;
|
||||
|
||||
bool get isRotationLocked => _isRotationLocked;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class WindowService {
|
||||
static const platform = MethodChannel('deckers.thibault/aves/window');
|
||||
|
@ -13,4 +14,40 @@ class WindowService {
|
|||
debugPrint('keepScreenOn failed with code=${e.code}, exception=${e.message}, details=${e.details}');
|
||||
}
|
||||
}
|
||||
|
||||
static Future<bool> isRotationLocked() async {
|
||||
try {
|
||||
final result = await platform.invokeMethod('isRotationLocked');
|
||||
if (result != null) return result as bool;
|
||||
} on PlatformException catch (e) {
|
||||
debugPrint('isRotationLocked failed with code=${e.code}, exception=${e.message}, details=${e.details}');
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static Future<void> requestOrientation([Orientation? orientation]) async {
|
||||
// cf Android `ActivityInfo.ScreenOrientation`
|
||||
late final int orientationCode;
|
||||
switch (orientation) {
|
||||
case Orientation.landscape:
|
||||
// SCREEN_ORIENTATION_USER_LANDSCAPE
|
||||
orientationCode = 11;
|
||||
break;
|
||||
case Orientation.portrait:
|
||||
// SCREEN_ORIENTATION_USER_PORTRAIT
|
||||
orientationCode = 12;
|
||||
break;
|
||||
default:
|
||||
// SCREEN_ORIENTATION_UNSPECIFIED
|
||||
orientationCode = -1;
|
||||
break;
|
||||
}
|
||||
try {
|
||||
await platform.invokeMethod('requestOrientation', <String, dynamic>{
|
||||
'orientation': orientationCode,
|
||||
});
|
||||
} on PlatformException catch (e) {
|
||||
debugPrint('requestOrientation failed with code=${e.code}, exception=${e.message}, details=${e.details}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ class AIcons {
|
|||
static const IconData rename = Icons.title_outlined;
|
||||
static const IconData rotateLeft = Icons.rotate_left_outlined;
|
||||
static const IconData rotateRight = Icons.rotate_right_outlined;
|
||||
static const IconData rotateScreen = Icons.screen_rotation_outlined;
|
||||
static const IconData search = Icons.search_outlined;
|
||||
static const IconData select = Icons.select_all_outlined;
|
||||
static const IconData setCover = MdiIcons.imageEditOutline;
|
||||
|
|
|
@ -35,13 +35,13 @@ class _AvesAppState extends State<AvesApp> {
|
|||
final ValueNotifier<AppMode> appModeNotifier = ValueNotifier(AppMode.main);
|
||||
late Future<void> _appSetup;
|
||||
final _mediaStoreSource = MediaStoreSource();
|
||||
final Debouncer _contentChangeDebouncer = Debouncer(delay: Durations.contentChangeDebounceDelay);
|
||||
final Debouncer _mediaStoreChangeDebouncer = Debouncer(delay: Durations.contentChangeDebounceDelay);
|
||||
final Set<String> changedUris = {};
|
||||
|
||||
// observers are not registered when using the same list object with different items
|
||||
// the list itself needs to be reassigned
|
||||
List<NavigatorObserver> _navigatorObservers = [];
|
||||
final EventChannel _contentChangeChannel = const EventChannel('deckers.thibault/aves/contentchange');
|
||||
final EventChannel _mediaStoreChangeChannel = const EventChannel('deckers.thibault/aves/mediastorechange');
|
||||
final EventChannel _newIntentChannel = const EventChannel('deckers.thibault/aves/intent');
|
||||
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey(debugLabel: 'app-navigator');
|
||||
|
||||
|
@ -52,7 +52,7 @@ class _AvesAppState extends State<AvesApp> {
|
|||
super.initState();
|
||||
initPlatformServices();
|
||||
_appSetup = _setup();
|
||||
_contentChangeChannel.receiveBroadcastStream().listen((event) => _onContentChange(event as String?));
|
||||
_mediaStoreChangeChannel.receiveBroadcastStream().listen((event) => _onMediaStoreChange(event as String?));
|
||||
_newIntentChannel.receiveBroadcastStream().listen((event) => _onNewIntent(event as Map?));
|
||||
}
|
||||
|
||||
|
@ -155,16 +155,16 @@ class _AvesAppState extends State<AvesApp> {
|
|||
));
|
||||
}
|
||||
|
||||
void _onContentChange(String? uri) {
|
||||
void _onMediaStoreChange(String? uri) {
|
||||
if (uri != null) changedUris.add(uri);
|
||||
if (changedUris.isNotEmpty) {
|
||||
_contentChangeDebouncer(() async {
|
||||
_mediaStoreChangeDebouncer(() async {
|
||||
final todo = changedUris.toSet();
|
||||
changedUris.clear();
|
||||
final tempUris = await _mediaStoreSource.refreshUris(todo);
|
||||
if (tempUris.isNotEmpty) {
|
||||
changedUris.addAll(tempUris);
|
||||
_onContentChange(null);
|
||||
_onMediaStoreChange(null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ class ThumbnailsSection extends StatelessWidget {
|
|||
final currentShowThumbnailRaw = context.select<Settings, bool>((s) => s.showThumbnailRaw);
|
||||
final currentShowThumbnailVideoDuration = context.select<Settings, bool>((s) => s.showThumbnailVideoDuration);
|
||||
|
||||
final iconSize = IconTheme.of(context).size! * MediaQuery.of(context).textScaleFactor;
|
||||
final iconSize = IconTheme.of(context).size! * MediaQuery.textScaleFactorOf(context);
|
||||
double opacityFor(bool enabled) => enabled ? 1 : .2;
|
||||
|
||||
return AvesExpansionTile(
|
||||
|
|
|
@ -37,6 +37,7 @@ class ViewerActionEditorPage extends StatelessWidget {
|
|||
EntryAction.rename,
|
||||
EntryAction.export,
|
||||
EntryAction.print,
|
||||
EntryAction.rotateScreen,
|
||||
EntryAction.viewSource,
|
||||
EntryAction.flip,
|
||||
EntryAction.rotateCCW,
|
||||
|
|
|
@ -12,6 +12,7 @@ import 'package:aves/ref/mime_types.dart';
|
|||
import 'package:aves/services/android_app_service.dart';
|
||||
import 'package:aves/services/image_op_events.dart';
|
||||
import 'package:aves/services/services.dart';
|
||||
import 'package:aves/services/window_service.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/utils/pedantic.dart';
|
||||
import 'package:aves/widgets/collection/collection_page.dart';
|
||||
|
@ -84,6 +85,9 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
|||
if (!success) showNoMatchingAppDialog(context);
|
||||
});
|
||||
break;
|
||||
case EntryAction.rotateScreen:
|
||||
_rotateScreen(context);
|
||||
break;
|
||||
case EntryAction.setAs:
|
||||
AndroidAppService.setAs(entry.uri, entry.mimeType).then((success) {
|
||||
if (!success) showNoMatchingAppDialog(context);
|
||||
|
@ -117,6 +121,17 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
|||
if (!success) showFeedback(context, context.l10n.genericFailureFeedback);
|
||||
}
|
||||
|
||||
Future<void> _rotateScreen(BuildContext context) async {
|
||||
switch (context.read<MediaQueryData>().orientation) {
|
||||
case Orientation.landscape:
|
||||
await WindowService.requestOrientation(Orientation.portrait);
|
||||
break;
|
||||
case Orientation.portrait:
|
||||
await WindowService.requestOrientation(Orientation.landscape);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _showDeleteDialog(BuildContext context, AvesEntry entry) async {
|
||||
final confirmed = await showDialog<bool>(
|
||||
context: context,
|
||||
|
|
|
@ -511,6 +511,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with SingleTickerPr
|
|||
|
||||
void _onLeave() {
|
||||
_showSystemUI();
|
||||
WindowService.requestOrientation();
|
||||
if (settings.keepScreenOn == KeepScreenOn.viewerOnly) {
|
||||
WindowService.keepScreenOn(false);
|
||||
}
|
||||
|
|
|
@ -99,6 +99,8 @@ class ViewerTopOverlay extends StatelessWidget {
|
|||
return targetEntry.hasGps;
|
||||
case EntryAction.viewSource:
|
||||
return targetEntry.isSvg;
|
||||
case EntryAction.rotateScreen:
|
||||
return settings.isRotationLocked;
|
||||
case EntryAction.share:
|
||||
case EntryAction.info:
|
||||
case EntryAction.open:
|
||||
|
@ -110,17 +112,22 @@ class ViewerTopOverlay extends StatelessWidget {
|
|||
}
|
||||
}
|
||||
|
||||
final quickActions = settings.viewerQuickActions.where(_canDo).take(availableCount - 1).toList();
|
||||
final inAppActions = EntryActions.inApp.where((action) => !quickActions.contains(action)).where(_canDo).toList();
|
||||
final externalAppActions = EntryActions.externalApp.where(_canDo).toList();
|
||||
final buttonRow = _TopOverlayRow(
|
||||
quickActions: quickActions,
|
||||
inAppActions: inAppActions,
|
||||
externalAppActions: externalAppActions,
|
||||
scale: scale,
|
||||
mainEntry: mainEntry,
|
||||
pageEntry: pageEntry,
|
||||
onActionSelected: onActionSelected,
|
||||
final buttonRow = Selector<Settings, bool>(
|
||||
selector: (context, s) => s.isRotationLocked,
|
||||
builder: (context, s, child) {
|
||||
final quickActions = settings.viewerQuickActions.where(_canDo).take(availableCount - 1).toList();
|
||||
final inAppActions = EntryActions.inApp.where((action) => !quickActions.contains(action)).where(_canDo).toList();
|
||||
final externalAppActions = EntryActions.externalApp.where(_canDo).toList();
|
||||
return _TopOverlayRow(
|
||||
quickActions: quickActions,
|
||||
inAppActions: inAppActions,
|
||||
externalAppActions: externalAppActions,
|
||||
scale: scale,
|
||||
mainEntry: mainEntry,
|
||||
pageEntry: pageEntry!,
|
||||
onActionSelected: onActionSelected,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
return settings.showOverlayMinimap && viewStateNotifier != null
|
||||
|
@ -212,6 +219,7 @@ class _TopOverlayRow extends StatelessWidget {
|
|||
case EntryAction.rotateCCW:
|
||||
case EntryAction.rotateCW:
|
||||
case EntryAction.share:
|
||||
case EntryAction.rotateScreen:
|
||||
case EntryAction.viewSource:
|
||||
child = IconButton(
|
||||
icon: Icon(action.getIcon()),
|
||||
|
@ -256,6 +264,7 @@ class _TopOverlayRow extends StatelessWidget {
|
|||
case EntryAction.rotateCCW:
|
||||
case EntryAction.rotateCW:
|
||||
case EntryAction.share:
|
||||
case EntryAction.rotateScreen:
|
||||
case EntryAction.viewSource:
|
||||
case EntryAction.debug:
|
||||
child = MenuRow(text: action.getText(context), icon: action.getIcon());
|
||||
|
|
Loading…
Reference in a new issue