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,
|
||||
left: 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.supportSeek) {
|
||||
children.add(MediaPlayerSeekWidget());
|
||||
children.add(MediaPlayerSeekBar());
|
||||
} else {
|
||||
children.add(MediaPlayerProgressWidget());
|
||||
children.add(MediaPlayerProgressBar());
|
||||
}
|
||||
Widget muteWidget;
|
||||
Widget volumeStepWidget;
|
||||
@ -411,172 +411,3 @@ 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 'pages/play_media.page.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();
|
||||
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging();
|
||||
|
@ -681,21 +681,22 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
|
||||
}
|
||||
if (playersCount > 0) {
|
||||
mediaMenuIcon = Stack(
|
||||
overflow: Overflow.visible,
|
||||
children: <Widget>[
|
||||
Icon(MaterialDesignIcons.getIconDataFromIconName(
|
||||
"mdi:television"), color: Colors.white,),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
right: 0,
|
||||
bottom: -4,
|
||||
right: -4,
|
||||
child: Container(
|
||||
height: 14,
|
||||
width: 14,
|
||||
height: 16,
|
||||
width: 16,
|
||||
decoration: new BoxDecoration(
|
||||
color: Colors.amber,
|
||||
color: Colors.orange,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
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,
|
||||
onPressed: () {
|
||||
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,
|
||||
items: mediaMenuItems
|
||||
).then((String val) {
|
||||
@ -785,7 +786,7 @@ class _MainPageState extends ReceiveShareState<MainPage> with WidgetsBindingObse
|
||||
"mdi:dots-vertical"), color: Colors.white,),
|
||||
onPressed: () {
|
||||
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,
|
||||
items: serviceMenuItems
|
||||
).then((String val) {
|
||||
|
Reference in New Issue
Block a user