diff --git a/assets b/assets index 1f2fad1c3..f12825bf1 160000 --- a/assets +++ b/assets @@ -1 +1 @@ -Subproject commit 1f2fad1c3bcec32571f4122e3daf585625c2b855 +Subproject commit f12825bf1c5316a3c27fc9576e0646c4b8dba6e1 diff --git a/lib/service/navigation_service.dart b/lib/service/navigation_service.dart index d958216d7..9ef58e01e 100644 --- a/lib/service/navigation_service.dart +++ b/lib/service/navigation_service.dart @@ -13,6 +13,7 @@ import 'package:autonomy_flutter/model/pair.dart'; import 'package:autonomy_flutter/screen/app_router.dart'; import 'package:autonomy_flutter/screen/artist_details/artist_details_page.dart'; import 'package:autonomy_flutter/screen/detail/artwork_detail_page.dart'; +import 'package:autonomy_flutter/screen/detail/preview/canvas_device_bloc.dart'; import 'package:autonomy_flutter/screen/feralfile_home/feralfile_home.dart'; import 'package:autonomy_flutter/screen/interactive_postcard/design_stamp.dart'; import 'package:autonomy_flutter/screen/irl_screen/webview_irl_screen.dart'; @@ -29,10 +30,18 @@ import 'package:autonomy_flutter/util/string_ext.dart'; import 'package:autonomy_flutter/util/style.dart'; import 'package:autonomy_flutter/util/subscription_detail_ext.dart'; import 'package:autonomy_flutter/util/ui_helper.dart'; +import 'package:autonomy_flutter/view/cast_button.dart'; +import 'package:autonomy_flutter/view/display_instruction_view.dart'; import 'package:autonomy_flutter/view/membership_card.dart'; +import 'package:autonomy_flutter/view/stream_device_view.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:feralfile_app_theme/feral_file_app_theme.dart'; +import 'package:feralfile_app_tv_proto/models/canvas_device.dart'; +import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:flutter_svg/svg.dart'; +import 'package:flutter_vibrate/flutter_vibrate.dart'; import 'package:nft_collection/database/nft_collection_database.dart'; import 'package:nft_collection/models/asset_token.dart'; // ignore_for_file: implementation_imports import 'package:overlay_support/src/overlay_state_finder.dart'; @@ -609,4 +618,305 @@ class NavigationService { } return argument; } + + Future showFlexibleDialog( + Widget content, { + bool isDismissible = false, + bool isRoundCorner = true, + Color? backgroundColor, + int autoDismissAfter = 0, + FeedbackType? feedback = FeedbackType.selection, + }) async { + await UIHelper.showFlexibleDialog( + context, + content, + isDismissible: isDismissible, + isRoundCorner: isRoundCorner, + backgroundColor: backgroundColor, + autoDismissAfter: autoDismissAfter, + feedback: feedback, + ); + } + + Widget _stepBuilder(BuildContext context, int step, Widget child) { + final numberFormater = NumberFormat('00'); + final theme = Theme.of(context); + return Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + numberFormater.format(step), + style: theme.textTheme.ppMori700White14, + ), + const SizedBox(width: 30), + Expanded(child: child), + ], + ); + } + + Widget _getStep1(BuildContext context, SupportedDisplayBranch displayBranch) { + final theme = Theme.of(context); + switch (displayBranch) { + case SupportedDisplayBranch.samsung: + return RichText( + textScaler: MediaQuery.textScalerOf(context), + text: TextSpan( + style: theme.textTheme.ppMori400White14, + children: [ + TextSpan( + text: "${'search_for'.tr()} ", + ), + WidgetSpan( + baseline: TextBaseline.alphabetic, + alignment: PlaceholderAlignment.baseline, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + border: Border.all(color: AppColor.white), + ), + padding: const EdgeInsets.symmetric(horizontal: 5), + child: Text( + textScaler: const TextScaler.linear(1), + 'feral_file'.tr(), + style: theme.textTheme.ppMori700White14, + ), + ), + ), + TextSpan( + text: " ${'app_from_samsung'.tr()}.", + ), + ], + ), + ); + case SupportedDisplayBranch.lg: + return const SizedBox(); + case SupportedDisplayBranch.chromecast: + case SupportedDisplayBranch.sony: + case SupportedDisplayBranch.Hisense: + case SupportedDisplayBranch.TCL: + return RichText( + textScaler: MediaQuery.textScalerOf(context), + text: TextSpan( + style: theme.textTheme.ppMori400White14, + children: [ + TextSpan( + text: "${'search_for'.tr()} ", + ), + WidgetSpan( + baseline: TextBaseline.alphabetic, + alignment: PlaceholderAlignment.baseline, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + border: Border.all(color: AppColor.white), + ), + padding: const EdgeInsets.symmetric(horizontal: 5), + child: Text( + textScaler: const TextScaler.linear(1), + 'feral_file'.tr(), + style: theme.textTheme.ppMori700White14, + ), + ), + ), + TextSpan( + text: " ${'in_app_store_section'.tr()}", + ), + ], + ), + ); + case SupportedDisplayBranch.other: + return RichText( + textScaler: MediaQuery.textScalerOf(context), + text: TextSpan( + style: theme.textTheme.ppMori400White14, + children: [ + TextSpan( + text: "${'type'.tr()} ", + ), + WidgetSpan( + baseline: TextBaseline.alphabetic, + alignment: PlaceholderAlignment.baseline, + child: Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(5), + border: Border.all(color: AppColor.white), + ), + padding: const EdgeInsets.symmetric(horizontal: 5), + child: Text( + textScaler: const TextScaler.linear(1), + 'https://display.feralfile.com', + style: theme.textTheme.ppMori700White14, + ), + ), + ), + TextSpan( + text: " ${'on_tv_browser'.tr()}.", + ), + ], + ), + ); + } + } + + Widget _getStep2(BuildContext context, SupportedDisplayBranch displayBranch) { + final theme = Theme.of(context); + switch (displayBranch) { + case SupportedDisplayBranch.lg: + return const SizedBox(); + case SupportedDisplayBranch.samsung: + case SupportedDisplayBranch.chromecast: + case SupportedDisplayBranch.sony: + case SupportedDisplayBranch.Hisense: + case SupportedDisplayBranch.TCL: + return Text( + 'install_and_launch_tv_app'.tr(), + style: theme.textTheme.ppMori400White14, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ); + case SupportedDisplayBranch.other: + return Text( + 'discover_the_daily'.tr(), + style: theme.textTheme.ppMori400White14, + maxLines: 2, + overflow: TextOverflow.ellipsis, + ); + } + } + + Widget _getStep(SupportedDisplayBranch displayBranch, Function? onScanQRTap) { + final theme = Theme.of(context); + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _stepBuilder( + context, + 1, + _getStep1( + context, + displayBranch, + ), + ), + const SizedBox(height: 10), + _stepBuilder( + context, + 2, + _getStep2( + context, + displayBranch, + ), + ), + const SizedBox( + height: 10, + ), + _stepBuilder( + context, + 3, + RichText( + textScaler: MediaQuery.textScalerOf(context), + text: TextSpan( + style: theme.textTheme.ppMori400White14, + children: [ + TextSpan( + recognizer: TapGestureRecognizer() + ..onTap = () { + onScanQRTap?.call(); + }, + text: 'scan_the_qr_code'.tr(), + style: onScanQRTap != null + ? const TextStyle( + decoration: TextDecoration.underline, + ) + : null, + ), + TextSpan( + text: " ${'on_your_TV'.tr()}", + ) + ], + ), + ), + ), + ], + ); + } + + Future showHowToDisplay( + SupportedDisplayBranch displayBranch, + Function? onScanQRTap, + ) async { + Widget child; + final theme = Theme.of(context); + child = Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Display Art on', + style: theme.textTheme.ppMori700White24, + ), + const SizedBox(height: 6), + displayBranch.logo, + ], + ), + ), + const SizedBox(width: 16), + GestureDetector( + onTap: () { + Navigator.pop(context); + injector().showStreamAction('', null); + }, + child: SvgPicture.asset('assets/images/left-arrow.svg', + width: 22, + height: 22, + colorFilter: const ColorFilter.mode( + AppColor.white, + BlendMode.srcIn, + )), + ) + ], + ), + const SizedBox(height: 20), + displayBranch.demoPicture(context), + const SizedBox(height: 20), + if (displayBranch.isComingSoon) + const SizedBox( + height: 125, + ) + else + _getStep(displayBranch, onScanQRTap), + ], + ), + ); + Navigator.pop(context); + unawaited(injector().showFlexibleDialog( + child, + isDismissible: true, + )); + } + + Future showStreamAction(String displayKey, + Function(CanvasDevice device)? onDeviceSelected) async { + keyboardManagerKey.currentState?.hideKeyboard(); + injector().showFlexibleDialog( + BlocProvider.value( + value: injector(), + child: StreamDeviceView( + displayKey: displayKey, + onDeviceSelected: (canvasDevice) { + onDeviceSelected?.call(canvasDevice); + }, + ), + ), + isDismissible: true, + ); + } } diff --git a/lib/util/error_handler.dart b/lib/util/error_handler.dart index 4e8cd19bb..a82611119 100644 --- a/lib/util/error_handler.dart +++ b/lib/util/error_handler.dart @@ -309,7 +309,9 @@ Future showErrorDialogFromException(Object exception, ); return true; } else { - navigationService.showErrorDialog(event); + if (!_isErrorIgnored(exception)) { + navigationService.showErrorDialog(event); + } return true; } } else { @@ -320,6 +322,13 @@ Future showErrorDialogFromException(Object exception, } } +bool _isErrorIgnored(Object exception) { + if (exception is FlutterError) { + return true; + } + return false; +} + void hideInfoDialog(BuildContext context) { Navigator.of(context).pop(); } diff --git a/lib/view/cast_button.dart b/lib/view/cast_button.dart index 482479054..0a86fddce 100644 --- a/lib/view/cast_button.dart +++ b/lib/view/cast_button.dart @@ -6,11 +6,11 @@ import 'package:autonomy_flutter/screen/detail/preview/canvas_device_bloc.dart'; import 'package:autonomy_flutter/screen/settings/subscription/upgrade_bloc.dart'; import 'package:autonomy_flutter/screen/settings/subscription/upgrade_state.dart'; import 'package:autonomy_flutter/service/iap_service.dart'; +import 'package:autonomy_flutter/service/navigation_service.dart'; import 'package:autonomy_flutter/util/log.dart'; import 'package:autonomy_flutter/util/subscription_detail_ext.dart'; import 'package:autonomy_flutter/util/ui_helper.dart'; import 'package:autonomy_flutter/view/membership_card.dart'; -import 'package:autonomy_flutter/view/stream_device_view.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:feralfile_app_theme/feral_file_app_theme.dart'; import 'package:feralfile_app_tv_proto/models/canvas_device.dart'; @@ -38,9 +38,10 @@ class FFCastButton extends StatefulWidget { State createState() => _FFCastButtonState(); } +final keyboardManagerKey = GlobalKey(); + class _FFCastButtonState extends State { late CanvasDeviceBloc _canvasDeviceBloc; - final keyboardManagerKey = GlobalKey(); final _upgradesBloc = injector.get(); @override @@ -65,7 +66,10 @@ class _FFCastButtonState extends State { return IconButton( onPressed: () async { if (!widget.shouldCheckSubscription || isSubscribed) { - await _showStreamAction(context, widget.displayKey); + await injector().showStreamAction( + widget.displayKey, + widget.onDeviceSelected, + ); } else { await _showUpgradeDialog(context); } @@ -127,24 +131,6 @@ class _FFCastButtonState extends State { ); } - Future _showStreamAction( - BuildContext context, String displayKey) async { - keyboardManagerKey.currentState?.hideKeyboard(); - await UIHelper.showFlexibleDialog( - context, - BlocProvider.value( - value: _canvasDeviceBloc, - child: StreamDeviceView( - displayKey: displayKey, - onDeviceSelected: (canvasDevice) { - widget.onDeviceSelected?.call(canvasDevice); - }, - ), - ), - isDismissible: true, - ); - } - Future _showUpgradeDialog(BuildContext context) async { await UIHelper.showDialog( context, diff --git a/lib/view/display_instruction_view.dart b/lib/view/display_instruction_view.dart index c8e9a053f..42e7847ee 100644 --- a/lib/view/display_instruction_view.dart +++ b/lib/view/display_instruction_view.dart @@ -1,13 +1,9 @@ -import 'dart:async'; - import 'package:autonomy_flutter/common/injector.dart'; import 'package:autonomy_flutter/service/navigation_service.dart'; -import 'package:autonomy_flutter/util/ui_helper.dart'; -import 'package:autonomy_flutter/view/primary_button.dart'; +import 'package:autonomy_flutter/util/style.dart'; import 'package:autonomy_flutter/view/responsive.dart'; import 'package:easy_localization/easy_localization.dart'; import 'package:feralfile_app_theme/feral_file_app_theme.dart'; -import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; @@ -19,306 +15,371 @@ class DisplayInstructionView extends StatelessWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); + final supportedDisplayBranches = _getSupportedDisplayBranches(); return Column( crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, children: [ - Text( - 'bring_art_into'.tr(), - style: theme.textTheme.ppMori400Grey14, + Padding( + padding: ResponsiveLayout.pageHorizontalEdgeInsets, + child: Text( + 'what_kinds_of_tv'.tr(), + style: theme.textTheme.ppMori400Grey14, + ), ), const SizedBox(height: 24), - PrimaryButton( - text: 'display_on_tv'.tr(), - color: AppColor.feralFileLightBlue, - onTap: () { - injector().hideInfoDialog(); - unawaited(_showHowToDisplay( - context, - HowToDisplayOnTV( - onScanQRTap: onScanQRTap, - ), - )); - }, - ), - const SizedBox(height: 16), - PrimaryButton( - text: 'display_on_browser'.tr(), - color: AppColor.feralFileLightBlue, - onTap: () { - injector().hideInfoDialog(); - unawaited(_showHowToDisplay( - context, - HowToDisplayOnBrowser( - onScanQRTap: onScanQRTap, - ), - )); + ListView.builder( + itemBuilder: (context, index) { + final item = supportedDisplayBranches[index]; + return Column( + children: [ + _item(context, item: item), + addOnlyDivider(color: AppColor.primaryBlack), + ], + ); }, + itemCount: supportedDisplayBranches.length, + shrinkWrap: true, ), ], ); } - Future _showHowToDisplay(BuildContext context, Widget child) async { - await UIHelper.showFlexibleDialog( - context, - child, - isDismissible: true, - ); + void _onDisplayTap( + BuildContext context, SupportedDisplayBranch displayBranch) { + injector().showHowToDisplay(displayBranch, onScanQRTap); } -} -class HowToDisplayOnTV extends StatelessWidget { - final Function? onScanQRTap; + List _getSupportedDisplayBranches() => [ + DisplayItem( + branch: SupportedDisplayBranch.samsung, + onTap: _onDisplayTap, + ), + DisplayItem( + branch: SupportedDisplayBranch.lg, + onTap: _onDisplayTap, + ), + DisplayItem( + branch: SupportedDisplayBranch.chromecast, + onTap: _onDisplayTap, + ), + DisplayItem( + branch: SupportedDisplayBranch.sony, + onTap: _onDisplayTap, + ), + DisplayItem( + branch: SupportedDisplayBranch.Hisense, + onTap: _onDisplayTap, + ), + DisplayItem( + branch: SupportedDisplayBranch.TCL, + onTap: _onDisplayTap, + ), + DisplayItem( + branch: SupportedDisplayBranch.other, + onTap: _onDisplayTap, + ), + ]; - const HowToDisplayOnTV({super.key, this.onScanQRTap}); + Widget _commingSoonLabel(BuildContext context) { + final theme = Theme.of(context); + return Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(50), + border: Border.all(color: AppColor.auQuickSilver), + ), + padding: EdgeInsets.symmetric(horizontal: 10, vertical: 4), + child: Text('coming_soon'.tr(), + style: theme.textTheme.ppMori400Grey14.copyWith( + color: AppColor.auQuickSilver, + )), + ); + } - @override - Widget build(BuildContext context) { + Widget _item(BuildContext context, {required DisplayItem item}) { final theme = Theme.of(context); - return Padding( - padding: ResponsiveLayout.pageHorizontalEdgeInsets, - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - Expanded( - child: RichText( - textScaler: MediaQuery.textScalerOf(context), - text: TextSpan( - style: theme.textTheme.ppMori700White24, - children: [ - TextSpan( - text: 'how_to_display'.tr(), - ), - ], - ), - ), + return GestureDetector( + onTap: () { + item.onTap?.call(context, item.branch); + }, + child: Container( + padding: ResponsiveLayout.pageHorizontalEdgeInsets + .copyWith(top: 24, bottom: 24), + child: Row( + children: [ + Expanded( + child: Text( + item.title, + style: theme.textTheme.ppMori400White14, ), - IconButton( - onPressed: () => Navigator.pop(context), - icon: Padding( - padding: const EdgeInsets.all(5), - child: SvgPicture.asset( - 'assets/images/circle_close.svg', - width: 22, - height: 22, - ), - ), - ) - ], - ), - const SizedBox(height: 20), - Container( - decoration: BoxDecoration( - color: AppColor.white, - borderRadius: BorderRadius.circular(10), ), - padding: const EdgeInsets.all(20), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - RichText( - textScaler: MediaQuery.textScalerOf(context), - text: TextSpan( - style: theme.textTheme.ppMori400Black14, - children: [ - TextSpan( - text: "${'step'.tr()} 1: ", - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - TextSpan( - text: "${'search_by_'.tr()} ", - ), - WidgetSpan( - baseline: TextBaseline.alphabetic, - alignment: PlaceholderAlignment.baseline, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - border: Border.all(), - ), - padding: const EdgeInsets.symmetric(horizontal: 5), - child: Text( - textScaler: const TextScaler.linear(1), - 'feral_file'.tr(), - style: theme.textTheme.ppMori700Black14, - ), - ), - ), - TextSpan( - text: " ${'in_google_play_store'.tr()}.", - ), - ], - ), + const SizedBox(width: 16), + if (item.branch.isComingSoon) + _commingSoonLabel(context) + else + SvgPicture.asset( + 'assets/images/iconForward.svg', + colorFilter: const ColorFilter.mode( + AppColor.white, + BlendMode.srcIn, ), - const SizedBox(height: 10), - RichText( - textScaler: MediaQuery.textScalerOf(context), - text: TextSpan( - style: theme.textTheme.ppMori400Black14, - children: [ - TextSpan( - text: "${'step'.tr()} 2: ", - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - TextSpan( - recognizer: TapGestureRecognizer() - ..onTap = () { - onScanQRTap?.call(); - }, - text: 'scan_the_qr_code'.tr(), - style: onScanQRTap != null - ? const TextStyle( - decoration: TextDecoration.underline, - ) - : null, - ), - TextSpan( - text: " ${'on_your_TV'.tr()}.", - ) - ], - ), - ), - ], - ), - ), - const SizedBox(height: 10), - ], + ) + ], + ), ), ); } } -class HowToDisplayOnBrowser extends StatelessWidget { - final Function? onScanQRTap; +enum SupportedDisplayBranch { + samsung, + lg, + chromecast, + sony, + Hisense, + TCL, + other; - const HowToDisplayOnBrowser({super.key, this.onScanQRTap}); + String get title { + switch (this) { + case SupportedDisplayBranch.samsung: + return 'Samsung (2023+)'; + case SupportedDisplayBranch.lg: + return 'LG'.tr(); + case SupportedDisplayBranch.chromecast: + return 'Chromecast'.tr(); + case SupportedDisplayBranch.sony: + return 'Sony'.tr(); + case SupportedDisplayBranch.Hisense: + return 'Hisense'.tr(); + case SupportedDisplayBranch.TCL: + return 'TCL'.tr(); + case SupportedDisplayBranch.other: + return 'Other'.tr(); + } + } - @override - Widget build(BuildContext context) { + bool get isComingSoon => this == SupportedDisplayBranch.lg; + + Widget get logo { + const color = AppColor.white; + const colorFilter = ColorFilter.mode(color, BlendMode.srcIn); + const height = 18.0; + switch (this) { + case SupportedDisplayBranch.samsung: + return SvgPicture.asset( + 'assets/images/samsung_logo.svg', + colorFilter: colorFilter, + height: height, + ); + case SupportedDisplayBranch.lg: + return SvgPicture.asset( + 'assets/images/lg_logo.svg', + colorFilter: colorFilter, + height: height, + ); + case SupportedDisplayBranch.chromecast: + return SvgPicture.asset( + 'assets/images/chromecast_logo.svg', + colorFilter: colorFilter, + height: height, + ); + case SupportedDisplayBranch.sony: + return SvgPicture.asset( + 'assets/images/sony_logo.svg', + colorFilter: colorFilter, + height: height, + ); + case SupportedDisplayBranch.Hisense: + return SvgPicture.asset( + 'assets/images/hisense_logo.svg', + colorFilter: colorFilter, + height: height, + ); + case SupportedDisplayBranch.TCL: + return SvgPicture.asset( + 'assets/images/tcl_logo.svg', + colorFilter: colorFilter, + height: height, + ); + case SupportedDisplayBranch.other: + return SvgPicture.asset( + 'assets/images/other_branch_logo.svg', + colorFilter: colorFilter, + height: height, + ); + } + } + + Widget demoPicture(BuildContext context) { final theme = Theme.of(context); - return Padding( - padding: ResponsiveLayout.pageHorizontalEdgeInsets, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( - children: [ - Expanded( - child: RichText( - textScaler: MediaQuery.textScalerOf(context), - text: TextSpan( - style: theme.textTheme.ppMori700White24, - children: [ - TextSpan( - text: 'how_to_display'.tr(), - ), - ], - ), + switch (this) { + case SupportedDisplayBranch.samsung: + return Stack( + alignment: Alignment.bottomLeft, + children: [ + Image.asset( + 'assets/images/Samsung_TV_living_room.png', + fit: BoxFit.fitWidth, + width: double.infinity, + ), + Container( + width: double.infinity, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + AppColor.primaryBlack, + AppColor.primaryBlack.withOpacity(0.5), + ], + stops: const [0.0, 0.8], ), ), - IconButton( - onPressed: () => Navigator.pop(context), - icon: Padding( - padding: const EdgeInsets.all(5), - child: SvgPicture.asset( - 'assets/images/circle_close.svg', - width: 22, - height: 22, - ), + child: Padding( + padding: const EdgeInsets.only(bottom: 6, top: 6, left: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Currently supporting 2023 onward models', + style: theme.textTheme.ppMori700White12, + ), + ], ), - ) - ], - ), - const SizedBox(height: 20), - Container( - decoration: BoxDecoration( - color: AppColor.white, - borderRadius: BorderRadius.circular(10), + ), ), - padding: const EdgeInsets.all(20), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - RichText( - textScaler: MediaQuery.textScalerOf(context), - text: TextSpan( - style: theme.textTheme.ppMori400Black14, - children: [ - TextSpan( - text: "${'step'.tr()} 1: ", - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - TextSpan( - text: "${'open'.tr()} ", - ), - WidgetSpan( - baseline: TextBaseline.alphabetic, - alignment: PlaceholderAlignment.baseline, - child: Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(5), - border: Border.all(), - ), - padding: const EdgeInsets.symmetric(horizontal: 5), - child: Text( - 'https://display.feralfile.com', - textScaler: const TextScaler.linear(1), - style: theme.textTheme.ppMori700Black14, - ), - ), - ), - TextSpan( - text: " ${'on_your_TV_web_browser'.tr()}.", - ), - ], - ), + ], + ); + case SupportedDisplayBranch.lg: + return Stack( + fit: StackFit.passthrough, + children: [ + Image.asset( + 'assets/images/Android_TV_living_room.png', + fit: BoxFit.fitWidth, + width: double.infinity, + ), + Positioned.fill( + bottom: 0, + child: Container( + width: double.infinity, + decoration: BoxDecoration( + color: AppColor.primaryBlack.withOpacity(0.5), ), - const SizedBox(height: 10), - RichText( - textScaler: MediaQuery.textScalerOf(context), - text: TextSpan( - style: theme.textTheme.ppMori400Black14, - children: [ - TextSpan( - text: "${'step'.tr()} 2: ", - style: const TextStyle( - fontWeight: FontWeight.bold, - ), - ), - TextSpan( - recognizer: TapGestureRecognizer() - ..onTap = () { - onScanQRTap?.call(); - }, - text: 'scan_the_qr_code'.tr(), - style: onScanQRTap != null - ? const TextStyle( - decoration: TextDecoration.underline, - ) - : null, - ), - TextSpan( - text: " ${'on_your_TV'.tr()}.", - ) - ], + alignment: Alignment.bottomLeft, + child: Container( + width: double.infinity, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + AppColor.primaryBlack, + AppColor.primaryBlack.withOpacity(0), + ], + ), + ), + child: Padding( + padding: const EdgeInsets.only(bottom: 6, top: 6, left: 10), + child: Text( + 'Support for LG TVs is coming soon. ', + style: theme.textTheme.ppMori700White12, + ), ), ), - ], + ), ), - ), - const SizedBox(height: 10), - ], - ), - ); + ], + ); + case SupportedDisplayBranch.chromecast: + case SupportedDisplayBranch.sony: + case SupportedDisplayBranch.Hisense: + case SupportedDisplayBranch.TCL: + return Stack( + alignment: Alignment.bottomLeft, + children: [ + Image.asset( + 'assets/images/Android_TV_living_room.png', + fit: BoxFit.fitWidth, + width: double.infinity, + ), + Container( + width: double.infinity, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + AppColor.primaryBlack, + AppColor.primaryBlack.withOpacity(0.5), + ], + stops: const [0.0, 0.8], + ), + ), + child: Padding( + padding: const EdgeInsets.only(bottom: 6, top: 6, left: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Currently supporting 2021 onward models', + style: theme.textTheme.ppMori700White12, + ), + ], + ), + ), + ), + ], + ); + case SupportedDisplayBranch.other: + return Stack( + alignment: Alignment.bottomLeft, + children: [ + Image.asset( + 'assets/images/Web_display_TV_living_room.png', + fit: BoxFit.fitWidth, + width: double.infinity, + ), + Container( + width: double.infinity, + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.bottomCenter, + end: Alignment.topCenter, + colors: [ + AppColor.primaryBlack, + AppColor.primaryBlack.withOpacity(0.5), + ], + stops: const [0.0, 0.8], + ), + ), + child: Padding( + padding: const EdgeInsets.only(bottom: 6, top: 6, left: 10), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Use the TV web browser, or any web browser.', + style: theme.textTheme.ppMori700White12, + ), + ], + ), + ), + ), + ], + ); + } } } + +class DisplayItem { + final SupportedDisplayBranch branch; + final Function(BuildContext context, SupportedDisplayBranch branch)? onTap; + + DisplayItem({required this.branch, required this.onTap}); + + Widget get logo => branch.logo; + + String get title => branch.title; +} diff --git a/lib/view/stream_device_view.dart b/lib/view/stream_device_view.dart index 0ffb54335..113fa1bf2 100644 --- a/lib/view/stream_device_view.dart +++ b/lib/view/stream_device_view.dart @@ -55,13 +55,13 @@ class _StreamDeviceViewState extends State { final connectedDevice = widget.displayKey == null ? null : state.lastSelectedActiveDeviceForKey(widget.displayKey!); - return Padding( - padding: ResponsiveLayout.pageHorizontalEdgeInsets, - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Row( + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: ResponsiveLayout.pageHorizontalEdgeInsets, + child: Row( children: [ Expanded( child: (connectedDevice != null) @@ -82,7 +82,7 @@ class _StreamDeviceViewState extends State { ), ) : Text( - 'display_art'.tr(), + 'display_art_on_your_tv'.tr(), style: theme.textTheme.ppMori700White24, )), IconButton( @@ -98,83 +98,82 @@ class _StreamDeviceViewState extends State { ) ], ), - if (devices.isNotEmpty) ...[ - const SizedBox(height: 40), - ListView.builder( - shrinkWrap: true, - physics: const NeverScrollableScrollPhysics(), - padding: EdgeInsets.zero, - itemCount: devices.length, - itemBuilder: (BuildContext context, int index) { - final device = devices[index].device; - final isControlling = - device.deviceId == connectedDevice?.deviceId; - return Column( - children: [ - Builder( - builder: (context) => StreamDrawerItem( - item: OptionItem( - title: device.name, - onTap: () { - log.info( - 'device selected: ${device.deviceId}'); - widget.onDeviceSelected?.call(device); - Navigator.pop(context); - }), - backgroundColor: connectedDevice == null - ? AppColor.white - : isControlling - ? AppColor.feralFileLightBlue - : AppColor.disabledColor, - isControlling: isControlling, - onRotateClicked: () => onRotate(context), - ), + ), + if (devices.isNotEmpty) ...[ + const SizedBox(height: 40), + ListView.builder( + shrinkWrap: true, + physics: const NeverScrollableScrollPhysics(), + padding: EdgeInsets.zero, + itemCount: devices.length, + itemBuilder: (BuildContext context, int index) { + final device = devices[index].device; + final isControlling = + device.deviceId == connectedDevice?.deviceId; + return Column( + children: [ + Builder( + builder: (context) => StreamDrawerItem( + item: OptionItem( + title: device.name, + onTap: () { + log.info('device selected: ${device.deviceId}'); + widget.onDeviceSelected?.call(device); + Navigator.pop(context); + }), + backgroundColor: connectedDevice == null + ? AppColor.white + : isControlling + ? AppColor.feralFileLightBlue + : AppColor.disabledColor, + isControlling: isControlling, + onRotateClicked: () => onRotate(context), ), - const SizedBox( - height: 15, - ) - ], - ); - }, - ), - if (connectedDevice != null) ...[ - Container( - decoration: BoxDecoration( - borderRadius: BorderRadius.circular(50), - border: Border.all( - color: AppColor.white, ), + const SizedBox( + height: 15, + ) + ], + ); + }, + ), + if (connectedDevice != null) ...[ + Container( + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(50), + border: Border.all( + color: AppColor.white, ), - width: MediaQuery.of(context).size.width, - child: Material( - type: MaterialType.transparency, - child: InkWell( - splashFactory: InkSparkle.splashFactory, - borderRadius: BorderRadius.circular(50), - child: Padding( - padding: const EdgeInsets.all(12), - child: Center( - child: Text( - 'disconnect'.tr(), - style: theme.textTheme.ppMori400White14, - ), + ), + width: MediaQuery.of(context).size.width, + child: Material( + type: MaterialType.transparency, + child: InkWell( + splashFactory: InkSparkle.splashFactory, + borderRadius: BorderRadius.circular(50), + child: Padding( + padding: const EdgeInsets.all(12), + child: Center( + child: Text( + 'disconnect'.tr(), + style: theme.textTheme.ppMori400White14, ), ), - onTap: () async { - await onDisconnect(); - }, ), + onTap: () async { + await onDisconnect(); + }, ), ), - const SizedBox(height: 30), - ], - ] else ...[ - const SizedBox(height: 20), - _instructionDetailWidget(context), + ), + const SizedBox(height: 30), ], - const SizedBox(height: 10), + ] else ...[ + const SizedBox(height: 15), + _instructionDetailWidget(context), ], - ), + const SizedBox(height: 10), + ], ); }, );