From 6663bcad72ea61f4805a00d78f678f4be1f59387 Mon Sep 17 00:00:00 2001 From: estevez-dev Date: Mon, 2 Sep 2019 21:08:20 +0300 Subject: [PATCH] Resolves #339 Authenticated webview for panels and config sections --- lib/entities/entity_wrapper.class.dart | 4 +- lib/main.dart | 55 +++++++++------ .../mobile_app_integration_manager.class.dart | 4 +- lib/panels/widgets/link_to_web_config.dart | 4 +- lib/ui_class/panel_class.dart | 22 ++---- lib/utils.class.dart | 42 ----------- lib/utils/launcher.dart | 69 +++++++++++++++++++ 7 files changed, 114 insertions(+), 86 deletions(-) create mode 100644 lib/utils/launcher.dart diff --git a/lib/entities/entity_wrapper.class.dart b/lib/entities/entity_wrapper.class.dart index 25bdecd..46a379b 100644 --- a/lib/entities/entity_wrapper.class.dart +++ b/lib/entities/entity_wrapper.class.dart @@ -60,7 +60,7 @@ class EntityWrapper { //TODO handle local urls Logger.w("Local urls is not supported yet"); } else { - HAUtils.launchURL(uiAction.tapService); + Launcher.launchURL(uiAction.tapService); } break; } @@ -100,7 +100,7 @@ class EntityWrapper { //TODO handle local urls Logger.w("Local urls is not supported yet"); } else { - HAUtils.launchURL(uiAction.holdService); + Launcher.launchURL(uiAction.holdService); } break; } diff --git a/lib/main.dart b/lib/main.dart index b81523c..e6bf49b 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -27,6 +27,7 @@ import 'package:android_alarm_manager/android_alarm_manager.dart'; import 'package:geolocator/geolocator.dart'; part 'const.dart'; +part 'utils/launcher.dart'; part 'entities/entity.class.dart'; part 'entities/entity_wrapper.class.dart'; part 'entities/timer/timer_entity.class.dart'; @@ -169,7 +170,7 @@ class HAClientApp extends StatelessWidget { appBar: new AppBar( leading: IconButton( icon: Icon(Icons.help), - onPressed: () => HAUtils.launchURLInCustomTab(context: context, url: "http://ha-client.homemade.systems/docs#authentication") + onPressed: () => Launcher.launchURLInCustomTab(context: context, url: "http://ha-client.homemade.systems/docs#authentication") ), title: new Text("Login with HA"), actions: [ @@ -549,11 +550,24 @@ class _MainPageState extends State with WidgetsBindingObserver, Ticker UserAccountsDrawerHeader( accountName: Text(HomeAssistant().userName), accountEmail: Text(ConnectionManager().displayHostname ?? "Not configured"), - /*onDetailsPressed: () { - setState(() { - _accountMenuExpanded = !_accountMenuExpanded; + onDetailsPressed: () { + final flutterWebViewPlugin = new FlutterWebviewPlugin(); + flutterWebViewPlugin.onStateChanged.listen((viewState) async { + if (viewState.type == WebViewState.startLoad) { + Logger.d("[WebView] Injecting external auth JS"); + rootBundle.loadString('assets/js/externalAuth.js').then((js){ + flutterWebViewPlugin.evalJavascript(js.replaceFirst("[token]", ConnectionManager()._token)); + }); + } }); - },*/ + Navigator.of(context).pushNamed( + "/webview", + arguments: { + "url": "${ConnectionManager().httpWebHost}/profile?external_auth=1", + "title": "Profile" + } + ); + }, currentAccountPicture: CircleAvatar( child: Text( HomeAssistant().userAvatarText, @@ -570,7 +584,14 @@ class _MainPageState extends State with WidgetsBindingObserver, Ticker menuItems.add( new ListTile( leading: Icon(MaterialDesignIcons.getIconDataFromIconName(panel.icon)), - title: Text("${panel.title}"), + title: Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text("${panel.title}"), + Container(width: 4.0,), + panel.isWebView ? Text("webview", style: TextStyle(fontSize: 8.0, color: Colors.black45),) : Container(width: 1.0,) + ], + ), onTap: () { Navigator.of(context).pop(); panel.handleOpen(context); @@ -580,14 +601,6 @@ class _MainPageState extends State with WidgetsBindingObserver, Ticker } }); } - //TODO check for loaded - menuItems.add( - new ListTile( - leading: Icon(MaterialDesignIcons.getIconDataFromIconName("mdi:home-assistant")), - title: Text("Open Web UI"), - onTap: () => HAUtils.launchURL(ConnectionManager().httpWebHost), - ) - ); menuItems.addAll([ Divider(), ListTile( @@ -614,7 +627,7 @@ class _MainPageState extends State with WidgetsBindingObserver, Ticker title: Text("Report an issue"), onTap: () { Navigator.of(context).pop(); - HAUtils.launchURL("https://github.com/estevez-dev/ha_client/issues/new"); + Launcher.launchURL("https://github.com/estevez-dev/ha_client/issues/new"); }, ), Divider(), @@ -632,7 +645,7 @@ class _MainPageState extends State with WidgetsBindingObserver, Ticker title: Text("Help"), onTap: () { Navigator.of(context).pop(); - HAUtils.launchURL("http://ha-client.homemade.systems/docs"); + Launcher.launchURL("http://ha-client.homemade.systems/docs"); }, ), new ListTile( @@ -640,7 +653,7 @@ class _MainPageState extends State with WidgetsBindingObserver, Ticker title: Text("Join Discord channel"), onTap: () { Navigator.of(context).pop(); - HAUtils.launchURL("https://discord.gg/AUzEvwn"); + Launcher.launchURL("https://discord.gg/AUzEvwn"); }, ), new AboutListTile( @@ -648,7 +661,7 @@ class _MainPageState extends State with WidgetsBindingObserver, Ticker GestureDetector( onTap: () { Navigator.of(context).pop(); - HAUtils.launchURL("http://ha-client.homemade.systems/"); + Launcher.launchURL("http://ha-client.homemade.systems/"); }, child: Text( "ha-client.homemade.systems", @@ -664,7 +677,7 @@ class _MainPageState extends State with WidgetsBindingObserver, Ticker GestureDetector( onTap: () { Navigator.of(context).pop(); - HAUtils.launchURLInCustomTab(context: context, url: "http://ha-client.homemade.systems/terms_and_conditions"); + Launcher.launchURLInCustomTab(context: context, url: "http://ha-client.homemade.systems/terms_and_conditions"); }, child: Text( "Terms and Conditions", @@ -680,7 +693,7 @@ class _MainPageState extends State with WidgetsBindingObserver, Ticker GestureDetector( onTap: () { Navigator.of(context).pop(); - HAUtils.launchURLInCustomTab(context: context, url: "http://ha-client.homemade.systems/privacy_policy"); + Launcher.launchURLInCustomTab(context: context, url: "http://ha-client.homemade.systems/privacy_policy"); }, child: Text( "Privacy Policy", @@ -765,7 +778,7 @@ class _MainPageState extends State with WidgetsBindingObserver, Ticker actions.add(FlatButton( child: Text("${action.title}", style: textStyle), onPressed: () { - HAUtils.launchURLInCustomTab(context: context, url: "${action.url}"); + Launcher.launchURLInCustomTab(context: context, url: "${action.url}"); }, )); break; diff --git a/lib/managers/mobile_app_integration_manager.class.dart b/lib/managers/mobile_app_integration_manager.class.dart index b54c8a9..da6577a 100644 --- a/lib/managers/mobile_app_integration_manager.class.dart +++ b/lib/managers/mobile_app_integration_manager.class.dart @@ -89,10 +89,10 @@ class MobileAppIntegrationManager { positiveText: "Report to GitHub", negativeText: "Report to Discord", onPositive: () { - HAUtils.launchURL("https://github.com/estevez-dev/ha_client/issues/new"); + Launcher.launchURL("https://github.com/estevez-dev/ha_client/issues/new"); }, onNegative: () { - HAUtils.launchURL("https://discord.gg/AUzEvwn"); + Launcher.launchURL("https://discord.gg/AUzEvwn"); }, )); } diff --git a/lib/panels/widgets/link_to_web_config.dart b/lib/panels/widgets/link_to_web_config.dart index 1cfa9f8..4c7b98c 100644 --- a/lib/panels/widgets/link_to_web_config.dart +++ b/lib/panels/widgets/link_to_web_config.dart @@ -18,7 +18,9 @@ class LinkToWebConfig extends StatelessWidget { overflow: TextOverflow.ellipsis, style: new TextStyle(fontWeight: FontWeight.bold, fontSize: Sizes.largeFontSize)), subtitle: Text("Tap to opne web version"), - onTap: () => HAUtils.launchURLInCustomTab(context: context, url: this.url), + onTap: () { + Launcher.launchAuthenticatedWebView(context: context, url: this.url, title: this.name); + }, ) ], ), diff --git a/lib/ui_class/panel_class.dart b/lib/ui_class/panel_class.dart index e91d373..1bc0f07 100644 --- a/lib/ui_class/panel_class.dart +++ b/lib/ui_class/panel_class.dart @@ -17,13 +17,15 @@ class Panel { final Map config; String icon; bool isHidden = true; + bool isWebView = false; Panel({this.id, this.type, this.title, this.urlPath, this.icon, this.config}) { if (icon == null || !icon.startsWith("mdi:")) { icon = Panel.iconsByComponent[type]; } Logger.d("New panel '$title'. type=$type, icon=$icon, urlPath=$urlPath"); - isHidden = (type == 'lovelace' || type == 'kiosk' || type == 'states'); + isHidden = (type == 'lovelace' || type == 'kiosk' || type == 'states' || type == 'profile' || type == 'developer-tools'); + isWebView = (type != 'config'); } void handleOpen(BuildContext context) { @@ -34,23 +36,7 @@ class Panel { ) ); } else { - String url = "${ConnectionManager().httpWebHost}/$urlPath?external_auth=1"; - final flutterWebViewPlugin = new FlutterWebviewPlugin(); - flutterWebViewPlugin.onStateChanged.listen((viewState) async { - if (viewState.type == WebViewState.startLoad) { - Logger.d("[WebView] Injecting external auth JS"); - rootBundle.loadString('assets/js/externalAuth.js').then((js){ - flutterWebViewPlugin.evalJavascript(js.replaceFirst("[token]", ConnectionManager()._token)); - }); - } - }); - Navigator.of(context).pushNamed( - "/webview", - arguments: { - "url": "$url", - "title": "${this.title}" - } - ); + Launcher.launchAuthenticatedWebView(context: context, url: "${ConnectionManager().httpWebHost}/$urlPath", title: "${this.title}"); } } diff --git a/lib/utils.class.dart b/lib/utils.class.dart index dd38c03..cd19de2 100644 --- a/lib/utils.class.dart +++ b/lib/utils.class.dart @@ -89,48 +89,6 @@ class HAErrorActionType { static const OPEN_CONNECTION_SETTINGS = 4; } -class HAUtils { - static void launchURL(String url) async { - if (await urlLauncher.canLaunch(url)) { - await urlLauncher.launch(url); - } else { - Logger.e( "Could not launch $url"); - } - } - - static void launchURLInCustomTab({BuildContext context, String url, bool enableDefaultShare: true, bool showPageTitle: true}) async { - try { - await launch( - "$url", - option: new CustomTabsOption( - toolbarColor: Theme.of(context).primaryColor, - enableDefaultShare: enableDefaultShare, - enableUrlBarHiding: true, - showPageTitle: showPageTitle, - animation: new CustomTabsAnimation.slideIn() - // or user defined animation. - /*animation: new CustomTabsAnimation( - startEnter: 'slide_up', - startExit: 'android:anim/fade_out', - endEnter: 'android:anim/fade_in', - endExit: 'slide_down', - )*/, - extraCustomTabs: [ - // ref. https://play.google.com/store/apps/details?id=org.mozilla.firefox - 'org.mozilla.firefox', - // ref. https://play.google.com/store/apps/details?id=com.microsoft.emmx - 'com.microsoft.emmx', - ], - ), - ); - } catch (e) { - Logger.w("Can't open custom tab: ${e.toString()}"); - Logger.w("Launching in default browser"); - HAUtils.launchURL(url); - } - } -} - class StateChangedEvent { String entityId; String newState; diff --git a/lib/utils/launcher.dart b/lib/utils/launcher.dart new file mode 100644 index 0000000..c859b41 --- /dev/null +++ b/lib/utils/launcher.dart @@ -0,0 +1,69 @@ +part of '../main.dart'; + +class Launcher { + + static void launchURL(String url) async { + if (await urlLauncher.canLaunch(url)) { + await urlLauncher.launch(url); + } else { + Logger.e( "Could not launch $url"); + } + } + + static void launchAuthenticatedWebView({BuildContext context, String url, String title}) { + if (url.contains("?")) { + url += "&external_auth=1"; + } else { + url += "?external_auth=1"; + } + final flutterWebViewPlugin = new FlutterWebviewPlugin(); + flutterWebViewPlugin.onStateChanged.listen((viewState) async { + if (viewState.type == WebViewState.startLoad) { + Logger.d("[WebView] Injecting external auth JS"); + rootBundle.loadString('assets/js/externalAuth.js').then((js){ + flutterWebViewPlugin.evalJavascript(js.replaceFirst("[token]", ConnectionManager()._token)); + }); + } + }); + Navigator.of(context).pushNamed( + "/webview", + arguments: { + "url": "$url", + "title": "${title ?? ''}" + } + ); + } + + static void launchURLInCustomTab({BuildContext context, String url, bool enableDefaultShare: true, bool showPageTitle: true}) async { + try { + await launch( + "$url", + option: new CustomTabsOption( + toolbarColor: Theme.of(context).primaryColor, + enableDefaultShare: enableDefaultShare, + enableUrlBarHiding: true, + showPageTitle: showPageTitle, + animation: new CustomTabsAnimation.slideIn() + // or user defined animation. + /*animation: new CustomTabsAnimation( + startEnter: 'slide_up', + startExit: 'android:anim/fade_out', + endEnter: 'android:anim/fade_in', + endExit: 'slide_down', + )*/, + extraCustomTabs: [ + // ref. https://play.google.com/store/apps/details?id=org.mozilla.firefox + 'org.mozilla.firefox', + // ref. https://play.google.com/store/apps/details?id=com.microsoft.emmx + 'com.microsoft.emmx', + ], + ), + ); + } catch (e) { + Logger.w("Can't open custom tab: ${e.toString()}"); + Logger.w("Launching in default browser"); + Launcher.launchURL(url); + } + } + +} \ No newline at end of file