Media player controls improvements
This commit is contained in:
@ -0,0 +1,53 @@
|
|||||||
|
part of '../../../main.dart';
|
||||||
|
|
||||||
|
class MediaPlayerProgressBar extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_MediaPlayerProgressBarState createState() => _MediaPlayerProgressBarState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MediaPlayerProgressBarState extends State<MediaPlayerProgressBar> {
|
||||||
|
|
||||||
|
Timer _timer;
|
||||||
|
|
||||||
|
@override
|
||||||
|
initState() {
|
||||||
|
super.initState();
|
||||||
|
_timer = Timer.periodic(Duration(seconds: 1), (_) {
|
||||||
|
setState(() {
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final EntityModel entityModel = EntityModel.of(context);
|
||||||
|
final MediaPlayerEntity entity = entityModel.entityWrapper.entity;
|
||||||
|
double progress;
|
||||||
|
DateTime lastUpdated = DateTime.tryParse("${entity.attributes["media_position_updated_at"]}")?.toLocal();
|
||||||
|
if (lastUpdated != null) {
|
||||||
|
Duration duration = Duration(seconds: entity._getIntAttributeValue("media_duration") ?? 1);
|
||||||
|
Duration position = Duration(seconds: entity._getIntAttributeValue("media_position") ?? 0);
|
||||||
|
int currentPosition = position.inSeconds;
|
||||||
|
int differenceInSeconds = DateTime
|
||||||
|
.now()
|
||||||
|
.difference(lastUpdated)
|
||||||
|
.inSeconds;
|
||||||
|
currentPosition = currentPosition + differenceInSeconds;
|
||||||
|
progress = (currentPosition <= duration.inSeconds) ? currentPosition / duration.inSeconds : 100;
|
||||||
|
} else {
|
||||||
|
progress = 0;
|
||||||
|
}
|
||||||
|
return LinearProgressIndicator(
|
||||||
|
value: progress,
|
||||||
|
backgroundColor: Colors.black45,
|
||||||
|
valueColor: AlwaysStoppedAnimation<Color>(EntityColor.stateColor(EntityState.on)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_timer?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -0,0 +1,114 @@
|
|||||||
|
part of '../../../main.dart';
|
||||||
|
|
||||||
|
class MediaPlayerSeekBar extends StatefulWidget {
|
||||||
|
@override
|
||||||
|
_MediaPlayerSeekBarState createState() => _MediaPlayerSeekBarState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _MediaPlayerSeekBarState extends State<MediaPlayerSeekBar> {
|
||||||
|
|
||||||
|
Timer _timer;
|
||||||
|
bool _seekStarted = false;
|
||||||
|
bool _changedHere = false;
|
||||||
|
double _currentPosition = 0;
|
||||||
|
final TextStyle _seekTextStyle = TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
color: Colors.blue,
|
||||||
|
fontWeight: FontWeight.bold
|
||||||
|
);
|
||||||
|
|
||||||
|
@override
|
||||||
|
initState() {
|
||||||
|
super.initState();
|
||||||
|
_timer = Timer.periodic(Duration(seconds: 1), (_) {
|
||||||
|
if (!_seekStarted && !_changedHere) {
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final EntityModel entityModel = EntityModel.of(context);
|
||||||
|
final MediaPlayerEntity entity = entityModel.entityWrapper.entity;
|
||||||
|
DateTime lastUpdated = DateTime.tryParse("${
|
||||||
|
entity.attributes["media_position_updated_at"]}")?.toLocal();
|
||||||
|
if (lastUpdated != null) {
|
||||||
|
Duration duration = Duration(
|
||||||
|
seconds: entity._getIntAttributeValue("media_duration") ?? 1);
|
||||||
|
Duration position = Duration(
|
||||||
|
seconds: entity._getIntAttributeValue("media_position") ?? 0);
|
||||||
|
if (entity.state == EntityState.playing && !_seekStarted &&
|
||||||
|
!_changedHere) {
|
||||||
|
_currentPosition = position.inSeconds.toDouble();
|
||||||
|
int differenceInSeconds = DateTime
|
||||||
|
.now()
|
||||||
|
.difference(lastUpdated)
|
||||||
|
.inSeconds;
|
||||||
|
_currentPosition = (_currentPosition <= duration.inSeconds) ? _currentPosition + differenceInSeconds : duration.inSeconds;
|
||||||
|
} else if (_changedHere) {
|
||||||
|
_changedHere = false;
|
||||||
|
}
|
||||||
|
return Padding(
|
||||||
|
padding: EdgeInsets.fromLTRB(Sizes.leftWidgetPadding, 20, Sizes.rightWidgetPadding, 0),
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: <Widget>[
|
||||||
|
Row(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
children: <Widget>[
|
||||||
|
Text("00:00"),
|
||||||
|
Expanded(
|
||||||
|
child: Text("${Duration(seconds: _currentPosition.toInt()).toString().split(".")[0]}",textAlign: TextAlign.center, style: _seekTextStyle),
|
||||||
|
),
|
||||||
|
Text("${duration.toString().split(".")[0]}")
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Container(height: 10,),
|
||||||
|
Slider(
|
||||||
|
min: 0,
|
||||||
|
activeColor: Colors.amber,
|
||||||
|
inactiveColor: Colors.black26,
|
||||||
|
max: duration.inSeconds.toDouble(),
|
||||||
|
value: _currentPosition,
|
||||||
|
onChangeStart: (val) {
|
||||||
|
_seekStarted = true;
|
||||||
|
},
|
||||||
|
onChanged: (val) {
|
||||||
|
setState(() {
|
||||||
|
_currentPosition = val;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
onChangeEnd: (val) {
|
||||||
|
_seekStarted = false;
|
||||||
|
Timer(Duration(milliseconds: 500), () {
|
||||||
|
if (!_seekStarted) {
|
||||||
|
eventBus.fire(ServiceCallEvent(
|
||||||
|
"media_player",
|
||||||
|
"media_seek",
|
||||||
|
"${entity.entityId}",
|
||||||
|
{"seek_position": val}
|
||||||
|
));
|
||||||
|
setState(() {
|
||||||
|
_changedHere = true;
|
||||||
|
_currentPosition = val;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return Container(width: 0, height: 0,);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
_timer?.cancel();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -26,7 +26,7 @@ class MediaPlayerWidget extends StatelessWidget {
|
|||||||
bottom: 0.0,
|
bottom: 0.0,
|
||||||
left: 0.0,
|
left: 0.0,
|
||||||
right: 0.0,
|
right: 0.0,
|
||||||
child: MediaPlayerProgressWidget()
|
child: MediaPlayerProgressBar()
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -306,9 +306,9 @@ class _MediaPlayerControlsState extends State<MediaPlayerControls> {
|
|||||||
];
|
];
|
||||||
if (entity.state != EntityState.off && entity.state != EntityState.unknown && entity.state != EntityState.unavailable) {
|
if (entity.state != EntityState.off && entity.state != EntityState.unknown && entity.state != EntityState.unavailable) {
|
||||||
if (entity.supportSeek) {
|
if (entity.supportSeek) {
|
||||||
children.add(MediaPlayerSeekWidget());
|
children.add(MediaPlayerSeekBar());
|
||||||
} else {
|
} else {
|
||||||
children.add(MediaPlayerProgressWidget());
|
children.add(MediaPlayerProgressBar());
|
||||||
}
|
}
|
||||||
Widget muteWidget;
|
Widget muteWidget;
|
||||||
Widget volumeStepWidget;
|
Widget volumeStepWidget;
|
||||||
@ -410,173 +410,4 @@ class _MediaPlayerControlsState extends State<MediaPlayerControls> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class MediaPlayerProgressWidget extends StatefulWidget {
|
|
||||||
@override
|
|
||||||
_MediaPlayerProgressWidgetState createState() => _MediaPlayerProgressWidgetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MediaPlayerProgressWidgetState extends State<MediaPlayerProgressWidget> {
|
|
||||||
|
|
||||||
Timer _timer;
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final EntityModel entityModel = EntityModel.of(context);
|
|
||||||
final MediaPlayerEntity entity = entityModel.entityWrapper.entity;
|
|
||||||
double progress;
|
|
||||||
try {
|
|
||||||
DateTime lastUpdated = DateTime.parse(
|
|
||||||
entity.attributes["media_position_updated_at"]).toLocal();
|
|
||||||
Duration duration = Duration(seconds: entity._getIntAttributeValue("media_duration") ?? 1);
|
|
||||||
Duration position = Duration(seconds: entity._getIntAttributeValue("media_position") ?? 0);
|
|
||||||
int currentPosition = position.inSeconds;
|
|
||||||
if (entity.state == EntityState.playing) {
|
|
||||||
_timer?.cancel();
|
|
||||||
_timer = Timer(Duration(seconds: 1), () {
|
|
||||||
setState(() {
|
|
||||||
});
|
|
||||||
});
|
|
||||||
int differenceInSeconds = DateTime
|
|
||||||
.now()
|
|
||||||
.difference(lastUpdated)
|
|
||||||
.inSeconds;
|
|
||||||
currentPosition = currentPosition + differenceInSeconds;
|
|
||||||
} else {
|
|
||||||
_timer?.cancel();
|
|
||||||
}
|
|
||||||
progress = currentPosition / duration.inSeconds;
|
|
||||||
return LinearProgressIndicator(
|
|
||||||
value: progress,
|
|
||||||
backgroundColor: Colors.black45,
|
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(EntityColor.stateColor(EntityState.on)),
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
_timer?.cancel();
|
|
||||||
progress = 0.0;
|
|
||||||
}
|
|
||||||
return LinearProgressIndicator(
|
|
||||||
value: progress,
|
|
||||||
backgroundColor: Colors.black45,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_timer?.cancel();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
class MediaPlayerSeekWidget extends StatefulWidget {
|
|
||||||
@override
|
|
||||||
_MediaPlayerSeekWidgetState createState() => _MediaPlayerSeekWidgetState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MediaPlayerSeekWidgetState extends State<MediaPlayerSeekWidget> {
|
|
||||||
|
|
||||||
Timer _timer;
|
|
||||||
bool _seekStarted = false;
|
|
||||||
double _currentPosition = 0;
|
|
||||||
final TextStyle _seekTextStyle = TextStyle(
|
|
||||||
fontSize: 20,
|
|
||||||
color: Colors.blue,
|
|
||||||
fontWeight: FontWeight.bold
|
|
||||||
);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final EntityModel entityModel = EntityModel.of(context);
|
|
||||||
final MediaPlayerEntity entity = entityModel.entityWrapper.entity;
|
|
||||||
//double progress;
|
|
||||||
try {
|
|
||||||
DateTime lastUpdated = DateTime.parse(
|
|
||||||
entity.attributes["media_position_updated_at"]).toLocal();
|
|
||||||
Duration duration = Duration(seconds: entity._getIntAttributeValue("media_duration") ?? 1);
|
|
||||||
Duration position = Duration(seconds: entity._getIntAttributeValue("media_position") ?? 0);
|
|
||||||
if (entity.state == EntityState.playing && !_seekStarted) {
|
|
||||||
_currentPosition = position.inSeconds.toDouble();
|
|
||||||
_timer?.cancel();
|
|
||||||
_timer = Timer(Duration(seconds: 1), () {
|
|
||||||
if (!_seekStarted) {
|
|
||||||
setState(() {
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
int differenceInSeconds = DateTime
|
|
||||||
.now()
|
|
||||||
.difference(lastUpdated)
|
|
||||||
.inSeconds;
|
|
||||||
_currentPosition += differenceInSeconds;
|
|
||||||
} else {
|
|
||||||
_timer?.cancel();
|
|
||||||
}
|
|
||||||
//progress = currentPosition / duration.inSeconds;
|
|
||||||
return Padding(
|
|
||||||
padding: EdgeInsets.fromLTRB(Sizes.leftWidgetPadding, 20, Sizes.rightWidgetPadding, 0),
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: <Widget>[
|
|
||||||
Row(
|
|
||||||
mainAxisSize: MainAxisSize.max,
|
|
||||||
children: <Widget>[
|
|
||||||
Text("00:00"),
|
|
||||||
Expanded(
|
|
||||||
child: Text("${Duration(seconds: _currentPosition.toInt()).toString().split(".")[0]}",textAlign: TextAlign.center, style: _seekTextStyle),
|
|
||||||
),
|
|
||||||
Text("${duration.toString().split(".")[0]}")
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Slider(
|
|
||||||
min: 0,
|
|
||||||
activeColor: Colors.amber,
|
|
||||||
inactiveColor: Colors.black26,
|
|
||||||
max: duration.inSeconds.toDouble(),
|
|
||||||
value: _currentPosition,
|
|
||||||
onChangeStart: (val) {
|
|
||||||
_seekStarted = true;
|
|
||||||
setState(() {
|
|
||||||
_currentPosition = val;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onChanged: (val) {
|
|
||||||
setState(() {
|
|
||||||
_currentPosition = val;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
onChangeEnd: (val) {
|
|
||||||
_seekStarted = false;
|
|
||||||
eventBus.fire(ServiceCallEvent(
|
|
||||||
"media_player",
|
|
||||||
"media_seek",
|
|
||||||
"${entity.entityId}",
|
|
||||||
{"seek_position": val}
|
|
||||||
));
|
|
||||||
setState(() {
|
|
||||||
_currentPosition = val;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} catch (e) {
|
|
||||||
_timer?.cancel();
|
|
||||||
//progress = 0.0;
|
|
||||||
}
|
|
||||||
return Container(width: 0, height: 0,);
|
|
||||||
/*return LinearProgressIndicator(
|
|
||||||
value: progress,
|
|
||||||
backgroundColor: Colors.black45,
|
|
||||||
);*/
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
_timer?.cancel();
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
@ -126,6 +126,8 @@ part 'cards/widgets/gauge_card_body.dart';
|
|||||||
part 'cards/widgets/light_card_body.dart';
|
part 'cards/widgets/light_card_body.dart';
|
||||||
part 'pages/play_media.page.dart';
|
part 'pages/play_media.page.dart';
|
||||||
part 'entities/entity_page_layout.widget.dart';
|
part 'entities/entity_page_layout.widget.dart';
|
||||||
|
part 'entities/media_player/widgets/media_player_seek_bar.widget.dart';
|
||||||
|
part 'entities/media_player/widgets/media_player_progress_bar.widget.dart';
|
||||||
|
|
||||||
EventBus eventBus = new EventBus();
|
EventBus eventBus = new EventBus();
|
||||||
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
|
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
|
||||||
|
@ -681,21 +681,22 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
|
|||||||
}
|
}
|
||||||
if (playersCount > 0) {
|
if (playersCount > 0) {
|
||||||
mediaMenuIcon = Stack(
|
mediaMenuIcon = Stack(
|
||||||
|
overflow: Overflow.visible,
|
||||||
children: <Widget>[
|
children: <Widget>[
|
||||||
Icon(MaterialDesignIcons.getIconDataFromIconName(
|
Icon(MaterialDesignIcons.getIconDataFromIconName(
|
||||||
"mdi:television"), color: Colors.white,),
|
"mdi:television"), color: Colors.white,),
|
||||||
Positioned(
|
Positioned(
|
||||||
bottom: 0,
|
bottom: -4,
|
||||||
right: 0,
|
right: -4,
|
||||||
child: Container(
|
child: Container(
|
||||||
height: 14,
|
height: 16,
|
||||||
width: 14,
|
width: 16,
|
||||||
decoration: new BoxDecoration(
|
decoration: new BoxDecoration(
|
||||||
color: Colors.amber,
|
color: Colors.orange,
|
||||||
shape: BoxShape.circle,
|
shape: BoxShape.circle,
|
||||||
),
|
),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: Text("$playersCount", style: TextStyle(fontSize: 10)),
|
child: Text("$playersCount", style: TextStyle(fontSize: 12)),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
@ -768,7 +769,7 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
|
|||||||
icon: mediaMenuIcon,
|
icon: mediaMenuIcon,
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showMenu(
|
showMenu(
|
||||||
position: RelativeRect.fromLTRB(MediaQuery.of(context).size.width, 70.0, 0.0, 0.0),
|
position: RelativeRect.fromLTRB(MediaQuery.of(context).size.width, 100.0, 50, 0.0),
|
||||||
context: context,
|
context: context,
|
||||||
items: mediaMenuItems
|
items: mediaMenuItems
|
||||||
).then((String val) {
|
).then((String val) {
|
||||||
@ -785,7 +786,7 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
|
|||||||
"mdi:dots-vertical"), color: Colors.white,),
|
"mdi:dots-vertical"), color: Colors.white,),
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
showMenu(
|
showMenu(
|
||||||
position: RelativeRect.fromLTRB(MediaQuery.of(context).size.width, 70.0, 0.0, 0.0),
|
position: RelativeRect.fromLTRB(MediaQuery.of(context).size.width, 100, 0.0, 0.0),
|
||||||
context: context,
|
context: context,
|
||||||
items: serviceMenuItems
|
items: serviceMenuItems
|
||||||
).then((String val) {
|
).then((String val) {
|
||||||
|
Reference in New Issue
Block a user