tv: handle more media key events

This commit is contained in:
Thibault Deckers 2023-03-10 12:50:14 +01:00
parent beb65a8d51
commit 1521db29a5
7 changed files with 39 additions and 39 deletions

View file

@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file.
- Vaults: custom pattern lock - Vaults: custom pattern lock
- Video: picture-in-picture - Video: picture-in-picture
- Video: handle skip next/previous media buttons - Video: handle skip next/previous media buttons
- TV: more media controls
### Changed ### Changed

View file

@ -11,7 +11,6 @@ import 'package:aves/model/source/enums/enums.dart';
import 'package:aves/model/vaults/vaults.dart'; import 'package:aves/model/vaults/vaults.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/file_utils.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';

View file

@ -1,7 +1,6 @@
import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/settings/settings.dart';
import 'package:aves/widgets/common/basic/markdown_container.dart'; import 'package:aves/widgets/common/basic/markdown_container.dart';
import 'package:aves/widgets/common/basic/scaffold.dart'; import 'package:aves/widgets/common/basic/scaffold.dart';
import 'package:aves/widgets/common/behaviour/intents.dart';
import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
@ -17,7 +16,6 @@ class PolicyPage extends StatefulWidget {
class _PolicyPageState extends State<PolicyPage> { class _PolicyPageState extends State<PolicyPage> {
late Future<String> _termsLoader; late Future<String> _termsLoader;
final ScrollController _scrollController = ScrollController();
static const termsPath = 'assets/terms.md'; static const termsPath = 'assets/terms.md';
static const termsDirection = TextDirection.ltr; static const termsDirection = TextDirection.ltr;
@ -39,11 +37,8 @@ class _PolicyPageState extends State<PolicyPage> {
child: FocusableActionDetector( child: FocusableActionDetector(
autofocus: true, autofocus: true,
shortcuts: const { shortcuts: const {
SingleActivator(LogicalKeyboardKey.arrowUp): VerticalScrollIntent.up(), SingleActivator(LogicalKeyboardKey.arrowUp): ScrollIntent(direction: AxisDirection.up, type: ScrollIncrementType.page),
SingleActivator(LogicalKeyboardKey.arrowDown): VerticalScrollIntent.down(), SingleActivator(LogicalKeyboardKey.arrowDown): ScrollIntent(direction: AxisDirection.down, type: ScrollIncrementType.page),
},
actions: {
VerticalScrollIntent: VerticalScrollIntentAction(scrollController: _scrollController),
}, },
child: Center( child: Center(
child: FutureBuilder<String>( child: FutureBuilder<String>(
@ -54,7 +49,7 @@ class _PolicyPageState extends State<PolicyPage> {
return Padding( return Padding(
padding: const EdgeInsets.symmetric(vertical: 8), padding: const EdgeInsets.symmetric(vertical: 8),
child: MarkdownContainer( child: MarkdownContainer(
scrollController: _scrollController, scrollController: PrimaryScrollController.of(context),
data: terms, data: terms,
textDirection: termsDirection, textDirection: termsDirection,
), ),

View file

@ -244,7 +244,9 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
final darkTheme = themeBrightness == AvesThemeBrightness.black ? Themes.blackTheme(darkAccent, initialized) : Themes.darkTheme(darkAccent, initialized); final darkTheme = themeBrightness == AvesThemeBrightness.black ? Themes.blackTheme(darkAccent, initialized) : Themes.darkTheme(darkAccent, initialized);
return Shortcuts( return Shortcuts(
shortcuts: { shortcuts: {
// handle Android TV remote `select` button // handle Android TV remote `select` button (KEYCODE_DPAD_CENTER)
// the following keys are already handled by default:
// KEYCODE_ENTER, KEYCODE_BUTTON_A, KEYCODE_NUMPAD_ENTER
LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(), LogicalKeySet(LogicalKeyboardKey.select): const ActivateIntent(),
}, },
child: MediaQuery.fromWindow( child: MediaQuery.fromWindow(

View file

@ -1,37 +1,22 @@
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
class VerticalScrollIntent extends Intent { class ScrollControllerAction extends CallbackAction<ScrollIntent> {
const VerticalScrollIntent({ ScrollControllerAction({
required this.type,
});
const VerticalScrollIntent.up() : type = VerticalScrollDirection.up;
const VerticalScrollIntent.down() : type = VerticalScrollDirection.down;
final VerticalScrollDirection type;
}
enum VerticalScrollDirection {
up,
down,
}
class VerticalScrollIntentAction extends CallbackAction<VerticalScrollIntent> {
VerticalScrollIntentAction({
required ScrollController scrollController, required ScrollController scrollController,
}) : super(onInvoke: (intent) => _onScrollIntent(intent, scrollController)); }) : super(onInvoke: (intent) => _onScrollIntent(intent, scrollController));
static void _onScrollIntent( static void _onScrollIntent(
VerticalScrollIntent intent, ScrollIntent intent,
ScrollController scrollController, ScrollController scrollController,
) { ) {
late int factor; late int factor;
switch (intent.type) { switch (intent.direction) {
case VerticalScrollDirection.up: case AxisDirection.up:
case AxisDirection.left:
factor = -1; factor = -1;
break; break;
case VerticalScrollDirection.down: case AxisDirection.down:
case AxisDirection.right:
factor = 1; factor = 1;
break; break;
} }

View file

@ -184,15 +184,33 @@ class _ViewerVerticalPageViewState extends State<ViewerVerticalPageView> {
Widget _buildImagePage() { Widget _buildImagePage() {
final useTvLayout = settings.useTvLayout; final useTvLayout = settings.useTvLayout;
Widget? child; // cf https://developer.android.com/training/tv/start/controllers#media-events
Map<ShortcutActivator, Intent>? shortcuts = { Map<ShortcutActivator, Intent>? shortcuts = {
const SingleActivator(LogicalKeyboardKey.arrowUp): useTvLayout ? const TvShowLessInfoIntent() : const _LeaveIntent(), const SingleActivator(LogicalKeyboardKey.arrowUp): useTvLayout ? const TvShowLessInfoIntent() : const _LeaveIntent(),
const SingleActivator(LogicalKeyboardKey.arrowDown): useTvLayout ? const _TvShowMoreInfoIntent() : const _ShowInfoIntent(), const SingleActivator(LogicalKeyboardKey.arrowDown): useTvLayout ? const _TvShowMoreInfoIntent() : const _ShowInfoIntent(),
const SingleActivator(LogicalKeyboardKey.mediaPause): const _PlayPauseIntent.pause(), // KEYCODE_MEDIA_PLAY_PAUSE / 85 / play/pause
const SingleActivator(LogicalKeyboardKey.mediaPlay): const _PlayPauseIntent.play(),
const SingleActivator(LogicalKeyboardKey.mediaPlayPause): const _PlayPauseIntent.toggle(), const SingleActivator(LogicalKeyboardKey.mediaPlayPause): const _PlayPauseIntent.toggle(),
// KEYCODE_MEDIA_STOP / 86 / stop
const SingleActivator(LogicalKeyboardKey.mediaStop): const _PlayPauseIntent.pause(),
// KEYCODE_MEDIA_NEXT / 87 / skip to next
const SingleActivator(LogicalKeyboardKey.mediaTrackNext): const _ShowNextIntent(),
// KEYCODE_MEDIA_PREVIOUS / 88 / skip to previous
const SingleActivator(LogicalKeyboardKey.mediaTrackPrevious): const _ShowPreviousIntent(),
// KEYCODE_MEDIA_PLAY / 126 / play
const SingleActivator(LogicalKeyboardKey.mediaPlay): const _PlayPauseIntent.play(),
// KEYCODE_MEDIA_PAUSE / 127 / pause
const SingleActivator(LogicalKeyboardKey.mediaPause): const _PlayPauseIntent.pause(),
// KEYCODE_BUTTON_L1 / 102 / skip to previous
const SingleActivator(LogicalKeyboardKey.gameButtonLeft1): const _ShowPreviousIntent(),
// KEYCODE_BUTTON_R1 / 103 / skip to next
const SingleActivator(LogicalKeyboardKey.gameButtonRight1): const _ShowNextIntent(),
// KEYCODE_BUTTON_START / 108 / pause
const SingleActivator(LogicalKeyboardKey.gameButtonStart): const _PlayPauseIntent.pause(),
// KEYCODE_BUTTON_SELECT / 109 / play/pause
const SingleActivator(LogicalKeyboardKey.gameButtonSelect): const _PlayPauseIntent.toggle(),
}; };
Widget? child;
if (hasCollection) { if (hasCollection) {
shortcuts.addAll(const { shortcuts.addAll(const {
SingleActivator(LogicalKeyboardKey.arrowLeft): _ShowPreviousIntent(), SingleActivator(LogicalKeyboardKey.arrowLeft): _ShowPreviousIntent(),

View file

@ -97,11 +97,11 @@ class _TvMetadataPageState extends State<TvMetadataPage> {
Expanded( Expanded(
child: FocusableActionDetector( child: FocusableActionDetector(
shortcuts: const { shortcuts: const {
SingleActivator(LogicalKeyboardKey.arrowUp): VerticalScrollIntent.up(), SingleActivator(LogicalKeyboardKey.arrowUp): ScrollIntent(direction: AxisDirection.up, type: ScrollIncrementType.page),
SingleActivator(LogicalKeyboardKey.arrowDown): VerticalScrollIntent.down(), SingleActivator(LogicalKeyboardKey.arrowDown): ScrollIntent(direction: AxisDirection.down, type: ScrollIncrementType.page),
}, },
actions: { actions: {
VerticalScrollIntent: VerticalScrollIntentAction(scrollController: _detailsScrollController), ScrollIntent: ScrollControllerAction(scrollController: _detailsScrollController),
}, },
child: SingleChildScrollView( child: SingleChildScrollView(
controller: _detailsScrollController, controller: _detailsScrollController,