diff --git a/lib/main.dart b/lib/main.dart index 3353fc7..8507f5f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -102,14 +102,16 @@ part 'entities/alarm_control_panel/widgets/alarm_control_panel_controls.widget.d part 'entities/vacuum/vacuum_entity.class.dart'; part 'entities/vacuum/widgets/vacuum_controls.dart'; part 'entities/vacuum/widgets/vacuum_state_button.dart'; -part 'pages/settings.page.dart'; +part 'pages/settings/connection_settings.part.dart'; part 'pages/purchase.page.dart'; part 'pages/widgets/product_purchase.widget.dart'; part 'pages/widgets/page_loading_indicator.dart'; part 'pages/widgets/page_loading_error.dart'; part 'pages/panel.page.dart'; part 'pages/main/main.page.dart'; -part 'pages/integration_settings.page.dart'; +part 'pages/settings/integration_settings.part.dart'; +part 'pages/settings/app_settings.page.dart'; +part 'pages/settings/lookandfeel_settings.part.dart'; part 'pages/zha_page.dart'; part 'home_assistant.class.dart'; part 'pages/log.page.dart'; @@ -173,8 +175,14 @@ void main() async { Crashlytics.instance.recordFlutterError(details); }; + WidgetsFlutterBinding.ensureInitialized(); + SharedPreferences prefs = await SharedPreferences.getInstance(); + AppTheme theme = AppTheme.values[prefs.getInt('app-theme') ?? AppTheme.defaultTheme]; + runZoned(() { - runApp(new HAClientApp()); + runApp(new HAClientApp( + theme: theme, + )); }, onError: (error, stack) { _reportError(error, stack); }); @@ -182,22 +190,34 @@ void main() async { class HAClientApp extends StatefulWidget { + final AppTheme theme; + + const HAClientApp({Key key, this.theme: AppTheme.defaultTheme}) : super(key: key); + @override _HAClientAppState createState() => new _HAClientAppState(); } class _HAClientAppState extends State { - StreamSubscription> _subscription; + StreamSubscription> _purchaseUpdateSubscription; + StreamSubscription _themeChangeSubscription; + AppTheme _currentTheme = AppTheme.defaultTheme; @override void initState() { InAppPurchaseConnection.enablePendingPurchases(); final Stream purchaseUpdates = InAppPurchaseConnection.instance.purchaseUpdatedStream; - _subscription = purchaseUpdates.listen((purchases) { + _purchaseUpdateSubscription = purchaseUpdates.listen((purchases) { _handlePurchaseUpdates(purchases); }); + _currentTheme = widget.theme; + _themeChangeSubscription = eventBus.on().listen((event){ + setState(() { + _currentTheme = event.theme; + }); + }); workManager.Workmanager.initialize( updateDeviceLocationIsolate, isInDebugMode: false @@ -226,14 +246,15 @@ class _HAClientAppState extends State { Widget build(BuildContext context) { return new MaterialApp( title: appName, - theme: HAClientTheme().lightTheme, + theme: HAClientTheme().getThemeData(_currentTheme), darkTheme: HAClientTheme().darkTheme, debugShowCheckedModeBanner: false, initialRoute: "/", routes: { "/": (context) => MainPage(title: 'HA Client'), - "/connection-settings": (context) => ConnectionSettingsPage(title: "Settings"), - "/integration-settings": (context) => IntegrationSettingsPage(title: "Integration settings"), + "/app-settings": (context) => AppSettingsPage(), + "/connection-settings": (context) => AppSettingsPage(showSection: AppSettingsSection.connectionSettings), + "/integration-settings": (context) => AppSettingsPage(showSection: AppSettingsSection.integrationSettings), "/putchase": (context) => PurchasePage(title: "Support app development"), "/play-media": (context) => PlayMediaPage( mediaUrl: "${ModalRoute.of(context).settings.arguments != null ? (ModalRoute.of(context).settings.arguments as Map)['url'] : ''}", @@ -278,7 +299,8 @@ class _HAClientAppState extends State { @override void dispose() { - _subscription.cancel(); + _purchaseUpdateSubscription.cancel(); + _themeChangeSubscription.cancel(); super.dispose(); } } diff --git a/lib/managers/theme_manager.dart b/lib/managers/theme_manager.dart index 2538e0d..10ec0a3 100644 --- a/lib/managers/theme_manager.dart +++ b/lib/managers/theme_manager.dart @@ -1,10 +1,8 @@ part of '../main.dart'; -class HAClientTheme { +enum AppTheme {darkTheme, defaultTheme, haTheme} - static const DEFAULT = 0; - static const DARK = 1; - static const HOMEASSISTANT = 2; +class HAClientTheme { static const TextTheme textTheme = TextTheme( display1: TextStyle(fontSize: 34, fontWeight: FontWeight.normal), @@ -80,6 +78,22 @@ class HAClientTheme { HAClientTheme._internal(); + ThemeData getThemeData(AppTheme theme) { + switch (theme) { + case AppTheme.darkTheme: + return darkTheme; + break; + case AppTheme.defaultTheme: + return lightTheme; + break; + case AppTheme.haTheme: + return lightTheme; + break; + default: + return lightTheme; + } + } + final ThemeData lightTheme = ThemeData.from( colorScheme: ColorScheme( primary: Color.fromRGBO(112, 154, 193, 1), diff --git a/lib/pages/integration_settings.page.dart b/lib/pages/integration_settings.page.dart deleted file mode 100644 index 6817776..0000000 --- a/lib/pages/integration_settings.page.dart +++ /dev/null @@ -1,202 +0,0 @@ -part of '../main.dart'; - -class IntegrationSettingsPage extends StatefulWidget { - IntegrationSettingsPage({Key key, this.title}) : super(key: key); - - final String title; - - @override - _IntegrationSettingsPageState createState() => new _IntegrationSettingsPageState(); -} - -class _IntegrationSettingsPageState extends State { - - int _locationInterval = LocationManager().defaultUpdateIntervalMinutes; - bool _locationTrackingEnabled = false; - bool _wait = false; - - @override - void initState() { - super.initState(); - _loadSettings(); - - } - - _loadSettings() async { - SharedPreferences prefs = await SharedPreferences.getInstance(); - await prefs.reload(); - SharedPreferences.getInstance().then((prefs) { - setState(() { - _locationTrackingEnabled = prefs.getBool("location-enabled") ?? false; - _locationInterval = prefs.getInt("location-interval") ?? LocationManager().defaultUpdateIntervalMinutes; - if (_locationInterval % 5 != 0) { - _locationInterval = 5 * (_locationInterval ~/ 5); - } - }); - }); - } - - void incLocationInterval() { - if (_locationInterval < 720) { - setState(() { - _locationInterval = _locationInterval + 5; - }); - } - } - - void decLocationInterval() { - if (_locationInterval > 5) { - setState(() { - _locationInterval = _locationInterval - 5; - }); - } - } - - restart() { - eventBus.fire(ShowPopupDialogEvent( - title: "Are you sure you want to restart Home Assistant?", - body: "This will restart your Home Assistant server.", - positiveText: "Sure. Make it so", - negativeText: "What?? No!", - onPositive: () { - ConnectionManager().callService(domain: "homeassistant", service: "restart"); - }, - )); - } - - stop() { - eventBus.fire(ShowPopupDialogEvent( - title: "Are you sure you want to STOP Home Assistant?", - body: "This will STOP your Home Assistant server. It means that your web interface as well as HA Client will not work untill you'll find a way to start your server using ssh or something.", - positiveText: "Sure. Make it so", - negativeText: "What?? No!", - onPositive: () { - ConnectionManager().callService(domain: "homeassistant", service: "stop"); - }, - )); - } - - updateRegistration() { - MobileAppIntegrationManager.checkAppRegistration(showOkDialog: true); - } - - resetRegistration() { - eventBus.fire(ShowPopupDialogEvent( - title: "Waaaait", - body: "If you don't whant to have duplicate integrations and entities in your HA for your current device, first you need to remove MobileApp integration from Integration settings in HA and restart server.", - positiveText: "Done it already", - negativeText: "Ok, I will", - onPositive: () { - MobileAppIntegrationManager.checkAppRegistration(showOkDialog: true, forceRegister: true); - }, - )); - } - - _switchLocationTrackingState(bool state) async { - if (state) { - await LocationManager().updateDeviceLocation(); - } - await LocationManager().setSettings(_locationTrackingEnabled, _locationInterval); - setState(() { - _wait = false; - }); - } - - @override - Widget build(BuildContext context) { - return new Scaffold( - appBar: new AppBar( - leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: (){ - Navigator.pop(context); - }), - title: new Text(widget.title), - ), - body: ListView( - scrollDirection: Axis.vertical, - padding: const EdgeInsets.all(20.0), - children: [ - Text("Location tracking", style: Theme.of(context).textTheme.title), - Container(height: Sizes.rowPadding,), - InkWell( - onTap: () => Launcher.launchURLInCustomTab(context: context, url: "http://ha-client.app/docs#location-tracking"), - child: Text( - "Please read documentation!", - style: Theme.of(context).textTheme.subhead.copyWith( - color: Colors.blue, - decoration: TextDecoration.underline - ) - ), - ), - Container(height: Sizes.rowPadding,), - Row( - children: [ - Text("Enable device location tracking"), - Switch( - value: _locationTrackingEnabled, - onChanged: _wait ? null : (value) { - setState(() { - _locationTrackingEnabled = value; - _wait = true; - }); - _switchLocationTrackingState(value); - }, - ), - ], - ), - Container(height: Sizes.rowPadding,), - Text("Location update interval in minutes:"), - Row( - mainAxisAlignment: MainAxisAlignment.center, - mainAxisSize: MainAxisSize.max, - children: [ - //Expanded(child: Container(),), - FlatButton( - padding: EdgeInsets.all(0.0), - child: Text("-", style: Theme.of(context).textTheme.title), - onPressed: () => decLocationInterval(), - ), - Text("$_locationInterval", style: Theme.of(context).textTheme.title), - FlatButton( - padding: EdgeInsets.all(0.0), - child: Text("+", style: Theme.of(context).textTheme.title), - onPressed: () => incLocationInterval(), - ), - ], - ), - Divider(), - Text("Integration status", style: Theme.of(context).textTheme.title), - Container(height: Sizes.rowPadding,), - Text( - "${HomeAssistant().userName}'s ${DeviceInfoManager().model}, ${DeviceInfoManager().osName} ${DeviceInfoManager().osVersion}", - style: Theme.of(context).textTheme.subtitle, - ), - Container(height: 6.0,), - Text("Here you can manually check if HA Client integration with your Home Assistant works fine. As mobileApp integration in Home Assistant is still in development, this is not 100% correct check."), - //Divider(), - Row( - mainAxisSize: MainAxisSize.max, - children: [ - RaisedButton( - color: Colors.blue, - onPressed: () => updateRegistration(), - child: Text("Check integration", style: Theme.of(context).textTheme.button) - ), - Container(width: 10.0,), - RaisedButton( - color: Colors.redAccent, - onPressed: () => resetRegistration(), - child: Text("Reset integration", style: Theme.of(context).textTheme.button) - ) - ], - ), - ] - ), - ); - } - - @override - void dispose() { - LocationManager().setSettings(_locationTrackingEnabled, _locationInterval); - super.dispose(); - } -} diff --git a/lib/pages/main/main.page.dart b/lib/pages/main/main.page.dart index a86d706..5ebdd38 100644 --- a/lib/pages/main/main.page.dart +++ b/lib/pages/main/main.page.dart @@ -366,20 +366,12 @@ class _MainPageState extends State with WidgetsBindingObserver, Ticker } menuItems.addAll([ Divider(), - ListTile( - leading: Icon(MaterialDesignIcons.getIconDataFromIconName("mdi:server-network")), - title: Text("Connection settings"), - onTap: () { - Navigator.of(context).pop(); - Navigator.of(context).pushNamed('/connection-settings'); - }, - ), ListTile( leading: Icon(MaterialDesignIcons.getIconDataFromIconName("mdi:cellphone-settings-variant")), - title: Text("Integration settings"), + title: Text("App settings"), onTap: () { Navigator.of(context).pop(); - Navigator.of(context).pushNamed('/integration-settings'); + Navigator.of(context).pushNamed('/app-settings'); }, ) ]); diff --git a/lib/pages/settings/app_settings.page.dart b/lib/pages/settings/app_settings.page.dart new file mode 100644 index 0000000..716c954 --- /dev/null +++ b/lib/pages/settings/app_settings.page.dart @@ -0,0 +1,104 @@ +part of '../../main.dart'; + +enum AppSettingsSection {menu, connectionSettings, integrationSettings, lookAndFeel} + +class AppSettingsPage extends StatefulWidget { + final AppSettingsSection showSection; + + AppSettingsPage({Key key, this.showSection: AppSettingsSection.menu}) : super(key: key); + + @override + _AppSettingsPageState createState() => new _AppSettingsPageState(); +} + +class _AppSettingsPageState extends State { + + var _currentSection; + + @override + void initState() { + super.initState(); + _currentSection = widget.showSection; + } + + Widget _buildMenuItem(BuildContext context, IconData icon,String title, AppSettingsSection section) { + return ListTile( + title: Text(title, style: Theme.of(context).textTheme.subhead), + leading: Icon(icon), + trailing: Icon(Icons.keyboard_arrow_right), + onTap: () { + setState(() { + _currentSection = section; + }); + }, + ); + } + + Widget _buildMenu(BuildContext context) { + return ListView( + children: [ + _buildMenuItem(context, MaterialDesignIcons.getIconDataFromIconName('mdi:network'), 'Connection settings', AppSettingsSection.connectionSettings), + _buildMenuItem(context, MaterialDesignIcons.getIconDataFromIconName('mdi:cellphone-android'), 'Integration settings', AppSettingsSection.integrationSettings), + _buildMenuItem(context, MaterialDesignIcons.getIconDataFromIconName('mdi:brush'), 'Look and feel', AppSettingsSection.lookAndFeel), + ], + ); + } + + @override + Widget build(BuildContext context) { + Widget section; + String title; + switch (_currentSection) { + case AppSettingsSection.menu: { + section = _buildMenu(context); + title = 'App settings'; + break; + } + case AppSettingsSection.connectionSettings: { + section = ConnectionSettingsPage(); + title = 'App settings - Connection'; + break; + } + case AppSettingsSection.integrationSettings: { + section = IntegrationSettingsPage(); + title = 'App settings - Integration'; + break; + } + case AppSettingsSection.lookAndFeel: { + section = LookAndFeelSettingsPage(); + title = 'App settings - Look&Feel'; + break; + } + default: + title = ':('; + section = PageLoadingIndicator(); + } + return WillPopScope( + child: Scaffold( + appBar: new AppBar( + leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: (){ + if (_currentSection == AppSettingsSection.menu) { + Navigator.pop(context); + } else { + setState(() { + _currentSection = AppSettingsSection.menu; + }); + } + }), + title: Text(title), + ), + body: section + ), + onWillPop: () { + if (_currentSection == AppSettingsSection.menu) { + return Future.value(true); + } else { + setState(() { + _currentSection = AppSettingsSection.menu; + }); + return Future.value(false); + } + }, + ); + } +} \ No newline at end of file diff --git a/lib/pages/settings.page.dart b/lib/pages/settings/connection_settings.part.dart similarity index 53% rename from lib/pages/settings.page.dart rename to lib/pages/settings/connection_settings.part.dart index 5aafa77..8fa7a7f 100644 --- a/lib/pages/settings.page.dart +++ b/lib/pages/settings/connection_settings.part.dart @@ -1,4 +1,4 @@ -part of '../main.dart'; +part of '../../main.dart'; class ConnectionSettingsPage extends StatefulWidget { ConnectionSettingsPage({Key key, this.title}) : super(key: key); @@ -108,115 +108,109 @@ class _ConnectionSettingsPageState extends State { @override Widget build(BuildContext context) { - return new Scaffold( - appBar: new AppBar( - leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: (){ - Navigator.pop(context); - }), - title: new Text(widget.title), - actions: [ - IconButton( - icon: Icon(Icons.check), - onPressed: (){ - if (_checkConfigChanged()) { - Logger.d("Settings changed. Saving..."); - _saveSettings().then((r) { - Navigator.pop(context); - eventBus.fire(SettingsChangedEvent(true)); + return ListView( + scrollDirection: Axis.vertical, + padding: const EdgeInsets.all(20.0), + children: [ + Text( + "Connection settings", + style: Theme.of(context).textTheme.headline, + ), + new Row( + children: [ + Text("Use ssl (HTTPS)"), + Switch( + value: (_newSocketProtocol == "wss"), + onChanged: (value) { + setState(() { + _newSocketProtocol = value ? "wss" : "ws"; }); - } else { - Logger.d("Settings was not changed"); - Navigator.pop(context); - } - } - ) - ], - ), - body: ListView( - scrollDirection: Axis.vertical, - padding: const EdgeInsets.all(20.0), - children: [ - Text( - "Connection settings", - style: Theme.of(context).textTheme.headline, + }, + ) + ], + ), + new TextField( + decoration: InputDecoration( + labelText: "Home Assistant domain or ip address" ), - new Row( - children: [ - Text("Use ssl (HTTPS)"), - Switch( - value: (_newSocketProtocol == "wss"), - onChanged: (value) { - setState(() { - _newSocketProtocol = value ? "wss" : "ws"; - }); - }, - ) - ], + controller: TextEditingController.fromValue(TextEditingValue(text: _newHassioDomain)), + onChanged: (value) { + _newHassioDomain = value; + } + ), + new TextField( + decoration: InputDecoration( + labelText: "Home Assistant port (default is 8123)" ), - new TextField( - decoration: InputDecoration( - labelText: "Home Assistant domain or ip address" - ), - controller: TextEditingController.fromValue(TextEditingValue(text: _newHassioDomain)), - onChanged: (value) { - _newHassioDomain = value; - } - ), - new TextField( - decoration: InputDecoration( - labelText: "Home Assistant port (default is 8123)" - ), - controller: TextEditingController.fromValue(TextEditingValue(text: _newHassioPort)), - onChanged: (value) { - _newHassioPort = value; - } - ), - new Text( - "Try ports 80 and 443 if default is not working and you don't know why.", - style: Theme.of(context).textTheme.caption, - ), - Padding( - padding: EdgeInsets.only(top: 20.0), - child: Text( - "UI", - style: Theme.of(context).textTheme.headline, - ), - ), - new Row( - children: [ - Text("Use Lovelace UI"), - Switch( - value: _newUseLovelace, - onChanged: (value) { - setState(() { - _newUseLovelace = value; - }); - }, - ) - ], - ), - Text( - "Authentication settings", + controller: TextEditingController.fromValue(TextEditingValue(text: _newHassioPort)), + onChanged: (value) { + _newHassioPort = value; + } + ), + new Text( + "Try ports 80 and 443 if default is not working and you don't know why.", + style: Theme.of(context).textTheme.caption, + ), + Padding( + padding: EdgeInsets.only(top: 20.0), + child: Text( + "UI", style: Theme.of(context).textTheme.headline, ), - Container(height: 10.0,), - Text( - "You can leave this field blank to make app generate new long-lived token automatically by asking you to login to your Home Assistant. Use this field only if you still want to use manually generated long-lived token. Leave it blank if you don't understand what we are talking about.", - style: Theme.of(context).textTheme.body1.copyWith( - color: Colors.redAccent - ), - ), - new TextField( - decoration: InputDecoration( - labelText: "Long-lived token" - ), - controller: TextEditingController.fromValue(TextEditingValue(text: _newLongLivedToken)), + ), + new Row( + children: [ + Text("Use Lovelace UI"), + Switch( + value: _newUseLovelace, onChanged: (value) { - _newLongLivedToken = value; - } + setState(() { + _newUseLovelace = value; + }); + }, + ) + ], + ), + Text( + "Authentication settings", + style: Theme.of(context).textTheme.headline, + ), + Container(height: 10.0,), + Text( + "You can leave this field blank to make app generate new long-lived token automatically by asking you to login to your Home Assistant. Use this field only if you still want to use manually generated long-lived token. Leave it blank if you don't understand what we are talking about.", + style: Theme.of(context).textTheme.body1.copyWith( + color: Colors.redAccent ), - ], - ), + ), + new TextField( + decoration: InputDecoration( + labelText: "Long-lived token" + ), + controller: TextEditingController.fromValue(TextEditingValue(text: _newLongLivedToken)), + onChanged: (value) { + _newLongLivedToken = value; + } + ), + Container( + height: Sizes.rowPadding, + ), + RaisedButton( + child: Text('Apply', style: Theme.of(context).textTheme.button), + color: Theme.of(context).primaryColorDark, + onPressed: () { + if (_checkConfigChanged()) { + Logger.d("Settings changed. Saving..."); + _saveSettings().then((r) { + Navigator.pop(context); + eventBus.fire(SettingsChangedEvent(true)); + }); + } else { + Logger.d("Settings was not changed"); + Navigator.pop(context); + } + }, + ) + ], ); } diff --git a/lib/pages/settings/integration_settings.part.dart b/lib/pages/settings/integration_settings.part.dart new file mode 100644 index 0000000..a3ad8b6 --- /dev/null +++ b/lib/pages/settings/integration_settings.part.dart @@ -0,0 +1,194 @@ +part of '../../main.dart'; + +class IntegrationSettingsPage extends StatefulWidget { + IntegrationSettingsPage({Key key, this.title}) : super(key: key); + + final String title; + + @override + _IntegrationSettingsPageState createState() => new _IntegrationSettingsPageState(); +} + +class _IntegrationSettingsPageState extends State { + + int _locationInterval = LocationManager().defaultUpdateIntervalMinutes; + bool _locationTrackingEnabled = false; + bool _wait = false; + + @override + void initState() { + super.initState(); + _loadSettings(); + + } + + _loadSettings() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.reload(); + SharedPreferences.getInstance().then((prefs) { + setState(() { + _locationTrackingEnabled = prefs.getBool("location-enabled") ?? false; + _locationInterval = prefs.getInt("location-interval") ?? LocationManager().defaultUpdateIntervalMinutes; + if (_locationInterval % 5 != 0) { + _locationInterval = 5 * (_locationInterval ~/ 5); + } + }); + }); + } + + void incLocationInterval() { + if (_locationInterval < 720) { + setState(() { + _locationInterval = _locationInterval + 5; + }); + } + } + + void decLocationInterval() { + if (_locationInterval > 5) { + setState(() { + _locationInterval = _locationInterval - 5; + }); + } + } + + restart() { + eventBus.fire(ShowPopupDialogEvent( + title: "Are you sure you want to restart Home Assistant?", + body: "This will restart your Home Assistant server.", + positiveText: "Sure. Make it so", + negativeText: "What?? No!", + onPositive: () { + ConnectionManager().callService(domain: "homeassistant", service: "restart"); + }, + )); + } + + stop() { + eventBus.fire(ShowPopupDialogEvent( + title: "Are you sure you want to STOP Home Assistant?", + body: "This will STOP your Home Assistant server. It means that your web interface as well as HA Client will not work untill you'll find a way to start your server using ssh or something.", + positiveText: "Sure. Make it so", + negativeText: "What?? No!", + onPositive: () { + ConnectionManager().callService(domain: "homeassistant", service: "stop"); + }, + )); + } + + updateRegistration() { + MobileAppIntegrationManager.checkAppRegistration(showOkDialog: true); + } + + resetRegistration() { + eventBus.fire(ShowPopupDialogEvent( + title: "Waaaait", + body: "If you don't whant to have duplicate integrations and entities in your HA for your current device, first you need to remove MobileApp integration from Integration settings in HA and restart server.", + positiveText: "Done it already", + negativeText: "Ok, I will", + onPositive: () { + MobileAppIntegrationManager.checkAppRegistration(showOkDialog: true, forceRegister: true); + }, + )); + } + + _switchLocationTrackingState(bool state) async { + if (state) { + await LocationManager().updateDeviceLocation(); + } + await LocationManager().setSettings(_locationTrackingEnabled, _locationInterval); + setState(() { + _wait = false; + }); + } + + @override + Widget build(BuildContext context) { + return ListView( + scrollDirection: Axis.vertical, + padding: const EdgeInsets.all(20.0), + children: [ + Text("Location tracking", style: Theme.of(context).textTheme.title), + Container(height: Sizes.rowPadding,), + InkWell( + onTap: () => Launcher.launchURLInCustomTab(context: context, url: "http://ha-client.app/docs#location-tracking"), + child: Text( + "Please read documentation!", + style: Theme.of(context).textTheme.subhead.copyWith( + color: Colors.blue, + decoration: TextDecoration.underline + ) + ), + ), + Container(height: Sizes.rowPadding,), + Row( + children: [ + Text("Enable device location tracking"), + Switch( + value: _locationTrackingEnabled, + onChanged: _wait ? null : (value) { + setState(() { + _locationTrackingEnabled = value; + _wait = true; + }); + _switchLocationTrackingState(value); + }, + ), + ], + ), + Container(height: Sizes.rowPadding,), + Text("Location update interval in minutes:"), + Row( + mainAxisAlignment: MainAxisAlignment.center, + mainAxisSize: MainAxisSize.max, + children: [ + //Expanded(child: Container(),), + FlatButton( + padding: EdgeInsets.all(0.0), + child: Text("-", style: Theme.of(context).textTheme.title), + onPressed: () => decLocationInterval(), + ), + Text("$_locationInterval", style: Theme.of(context).textTheme.title), + FlatButton( + padding: EdgeInsets.all(0.0), + child: Text("+", style: Theme.of(context).textTheme.title), + onPressed: () => incLocationInterval(), + ), + ], + ), + Divider(), + Text("Integration status", style: Theme.of(context).textTheme.title), + Container(height: Sizes.rowPadding,), + Text( + "${HomeAssistant().userName}'s ${DeviceInfoManager().model}, ${DeviceInfoManager().osName} ${DeviceInfoManager().osVersion}", + style: Theme.of(context).textTheme.subtitle, + ), + Container(height: 6.0,), + Text("Here you can manually check if HA Client integration with your Home Assistant works fine. As mobileApp integration in Home Assistant is still in development, this is not 100% correct check."), + //Divider(), + Row( + mainAxisSize: MainAxisSize.max, + children: [ + RaisedButton( + color: Colors.blue, + onPressed: () => updateRegistration(), + child: Text("Check integration", style: Theme.of(context).textTheme.button) + ), + Container(width: 10.0,), + RaisedButton( + color: Colors.redAccent, + onPressed: () => resetRegistration(), + child: Text("Reset integration", style: Theme.of(context).textTheme.button) + ) + ], + ), + ] + ); + } + + @override + void dispose() { + LocationManager().setSettings(_locationTrackingEnabled, _locationInterval); + super.dispose(); + } +} diff --git a/lib/pages/settings/lookandfeel_settings.part.dart b/lib/pages/settings/lookandfeel_settings.part.dart new file mode 100644 index 0000000..a9e0f03 --- /dev/null +++ b/lib/pages/settings/lookandfeel_settings.part.dart @@ -0,0 +1,79 @@ +part of '../../main.dart'; + +class LookAndFeelSettingsPage extends StatefulWidget { + LookAndFeelSettingsPage({Key key, this.title}) : super(key: key); + + final String title; + + @override + _LookAndFeelSettingsPageState createState() => new _LookAndFeelSettingsPageState(); +} + +class _LookAndFeelSettingsPageState extends State { + + AppTheme _currentTheme; + bool _changed = false; + + @override + void initState() { + super.initState(); + _loadSettings(); + } + + _loadSettings() async { + SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.reload(); + SharedPreferences.getInstance().then((prefs) { + setState(() { + _currentTheme = AppTheme.values[prefs.getInt("app-theme") ?? AppTheme.defaultTheme]; + }); + }); + } + + _saveSettings(AppTheme theme) { + SharedPreferences.getInstance().then((prefs) { + prefs.setInt('app-theme', theme.index); + setState(() { + _currentTheme = theme; + eventBus.fire(ChangeThemeEvent(_currentTheme)); + }); + }); + } + + Map appThemeName = { + AppTheme.defaultTheme: 'Default', + AppTheme.haTheme: 'Home Assistant theme', + AppTheme.darkTheme: 'Dark theme' + }; + + @override + Widget build(BuildContext context) { + return ListView( + scrollDirection: Axis.vertical, + padding: const EdgeInsets.all(20.0), + children: [ + Text("Application scheme:", style: Theme.of(context).textTheme.body2), + Container(height: Sizes.rowPadding), + DropdownButton( + value: _currentTheme, + iconSize: 30.0, + isExpanded: true, + style: Theme.of(context).textTheme.title, + //hint: Text("Select ${caption.toLowerCase()}"), + items: AppTheme.values.map((value) { + return new DropdownMenuItem( + value: value, + child: Text('${appThemeName[value]}'), + ); + }).toList(), + onChanged: (theme) => _saveSettings(theme), + ) + ] + ); + } + + @override + void dispose() { + super.dispose(); + } +} diff --git a/lib/types/event_bus_events.dart b/lib/types/event_bus_events.dart index ae29238..df0acc2 100644 --- a/lib/types/event_bus_events.dart +++ b/lib/types/event_bus_events.dart @@ -28,6 +28,13 @@ class ReloadUIEvent { ReloadUIEvent(); } +class ChangeThemeEvent { + + final AppTheme theme; + + ChangeThemeEvent(this.theme); +} + class StartAuthEvent { String oauthUrl; bool showButton;