Resolves #339 Authenticated webview for panels and config sections

This commit is contained in:
estevez-dev 2019-09-02 21:08:20 +03:00
parent 113cd29f74
commit 6663bcad72
7 changed files with 114 additions and 86 deletions

View File

@ -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;
}

View File

@ -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: <Widget>[
@ -549,11 +550,24 @@ class _MainPageState extends State<MainPage> 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<MainPage> with WidgetsBindingObserver, Ticker
menuItems.add(
new ListTile(
leading: Icon(MaterialDesignIcons.getIconDataFromIconName(panel.icon)),
title: Text("${panel.title}"),
title: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
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<MainPage> 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<MainPage> 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<MainPage> 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<MainPage> 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<MainPage> 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<MainPage> 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<MainPage> 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<MainPage> 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;

View File

@ -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");
},
));
}

View File

@ -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);
},
)
],
),

View File

@ -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}");
}
}

View File

@ -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: <String>[
// 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;

69
lib/utils/launcher.dart Normal file
View File

@ -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: <String>[
// 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);
}
}
}