Skip to content

Commit

Permalink
Merge pull request #667 from lamarios/feature/force-android-tv-ui
Browse files Browse the repository at this point in the history
add settings to force the TV ui, can be useful for some tv boxes
  • Loading branch information
lamarios authored Jan 31, 2025
2 parents cd849ae + caf0168 commit 2b694ec
Show file tree
Hide file tree
Showing 9 changed files with 64 additions and 18 deletions.
4 changes: 3 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -1383,5 +1383,7 @@
"premieresIn": "Premieres in {formattedDuration}",
"screenControls": "Screen controls",
"screenControlsExplanation": "When watching a video in full screen, Vertically dragging from the left or the right will adjust the brightness or volume respectively",
"retry": "Retry"
"retry": "Retry",
"forceTvUi": "Force TV interface",
"forceTvUiExplanation": "Force the interface to be the TV experience, can be useful for some devices that do not have the leanback system config. App restart required"
}
1 change: 1 addition & 0 deletions lib/settings/models/db/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ const dearrowSettingName = 'dearrow';
const dearrowThumbnailsSettingName = "dearrow-thumbnails";
const fullScreenOnLandscapeSettingName = "fullscreen-on-landscape";
const screenControlsSettingName = "screen-controls";
const forceTvUiSettingName = "force-tv-ui";

const onOpenSettingName = "on-open";

Expand Down
5 changes: 5 additions & 0 deletions lib/settings/states/settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,8 @@ class SettingsCubit extends Cubit<SettingsState> {

setFillFullscreen(bool b) async => await _set(fillFullScreen, b);

setForceTvUi(bool b) async => await _set(forceTvUiSettingName, b);

_setLocale(String? s) async {
await _set(localeSettingName, s);
await fileDb.setLocale(s);
Expand Down Expand Up @@ -563,6 +565,9 @@ class SettingsState with _$SettingsState {
bool get screenControls =>
(_get(screenControlsSettingName)?.value ?? 'true') == 'true';

bool get forceTvUi =>
(_get(forceTvUiSettingName)?.value ?? 'false') == 'true';

List<HomeDataSource> get appLayout {
var savedLayout = _get(appLayoutSettingName)?.value;
// String? savedLayout;
Expand Down
7 changes: 7 additions & 0 deletions lib/settings/views/screens/appearance.dart
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,13 @@ class AppearanceSettingsScreen extends StatelessWidget {
context, state.navigationBarLabelBehavior)),
onPressed: (ctx) => customizeNavigationLabel(ctx),
),
SettingsTile.switchTile(
leading: const Icon(Icons.tv),
initialValue: state.forceTvUi,
onToggle: cubit.setForceTvUi,
title: Text(locals.forceTvUi),
description: Text(locals.forceTvUiExplanation),
),
],
),
],
Expand Down
19 changes: 13 additions & 6 deletions lib/settings/views/tv/screens/settings.dart
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:clipious/app/states/app.dart';
import 'package:clipious/extensions.dart';
import 'package:clipious/router.dart';
import 'package:clipious/utils.dart';
import 'package:clipious/utils/views/tv/components/tv_button.dart';
import 'package:clipious/utils/views/tv/components/tv_overscan.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:locale_names/locale_names.dart';
import 'package:logging/logging.dart';

