Share media url to HA CLient to play on media_player

This commit is contained in:
estevez-dev 2019-09-09 12:25:13 +03:00
parent fc228d85ae
commit 0a6ff4586d
13 changed files with 311 additions and 30 deletions

View File

@ -3,8 +3,9 @@ package com.keyboardcrumbs.hassclient;
import android.os.Bundle;
import io.flutter.app.FlutterActivity;
import io.flutter.plugins.GeneratedPluginRegistrant;
import io.flutter.plugins.share.FlutterShareReceiverActivity;
public class MainActivity extends FlutterActivity {
public class MainActivity extends FlutterShareReceiverActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

View File

@ -253,7 +253,7 @@ class CardWidget extends StatelessWidget {
return Wrap(
//spacing: 5.0,
//alignment: WrapAlignment.spaceEvenly,
runSpacing: Sizes.rowPadding*2,
runSpacing: Sizes.doubleRowPadding,
children: buttons,
);
}

View File

@ -112,4 +112,5 @@ class Sizes {
static const largeFontSize = 24.0;
static const inputWidth = 160.0;
static const rowPadding = 10.0;
static const doubleRowPadding = rowPadding*2;
}

View File

@ -149,6 +149,17 @@ class EntityCollection {
return _allEntities[entityId] != null;
}
List<Entity> getByDomains(List<String> domains) {
List<Entity> result = [];
_allEntities.forEach((id, entity) {
if (domains.contains(entity.domain)) {
Logger.d("getByDomain: ${entity.isHidden}");
result.add(entity);
}
});
return result;
}
List<Entity> filterEntitiesForDefaultView() {
List<Entity> result = [];
List<Entity> groups = [];

View File

@ -34,18 +34,7 @@ class DefaultEntityContainer extends StatelessWidget {
],
);
}
return InkWell(
onLongPress: () {
if (entityModel.handleTap) {
entityModel.entityWrapper.handleHold();
}
},
onTap: () {
if (entityModel.handleTap) {
entityModel.entityWrapper.handleTap();
}
},
child: Row(
Widget result = Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
EntityIcon(),
@ -59,7 +48,23 @@ class DefaultEntityContainer extends StatelessWidget {
),
state
],
),
);
if (entityModel.handleTap) {
return InkWell(
onLongPress: () {
if (entityModel.handleTap) {
entityModel.entityWrapper.handleHold();
}
},
onTap: () {
if (entityModel.handleTap) {
entityModel.entityWrapper.handleTap();
}
},
child: result,
);
} else {
return result;
}
}
}

View File

