Android TV support

This commit is contained in:
kilowatt 2020-10-31 23:52:23 +03:00
parent dab540674b
commit f91fe8f216
7 changed files with 184 additions and 112 deletions

View file

@ -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);
}),
),
)
);
})
);
}
}

View file

@ -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),
)
],
);

View file

@ -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(

View file

@ -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) {