Android TV support
This commit is contained in:
parent
dab540674b
commit
f91fe8f216
7 changed files with 184 additions and 112 deletions
|
@ -143,79 +143,58 @@ class _HomePageScreenState extends State<HomePageScreen> {
|
|||
));
|
||||
if (_error)
|
||||
return ErrorScreen();
|
||||
return ListView.builder(
|
||||
shrinkWrap: true,
|
||||
physics: NeverScrollableScrollPhysics(),
|
||||
itemCount: _homePage.sections.length,
|
||||
itemBuilder: (context, i) {
|
||||
return HomepageSectionWidget(_homePage.sections[i]);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class HomepageSectionWidget extends StatelessWidget {
|
||||
|
||||
final HomePageSection section;
|
||||
HomepageSectionWidget(this.section);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
child: Text(
|
||||
section.title,
|
||||
children: List.generate(_homePage.sections.length, (i) {
|
||||
HomePageSection section = _homePage.sections[i];
|
||||
return ListTile(
|
||||
title: Text(
|
||||
section.title??'',
|
||||
textAlign: TextAlign.left,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0,
|
||||
fontWeight: FontWeight.w900
|
||||
fontSize: 20.0,
|
||||
fontWeight: FontWeight.w900
|
||||
),
|
||||
),
|
||||
padding: EdgeInsets.symmetric(horizontal: 12.0, vertical: 8.0)
|
||||
),
|
||||
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: List.generate(section.items.length + 1, (i) {
|
||||
//Has more items
|
||||
if (i == section.items.length) {
|
||||
if (section.hasMore??false) {
|
||||
return FlatButton(
|
||||
child: Text(
|
||||
'Show more'.i18n,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0
|
||||
),
|
||||
),
|
||||
onPressed: () => Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => Scaffold(
|
||||
appBar: FreezerAppBar(section.title),
|
||||
body: SingleChildScrollView(
|
||||
child: HomePageScreen(
|
||||
channel: DeezerChannel(target: section.pagePath)
|
||||
)
|
||||
subtitle: SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
children: List.generate(section.items.length + 1, (j) {
|
||||
//Has more items
|
||||
if (j == section.items.length) {
|
||||
if (section.hasMore ?? false) {
|
||||
return FlatButton(
|
||||
child: Text(
|
||||
'Show more'.i18n,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
fontSize: 20.0
|
||||
),
|
||||
),
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
return Container(height: 0, width: 0);
|
||||
}
|
||||
//Show item
|
||||
HomePageItem item = section.items[i];
|
||||
return HomePageItemWidget(item);
|
||||
}),
|
||||
),
|
||||
),
|
||||
Container(height: 8.0),
|
||||
],
|
||||
onPressed: () => Navigator.of(context).push(MaterialPageRoute(
|
||||
builder: (context) => Scaffold(
|
||||
appBar: FreezerAppBar(section.title),
|
||||
body: SingleChildScrollView(
|
||||
child: HomePageScreen(
|
||||
channel: DeezerChannel(target: section.pagePath)
|
||||
)
|
||||
),
|
||||
),
|
||||
)),
|
||||
);
|
||||
}
|
||||
return Container(height: 0, width: 0);
|
||||
}
|
||||
|
||||
//Show item
|
||||
HomePageItem item = section.items[j];
|
||||
return HomePageItemWidget(item);
|
||||
}),
|
||||
),
|
||||
)
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:freezer/api/deezer.dart';
|
||||
import 'package:freezer/api/player.dart';
|
||||
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
|
||||
|
@ -111,6 +112,17 @@ class _LoginWidgetState extends State<LoginWidget> {
|
|||
_start();
|
||||
}
|
||||
|
||||
// ARL auth: called on "Save" click, Enter and DPAD_Center press
|
||||
void goARL(FocusNode node, TextEditingController _controller) {
|
||||
if (node != null) {
|
||||
node.unfocus();
|
||||
}
|
||||
_controller.clear();
|
||||
settings.arl = _arl.trim();
|
||||
Navigator.of(context).pop();
|
||||
_update();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
|
||||
|
@ -121,7 +133,14 @@ class _LoginWidgetState extends State<LoginWidget> {
|
|||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
|
||||
TextEditingController _controller = new TextEditingController();
|
||||
// For "DPAD center" key handling on remote controls
|
||||
FocusNode focusNode = FocusNode(skipTraversal: true,descendantsAreFocusable: false,onKey: (node, event) {
|
||||
if (event.logicalKey == LogicalKeyboardKey.select) {
|
||||
goARL(node, _controller);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
if (settings.arl == null)
|
||||
return Scaffold(
|
||||
body: Padding(
|
||||
|
@ -165,6 +184,7 @@ class _LoginWidgetState extends State<LoginWidget> {
|
|||
showDialog(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
Future.delayed(Duration(seconds: 1), () => {focusNode.requestFocus()}); // autofocus doesn't work - it's replacement
|
||||
return AlertDialog(
|
||||
title: Text('Enter ARL'.i18n),
|
||||
content: Container(
|
||||
|
@ -173,16 +193,17 @@ class _LoginWidgetState extends State<LoginWidget> {
|
|||
decoration: InputDecoration(
|
||||
labelText: 'Token (ARL)'.i18n
|
||||
),
|
||||
focusNode: focusNode,
|
||||
controller: _controller,
|
||||
onSubmitted: (String s) {
|
||||
goARL(focusNode, _controller);
|
||||
},
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
FlatButton(
|
||||
child: Text('Save'.i18n),
|
||||
onPressed: () {
|
||||
settings.arl = _arl.trim();
|
||||
Navigator.of(context).pop();
|
||||
_update();
|
||||
},
|
||||
onPressed: () => goARL(null, _controller),
|
||||
)
|
||||
],
|
||||
);
|
||||
|
|
|
@ -20,6 +20,7 @@ class PlayerBar extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var focusNode = FocusNode();
|
||||
return GestureDetector(
|
||||
onHorizontalDragUpdate: (details) async {
|
||||
if (_gestureRegistered) return;
|
||||
|
@ -46,9 +47,11 @@ class PlayerBar extends StatelessWidget {
|
|||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
color: Theme.of(context).bottomAppBarColor,
|
||||
// For Android TV: indicate focus by grey
|
||||
color: focusNode.hasFocus ? Colors.black26 : Theme.of(context).bottomAppBarColor,
|
||||
child: ListTile(
|
||||
dense: true,
|
||||
focusNode: focusNode,
|
||||
contentPadding: EdgeInsets.symmetric(horizontal: 8.0),
|
||||
onTap: () {
|
||||
Navigator.of(context).push(MaterialPageRoute(
|
||||
|
|
|
@ -628,6 +628,7 @@ class _SeekBarState extends State<SeekBar> {
|
|||
Container(
|
||||
height: 32.0,
|
||||
child: Slider(
|
||||
focusNode: FocusNode(canRequestFocus: false, skipTraversal: true), // Don't focus on Slider - it doesn't work (and not needed)
|
||||
value: position,
|
||||
max: duration,
|
||||
onChangeStart: (double d) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue