From 71bf3ad0490984041db3b34f6d48dc9b6c8501b9 Mon Sep 17 00:00:00 2001 From: Oliver Date: Tue, 13 Jun 2023 19:53:50 +1000 Subject: [PATCH] App orientation (#369) * Configurable screen orientation - Follow system (default) - Fixed in portrait - Fixed in landscape * Fix for dialog --- assets/release_notes.md | 2 + lib/l10n/app_en.arb | 15 ++++ lib/main.dart | 26 ++++++- lib/preferences.dart | 7 ++ lib/settings/app_settings.dart | 130 +++++++++++++++++++++++---------- lib/widget/dialogs.dart | 45 ++++++++++++ 6 files changed, 185 insertions(+), 40 deletions(-) diff --git a/assets/release_notes.md b/assets/release_notes.md index 3aa2c2b4..2a843ce8 100644 --- a/assets/release_notes.md +++ b/assets/release_notes.md @@ -1,5 +1,7 @@ ### - --- + +- Adds options for configuring screen orientation - Improvements to barcode scanning - Translation updates diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 7116851a..bdda8dd8 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -627,6 +627,21 @@ "onOrderDetails": "Items currently on order", "@onOrderDetails": {}, + "orientation": "Screen Orientation", + "@orientation": {}, + + "orientationDetail": "Screen orientation (requires restart)", + "@orientationDetail": {}, + + "orientationLandscape": "Landscape", + "@orientationLandscape": {}, + + "orientationPortrait": "Portrait", + "@orientationPortrait": {}, + + "orientationSystem": "System", + "@orientationSystem": {}, + "outstanding": "Outstanding", "@outstanding": {}, diff --git a/lib/main.dart b/lib/main.dart index f41357af..08b0a305 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -55,9 +55,29 @@ Future main() async { ); }; - runApp( - InvenTreeApp(savedThemeMode) - ); + final int orientation = await InvenTreeSettingsManager().getValue(INV_SCREEN_ORIENTATION, SCREEN_ORIENTATION_SYSTEM) as int; + + List orientations = []; + + switch (orientation) { + case SCREEN_ORIENTATION_PORTRAIT: + orientations.add(DeviceOrientation.portraitUp); + break; + case SCREEN_ORIENTATION_LANDSCAPE: + orientations.add(DeviceOrientation.landscapeLeft); + break; + default: + orientations.add(DeviceOrientation.portraitUp); + orientations.add(DeviceOrientation.landscapeLeft); + orientations.add(DeviceOrientation.landscapeRight); + break; + } + + SystemChrome.setPreferredOrientations(orientations).then((_) { + runApp( + InvenTreeApp(savedThemeMode) + ); + }); }, (Object error, StackTrace stackTrace) async { sentryReportError("main.runZonedGuarded", error, stackTrace); diff --git a/lib/preferences.dart b/lib/preferences.dart index 23404dfc..694ad7ca 100644 --- a/lib/preferences.dart +++ b/lib/preferences.dart @@ -15,6 +15,13 @@ const String INV_HOME_SHOW_MANUFACTURERS = "homeShowManufacturers"; const String INV_HOME_SHOW_CUSTOMERS = "homeShowCustomers"; const String INV_HOME_SHOW_SUPPLIERS = "homeShowSuppliers"; +const String INV_SCREEN_ORIENTATION = "appScreenOrientation"; + +// Available screen orientation values +const int SCREEN_ORIENTATION_SYSTEM = 0; +const int SCREEN_ORIENTATION_PORTRAIT = 1; +const int SCREEN_ORIENTATION_LANDSCAPE = 2; + const String INV_SOUNDS_BARCODE = "barcodeSounds"; const String INV_SOUNDS_SERVER = "serverSounds"; diff --git a/lib/settings/app_settings.dart b/lib/settings/app_settings.dart index 42c94a00..00c27a7d 100644 --- a/lib/settings/app_settings.dart +++ b/lib/settings/app_settings.dart @@ -3,6 +3,8 @@ import "package:flutter/material.dart"; import "package:adaptive_theme/adaptive_theme.dart"; import "package:font_awesome_flutter/font_awesome_flutter.dart"; import "package:flutter_localized_locales/flutter_localized_locales.dart"; +import "package:inventree/app_colors.dart"; +import "package:inventree/widget/dialogs.dart"; import "package:one_context/one_context.dart"; import "package:inventree/api_form.dart"; @@ -34,6 +36,8 @@ class _InvenTreeAppSettingsState extends State { bool darkMode = false; + int screenOrientation = SCREEN_ORIENTATION_SYSTEM; + Locale? locale; @override @@ -51,6 +55,7 @@ class _InvenTreeAppSettingsState extends State { serverSounds = await InvenTreeSettingsManager().getValue(INV_SOUNDS_SERVER, true) as bool; reportErrors = await InvenTreeSettingsManager().getValue(INV_REPORT_ERRORS, true) as bool; strictHttps = await InvenTreeSettingsManager().getValue(INV_STRICT_HTTPS, false) as bool; + screenOrientation = await InvenTreeSettingsManager().getValue(INV_SCREEN_ORIENTATION, SCREEN_ORIENTATION_SYSTEM) as int; darkMode = AdaptiveTheme.of(context).mode.isDark; @@ -116,9 +121,9 @@ class _InvenTreeAppSettingsState extends State { InvenTreeApp.of(context)?.setLocale(locale); } ); - } + @override Widget build(BuildContext context) { @@ -128,6 +133,21 @@ class _InvenTreeAppSettingsState extends State { languageName = LocaleNames.of(context)!.nameOf(locale.toString()) ?? L10().languageDefault; } + IconData orientationIcon = Icons.screen_rotation; + + switch (screenOrientation) { + case SCREEN_ORIENTATION_PORTRAIT: + orientationIcon = Icons.screen_lock_portrait; + break; + case SCREEN_ORIENTATION_LANDSCAPE: + orientationIcon = Icons.screen_lock_landscape; + break; + case SCREEN_ORIENTATION_SYSTEM: + default: + orientationIcon = Icons.screen_rotation; + break; + } + return Scaffold( key: _settingsKey, appBar: AppBar( @@ -138,42 +158,6 @@ class _InvenTreeAppSettingsState extends State { children: [ /* Sound Settings */ Divider(height: 3), - ListTile( - title: Text( - L10().sounds, - style: TextStyle(fontWeight: FontWeight.bold), - ), - leading: FaIcon(FontAwesomeIcons.volumeHigh), - ), - ListTile( - title: Text(L10().serverError), - subtitle: Text(L10().soundOnServerError), - leading: FaIcon(FontAwesomeIcons.server), - trailing: Switch( - value: serverSounds, - onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_SOUNDS_SERVER, value); - setState(() { - serverSounds = value; - }); - }, - ), - ), - ListTile( - title: Text(L10().barcodeTones), - subtitle: Text(L10().soundOnBarcodeAction), - leading: Icon(Icons.qr_code), - trailing: Switch( - value: barcodeSounds, - onChanged: (bool value) { - InvenTreeSettingsManager().setValue(INV_SOUNDS_BARCODE, value); - setState(() { - barcodeSounds = value; - }); - }, - ), - ), - Divider(height: 1), ListTile( title: Text( L10().appSettings, @@ -199,6 +183,41 @@ class _InvenTreeAppSettingsState extends State { } ) ), + GestureDetector( + child: ListTile( + title: Text(L10().orientation), + subtitle: Text(L10().orientationDetail), + leading: Icon(Icons.screen_rotation_alt), + trailing: Icon(orientationIcon), + ), + onTap: () async { + choiceDialog( + L10().orientation, + [ + ListTile( + leading: Icon(Icons.screen_rotation, color: screenOrientation == SCREEN_ORIENTATION_SYSTEM ? COLOR_ACTION : null), + title: Text(L10().orientationSystem), + ), + ListTile( + leading: Icon(Icons.screen_lock_portrait, color: screenOrientation == SCREEN_ORIENTATION_PORTRAIT ? COLOR_ACTION : null), + title: Text(L10().orientationPortrait), + ), + ListTile( + leading: Icon(Icons.screen_lock_landscape, color: screenOrientation == SCREEN_ORIENTATION_LANDSCAPE ? COLOR_ACTION : null), + title: Text(L10().orientationLandscape), + ) + ], + onSelected: (idx) async { + screenOrientation = idx as int; + + InvenTreeSettingsManager().setValue(INV_SCREEN_ORIENTATION, screenOrientation); + + setState(() { + }); + } + ); + }, + ), ListTile( title: Text(L10().strictHttps), subtitle: Text(L10().strictHttpsDetails), @@ -235,6 +254,43 @@ class _InvenTreeAppSettingsState extends State { }, ), ), + ListTile( + title: Text( + L10().sounds, + style: TextStyle(fontWeight: FontWeight.bold), + ), + leading: FaIcon(FontAwesomeIcons.volumeHigh), + ), + Divider(), + ListTile( + title: Text(L10().serverError), + subtitle: Text(L10().soundOnServerError), + leading: FaIcon(FontAwesomeIcons.server), + trailing: Switch( + value: serverSounds, + onChanged: (bool value) { + InvenTreeSettingsManager().setValue(INV_SOUNDS_SERVER, value); + setState(() { + serverSounds = value; + }); + }, + ), + ), + ListTile( + title: Text(L10().barcodeTones), + subtitle: Text(L10().soundOnBarcodeAction), + leading: Icon(Icons.qr_code), + trailing: Switch( + value: barcodeSounds, + onChanged: (bool value) { + InvenTreeSettingsManager().setValue(INV_SOUNDS_BARCODE, value); + setState(() { + barcodeSounds = value; + }); + }, + ), + ), + Divider(height: 1), ] ) ) diff --git a/lib/widget/dialogs.dart b/lib/widget/dialogs.dart index 280c0e90..1c86ef2f 100644 --- a/lib/widget/dialogs.dart +++ b/lib/widget/dialogs.dart @@ -10,6 +10,51 @@ import "package:inventree/preferences.dart"; import "package:inventree/widget/snacks.dart"; +/* + * Launch a dialog allowing the user to select from a list of options + */ +Future choiceDialog(String title, List items, {Function? onSelected}) async { + + List choices = []; + + for (int idx = 0; idx < items.length; idx++) { + choices.add( + GestureDetector( + child: items[idx], + onTap: () { + Navigator.pop(OneContext().context!); + if (onSelected != null) { + onSelected(idx); + } + }, + ) + ); + } + + OneContext().showDialog( + builder: (BuildContext context) { + return AlertDialog( + title: Text(title), + content: SingleChildScrollView( + child: Column( + children: choices, + ) + ), + actions: [ + TextButton( + child: Text(L10().cancel), + onPressed: () { + Navigator.pop(OneContext().context!); + }, + ) + ], + ); + } + ); + +} + + /* * Display a "confirmation" dialog allowing the user to accept or reject an action */