Settings page and theme selection
This commit is contained in:
parent
68d14bd13d
commit
5f23e108a1
@ -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/vacuum_entity.class.dart';
|
||||||
part 'entities/vacuum/widgets/vacuum_controls.dart';
|
part 'entities/vacuum/widgets/vacuum_controls.dart';
|
||||||
part 'entities/vacuum/widgets/vacuum_state_button.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/purchase.page.dart';
|
||||||
part 'pages/widgets/product_purchase.widget.dart';
|
part 'pages/widgets/product_purchase.widget.dart';
|
||||||
part 'pages/widgets/page_loading_indicator.dart';
|
part 'pages/widgets/page_loading_indicator.dart';
|
||||||
part 'pages/widgets/page_loading_error.dart';
|
part 'pages/widgets/page_loading_error.dart';
|
||||||
part 'pages/panel.page.dart';
|
part 'pages/panel.page.dart';
|
||||||
part 'pages/main/main.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 'pages/zha_page.dart';
|
||||||
part 'home_assistant.class.dart';
|
part 'home_assistant.class.dart';
|
||||||
part 'pages/log.page.dart';
|
part 'pages/log.page.dart';
|
||||||
@ -173,8 +175,14 @@ void main() async {
|
|||||||
Crashlytics.instance.recordFlutterError(details);
|
Crashlytics.instance.recordFlutterError(details);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
SharedPreferences prefs = await SharedPreferences.getInstance();
|
||||||
|
AppTheme theme = AppTheme.values[prefs.getInt('app-theme') ?? AppTheme.defaultTheme];
|
||||||
|
|
||||||
runZoned(() {
|
runZoned(() {
|
||||||
runApp(new HAClientApp());
|
runApp(new HAClientApp(
|
||||||
|
theme: theme,
|
||||||
|
));
|
||||||
}, onError: (error, stack) {
|
}, onError: (error, stack) {
|
||||||
_reportError(error, stack);
|
_reportError(error, stack);
|
||||||
});
|
});
|
||||||
@ -182,22 +190,34 @@ void main() async {
|
|||||||
|
|
||||||
class HAClientApp extends StatefulWidget {
|
class HAClientApp extends StatefulWidget {
|
||||||
|
|
||||||
|
final AppTheme theme;
|
||||||
|
|
||||||
|
const HAClientApp({Key key, this.theme: AppTheme.defaultTheme}) : super(key: key);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
_HAClientAppState createState() => new _HAClientAppState();
|
_HAClientAppState createState() => new _HAClientAppState();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _HAClientAppState extends State<HAClientApp> {
|
class _HAClientAppState extends State<HAClientApp> {
|
||||||
StreamSubscription<List<PurchaseDetails>> _subscription;
|
StreamSubscription<List<PurchaseDetails>> _purchaseUpdateSubscription;
|
||||||
|
StreamSubscription _themeChangeSubscription;
|
||||||
|
AppTheme _currentTheme = AppTheme.defaultTheme;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
InAppPurchaseConnection.enablePendingPurchases();
|
InAppPurchaseConnection.enablePendingPurchases();
|
||||||
final Stream purchaseUpdates =
|
final Stream purchaseUpdates =
|
||||||
InAppPurchaseConnection.instance.purchaseUpdatedStream;
|
InAppPurchaseConnection.instance.purchaseUpdatedStream;
|
||||||
_subscription = purchaseUpdates.listen((purchases) {
|
_purchaseUpdateSubscription = purchaseUpdates.listen((purchases) {
|
||||||
_handlePurchaseUpdates(purchases);
|
_handlePurchaseUpdates(purchases);
|
||||||
});
|
});
|
||||||
|
_currentTheme = widget.theme;
|
||||||
|
_themeChangeSubscription = eventBus.on<ChangeThemeEvent>().listen((event){
|
||||||
|
setState(() {
|
||||||
|
_currentTheme = event.theme;
|
||||||
|
});
|
||||||
|
});
|
||||||
workManager.Workmanager.initialize(
|
workManager.Workmanager.initialize(
|
||||||
updateDeviceLocationIsolate,
|
updateDeviceLocationIsolate,
|
||||||
isInDebugMode: false
|
isInDebugMode: false
|
||||||
@ -226,14 +246,15 @@ class _HAClientAppState extends State<HAClientApp> {
|
|||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return new MaterialApp(
|
return new MaterialApp(
|
||||||
title: appName,
|
title: appName,
|
||||||
theme: HAClientTheme().lightTheme,
|
theme: HAClientTheme().getThemeData(_currentTheme),
|
||||||
darkTheme: HAClientTheme().darkTheme,
|
darkTheme: HAClientTheme().darkTheme,
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
initialRoute: "/",
|
initialRoute: "/",
|
||||||
routes: {
|
routes: {
|
||||||
"/": (context) => MainPage(title: 'HA Client'),
|
"/": (context) => MainPage(title: 'HA Client'),
|
||||||
"/connection-settings": (context) => ConnectionSettingsPage(title: "Settings"),
|
"/app-settings": (context) => AppSettingsPage(),
|
||||||
"/integration-settings": (context) => IntegrationSettingsPage(title: "Integration settings"),
|
"/connection-settings": (context) => AppSettingsPage(showSection: AppSettingsSection.connectionSettings),
|
||||||
|
"/integration-settings": (context) => AppSettingsPage(showSection: AppSettingsSection.integrationSettings),
|
||||||
"/putchase": (context) => PurchasePage(title: "Support app development"),
|
"/putchase": (context) => PurchasePage(title: "Support app development"),
|
||||||
"/play-media": (context) => PlayMediaPage(
|
"/play-media": (context) => PlayMediaPage(
|
||||||
mediaUrl: "${ModalRoute.of(context).settings.arguments != null ? (ModalRoute.of(context).settings.arguments as Map)['url'] : ''}",
|
mediaUrl: "${ModalRoute.of(context).settings.arguments != null ? (ModalRoute.of(context).settings.arguments as Map)['url'] : ''}",
|
||||||
@ -278,7 +299,8 @@ class _HAClientAppState extends State<HAClientApp> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_subscription.cancel();
|
_purchaseUpdateSubscription.cancel();
|
||||||
|
_themeChangeSubscription.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,10 +1,8 @@
|
|||||||
part of '../main.dart';
|
part of '../main.dart';
|
||||||
|
|
||||||
class HAClientTheme {
|
enum AppTheme {darkTheme, defaultTheme, haTheme}
|
||||||
|
|
||||||
static const DEFAULT = 0;
|
class HAClientTheme {
|
||||||
static const DARK = 1;
|
|
||||||
static const HOMEASSISTANT = 2;
|
|
||||||
|
|
||||||
static const TextTheme textTheme = TextTheme(
|
static const TextTheme textTheme = TextTheme(
|
||||||
display1: TextStyle(fontSize: 34, fontWeight: FontWeight.normal),
|
display1: TextStyle(fontSize: 34, fontWeight: FontWeight.normal),
|
||||||
@ -80,6 +78,22 @@ class HAClientTheme {
|
|||||||
|
|
||||||
HAClientTheme._internal();
|
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(
|
final ThemeData lightTheme = ThemeData.from(
|
||||||
colorScheme: ColorScheme(
|
colorScheme: ColorScheme(
|
||||||
primary: Color.fromRGBO(112, 154, 193, 1),
|
primary: Color.fromRGBO(112, 154, 193, 1),
|
||||||
|
@ -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<IntegrationSettingsPage> {
|
|
||||||
|
|
||||||
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: <Widget>[
|
|
||||||
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: <Widget>[
|
|
||||||
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: <Widget>[
|
|
||||||
//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: <Widget>[
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
}
|
|
@ -366,20 +366,12 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
}
|
}
|
||||||
menuItems.addAll([
|
menuItems.addAll([
|
||||||
Divider(),
|
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(
|
ListTile(
|
||||||
leading: Icon(MaterialDesignIcons.getIconDataFromIconName("mdi:cellphone-settings-variant")),
|
leading: Icon(MaterialDesignIcons.getIconDataFromIconName("mdi:cellphone-settings-variant")),
|
||||||
title: Text("Integration settings"),
|
title: Text("App settings"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
Navigator.of(context).pushNamed('/integration-settings');
|
Navigator.of(context).pushNamed('/app-settings');
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
|
104
lib/pages/settings/app_settings.page.dart
Normal file
104
lib/pages/settings/app_settings.page.dart
Normal file
@ -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<AppSettingsPage> {
|
||||||
|
|
||||||
|
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: <Widget>[
|
||||||
|
_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);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
part of '../main.dart';
|
part of '../../main.dart';
|
||||||
|
|
||||||
class ConnectionSettingsPage extends StatefulWidget {
|
class ConnectionSettingsPage extends StatefulWidget {
|
||||||
ConnectionSettingsPage({Key key, this.title}) : super(key: key);
|
ConnectionSettingsPage({Key key, this.title}) : super(key: key);
|
||||||
@ -108,31 +108,7 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return new Scaffold(
|
return ListView(
|
||||||
appBar: new AppBar(
|
|
||||||
leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: (){
|
|
||||||
Navigator.pop(context);
|
|
||||||
}),
|
|
||||||
title: new Text(widget.title),
|
|
||||||
actions: <Widget>[
|
|
||||||
IconButton(
|
|
||||||
icon: Icon(Icons.check),
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
body: ListView(
|
|
||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.vertical,
|
||||||
padding: const EdgeInsets.all(20.0),
|
padding: const EdgeInsets.all(20.0),
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
@ -215,8 +191,26 @@ class _ConnectionSettingsPageState extends State<ConnectionSettingsPage> {
|
|||||||
_newLongLivedToken = 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);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
194
lib/pages/settings/integration_settings.part.dart
Normal file
194
lib/pages/settings/integration_settings.part.dart
Normal file
@ -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<IntegrationSettingsPage> {
|
||||||
|
|
||||||
|
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: <Widget>[
|
||||||
|
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: <Widget>[
|
||||||
|
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: <Widget>[
|
||||||
|
//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: <Widget>[
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
79
lib/pages/settings/lookandfeel_settings.part.dart
Normal file
79
lib/pages/settings/lookandfeel_settings.part.dart
Normal file
@ -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<LookAndFeelSettingsPage> {
|
||||||
|
|
||||||
|
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: <Widget>[
|
||||||
|
Text("Application scheme:", style: Theme.of(context).textTheme.body2),
|
||||||
|
Container(height: Sizes.rowPadding),
|
||||||
|
DropdownButton<AppTheme>(
|
||||||
|
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<AppTheme>(
|
||||||
|
value: value,
|
||||||
|
child: Text('${appThemeName[value]}'),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
onChanged: (theme) => _saveSettings(theme),
|
||||||
|
)
|
||||||
|
]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
}
|
@ -28,6 +28,13 @@ class ReloadUIEvent {
|
|||||||
ReloadUIEvent();
|
ReloadUIEvent();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ChangeThemeEvent {
|
||||||
|
|
||||||
|
final AppTheme theme;
|
||||||
|
|
||||||
|
ChangeThemeEvent(this.theme);
|
||||||
|
}
|
||||||
|
|
||||||
class StartAuthEvent {
|
class StartAuthEvent {
|
||||||
String oauthUrl;
|
String oauthUrl;
|
||||||
bool showButton;
|
bool showButton;
|
||||||
|
Reference in New Issue
Block a user