@ -11,6 +11,7 @@ class HomeAssistant {
EntityCollection entities;
HomeAssistantUI ui;
Map _instanceConfig = {};
Map services;
String _userName;
HSVColor savedColor;
@ -115,7 +116,11 @@ class HomeAssistant {
}
Future _getServices() async {
await ConnectionManager().sendSocketMessage(type: "get_services").then((data) => Logger.d("Services received")).catchError((e) {
await ConnectionManager().sendSocketMessage(type: "get_services").then((data) {
Logger.d("Got ${data.length} services");
Logger.d("Media extractor: ${data["media_extractor"]}");
services = data;
}).catchError((e) {
Logger.w("Can't get services: ${e}");
});
}

View File

@ -23,6 +23,8 @@ import 'package:device_info/device_info.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:in_app_purchase/in_app_purchase.dart';
import 'plugins/circular_slider/single_circular_slider.dart';
import 'package:share/receive_share_state.dart';
import 'package:share/share.dart';
import 'utils/logger.dart';
@ -121,6 +123,7 @@ part 'types/ha_error.dart';
part 'types/event_bus_events.dart';
part 'cards/widgets/gauge_card_body.dart';
part 'cards/widgets/light_card_body.dart';
part 'pages/play_media.page.dart';
EventBus eventBus = new EventBus();
@ -167,6 +170,7 @@ class HAClientApp extends StatelessWidget {
"/": (context) => MainPage(title: 'HA Client'),
"/connection-settings": (context) => ConnectionSettingsPage(title: "Settings"),
"/putchase": (context) => PurchasePage(title: "Support app development"),
"/play-media": (context) => PlayMediaPage(mediaUrl: "${(ModalRoute.of(context).settings.arguments as Map)['url']}",),
"/log-view": (context) => LogViewPage(title: "Log"),
"/login": (context) => WebviewScaffold(
url: "${ConnectionManager().oauthUrl}",

View File

@ -9,7 +9,7 @@ class MainPage extends StatefulWidget {
_MainPageState createState() => new _MainPageState();
}
class _MainPageState extends State<MainPage> with WidgetsBindingObserver, TickerProviderStateMixin {
class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObserver, TickerProviderStateMixin {
StreamSubscription<List<PurchaseDetails>> _subscription;
StreamSubscription _stateSubscription;
@ -25,6 +25,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
int _previousViewCount;
bool _showLoginButton = false;
bool _preventAppRefresh = false;
String _savedSharedText;
@override
void initState() {
@ -34,6 +35,7 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
_handlePurchaseUpdates(purchases);
});
super.initState();
enableShareReceiving();
WidgetsBinding.instance.addObserver(this);
_firebaseMessaging.configure(
@ -76,6 +78,12 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
}
@override void receiveShare(Share shared) {
if (shared.mimeType == ShareType.TYPE_PLAIN_TEXT) {
_savedSharedText = shared.text;
}
}
Future onSelectNotification(String payload) async {
if (payload != null) {
Logger.d('Notification clicked: ' + payload);
@ -121,6 +129,11 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
}
_fetchData() async {
if (_savedSharedText != null && !HomeAssistant().isNoEntities) {
Logger.d("Got shared text: $_savedSharedText");
Navigator.pushNamed(context, "/play-media", arguments: {"url": _savedSharedText});
_savedSharedText = null;
}
await HomeAssistant().fetchData().then((_) {
_hideBottomBar();
int currentViewCount = HomeAssistant().ui?.views?.length ?? 0;
@ -659,6 +672,11 @@ class _MainPageState extends State<MainPage> with WidgetsBindingObserver, Ticker
primary: true,
title: Text(HomeAssistant().locationName ?? ""),
actions: <Widget>[
IconButton(
icon: Icon(MaterialDesignIcons.getIconDataFromIconName(
"mdi:television"), color: Colors.white,),
onPressed: () => Navigator.pushNamed(context, "/play-media", arguments: {"url": ""})
),
IconButton(
icon: Icon(MaterialDesignIcons.getIconDataFromIconName(
"mdi:dots-vertical"), color: Colors.white,),

View File

@ -0,0 +1,225 @@
part of '../main.dart';
class PlayMediaPage extends StatefulWidget {
final String mediaUrl;
PlayMediaPage({Key key, this.mediaUrl}) : super(key: key);
@override
_PlayMediaPageState createState() => new _PlayMediaPageState();
}
class _PlayMediaPageState extends State<PlayMediaPage> {
bool _loaded = false;
String _error = "";
String _validationMessage = "";
List<Entity> _players;
String _mediaUrl;
String _contentType;
bool _useMediaExtractor = false;
bool _isMediaExtractorExist = false;
StreamSubscription _stateSubscription;
StreamSubscription _refreshDataSubscription;
final List<String> _contentTypes = ["movie", "video", "music", "image", "image/jpg", "playlist"];
@override
void initState() {
super.initState();
_mediaUrl = widget.mediaUrl;
_contentType = _contentTypes[0];
_stateSubscription = eventBus.on<StateChangedEvent>().listen((event) {
if (event.entityId.contains("media_player")) {
Logger.d("State change event handled by play media page: ${event.entityId}");
setState(() {});
}
});
_refreshDataSubscription = eventBus.on<RefreshDataFinishedEvent>().listen((event) {
_loadMediaEntities();
});
_loadMediaEntities();
}
_loadMediaEntities() async {
if (HomeAssistant().isNoEntities) {
setState(() {
_loaded = false;
});
} else {
_isMediaExtractorExist = HomeAssistant().services.containsKey("media_extractor");
//_useMediaExtractor = _isMediaExtractorExist;
_players = HomeAssistant().entities.getByDomains(["media_player"]);
setState(() {
if (_players.isNotEmpty) {
_loaded = true;
} else {
_loaded = false;
_error = "Looks like you don't have any media player";
}
});
}
}
void _playMedia(Entity entity) {
if (_mediaUrl == null || _mediaUrl.isEmpty) {
setState(() {
_validationMessage = "Media url must be specified";
});
} else {
String serviceDomain;
if (_useMediaExtractor) {
serviceDomain = "media_extractor";
} else {
serviceDomain = "media_player";
}
Navigator.pop(context);
ConnectionManager().callService(
domain: serviceDomain,
entityId: entity.entityId,
service: "play_media",
additionalServiceData: {
"media_content_id": _mediaUrl,
"media_content_type": _contentType
}
);
eventBus.fire(ShowEntityPageEvent(entity));
}
}
@override
Widget build(BuildContext context) {
Widget body;
if (!_loaded) {
body = _error.isEmpty ? PageLoadingIndicator() : PageLoadingError(errorText: _error);
} else {
List<Widget> children = [];
children.add(CardHeader(name: "Media:"));
children.add(
TextField(
maxLines: 5,
minLines: 1,
decoration: InputDecoration(
labelText: "Media url"
),
controller: TextEditingController.fromValue(TextEditingValue(text: _mediaUrl)),
onChanged: (value) {
_mediaUrl = value;
}
),
);
if (_validationMessage.isNotEmpty) {
children.add(Text(
"$_validationMessage",
style: TextStyle(color: Colors.red)
));
}
children.addAll(<Widget>[
Container(height: Sizes.rowPadding,),
DropdownButton<String>(
value: _contentType,
isExpanded: true,
items: _contentTypes.map((String value) {
return new DropdownMenuItem<String>(
value: value,
child: new Text(value),
);
}).toList(),
onChanged: (value) {
setState(() {
_contentType = value;
});
},
)
]
);
if (_isMediaExtractorExist) {
children.addAll(<Widget>[
Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Text("Use media extractor"),
),
Switch(
value: _useMediaExtractor,
onChanged: (value) => setState((){_useMediaExtractor = value;}),
),
],
),
Container(
height: Sizes.rowPadding,
)
]
);
} else {
children.addAll(<Widget>[
Row(
mainAxisSize: MainAxisSize.max,
children: <Widget>[
Expanded(
child: Text("You can use media extractor here"),
),
GestureDetector(
onTap: () {
Launcher.launchURLInCustomTab(
context: context,
url: "https://www.home-assistant.io/components/media_extractor/"
);
},
child: Text(
"How?",
style: TextStyle(
color: Colors.blue,
decoration: TextDecoration.underline
),
),
),
],
),
Container(
height: Sizes.doubleRowPadding,
)
]
);
}
children.add(CardHeader(name: "Play on:"));
children.addAll(
_players.map((player) => InkWell(
child: EntityModel(
entityWrapper: EntityWrapper(entity: player),
handleTap: false,
child: Padding(
padding: EdgeInsets.only(bottom: Sizes.doubleRowPadding),
child: DefaultEntityContainer(state: player._buildStatePart(context)),
)
),
onTap: () => _playMedia(player),
))
);
body = ListView(
padding: EdgeInsets.all(Sizes.leftWidgetPadding),
scrollDirection: Axis.vertical,
children: children
);
}
return new Scaffold(
appBar: new AppBar(
leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: (){
Navigator.pop(context);
}),
title: new Text("Play media"),
),
body: body,
);
}
@override
void dispose(){
_stateSubscription?.cancel();
_refreshDataSubscription?.cancel();
super.dispose();
}
}

View File

@ -23,7 +23,6 @@ class Panel {
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' || type == 'profile' || type == 'developer-tools');
isWebView = (type != 'config');
}

View File

@ -17,7 +17,7 @@ class LinkToWebConfig extends StatelessWidget {
textAlign: TextAlign.left,
overflow: TextOverflow.ellipsis,
style: new TextStyle(fontWeight: FontWeight.bold, fontSize: Sizes.largeFontSize)),
subtitle: Text("Tap to opne web version"),
subtitle: Text("Tap to open web version"),
onTap: () {
Launcher.launchAuthenticatedWebView(context: context, url: this.url, title: this.name);
},

View File

@ -284,6 +284,15 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.3"
share:
dependency: "direct main"
description:
path: "."
ref: HEAD
resolved-ref: "1f8b139ca0bd35b643ef4f5ccce3a1b09931f16a"
url: "https://github.com/d-silveira/flutter-share.git"
source: git
version: "0.6.0"
shared_preferences:
dependency: "direct main"
description:

View File

@ -26,6 +26,9 @@ dependencies:
flutter_secure_storage: ^3.2.1+1
device_info: ^0.4.0+2
flutter_local_notifications: ^0.8.2
share:
git:
url: https://github.com/d-silveira/flutter-share.git
dev_dependencies:
flutter_test: