Resolves #339 Authenticated webview for panels and config sections
This commit is contained in:
parent
113cd29f74
commit
6663bcad72
@ -60,7 +60,7 @@ class EntityWrapper {
|
|||||||
//TODO handle local urls
|
//TODO handle local urls
|
||||||
Logger.w("Local urls is not supported yet");
|
Logger.w("Local urls is not supported yet");
|
||||||
} else {
|
} else {
|
||||||
HAUtils.launchURL(uiAction.tapService);
|
Launcher.launchURL(uiAction.tapService);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ class EntityWrapper {
|
|||||||
//TODO handle local urls
|
//TODO handle local urls
|
||||||
Logger.w("Local urls is not supported yet");
|
Logger.w("Local urls is not supported yet");
|
||||||
} else {
|
} else {
|
||||||
HAUtils.launchURL(uiAction.holdService);
|
Launcher.launchURL(uiAction.holdService);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ import 'package:android_alarm_manager/android_alarm_manager.dart';
|
|||||||
import 'package:geolocator/geolocator.dart';
|
import 'package:geolocator/geolocator.dart';
|
||||||
|
|
||||||
part 'const.dart';
|
part 'const.dart';
|
||||||
|
part 'utils/launcher.dart';
|
||||||
part 'entities/entity.class.dart';
|
part 'entities/entity.class.dart';
|
||||||
part 'entities/entity_wrapper.class.dart';
|
part 'entities/entity_wrapper.class.dart';
|
||||||
part 'entities/timer/timer_entity.class.dart';
|
part 'entities/timer/timer_entity.class.dart';
|
||||||
@ -169,7 +170,7 @@ class HAClientApp extends StatelessWidget {
|
|||||||
appBar: new AppBar(
|
appBar: new AppBar(
|
||||||
leading: IconButton(
|
leading: IconButton(
|
||||||
icon: Icon(Icons.help),
|
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"),
|
title: new Text("Login with HA"),
|
||||||
actions: <Widget>[
|
actions: <Widget>[
|
||||||
@ -549,11 +550,24 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
UserAccountsDrawerHeader(
|
UserAccountsDrawerHeader(
|
||||||
accountName: Text(HomeAssistant().userName),
|
accountName: Text(HomeAssistant().userName),
|
||||||
accountEmail: Text(ConnectionManager().displayHostname ?? "Not configured"),
|
accountEmail: Text(ConnectionManager().displayHostname ?? "Not configured"),
|
||||||
/*onDetailsPressed: () {
|
onDetailsPressed: () {
|
||||||
setState(() {
|
final flutterWebViewPlugin = new FlutterWebviewPlugin();
|
||||||
_accountMenuExpanded = !_accountMenuExpanded;
|
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(
|
currentAccountPicture: CircleAvatar(
|
||||||
child: Text(
|
child: Text(
|
||||||
HomeAssistant().userAvatarText,
|
HomeAssistant().userAvatarText,
|
||||||
@ -570,7 +584,14 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
menuItems.add(
|
menuItems.add(
|
||||||
new ListTile(
|
new ListTile(
|
||||||
leading: Icon(MaterialDesignIcons.getIconDataFromIconName(panel.icon)),
|
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: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
panel.handleOpen(context);
|
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([
|
menuItems.addAll([
|
||||||
Divider(),
|
Divider(),
|
||||||
ListTile(
|
ListTile(
|
||||||
@ -614,7 +627,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
title: Text("Report an issue"),
|
title: Text("Report an issue"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
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(),
|
Divider(),
|
||||||
@ -632,7 +645,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
title: Text("Help"),
|
title: Text("Help"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
HAUtils.launchURL("http://ha-client.homemade.systems/docs");
|
Launcher.launchURL("http://ha-client.homemade.systems/docs");
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
new ListTile(
|
new ListTile(
|
||||||
@ -640,7 +653,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
title: Text("Join Discord channel"),
|
title: Text("Join Discord channel"),
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
HAUtils.launchURL("https://discord.gg/AUzEvwn");
|
Launcher.launchURL("https://discord.gg/AUzEvwn");
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
new AboutListTile(
|
new AboutListTile(
|
||||||
@ -648,7 +661,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
HAUtils.launchURL("http://ha-client.homemade.systems/");
|
Launcher.launchURL("http://ha-client.homemade.systems/");
|
||||||
},
|
},
|
||||||
child: Text(
|
child: Text(
|
||||||
"ha-client.homemade.systems",
|
"ha-client.homemade.systems",
|
||||||
@ -664,7 +677,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
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(
|
child: Text(
|
||||||
"Terms and Conditions",
|
"Terms and Conditions",
|
||||||
@ -680,7 +693,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
GestureDetector(
|
GestureDetector(
|
||||||
onTap: () {
|
onTap: () {
|
||||||
Navigator.of(context).pop();
|
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(
|
child: Text(
|
||||||
"Privacy Policy",
|
"Privacy Policy",
|
||||||
@ -765,7 +778,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
|
|||||||
actions.add(FlatButton(
|
actions.add(FlatButton(
|
||||||
child: Text("${action.title}", style: textStyle),
|
child: Text("${action.title}", style: textStyle),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
HAUtils.launchURLInCustomTab(context: context, url: "${action.url}");
|
Launcher.launchURLInCustomTab(context: context, url: "${action.url}");
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
break;
|
break;
|
||||||
|
@ -89,10 +89,10 @@ class MobileAppIntegrationManager {
|
|||||||
positiveText: "Report to GitHub",
|
positiveText: "Report to GitHub",
|
||||||
negativeText: "Report to Discord",
|
negativeText: "Report to Discord",
|
||||||
onPositive: () {
|
onPositive: () {
|
||||||
HAUtils.launchURL("https://github.com/estevez-dev/ha_client/issues/new");
|
Launcher.launchURL("https://github.com/estevez-dev/ha_client/issues/new");
|
||||||
},
|
},
|
||||||
onNegative: () {
|
onNegative: () {
|
||||||
HAUtils.launchURL("https://discord.gg/AUzEvwn");
|
Launcher.launchURL("https://discord.gg/AUzEvwn");
|
||||||
},
|
},
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,9 @@ class LinkToWebConfig extends StatelessWidget {
|
|||||||
overflow: TextOverflow.ellipsis,
|
overflow: TextOverflow.ellipsis,
|
||||||
style: new TextStyle(fontWeight: FontWeight.bold, fontSize: Sizes.largeFontSize)),
|
style: new TextStyle(fontWeight: FontWeight.bold, fontSize: Sizes.largeFontSize)),
|
||||||
subtitle: Text("Tap to opne web version"),
|
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);
|
||||||
|
},
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -17,13 +17,15 @@ class Panel {
|
|||||||
final Map config;
|
final Map config;
|
||||||
String icon;
|
String icon;
|
||||||
bool isHidden = true;
|
bool isHidden = true;
|
||||||
|
bool isWebView = false;
|
||||||
|
|
||||||
Panel({this.id, this.type, this.title, this.urlPath, this.icon, this.config}) {
|
Panel({this.id, this.type, this.title, this.urlPath, this.icon, this.config}) {
|
||||||
if (icon == null || !icon.startsWith("mdi:")) {
|
if (icon == null || !icon.startsWith("mdi:")) {
|
||||||
icon = Panel.iconsByComponent[type];
|
icon = Panel.iconsByComponent[type];
|
||||||
}
|
}
|
||||||
Logger.d("New panel '$title'. type=$type, icon=$icon, urlPath=$urlPath");
|
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) {
|
void handleOpen(BuildContext context) {
|
||||||
@ -34,23 +36,7 @@ class Panel {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
String url = "${ConnectionManager().httpWebHost}/$urlPath?external_auth=1";
|
Launcher.launchAuthenticatedWebView(context: context, url: "${ConnectionManager().httpWebHost}/$urlPath", title: "${this.title}");
|
||||||
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}"
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,48 +89,6 @@ class HAErrorActionType {
|
|||||||
static const OPEN_CONNECTION_SETTINGS = 4;
|
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 {
|
class StateChangedEvent {
|
||||||
String entityId;
|
String entityId;
|
||||||
String newState;
|
String newState;
|
||||||
|
69
lib/utils/launcher.dart
Normal file
69
lib/utils/launcher.dart
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Reference in New Issue
Block a user