diff --git a/CHANGELOG.md b/CHANGELOG.md index e5eeefb2b..2e70fd722 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to this project will be documented in this file. - Vaults: custom pattern lock - Video: picture-in-picture - Video: handle skip next/previous media buttons +- TV: more media controls ### Changed diff --git a/lib/model/source/media_store_source.dart b/lib/model/source/media_store_source.dart index 546180916..22595cd36 100644 --- a/lib/model/source/media_store_source.dart +++ b/lib/model/source/media_store_source.dart @@ -11,7 +11,6 @@ import 'package:aves/model/source/enums/enums.dart'; import 'package:aves/model/vaults/vaults.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/utils/android_file_utils.dart'; -import 'package:aves/utils/file_utils.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; diff --git a/lib/widgets/about/policy_page.dart b/lib/widgets/about/policy_page.dart index e5c953087..062136870 100644 --- a/lib/widgets/about/policy_page.dart +++ b/lib/widgets/about/policy_page.dart @@ -1,7 +1,6 @@ import 'package:aves/model/settings/settings.dart'; import 'package:aves/widgets/common/basic/markdown_container.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:flutter/material.dart'; import 'package:flutter/services.dart'; @@ -17,7 +16,6 @@ class PolicyPage extends StatefulWidget { class _PolicyPageState extends State { late Future _termsLoader; - final ScrollController _scrollController = ScrollController(); static const termsPath = 'assets/terms.md'; static const termsDirection = TextDirection.ltr; @@ -39,11 +37,8 @@ class _PolicyPageState extends State { child: FocusableActionDetector( autofocus: true, shortcuts: const { - SingleActivator(LogicalKeyboardKey.arrowUp): VerticalScrollIntent.up(), - SingleActivator(LogicalKeyboardKey.arrowDown): VerticalScrollIntent.down(), - }, - actions: { - VerticalScrollIntent: VerticalScrollIntentAction(scrollController: _scrollController), + SingleActivator(LogicalKeyboardKey.arrowUp): ScrollIntent(direction: AxisDirection.up, type: ScrollIncrementType.page), + SingleActivator(LogicalKeyboardKey.arrowDown): ScrollIntent(direction: AxisDirection.down, type: ScrollIncrementType.page), }, child: Center( child: FutureBuilder( @@ -54,7 +49,7 @@ class _PolicyPageState extends State { return Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: MarkdownContainer( - scrollController: _scrollController, + scrollController: PrimaryScrollController.of(context), data: terms, textDirection: termsDirection, ), diff --git a/lib/widgets/aves_app.dart b/lib/widgets/aves_app.dart index a2ad8684a..0e6ce5ef4 100644 --- a/lib/widgets/aves_app.dart +++ b/lib/widgets/aves_app.dart @@ -244,7 +244,9 @@ class _AvesAppState extends State with WidgetsBindingObserver { final darkTheme = themeBrightness == AvesThemeBrightness.black ? Themes.blackTheme(darkAccent, initialized) : Themes.darkTheme(darkAccent, initialized); return 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(), }, child: MediaQuery.fromWindow( diff --git a/lib/widgets/common/behaviour/intents.dart b/lib/widgets/common/behaviour/intents.dart index a01585737..d58dc2e8f 100644 --- a/lib/widgets/common/behaviour/intents.dart +++ b/lib/widgets/common/behaviour/intents.dart @@ -1,37 +1,22 @@ import 'package:flutter/widgets.dart'; -class VerticalScrollIntent extends Intent { - const VerticalScrollIntent({ - 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 { - VerticalScrollIntentAction({ +class ScrollControllerAction extends CallbackAction { + ScrollControllerAction({ required ScrollController scrollController, }) : super(onInvoke: (intent) => _onScrollIntent(intent, scrollController)); static void _onScrollIntent( - VerticalScrollIntent intent, + ScrollIntent intent, ScrollController scrollController, ) { late int factor; - switch (intent.type) { - case VerticalScrollDirection.up: + switch (intent.direction) { + case AxisDirection.up: + case AxisDirection.left: factor = -1; break; - case VerticalScrollDirection.down: + case AxisDirection.down: + case AxisDirection.right: factor = 1; break; } diff --git a/lib/widgets/viewer/entry_vertical_pager.dart b/lib/widgets/viewer/entry_vertical_pager.dart index 45d1507e3..8b73e3be5 100644 --- a/lib/widgets/viewer/entry_vertical_pager.dart +++ b/lib/widgets/viewer/entry_vertical_pager.dart @@ -184,15 +184,33 @@ class _ViewerVerticalPageViewState extends State { Widget _buildImagePage() { final useTvLayout = settings.useTvLayout; - Widget? child; + // cf https://developer.android.com/training/tv/start/controllers#media-events Map? shortcuts = { const SingleActivator(LogicalKeyboardKey.arrowUp): useTvLayout ? const TvShowLessInfoIntent() : const _LeaveIntent(), const SingleActivator(LogicalKeyboardKey.arrowDown): useTvLayout ? const _TvShowMoreInfoIntent() : const _ShowInfoIntent(), - const SingleActivator(LogicalKeyboardKey.mediaPause): const _PlayPauseIntent.pause(), - const SingleActivator(LogicalKeyboardKey.mediaPlay): const _PlayPauseIntent.play(), + // KEYCODE_MEDIA_PLAY_PAUSE / 85 / play/pause 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) { shortcuts.addAll(const { SingleActivator(LogicalKeyboardKey.arrowLeft): _ShowPreviousIntent(), diff --git a/lib/widgets/viewer/info/metadata/tv_page.dart b/lib/widgets/viewer/info/metadata/tv_page.dart index 38273c361..dbf3033f4 100644 --- a/lib/widgets/viewer/info/metadata/tv_page.dart +++ b/lib/widgets/viewer/info/metadata/tv_page.dart @@ -97,11 +97,11 @@ class _TvMetadataPageState extends State { Expanded( child: FocusableActionDetector( shortcuts: const { - SingleActivator(LogicalKeyboardKey.arrowUp): VerticalScrollIntent.up(), - SingleActivator(LogicalKeyboardKey.arrowDown): VerticalScrollIntent.down(), + SingleActivator(LogicalKeyboardKey.arrowUp): ScrollIntent(direction: AxisDirection.up, type: ScrollIncrementType.page), + SingleActivator(LogicalKeyboardKey.arrowDown): ScrollIntent(direction: AxisDirection.down, type: ScrollIncrementType.page), }, actions: { - VerticalScrollIntent: VerticalScrollIntentAction(scrollController: _detailsScrollController), + ScrollIntent: ScrollControllerAction(scrollController: _detailsScrollController), }, child: SingleChildScrollView( controller: _detailsScrollController,