Expand Down Expand Up @@ -340,6 +340,13 @@ class TVSettingsScreen extends StatelessWidget {
trailing: Switch(
onChanged: (value) {}, value: state.blackBackground),
),
SettingsTile(
title: locals.forceTvUi,
description: locals.forceTvUiExplanation,
onSelected: (context) => cubit.setForceTvUi(!state.forceTvUi),
trailing:
Switch(onChanged: (value) {}, value: state.forceTvUi),
),
SettingsTitle(title: locals.about),
SettingsTile(
title: '${locals.name}: ${state.packageInfo.appName}',
Expand Down Expand Up @@ -505,7 +512,7 @@ class SettingsTile extends StatelessWidget {
enabled: enabled ?? true,
leading: leading,
trailing: trailing,
selectedTileColor: colors.secondaryContainer.withOpacity(0.5),
selectedTileColor: colors.secondaryContainer.withValues(alpha: 0.5),
selected: hasFocus,
title: Column(
crossAxisAlignment: CrossAxisAlignment.start,
Expand All @@ -515,7 +522,7 @@ class SettingsTile extends StatelessWidget {
style: textTheme.headlineSmall!.copyWith(
color: enabled ?? true
? colors.primary
: colors.primary.withOpacity(0.5)),
: colors.primary.withValues(alpha: 0.5)),
),
description != null
? Text(
Expand Down
5 changes: 4 additions & 1 deletion lib/utils.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import 'dart:math';

import 'package:clipious/settings/models/db/settings.dart';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
Expand Down Expand Up @@ -203,9 +204,11 @@ DeviceType getDeviceType() {
}

Future<bool> isDeviceTv() async {
final forceTv = db.getSettings(forceTvUiSettingName);
DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
AndroidDeviceInfo androidInfo = await deviceInfo.androidInfo;
return androidInfo.systemFeatures.contains('android.software.leanback');
return forceTv?.value == 'true' ||
androidInfo.systemFeatures.contains('android.software.leanback');
}

int getGridCount(BuildContext context) {
Expand Down
3 changes: 1 addition & 2 deletions lib/videos/models/video.dart
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,7 @@ class Video with _$Video implements ShareLinks, IdedVideo {
const factory Video(
{required String videoId,
int? viewCount,
// not used in the code and causes issues
// @JsonKey(fromJson: _parsePublished) int? published,
@JsonKey(fromJson: _parsePublished) int? published,
int? index,
String? indexId,
String? publishedText,
Expand Down
36 changes: 28 additions & 8 deletions lib/videos/models/video.freezed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ Video _$VideoFromJson(Map<String, dynamic> json) {
/// @nodoc
mixin _$Video {
String get videoId => throw _privateConstructorUsedError;
int? get viewCount =>
throw _privateConstructorUsedError; // not used in the code and causes issues
// @JsonKey(fromJson: _parsePublished) int? published,
int? get viewCount => throw _privateConstructorUsedError;
@JsonKey(fromJson: _parsePublished)
int? get published => throw _privateConstructorUsedError;
int? get index => throw _privateConstructorUsedError;
String? get indexId => throw _privateConstructorUsedError;
String? get publishedText => throw _privateConstructorUsedError;
Expand Down Expand Up @@ -88,6 +88,7 @@ abstract class $VideoCopyWith<$Res> {
$Res call(
{String videoId,
int? viewCount,
@JsonKey(fromJson: _parsePublished) int? published,
int? index,
String? indexId,
String? publishedText,
Expand Down Expand Up @@ -149,6 +150,7 @@ class _$VideoCopyWithImpl<$Res, $Val extends Video>
$Res call({
Object? videoId = null,
Object? viewCount = freezed,
Object? published = freezed,
Object? index = freezed,
Object? indexId = freezed,
Object? publishedText = freezed,
Expand Down Expand Up @@ -199,6 +201,10 @@ class _$VideoCopyWithImpl<$Res, $Val extends Video>
? _value.viewCount
: viewCount // ignore: cast_nullable_to_non_nullable
as int?,
published: freezed == published
? _value.published
: published // ignore: cast_nullable_to_non_nullable
as int?,
index: freezed == index
? _value.index
: index // ignore: cast_nullable_to_non_nullable
Expand Down Expand Up @@ -373,6 +379,7 @@ abstract class _$$VideoImplCopyWith<$Res> implements $VideoCopyWith<$Res> {
$Res call(
{String videoId,
int? viewCount,
@JsonKey(fromJson: _parsePublished) int? published,
int? index,
String? indexId,
String? publishedText,
Expand Down Expand Up @@ -432,6 +439,7 @@ class __$$VideoImplCopyWithImpl<$Res>
$Res call({
Object? videoId = null,
Object? viewCount = freezed,
Object? published = freezed,
Object? index = freezed,
Object? indexId = freezed,
Object? publishedText = freezed,
Expand Down Expand Up @@ -482,6 +490,10 @@ class __$$VideoImplCopyWithImpl<$Res>
? _value.viewCount
: viewCount // ignore: cast_nullable_to_non_nullable
as int?,
published: freezed == published
? _value.published
: published // ignore: cast_nullable_to_non_nullable
as int?,
index: freezed == index
? _value.index
: index // ignore: cast_nullable_to_non_nullable
Expand Down Expand Up @@ -652,6 +664,7 @@ class _$VideoImpl extends _Video {
const _$VideoImpl(
{required this.videoId,
this.viewCount,
@JsonKey(fromJson: _parsePublished) this.published,
this.index,
this.indexId,
this.publishedText,
Expand Down Expand Up @@ -715,8 +728,9 @@ class _$VideoImpl extends _Video {
final String videoId;
@override
final int? viewCount;
// not used in the code and causes issues
// @JsonKey(fromJson: _parsePublished) int? published,
@override
@JsonKey(fromJson: _parsePublished)
final int? published;
@override
final int? index;
@override
Expand Down Expand Up @@ -872,7 +886,7 @@ class _$VideoImpl extends _Video {

@override
String toString() {
return 'Video(videoId: $videoId, viewCount: $viewCount, index: $index, indexId: $indexId, publishedText: $publishedText, isUpcoming: $isUpcoming, premiereTimestamp: $premiereTimestamp, dashUrl: $dashUrl, description: $description, descriptionHtml: $descriptionHtml, keywords: $keywords, likeCount: $likeCount, dislikeCount: $dislikeCount, paid: $paid, premium: $premium, isFamilyFriendly: $isFamilyFriendly, allowedRegions: $allowedRegions, genre: $genre, genreUrl: $genreUrl, authorThumbnails: $authorThumbnails, subCountText: $subCountText, allowRatings: $allowRatings, rating: $rating, isListed: $isListed, liveNow: $liveNow, hlsUrl: $hlsUrl, adaptiveFormats: $adaptiveFormats, formatStreams: $formatStreams, captions: $captions, recommendedVideos: $recommendedVideos, title: $title, lengthSeconds: $lengthSeconds, author: $author, authorId: $authorId, authorUrl: $authorUrl, videoThumbnails: $videoThumbnails, filtered: $filtered, matchedFilters: $matchedFilters, filterHide: $filterHide, deArrowed: $deArrowed, deArrowThumbnailUrl: $deArrowThumbnailUrl, viewCountText: $viewCountText)';
return 'Video(videoId: $videoId, viewCount: $viewCount, published: $published, index: $index, indexId: $indexId, publishedText: $publishedText, isUpcoming: $isUpcoming, premiereTimestamp: $premiereTimestamp, dashUrl: $dashUrl, description: $description, descriptionHtml: $descriptionHtml, keywords: $keywords, likeCount: $likeCount, dislikeCount: $dislikeCount, paid: $paid, premium: $premium, isFamilyFriendly: $isFamilyFriendly, allowedRegions: $allowedRegions, genre: $genre, genreUrl: $genreUrl, authorThumbnails: $authorThumbnails, subCountText: $subCountText, allowRatings: $allowRatings, rating: $rating, isListed: $isListed, liveNow: $liveNow, hlsUrl: $hlsUrl, adaptiveFormats: $adaptiveFormats, formatStreams: $formatStreams, captions: $captions, recommendedVideos: $recommendedVideos, title: $title, lengthSeconds: $lengthSeconds, author: $author, authorId: $authorId, authorUrl: $authorUrl, videoThumbnails: $videoThumbnails, filtered: $filtered, matchedFilters: $matchedFilters, filterHide: $filterHide, deArrowed: $deArrowed, deArrowThumbnailUrl: $deArrowThumbnailUrl, viewCountText: $viewCountText)';
}

@override
Expand All @@ -883,6 +897,8 @@ class _$VideoImpl extends _Video {
(identical(other.videoId, videoId) || other.videoId == videoId) &&
(identical(other.viewCount, viewCount) ||
other.viewCount == viewCount) &&
(identical(other.published, published) ||
other.published == published) &&
(identical(other.index, index) || other.index == index) &&
(identical(other.indexId, indexId) || other.indexId == indexId) &&
(identical(other.publishedText, publishedText) ||
Expand Down Expand Up @@ -958,6 +974,7 @@ class _$VideoImpl extends _Video {
runtimeType,
videoId,
viewCount,
published,
index,
indexId,
publishedText,
Expand Down Expand Up @@ -1020,6 +1037,7 @@ abstract class _Video extends Video implements ShareLinks, IdedVideo {
const factory _Video(
{required final String videoId,
final int? viewCount,
@JsonKey(fromJson: _parsePublished) final int? published,
final int? index,
final String? indexId,
final String? publishedText,
Expand Down Expand Up @@ -1072,8 +1090,10 @@ abstract class _Video extends Video implements ShareLinks, IdedVideo {
@override
String get videoId;
@override
int? get viewCount; // not used in the code and causes issues
// @JsonKey(fromJson: _parsePublished) int? published,
int? get viewCount;
@override
@JsonKey(fromJson: _parsePublished)
int? get published;
@override
int? get index;
@override
Expand Down
2 changes: 2 additions & 0 deletions lib/videos/models/video.g.dart

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 2b694ec

Please sign in to comment.