mirror of
https://github.com/Dr-Blank/Vaani.git
synced 2026-02-17 06:49:34 +00:00
添加语言切换
This commit is contained in:
parent
e0deb84123
commit
e06c834d0e
21 changed files with 1416 additions and 281 deletions
|
|
@ -18,6 +18,7 @@ import 'package:vaani/features/item_viewer/view/library_item_page.dart';
|
||||||
import 'package:vaani/features/per_book_settings/providers/book_settings_provider.dart';
|
import 'package:vaani/features/per_book_settings/providers/book_settings_provider.dart';
|
||||||
import 'package:vaani/features/player/providers/audiobook_player.dart';
|
import 'package:vaani/features/player/providers/audiobook_player.dart';
|
||||||
import 'package:vaani/features/player/providers/player_form.dart';
|
import 'package:vaani/features/player/providers/player_form.dart';
|
||||||
|
import 'package:vaani/generated/l10n.dart';
|
||||||
import 'package:vaani/main.dart';
|
import 'package:vaani/main.dart';
|
||||||
import 'package:vaani/router/router.dart';
|
import 'package:vaani/router/router.dart';
|
||||||
import 'package:vaani/settings/api_settings_provider.dart';
|
import 'package:vaani/settings/api_settings_provider.dart';
|
||||||
|
|
@ -76,11 +77,9 @@ class LibraryItemActions extends HookConsumerWidget {
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
appLogger.fine('Sharing');
|
appLogger.fine('Sharing');
|
||||||
var currentServerUrl =
|
var currentServerUrl = apiSettings.activeServer!.serverUrl;
|
||||||
apiSettings.activeServer!.serverUrl;
|
|
||||||
if (!currentServerUrl.hasScheme) {
|
if (!currentServerUrl.hasScheme) {
|
||||||
currentServerUrl =
|
currentServerUrl = Uri.https(currentServerUrl.toString());
|
||||||
Uri.https(currentServerUrl.toString());
|
|
||||||
}
|
}
|
||||||
handleLaunchUrl(
|
handleLaunchUrl(
|
||||||
Uri.parse(
|
Uri.parse(
|
||||||
|
|
@ -139,8 +138,7 @@ class LibraryItemActions extends HookConsumerWidget {
|
||||||
FileDownloader()
|
FileDownloader()
|
||||||
.database
|
.database
|
||||||
.deleteRecordWithId(
|
.deleteRecordWithId(
|
||||||
record
|
record.task.taskId,
|
||||||
.task.taskId,
|
|
||||||
);
|
);
|
||||||
Navigator.pop(context);
|
Navigator.pop(context);
|
||||||
},
|
},
|
||||||
|
|
@ -159,8 +157,7 @@ class LibraryItemActions extends HookConsumerWidget {
|
||||||
},
|
},
|
||||||
onTap: () async {
|
onTap: () async {
|
||||||
// open the file location
|
// open the file location
|
||||||
final didOpen =
|
final didOpen = await FileDownloader().openFile(
|
||||||
await FileDownloader().openFile(
|
|
||||||
task: record.task,
|
task: record.task,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
@ -229,9 +226,7 @@ class LibItemDownloadButton extends HookConsumerWidget {
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
appLogger.fine('Pressed download button');
|
appLogger.fine('Pressed download button');
|
||||||
|
|
||||||
ref
|
ref.read(downloadManagerProvider.notifier).queueAudioBookDownload(item);
|
||||||
.read(downloadManagerProvider.notifier)
|
|
||||||
.queueAudioBookDownload(item);
|
|
||||||
},
|
},
|
||||||
icon: const Icon(
|
icon: const Icon(
|
||||||
Icons.download_rounded,
|
Icons.download_rounded,
|
||||||
|
|
@ -250,10 +245,7 @@ class ItemCurrentlyInDownloadQueue extends HookConsumerWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final progress = ref
|
final progress = ref.watch(itemDownloadProgressProvider(item.id)).valueOrNull?.clamp(0.05, 1.0);
|
||||||
.watch(itemDownloadProgressProvider(item.id))
|
|
||||||
.valueOrNull
|
|
||||||
?.clamp(0.05, 1.0);
|
|
||||||
|
|
||||||
if (progress == 1) {
|
if (progress == 1) {
|
||||||
return AlreadyItemDownloadedButton(item: item);
|
return AlreadyItemDownloadedButton(item: item);
|
||||||
|
|
@ -366,7 +358,7 @@ class DownloadSheet extends HookConsumerWidget {
|
||||||
// },
|
// },
|
||||||
// ),
|
// ),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: const Text('Delete'),
|
title: Text(S.of(context).delete),
|
||||||
leading: const Icon(
|
leading: const Icon(
|
||||||
Icons.delete_rounded,
|
Icons.delete_rounded,
|
||||||
),
|
),
|
||||||
|
|
@ -377,28 +369,26 @@ class DownloadSheet extends HookConsumerWidget {
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('Delete'),
|
title: Text(S.of(context).delete),
|
||||||
content: Text(
|
content: Text(
|
||||||
'Are you sure you want to delete ${item.media.metadata.title}?',
|
S.of(context).deleteDialog(item.media.metadata.title ?? ''),
|
||||||
),
|
),
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
// delete the file
|
// delete the file
|
||||||
ref
|
ref.read(downloadManagerProvider.notifier).deleteDownloadedItem(
|
||||||
.read(downloadManagerProvider.notifier)
|
|
||||||
.deleteDownloadedItem(
|
|
||||||
item,
|
item,
|
||||||
);
|
);
|
||||||
GoRouter.of(context).pop(true);
|
GoRouter.of(context).pop(true);
|
||||||
},
|
},
|
||||||
child: const Text('Yes'),
|
child: Text(S.of(context).yes),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
GoRouter.of(context).pop(false);
|
GoRouter.of(context).pop(false);
|
||||||
},
|
},
|
||||||
child: const Text('No'),
|
child: Text(S.of(context).no),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
@ -406,12 +396,12 @@ class DownloadSheet extends HookConsumerWidget {
|
||||||
);
|
);
|
||||||
|
|
||||||
if (wasDeleted ?? false) {
|
if (wasDeleted ?? false) {
|
||||||
appLogger.fine('Deleted ${item.media.metadata.title}');
|
appLogger.fine(S.of(context).deleted(item.media.metadata.title ?? ''));
|
||||||
GoRouter.of(context).pop();
|
GoRouter.of(context).pop();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(
|
content: Text(
|
||||||
'Deleted ${item.media.metadata.title}',
|
S.of(context).deleted(item.media.metadata.title ?? ''),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
@ -445,19 +435,19 @@ class _LibraryItemPlayButton extends HookConsumerWidget {
|
||||||
if (!isCurrentBookSetInPlayer) {
|
if (!isCurrentBookSetInPlayer) {
|
||||||
// either play or resume or listen again based on the progress
|
// either play or resume or listen again based on the progress
|
||||||
if (isBookCompleted) {
|
if (isBookCompleted) {
|
||||||
return 'Listen Again';
|
return S.of(context).homeListenAgain;
|
||||||
}
|
}
|
||||||
// if some progress is made, then 'continue listening'
|
// if some progress is made, then 'continue listening'
|
||||||
if (userMediaProgress?.progress != null) {
|
if (userMediaProgress?.progress != null) {
|
||||||
return 'Continue Listening';
|
return S.of(context).homeContinueListening;
|
||||||
}
|
}
|
||||||
return 'Start Listening';
|
return S.of(context).homeStartListening;
|
||||||
} else {
|
} else {
|
||||||
// if book is set to player
|
// if book is set to player
|
||||||
if (isPlayingThisBook) {
|
if (isPlayingThisBook) {
|
||||||
return 'Pause';
|
return S.of(context).pause;
|
||||||
}
|
}
|
||||||
return 'Resume';
|
return S.of(context).resume;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -529,8 +519,7 @@ Future<void> libraryItemPlayButtonOnPressed({
|
||||||
appLogger.info('Setting the book ${book.libraryItemId}');
|
appLogger.info('Setting the book ${book.libraryItemId}');
|
||||||
appLogger.info('Initial position: ${userMediaProgress?.currentTime}');
|
appLogger.info('Initial position: ${userMediaProgress?.currentTime}');
|
||||||
final downloadManager = ref.watch(simpleDownloadManagerProvider);
|
final downloadManager = ref.watch(simpleDownloadManagerProvider);
|
||||||
final libItem =
|
final libItem = await ref.read(libraryItemProvider(book.libraryItemId).future);
|
||||||
await ref.read(libraryItemProvider(book.libraryItemId).future);
|
|
||||||
final downloadedUris = await downloadManager.getDownloadedFilesUri(libItem);
|
final downloadedUris = await downloadManager.getDownloadedFilesUri(libItem);
|
||||||
setSourceFuture = player.setSourceAudiobook(
|
setSourceFuture = player.setSourceAudiobook(
|
||||||
book,
|
book,
|
||||||
|
|
@ -546,27 +535,23 @@ Future<void> libraryItemPlayButtonOnPressed({
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// set the volume as this is the first time playing and dismissing causes the volume to go to 0
|
// set the volume as this is the first time playing and dismissing causes the volume to go to 0
|
||||||
var bookPlayerSettings =
|
var bookPlayerSettings = ref.read(bookSettingsProvider(book.libraryItemId)).playerSettings;
|
||||||
ref.read(bookSettingsProvider(book.libraryItemId)).playerSettings;
|
|
||||||
var appPlayerSettings = ref.read(appSettingsProvider).playerSettings;
|
var appPlayerSettings = ref.read(appSettingsProvider).playerSettings;
|
||||||
|
|
||||||
var configurePlayerForEveryBook =
|
var configurePlayerForEveryBook = appPlayerSettings.configurePlayerForEveryBook;
|
||||||
appPlayerSettings.configurePlayerForEveryBook;
|
|
||||||
|
|
||||||
await Future.wait([
|
await Future.wait([
|
||||||
setSourceFuture ?? Future.value(),
|
setSourceFuture ?? Future.value(),
|
||||||
// set the volume
|
// set the volume
|
||||||
player.setVolume(
|
player.setVolume(
|
||||||
configurePlayerForEveryBook
|
configurePlayerForEveryBook
|
||||||
? bookPlayerSettings.preferredDefaultVolume ??
|
? bookPlayerSettings.preferredDefaultVolume ?? appPlayerSettings.preferredDefaultVolume
|
||||||
appPlayerSettings.preferredDefaultVolume
|
|
||||||
: appPlayerSettings.preferredDefaultVolume,
|
: appPlayerSettings.preferredDefaultVolume,
|
||||||
),
|
),
|
||||||
// set the speed
|
// set the speed
|
||||||
player.setSpeed(
|
player.setSpeed(
|
||||||
configurePlayerForEveryBook
|
configurePlayerForEveryBook
|
||||||
? bookPlayerSettings.preferredDefaultSpeed ??
|
? bookPlayerSettings.preferredDefaultSpeed ?? appPlayerSettings.preferredDefaultSpeed
|
||||||
appPlayerSettings.preferredDefaultSpeed
|
|
||||||
: appPlayerSettings.preferredDefaultSpeed,
|
: appPlayerSettings.preferredDefaultSpeed,
|
||||||
),
|
),
|
||||||
]);
|
]);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:vaani/api/library_item_provider.dart';
|
import 'package:vaani/api/library_item_provider.dart';
|
||||||
|
import 'package:vaani/generated/l10n.dart';
|
||||||
import 'package:vaani/shared/extensions/model_conversions.dart';
|
import 'package:vaani/shared/extensions/model_conversions.dart';
|
||||||
|
|
||||||
class LibraryItemMetadata extends HookConsumerWidget {
|
class LibraryItemMetadata extends HookConsumerWidget {
|
||||||
|
|
@ -24,9 +25,8 @@ class LibraryItemMetadata extends HookConsumerWidget {
|
||||||
if (book == null) {
|
if (book == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final duration = book.audioFiles
|
final duration =
|
||||||
.map((e) => e.duration)
|
book.audioFiles.map((e) => e.duration).reduce((value, element) => value + element);
|
||||||
.reduce((value, element) => value + element);
|
|
||||||
final hours = duration.inHours;
|
final hours = duration.inHours;
|
||||||
final minutes = duration.inMinutes.remainder(60);
|
final minutes = duration.inMinutes.remainder(60);
|
||||||
return '${hours}h ${minutes}m';
|
return '${hours}h ${minutes}m';
|
||||||
|
|
@ -41,10 +41,12 @@ class LibraryItemMetadata extends HookConsumerWidget {
|
||||||
if (book == null) {
|
if (book == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
final size = book.audioFiles
|
final size =
|
||||||
.map((e) => e.metadata.size)
|
book.audioFiles.map((e) => e.metadata.size).reduce((value, element) => value + element);
|
||||||
.reduce((value, element) => value + element);
|
if (size / 1024 / 1024 < 1024) {
|
||||||
return '${size / 1024 ~/ 1024} MB';
|
return '${(size / 1024 / 1024).toStringAsFixed(2)} MB';
|
||||||
|
}
|
||||||
|
return '${(size / 1024 / 1024 / 1024).toStringAsFixed(2)} GB';
|
||||||
}
|
}
|
||||||
|
|
||||||
/// will return the codec and bitrate of the book
|
/// will return the codec and bitrate of the book
|
||||||
|
|
@ -64,21 +66,21 @@ class LibraryItemMetadata extends HookConsumerWidget {
|
||||||
// duration of the book
|
// duration of the book
|
||||||
_MetadataItem(
|
_MetadataItem(
|
||||||
title: switch (itemBookMetadata?.abridged) {
|
title: switch (itemBookMetadata?.abridged) {
|
||||||
true => 'Abridged',
|
true => S.of(context).bookMetadataAbridged,
|
||||||
false => 'Unabridged',
|
false => S.of(context).bookMetadataUnabridged,
|
||||||
_ => 'Length',
|
_ => S.of(context).bookMetadataLength,
|
||||||
},
|
},
|
||||||
value: getDurationFormatted() ?? 'time is just a concept',
|
value: getDurationFormatted() ?? 'time is just a concept',
|
||||||
),
|
),
|
||||||
_MetadataItem(
|
_MetadataItem(
|
||||||
title: 'Published',
|
title: S.of(context).bookMetadataPublished,
|
||||||
value: itemBookMetadata?.publishedDate ??
|
value: itemBookMetadata?.publishedDate ??
|
||||||
itemBookMetadata?.publishedYear ??
|
itemBookMetadata?.publishedYear ??
|
||||||
'Unknown',
|
S.of(context).unknown,
|
||||||
),
|
),
|
||||||
_MetadataItem(
|
_MetadataItem(
|
||||||
title: getCodecAndBitrate() ?? 'Codec & Bitrate',
|
title: getCodecAndBitrate() ?? 'Codec & Bitrate',
|
||||||
value: getSizeFormatted() ?? 'Unknown',
|
value: getSizeFormatted() ?? S.of(context).unknown,
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
return Padding(
|
return Padding(
|
||||||
|
|
@ -96,10 +98,7 @@ class LibraryItemMetadata extends HookConsumerWidget {
|
||||||
return VerticalDivider(
|
return VerticalDivider(
|
||||||
indent: 6,
|
indent: 6,
|
||||||
endIndent: 6,
|
endIndent: 6,
|
||||||
color: Theme.of(context)
|
color: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.6),
|
||||||
.colorScheme
|
|
||||||
.onSurface
|
|
||||||
.withValues(alpha: 0.6),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:vaani/api/library_item_provider.dart';
|
import 'package:vaani/api/library_item_provider.dart';
|
||||||
import 'package:vaani/features/item_viewer/view/library_item_sliver_app_bar.dart';
|
import 'package:vaani/features/item_viewer/view/library_item_sliver_app_bar.dart';
|
||||||
import 'package:vaani/features/player/view/mini_player_bottom_padding.dart';
|
import 'package:vaani/features/player/view/mini_player_bottom_padding.dart';
|
||||||
|
import 'package:vaani/generated/l10n.dart';
|
||||||
import 'package:vaani/router/models/library_item_extras.dart';
|
import 'package:vaani/router/models/library_item_extras.dart';
|
||||||
import 'package:vaani/shared/widgets/expandable_description.dart';
|
import 'package:vaani/shared/widgets/expandable_description.dart';
|
||||||
|
|
||||||
|
|
@ -27,8 +28,7 @@ class LibraryItemPage extends HookConsumerWidget {
|
||||||
static const double _showFabThreshold = 300.0;
|
static const double _showFabThreshold = 300.0;
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final additionalItemData =
|
final additionalItemData = extra is LibraryItemExtras ? extra as LibraryItemExtras : null;
|
||||||
extra is LibraryItemExtras ? extra as LibraryItemExtras : null;
|
|
||||||
final scrollController = useScrollController();
|
final scrollController = useScrollController();
|
||||||
final showFab = useState(false);
|
final showFab = useState(false);
|
||||||
|
|
||||||
|
|
@ -150,8 +150,10 @@ class LibraryItemDescription extends HookConsumerWidget {
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
return ExpandableDescription(
|
return ExpandableDescription(
|
||||||
title: 'About the Book',
|
title: S.of(context).bookAbout,
|
||||||
content: item.media.metadata.description ?? 'Sorry, no description found',
|
content: item.media.metadata.description ?? S.of(context).bookAboutDefault,
|
||||||
|
readMoreText: S.of(context).readMore,
|
||||||
|
readLessText: S.of(context).readLess,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -166,10 +168,8 @@ double calculateWidth(
|
||||||
/// height ratio of the cover image to the available height
|
/// height ratio of the cover image to the available height
|
||||||
double maxHeightToUse = 0.25,
|
double maxHeightToUse = 0.25,
|
||||||
}) {
|
}) {
|
||||||
final availHeight =
|
final availHeight = min(constraints.maxHeight, MediaQuery.of(context).size.height);
|
||||||
min(constraints.maxHeight, MediaQuery.of(context).size.height);
|
final availWidth = min(constraints.maxWidth, MediaQuery.of(context).size.width);
|
||||||
final availWidth =
|
|
||||||
min(constraints.maxWidth, MediaQuery.of(context).size.width);
|
|
||||||
|
|
||||||
// make the width widthRatio of the available width
|
// make the width widthRatio of the available width
|
||||||
var width = availWidth * widthRatio;
|
var width = availWidth * widthRatio;
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,8 @@ class PlayerWhenMinimized extends HookConsumerWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final player = ref.watch(audiobookPlayerProvider);
|
final player = ref.watch(audiobookPlayerProvider);
|
||||||
|
final currentChapter = ref.watch(currentPlayingChapterProvider);
|
||||||
|
|
||||||
final vanishingPercentage = 1 - percentageMiniplayer;
|
final vanishingPercentage = 1 - percentageMiniplayer;
|
||||||
final progress = useStream(player.slowPositionStream, initialData: Duration.zero);
|
final progress = useStream(player.slowPositionStream, initialData: Duration.zero);
|
||||||
|
|
||||||
|
|
@ -77,7 +79,7 @@ class PlayerWhenMinimized extends HookConsumerWidget {
|
||||||
children: [
|
children: [
|
||||||
// AutoScrollText(
|
// AutoScrollText(
|
||||||
Text(
|
Text(
|
||||||
bookMetaExpanded?.title ?? '',
|
'${bookMetaExpanded?.title ?? ''} - ${currentChapter?.title ?? ''}',
|
||||||
maxLines: 1, overflow: TextOverflow.ellipsis,
|
maxLines: 1, overflow: TextOverflow.ellipsis,
|
||||||
// velocity:
|
// velocity:
|
||||||
// const Velocity(pixelsPerSecond: Offset(16, 0)),
|
// const Velocity(pixelsPerSecond: Offset(16, 0)),
|
||||||
|
|
|
||||||
72
lib/generated/intl/messages_all.dart
Normal file
72
lib/generated/intl/messages_all.dart
Normal file
|
|
@ -0,0 +1,72 @@
|
||||||
|
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
|
||||||
|
// This is a library that looks up messages for specific locales by
|
||||||
|
// delegating to the appropriate library.
|
||||||
|
|
||||||
|
// Ignore issues from commonly used lints in this file.
|
||||||
|
// ignore_for_file:implementation_imports, file_names, unnecessary_new
|
||||||
|
// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering
|
||||||
|
// ignore_for_file:argument_type_not_assignable, invalid_assignment
|
||||||
|
// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases
|
||||||
|
// ignore_for_file:comment_references
|
||||||
|
|
||||||
|
import 'dart:async';
|
||||||
|
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:intl/message_lookup_by_library.dart';
|
||||||
|
import 'package:intl/src/intl_helpers.dart';
|
||||||
|
|
||||||
|
import 'messages_en.dart' as messages_en;
|
||||||
|
import 'messages_zh.dart' as messages_zh;
|
||||||
|
|
||||||
|
typedef Future<dynamic> LibraryLoader();
|
||||||
|
Map<String, LibraryLoader> _deferredLibraries = {
|
||||||
|
'en': () => new SynchronousFuture(null),
|
||||||
|
'zh': () => new SynchronousFuture(null),
|
||||||
|
};
|
||||||
|
|
||||||
|
MessageLookupByLibrary? _findExact(String localeName) {
|
||||||
|
switch (localeName) {
|
||||||
|
case 'en':
|
||||||
|
return messages_en.messages;
|
||||||
|
case 'zh':
|
||||||
|
return messages_zh.messages;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// User programs should call this before using [localeName] for messages.
|
||||||
|
Future<bool> initializeMessages(String localeName) {
|
||||||
|
var availableLocale = Intl.verifiedLocale(
|
||||||
|
localeName,
|
||||||
|
(locale) => _deferredLibraries[locale] != null,
|
||||||
|
onFailure: (_) => null,
|
||||||
|
);
|
||||||
|
if (availableLocale == null) {
|
||||||
|
return new SynchronousFuture(false);
|
||||||
|
}
|
||||||
|
var lib = _deferredLibraries[availableLocale];
|
||||||
|
lib == null ? new SynchronousFuture(false) : lib();
|
||||||
|
initializeInternalMessageLookup(() => new CompositeMessageLookup());
|
||||||
|
messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);
|
||||||
|
return new SynchronousFuture(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool _messagesExistFor(String locale) {
|
||||||
|
try {
|
||||||
|
return _findExact(locale) != null;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {
|
||||||
|
var actualLocale = Intl.verifiedLocale(
|
||||||
|
locale,
|
||||||
|
_messagesExistFor,
|
||||||
|
onFailure: (_) => null,
|
||||||
|
);
|
||||||
|
if (actualLocale == null) return null;
|
||||||
|
return _findExact(actualLocale);
|
||||||
|
}
|
||||||
150
lib/generated/intl/messages_en.dart
Normal file
150
lib/generated/intl/messages_en.dart
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
|
||||||
|
// This is a library that provides messages for a en locale. All the
|
||||||
|
// messages from the main program should be duplicated here with the same
|
||||||
|
// function name.
|
||||||
|
|
||||||
|
// Ignore issues from commonly used lints in this file.
|
||||||
|
// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
|
||||||
|
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
|
||||||
|
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
|
||||||
|
// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes
|
||||||
|
// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes
|
||||||
|
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:intl/message_lookup_by_library.dart';
|
||||||
|
|
||||||
|
final messages = new MessageLookup();
|
||||||
|
|
||||||
|
typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||||
|
|
||||||
|
class MessageLookup extends MessageLookupByLibrary {
|
||||||
|
String get localeName => 'en';
|
||||||
|
|
||||||
|
static String m0(item) => "Are you sure you want to delete ${item}?";
|
||||||
|
|
||||||
|
static String m1(item) => "Deleted ${item}";
|
||||||
|
|
||||||
|
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||||
|
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||||
|
"appSettings": MessageLookupByLibrary.simpleMessage("App Settings"),
|
||||||
|
"appearance": MessageLookupByLibrary.simpleMessage("Appearance"),
|
||||||
|
"autoTurnOnSleepTimer": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Auto Turn On Sleep Timer",
|
||||||
|
),
|
||||||
|
"automaticallyDescription": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Automatically turn on the sleep timer based on the time of day",
|
||||||
|
),
|
||||||
|
"backup": MessageLookupByLibrary.simpleMessage("Backup"),
|
||||||
|
"backupAndRestore": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Backup and Restore",
|
||||||
|
),
|
||||||
|
"bookAbout": MessageLookupByLibrary.simpleMessage("About the Book"),
|
||||||
|
"bookAboutDefault": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Sorry, no description found",
|
||||||
|
),
|
||||||
|
"bookMetadataAbridged": MessageLookupByLibrary.simpleMessage("Abridged"),
|
||||||
|
"bookMetadataLength": MessageLookupByLibrary.simpleMessage("Length"),
|
||||||
|
"bookMetadataPublished": MessageLookupByLibrary.simpleMessage("Published"),
|
||||||
|
"bookMetadataUnabridged": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Unabridged",
|
||||||
|
),
|
||||||
|
"cancel": MessageLookupByLibrary.simpleMessage("Cancel"),
|
||||||
|
"copyToClipboard": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Copy to Clipboard",
|
||||||
|
),
|
||||||
|
"copyToClipboardDescription": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Copy the app settings to the clipboard",
|
||||||
|
),
|
||||||
|
"copyToClipboardToast": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Settings copied to clipboard",
|
||||||
|
),
|
||||||
|
"delete": MessageLookupByLibrary.simpleMessage("Delete"),
|
||||||
|
"deleteDialog": m0,
|
||||||
|
"deleted": m1,
|
||||||
|
"explore": MessageLookupByLibrary.simpleMessage("explore"),
|
||||||
|
"exploreTooltip": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Search and Explore",
|
||||||
|
),
|
||||||
|
"general": MessageLookupByLibrary.simpleMessage("General"),
|
||||||
|
"home": MessageLookupByLibrary.simpleMessage("Home"),
|
||||||
|
"homeContinueListening": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Continue Listening",
|
||||||
|
),
|
||||||
|
"homeListenAgain": MessageLookupByLibrary.simpleMessage("Listen Again"),
|
||||||
|
"homePageSettings": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Home Page Settings",
|
||||||
|
),
|
||||||
|
"homePageSettingsDescription": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Customize the home page",
|
||||||
|
),
|
||||||
|
"homeStartListening": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Start Listening",
|
||||||
|
),
|
||||||
|
"language": MessageLookupByLibrary.simpleMessage("Language"),
|
||||||
|
"languageDescription": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Language switch",
|
||||||
|
),
|
||||||
|
"library": MessageLookupByLibrary.simpleMessage("Library"),
|
||||||
|
"libraryTooltip": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Browse your library",
|
||||||
|
),
|
||||||
|
"no": MessageLookupByLibrary.simpleMessage("No"),
|
||||||
|
"notificationMediaPlayer": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Notification Media Player",
|
||||||
|
),
|
||||||
|
"notificationMediaPlayerDescription": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Customize the media player in notifications",
|
||||||
|
),
|
||||||
|
"ok": MessageLookupByLibrary.simpleMessage("OK"),
|
||||||
|
"pause": MessageLookupByLibrary.simpleMessage("Pause"),
|
||||||
|
"play": MessageLookupByLibrary.simpleMessage("Play"),
|
||||||
|
"playerSettings": MessageLookupByLibrary.simpleMessage("Player Settings"),
|
||||||
|
"playerSettingsDescription": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Customize the player settings",
|
||||||
|
),
|
||||||
|
"readLess": MessageLookupByLibrary.simpleMessage("Read Less"),
|
||||||
|
"readMore": MessageLookupByLibrary.simpleMessage("Read More"),
|
||||||
|
"reset": MessageLookupByLibrary.simpleMessage("Reset"),
|
||||||
|
"resetAppSettings": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Reset App Settings",
|
||||||
|
),
|
||||||
|
"resetAppSettingsDescription": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Reset the app settings to the default values",
|
||||||
|
),
|
||||||
|
"resetAppSettingsDialog": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Are you sure you want to reset the app settings?",
|
||||||
|
),
|
||||||
|
"restore": MessageLookupByLibrary.simpleMessage("Restore"),
|
||||||
|
"restoreBackup": MessageLookupByLibrary.simpleMessage("Restore Backup"),
|
||||||
|
"restoreBackupHint": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Paste the backup here",
|
||||||
|
),
|
||||||
|
"restoreBackupInvalid": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Invalid backup",
|
||||||
|
),
|
||||||
|
"restoreBackupSuccess": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Settings restored",
|
||||||
|
),
|
||||||
|
"restoreBackupValidator": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Please paste the backup here",
|
||||||
|
),
|
||||||
|
"restoreDescription": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Restore the app settings from the backup",
|
||||||
|
),
|
||||||
|
"resume": MessageLookupByLibrary.simpleMessage("Resume"),
|
||||||
|
"shakeDetector": MessageLookupByLibrary.simpleMessage("Shake Detector"),
|
||||||
|
"shakeDetectorDescription": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Customize the shake detector settings",
|
||||||
|
),
|
||||||
|
"themeSettings": MessageLookupByLibrary.simpleMessage("Theme Settings"),
|
||||||
|
"themeSettingsDescription": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Customize the app theme",
|
||||||
|
),
|
||||||
|
"unknown": MessageLookupByLibrary.simpleMessage("Unknown"),
|
||||||
|
"yes": MessageLookupByLibrary.simpleMessage("Yes"),
|
||||||
|
"you": MessageLookupByLibrary.simpleMessage("You"),
|
||||||
|
"youTooltip": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"Your Profile and Settings",
|
||||||
|
),
|
||||||
|
};
|
||||||
|
}
|
||||||
104
lib/generated/intl/messages_zh.dart
Normal file
104
lib/generated/intl/messages_zh.dart
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
|
||||||
|
// This is a library that provides messages for a zh locale. All the
|
||||||
|
// messages from the main program should be duplicated here with the same
|
||||||
|
// function name.
|
||||||
|
|
||||||
|
// Ignore issues from commonly used lints in this file.
|
||||||
|
// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new
|
||||||
|
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
|
||||||
|
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
|
||||||
|
// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes
|
||||||
|
// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes
|
||||||
|
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'package:intl/message_lookup_by_library.dart';
|
||||||
|
|
||||||
|
final messages = new MessageLookup();
|
||||||
|
|
||||||
|
typedef String MessageIfAbsent(String messageStr, List<dynamic> args);
|
||||||
|
|
||||||
|
class MessageLookup extends MessageLookupByLibrary {
|
||||||
|
String get localeName => 'zh';
|
||||||
|
|
||||||
|
static String m0(item) => "确定要删除 ${item} 吗?";
|
||||||
|
|
||||||
|
static String m1(item) => "已删除 ${item}";
|
||||||
|
|
||||||
|
final messages = _notInlinedMessages(_notInlinedMessages);
|
||||||
|
static Map<String, Function> _notInlinedMessages(_) => <String, Function>{
|
||||||
|
"appSettings": MessageLookupByLibrary.simpleMessage("应用设置"),
|
||||||
|
"appearance": MessageLookupByLibrary.simpleMessage("外观"),
|
||||||
|
"autoTurnOnSleepTimer": MessageLookupByLibrary.simpleMessage("自动开启睡眠定时器"),
|
||||||
|
"automaticallyDescription": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"根据一天中的时间自动打开睡眠定时器",
|
||||||
|
),
|
||||||
|
"backup": MessageLookupByLibrary.simpleMessage("备份"),
|
||||||
|
"backupAndRestore": MessageLookupByLibrary.simpleMessage("备份与恢复"),
|
||||||
|
"bookAbout": MessageLookupByLibrary.simpleMessage("关于本书"),
|
||||||
|
"bookAboutDefault": MessageLookupByLibrary.simpleMessage("抱歉,找不到描述"),
|
||||||
|
"cancel": MessageLookupByLibrary.simpleMessage("取消"),
|
||||||
|
"copyToClipboard": MessageLookupByLibrary.simpleMessage("复制到剪贴板"),
|
||||||
|
"copyToClipboardDescription": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"将应用程序设置复制到剪贴板",
|
||||||
|
),
|
||||||
|
"copyToClipboardToast": MessageLookupByLibrary.simpleMessage("设置已复制到剪贴板"),
|
||||||
|
"delete": MessageLookupByLibrary.simpleMessage("Delete"),
|
||||||
|
"deleteDialog": m0,
|
||||||
|
"deleted": m1,
|
||||||
|
"explore": MessageLookupByLibrary.simpleMessage("探索"),
|
||||||
|
"exploreTooltip": MessageLookupByLibrary.simpleMessage("搜索和探索"),
|
||||||
|
"general": MessageLookupByLibrary.simpleMessage("通用"),
|
||||||
|
"home": MessageLookupByLibrary.simpleMessage("首页"),
|
||||||
|
"homeContinueListening": MessageLookupByLibrary.simpleMessage("继续收听"),
|
||||||
|
"homeListenAgain": MessageLookupByLibrary.simpleMessage("再听一遍"),
|
||||||
|
"homePageSettings": MessageLookupByLibrary.simpleMessage("主页设置"),
|
||||||
|
"homePageSettingsDescription": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"自定义主页",
|
||||||
|
),
|
||||||
|
"homeStartListening": MessageLookupByLibrary.simpleMessage("开始收听"),
|
||||||
|
"language": MessageLookupByLibrary.simpleMessage("语言"),
|
||||||
|
"languageDescription": MessageLookupByLibrary.simpleMessage("语言切换"),
|
||||||
|
"library": MessageLookupByLibrary.simpleMessage("媒体库"),
|
||||||
|
"libraryTooltip": MessageLookupByLibrary.simpleMessage("浏览您的媒体库"),
|
||||||
|
"no": MessageLookupByLibrary.simpleMessage("否"),
|
||||||
|
"notificationMediaPlayer": MessageLookupByLibrary.simpleMessage("通知媒体播放器"),
|
||||||
|
"notificationMediaPlayerDescription": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"在通知中自定义媒体播放器",
|
||||||
|
),
|
||||||
|
"ok": MessageLookupByLibrary.simpleMessage("确定"),
|
||||||
|
"pause": MessageLookupByLibrary.simpleMessage("暂停"),
|
||||||
|
"play": MessageLookupByLibrary.simpleMessage("播放"),
|
||||||
|
"playerSettings": MessageLookupByLibrary.simpleMessage("播放器设置"),
|
||||||
|
"playerSettingsDescription": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"自定义播放器设置",
|
||||||
|
),
|
||||||
|
"readLess": MessageLookupByLibrary.simpleMessage("折叠"),
|
||||||
|
"readMore": MessageLookupByLibrary.simpleMessage("展开"),
|
||||||
|
"reset": MessageLookupByLibrary.simpleMessage("重置"),
|
||||||
|
"resetAppSettings": MessageLookupByLibrary.simpleMessage("重置应用程序设置"),
|
||||||
|
"resetAppSettingsDescription": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"将应用程序设置重置为默认值",
|
||||||
|
),
|
||||||
|
"resetAppSettingsDialog": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"您确定要重置应用程序设置吗?",
|
||||||
|
),
|
||||||
|
"restore": MessageLookupByLibrary.simpleMessage("恢复"),
|
||||||
|
"restoreBackup": MessageLookupByLibrary.simpleMessage("恢复备份"),
|
||||||
|
"restoreBackupHint": MessageLookupByLibrary.simpleMessage("将备份粘贴到此处"),
|
||||||
|
"restoreBackupInvalid": MessageLookupByLibrary.simpleMessage("无效备份"),
|
||||||
|
"restoreBackupSuccess": MessageLookupByLibrary.simpleMessage("设置已恢复"),
|
||||||
|
"restoreBackupValidator": MessageLookupByLibrary.simpleMessage("请将备份粘贴到此处"),
|
||||||
|
"restoreDescription": MessageLookupByLibrary.simpleMessage("从备份中还原应用程序设置"),
|
||||||
|
"resume": MessageLookupByLibrary.simpleMessage("继续"),
|
||||||
|
"shakeDetector": MessageLookupByLibrary.simpleMessage("抖动检测器"),
|
||||||
|
"shakeDetectorDescription": MessageLookupByLibrary.simpleMessage(
|
||||||
|
"自定义抖动检测器设置",
|
||||||
|
),
|
||||||
|
"themeSettings": MessageLookupByLibrary.simpleMessage("主题设置"),
|
||||||
|
"themeSettingsDescription": MessageLookupByLibrary.simpleMessage("自定义应用主题"),
|
||||||
|
"unknown": MessageLookupByLibrary.simpleMessage("未知"),
|
||||||
|
"yes": MessageLookupByLibrary.simpleMessage("是"),
|
||||||
|
"you": MessageLookupByLibrary.simpleMessage("我的"),
|
||||||
|
"youTooltip": MessageLookupByLibrary.simpleMessage("您的个人资料和设置"),
|
||||||
|
};
|
||||||
|
}
|
||||||
598
lib/generated/l10n.dart
Normal file
598
lib/generated/l10n.dart
Normal file
|
|
@ -0,0 +1,598 @@
|
||||||
|
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
import 'intl/messages_all.dart';
|
||||||
|
|
||||||
|
// **************************************************************************
|
||||||
|
// Generator: Flutter Intl IDE plugin
|
||||||
|
// Made by Localizely
|
||||||
|
// **************************************************************************
|
||||||
|
|
||||||
|
// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars
|
||||||
|
// ignore_for_file: join_return_with_assignment, prefer_final_in_for_each
|
||||||
|
// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes
|
||||||
|
|
||||||
|
class S {
|
||||||
|
S();
|
||||||
|
|
||||||
|
static S? _current;
|
||||||
|
|
||||||
|
static S get current {
|
||||||
|
assert(
|
||||||
|
_current != null,
|
||||||
|
'No instance of S was loaded. Try to initialize the S delegate before accessing S.current.',
|
||||||
|
);
|
||||||
|
return _current!;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const AppLocalizationDelegate delegate = AppLocalizationDelegate();
|
||||||
|
|
||||||
|
static Future<S> load(Locale locale) {
|
||||||
|
final name = (locale.countryCode?.isEmpty ?? false)
|
||||||
|
? locale.languageCode
|
||||||
|
: locale.toString();
|
||||||
|
final localeName = Intl.canonicalizedLocale(name);
|
||||||
|
return initializeMessages(localeName).then((_) {
|
||||||
|
Intl.defaultLocale = localeName;
|
||||||
|
final instance = S();
|
||||||
|
S._current = instance;
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
static S of(BuildContext context) {
|
||||||
|
final instance = S.maybeOf(context);
|
||||||
|
assert(
|
||||||
|
instance != null,
|
||||||
|
'No instance of S present in the widget tree. Did you add S.delegate in localizationsDelegates?',
|
||||||
|
);
|
||||||
|
return instance!;
|
||||||
|
}
|
||||||
|
|
||||||
|
static S? maybeOf(BuildContext context) {
|
||||||
|
return Localizations.of<S>(context, S);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Yes`
|
||||||
|
String get yes {
|
||||||
|
return Intl.message('Yes', name: 'yes', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `No`
|
||||||
|
String get no {
|
||||||
|
return Intl.message('No', name: 'no', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `OK`
|
||||||
|
String get ok {
|
||||||
|
return Intl.message('OK', name: 'ok', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Cancel`
|
||||||
|
String get cancel {
|
||||||
|
return Intl.message('Cancel', name: 'cancel', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Reset`
|
||||||
|
String get reset {
|
||||||
|
return Intl.message('Reset', name: 'reset', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Delete`
|
||||||
|
String get delete {
|
||||||
|
return Intl.message('Delete', name: 'delete', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Are you sure you want to delete {item}?`
|
||||||
|
String deleteDialog(String item) {
|
||||||
|
return Intl.message(
|
||||||
|
'Are you sure you want to delete $item?',
|
||||||
|
name: 'deleteDialog',
|
||||||
|
desc: '',
|
||||||
|
args: [item],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Deleted {item}`
|
||||||
|
String deleted(String item) {
|
||||||
|
return Intl.message(
|
||||||
|
'Deleted $item',
|
||||||
|
name: 'deleted',
|
||||||
|
desc: '已删除 {}',
|
||||||
|
args: [item],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Pause`
|
||||||
|
String get pause {
|
||||||
|
return Intl.message('Pause', name: 'pause', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Play`
|
||||||
|
String get play {
|
||||||
|
return Intl.message('Play', name: 'play', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Resume`
|
||||||
|
String get resume {
|
||||||
|
return Intl.message('Resume', name: 'resume', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Unknown`
|
||||||
|
String get unknown {
|
||||||
|
return Intl.message('Unknown', name: 'unknown', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Read More`
|
||||||
|
String get readMore {
|
||||||
|
return Intl.message('Read More', name: 'readMore', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Read Less`
|
||||||
|
String get readLess {
|
||||||
|
return Intl.message('Read Less', name: 'readLess', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Home`
|
||||||
|
String get home {
|
||||||
|
return Intl.message('Home', name: 'home', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Listen Again`
|
||||||
|
String get homeListenAgain {
|
||||||
|
return Intl.message(
|
||||||
|
'Listen Again',
|
||||||
|
name: 'homeListenAgain',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Continue Listening`
|
||||||
|
String get homeContinueListening {
|
||||||
|
return Intl.message(
|
||||||
|
'Continue Listening',
|
||||||
|
name: 'homeContinueListening',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Start Listening`
|
||||||
|
String get homeStartListening {
|
||||||
|
return Intl.message(
|
||||||
|
'Start Listening',
|
||||||
|
name: 'homeStartListening',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `About the Book`
|
||||||
|
String get bookAbout {
|
||||||
|
return Intl.message(
|
||||||
|
'About the Book',
|
||||||
|
name: 'bookAbout',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Sorry, no description found`
|
||||||
|
String get bookAboutDefault {
|
||||||
|
return Intl.message(
|
||||||
|
'Sorry, no description found',
|
||||||
|
name: 'bookAboutDefault',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Abridged`
|
||||||
|
String get bookMetadataAbridged {
|
||||||
|
return Intl.message(
|
||||||
|
'Abridged',
|
||||||
|
name: 'bookMetadataAbridged',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Unabridged`
|
||||||
|
String get bookMetadataUnabridged {
|
||||||
|
return Intl.message(
|
||||||
|
'Unabridged',
|
||||||
|
name: 'bookMetadataUnabridged',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Length`
|
||||||
|
String get bookMetadataLength {
|
||||||
|
return Intl.message(
|
||||||
|
'Length',
|
||||||
|
name: 'bookMetadataLength',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Published`
|
||||||
|
String get bookMetadataPublished {
|
||||||
|
return Intl.message(
|
||||||
|
'Published',
|
||||||
|
name: 'bookMetadataPublished',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Library`
|
||||||
|
String get library {
|
||||||
|
return Intl.message('Library', name: 'library', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Browse your library`
|
||||||
|
String get libraryTooltip {
|
||||||
|
return Intl.message(
|
||||||
|
'Browse your library',
|
||||||
|
name: 'libraryTooltip',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `explore`
|
||||||
|
String get explore {
|
||||||
|
return Intl.message('explore', name: 'explore', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Search and Explore`
|
||||||
|
String get exploreTooltip {
|
||||||
|
return Intl.message(
|
||||||
|
'Search and Explore',
|
||||||
|
name: 'exploreTooltip',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `You`
|
||||||
|
String get you {
|
||||||
|
return Intl.message('You', name: 'you', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Your Profile and Settings`
|
||||||
|
String get youTooltip {
|
||||||
|
return Intl.message(
|
||||||
|
'Your Profile and Settings',
|
||||||
|
name: 'youTooltip',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `App Settings`
|
||||||
|
String get appSettings {
|
||||||
|
return Intl.message(
|
||||||
|
'App Settings',
|
||||||
|
name: 'appSettings',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `General`
|
||||||
|
String get general {
|
||||||
|
return Intl.message('General', name: 'general', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Language`
|
||||||
|
String get language {
|
||||||
|
return Intl.message('Language', name: 'language', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Language switch`
|
||||||
|
String get languageDescription {
|
||||||
|
return Intl.message(
|
||||||
|
'Language switch',
|
||||||
|
name: 'languageDescription',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Player Settings`
|
||||||
|
String get playerSettings {
|
||||||
|
return Intl.message(
|
||||||
|
'Player Settings',
|
||||||
|
name: 'playerSettings',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Customize the player settings`
|
||||||
|
String get playerSettingsDescription {
|
||||||
|
return Intl.message(
|
||||||
|
'Customize the player settings',
|
||||||
|
name: 'playerSettingsDescription',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Auto Turn On Sleep Timer`
|
||||||
|
String get autoTurnOnSleepTimer {
|
||||||
|
return Intl.message(
|
||||||
|
'Auto Turn On Sleep Timer',
|
||||||
|
name: 'autoTurnOnSleepTimer',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Automatically turn on the sleep timer based on the time of day`
|
||||||
|
String get automaticallyDescription {
|
||||||
|
return Intl.message(
|
||||||
|
'Automatically turn on the sleep timer based on the time of day',
|
||||||
|
name: 'automaticallyDescription',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Shake Detector`
|
||||||
|
String get shakeDetector {
|
||||||
|
return Intl.message(
|
||||||
|
'Shake Detector',
|
||||||
|
name: 'shakeDetector',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Customize the shake detector settings`
|
||||||
|
String get shakeDetectorDescription {
|
||||||
|
return Intl.message(
|
||||||
|
'Customize the shake detector settings',
|
||||||
|
name: 'shakeDetectorDescription',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Appearance`
|
||||||
|
String get appearance {
|
||||||
|
return Intl.message('Appearance', name: 'appearance', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Theme Settings`
|
||||||
|
String get themeSettings {
|
||||||
|
return Intl.message(
|
||||||
|
'Theme Settings',
|
||||||
|
name: 'themeSettings',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Customize the app theme`
|
||||||
|
String get themeSettingsDescription {
|
||||||
|
return Intl.message(
|
||||||
|
'Customize the app theme',
|
||||||
|
name: 'themeSettingsDescription',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Notification Media Player`
|
||||||
|
String get notificationMediaPlayer {
|
||||||
|
return Intl.message(
|
||||||
|
'Notification Media Player',
|
||||||
|
name: 'notificationMediaPlayer',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Customize the media player in notifications`
|
||||||
|
String get notificationMediaPlayerDescription {
|
||||||
|
return Intl.message(
|
||||||
|
'Customize the media player in notifications',
|
||||||
|
name: 'notificationMediaPlayerDescription',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Home Page Settings`
|
||||||
|
String get homePageSettings {
|
||||||
|
return Intl.message(
|
||||||
|
'Home Page Settings',
|
||||||
|
name: 'homePageSettings',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Customize the home page`
|
||||||
|
String get homePageSettingsDescription {
|
||||||
|
return Intl.message(
|
||||||
|
'Customize the home page',
|
||||||
|
name: 'homePageSettingsDescription',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Backup and Restore`
|
||||||
|
String get backupAndRestore {
|
||||||
|
return Intl.message(
|
||||||
|
'Backup and Restore',
|
||||||
|
name: 'backupAndRestore',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Copy to Clipboard`
|
||||||
|
String get copyToClipboard {
|
||||||
|
return Intl.message(
|
||||||
|
'Copy to Clipboard',
|
||||||
|
name: 'copyToClipboard',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Copy the app settings to the clipboard`
|
||||||
|
String get copyToClipboardDescription {
|
||||||
|
return Intl.message(
|
||||||
|
'Copy the app settings to the clipboard',
|
||||||
|
name: 'copyToClipboardDescription',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Settings copied to clipboard`
|
||||||
|
String get copyToClipboardToast {
|
||||||
|
return Intl.message(
|
||||||
|
'Settings copied to clipboard',
|
||||||
|
name: 'copyToClipboardToast',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Restore`
|
||||||
|
String get restore {
|
||||||
|
return Intl.message('Restore', name: 'restore', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Restore the app settings from the backup`
|
||||||
|
String get restoreDescription {
|
||||||
|
return Intl.message(
|
||||||
|
'Restore the app settings from the backup',
|
||||||
|
name: 'restoreDescription',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Restore Backup`
|
||||||
|
String get restoreBackup {
|
||||||
|
return Intl.message(
|
||||||
|
'Restore Backup',
|
||||||
|
name: 'restoreBackup',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Backup`
|
||||||
|
String get backup {
|
||||||
|
return Intl.message('Backup', name: 'backup', desc: '', args: []);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Paste the backup here`
|
||||||
|
String get restoreBackupHint {
|
||||||
|
return Intl.message(
|
||||||
|
'Paste the backup here',
|
||||||
|
name: 'restoreBackupHint',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Please paste the backup here`
|
||||||
|
String get restoreBackupValidator {
|
||||||
|
return Intl.message(
|
||||||
|
'Please paste the backup here',
|
||||||
|
name: 'restoreBackupValidator',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Invalid backup`
|
||||||
|
String get restoreBackupInvalid {
|
||||||
|
return Intl.message(
|
||||||
|
'Invalid backup',
|
||||||
|
name: 'restoreBackupInvalid',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Settings restored`
|
||||||
|
String get restoreBackupSuccess {
|
||||||
|
return Intl.message(
|
||||||
|
'Settings restored',
|
||||||
|
name: 'restoreBackupSuccess',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Reset App Settings`
|
||||||
|
String get resetAppSettings {
|
||||||
|
return Intl.message(
|
||||||
|
'Reset App Settings',
|
||||||
|
name: 'resetAppSettings',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Reset the app settings to the default values`
|
||||||
|
String get resetAppSettingsDescription {
|
||||||
|
return Intl.message(
|
||||||
|
'Reset the app settings to the default values',
|
||||||
|
name: 'resetAppSettingsDescription',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `Are you sure you want to reset the app settings?`
|
||||||
|
String get resetAppSettingsDialog {
|
||||||
|
return Intl.message(
|
||||||
|
'Are you sure you want to reset the app settings?',
|
||||||
|
name: 'resetAppSettingsDialog',
|
||||||
|
desc: '',
|
||||||
|
args: [],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class AppLocalizationDelegate extends LocalizationsDelegate<S> {
|
||||||
|
const AppLocalizationDelegate();
|
||||||
|
|
||||||
|
List<Locale> get supportedLocales {
|
||||||
|
return const <Locale>[
|
||||||
|
Locale.fromSubtags(languageCode: 'en'),
|
||||||
|
Locale.fromSubtags(languageCode: 'zh'),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool isSupported(Locale locale) => _isSupported(locale);
|
||||||
|
@override
|
||||||
|
Future<S> load(Locale locale) => S.load(locale);
|
||||||
|
@override
|
||||||
|
bool shouldReload(AppLocalizationDelegate old) => false;
|
||||||
|
|
||||||
|
bool _isSupported(Locale locale) {
|
||||||
|
for (var supportedLocale in supportedLocales) {
|
||||||
|
if (supportedLocale.languageCode == locale.languageCode) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
87
lib/l10n/intl_en.arb
Normal file
87
lib/l10n/intl_en.arb
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
{
|
||||||
|
"@@locale": "en",
|
||||||
|
|
||||||
|
"yes": "Yes",
|
||||||
|
"no": "No",
|
||||||
|
"ok": "OK",
|
||||||
|
"cancel": "Cancel",
|
||||||
|
"reset": "Reset",
|
||||||
|
"delete": "Delete",
|
||||||
|
"deleteDialog": "Are you sure you want to delete {item}?",
|
||||||
|
"@deleteDialog": {
|
||||||
|
"placeholders": {
|
||||||
|
"item": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deleted": "Deleted {item}",
|
||||||
|
"@deleted": {
|
||||||
|
"description": "已删除 {}",
|
||||||
|
"placeholders": {
|
||||||
|
"item": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"pause": "Pause",
|
||||||
|
"play": "Play",
|
||||||
|
"resume": "Resume",
|
||||||
|
"unknown": "Unknown",
|
||||||
|
"readMore": "Read More",
|
||||||
|
"readLess": "Read Less",
|
||||||
|
|
||||||
|
"home": "Home",
|
||||||
|
"homeListenAgain": "Listen Again",
|
||||||
|
"homeContinueListening": "Continue Listening",
|
||||||
|
"homeStartListening": "Start Listening",
|
||||||
|
"bookAbout": "About the Book",
|
||||||
|
"bookAboutDefault": "Sorry, no description found",
|
||||||
|
"bookMetadataAbridged": "Abridged",
|
||||||
|
"bookMetadataUnabridged": "Unabridged",
|
||||||
|
"bookMetadataLength": "Length",
|
||||||
|
"bookMetadataPublished": "Published",
|
||||||
|
|
||||||
|
"library": "Library",
|
||||||
|
"libraryTooltip": "Browse your library",
|
||||||
|
"explore": "explore",
|
||||||
|
"exploreTooltip": "Search and Explore",
|
||||||
|
"you": "You",
|
||||||
|
"youTooltip": "Your Profile and Settings",
|
||||||
|
|
||||||
|
"appSettings": "App Settings",
|
||||||
|
"general": "General",
|
||||||
|
"language": "Language",
|
||||||
|
"languageDescription": "Language switch",
|
||||||
|
"playerSettings": "Player Settings",
|
||||||
|
"playerSettingsDescription": "Customize the player settings",
|
||||||
|
"autoTurnOnSleepTimer": "Auto Turn On Sleep Timer",
|
||||||
|
"automaticallyDescription": "Automatically turn on the sleep timer based on the time of day",
|
||||||
|
"shakeDetector": "Shake Detector",
|
||||||
|
"shakeDetectorDescription": "Customize the shake detector settings",
|
||||||
|
"appearance": "Appearance",
|
||||||
|
"themeSettings": "Theme Settings",
|
||||||
|
"themeSettingsDescription": "Customize the app theme",
|
||||||
|
"notificationMediaPlayer": "Notification Media Player",
|
||||||
|
"notificationMediaPlayerDescription": "Customize the media player in notifications",
|
||||||
|
"homePageSettings": "Home Page Settings",
|
||||||
|
"homePageSettingsDescription": "Customize the home page",
|
||||||
|
"backupAndRestore": "Backup and Restore",
|
||||||
|
"copyToClipboard": "Copy to Clipboard",
|
||||||
|
"copyToClipboardDescription": "Copy the app settings to the clipboard",
|
||||||
|
"copyToClipboardToast": "Settings copied to clipboard",
|
||||||
|
"restore": "Restore",
|
||||||
|
"restoreDescription": "Restore the app settings from the backup",
|
||||||
|
"restoreBackup": "Restore Backup",
|
||||||
|
"backup": "Backup",
|
||||||
|
"restoreBackupHint": "Paste the backup here",
|
||||||
|
"restoreBackupValidator": "Please paste the backup here",
|
||||||
|
"restoreBackupInvalid": "Invalid backup",
|
||||||
|
"restoreBackupSuccess": "Settings restored",
|
||||||
|
|
||||||
|
"resetAppSettings": "Reset App Settings",
|
||||||
|
"resetAppSettingsDescription": "Reset the app settings to the default values",
|
||||||
|
"resetAppSettingsDialog": "Are you sure you want to reset the app settings?"
|
||||||
|
|
||||||
|
}
|
||||||
84
lib/l10n/intl_zh.arb
Normal file
84
lib/l10n/intl_zh.arb
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
{
|
||||||
|
"@@locale": "zh",
|
||||||
|
|
||||||
|
"yes": "是",
|
||||||
|
"no": "否",
|
||||||
|
"ok": "确定",
|
||||||
|
"cancel": "取消",
|
||||||
|
"reset": "重置",
|
||||||
|
"delete": "Delete",
|
||||||
|
"deleteDialog": "确定要删除 {item} 吗?",
|
||||||
|
"@deleteDialog": {
|
||||||
|
"placeholders": {
|
||||||
|
"item": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"deleted": "已删除 {item}",
|
||||||
|
"@deleted": {
|
||||||
|
"description": "已删除 {}",
|
||||||
|
"placeholders": {
|
||||||
|
"item": {
|
||||||
|
"type": "String"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
"pause": "暂停",
|
||||||
|
"play": "播放",
|
||||||
|
"resume": "继续",
|
||||||
|
"unknown": "未知",
|
||||||
|
"readMore": "展开",
|
||||||
|
"readLess": "折叠",
|
||||||
|
|
||||||
|
"home": "首页",
|
||||||
|
"homeListenAgain": "再听一遍",
|
||||||
|
"homeContinueListening": "继续收听",
|
||||||
|
"homeStartListening": "开始收听",
|
||||||
|
"bookAbout": "关于本书",
|
||||||
|
"bookAboutDefault": "抱歉,找不到描述",
|
||||||
|
|
||||||
|
"library": "媒体库",
|
||||||
|
"libraryTooltip": "浏览您的媒体库",
|
||||||
|
"explore": "探索",
|
||||||
|
"exploreTooltip": "搜索和探索",
|
||||||
|
"you": "我的",
|
||||||
|
"youTooltip": "您的个人资料和设置",
|
||||||
|
|
||||||
|
"appSettings": "应用设置",
|
||||||
|
"general": "通用",
|
||||||
|
"language": "语言",
|
||||||
|
"languageDescription": "语言切换",
|
||||||
|
"playerSettings": "播放器设置",
|
||||||
|
"playerSettingsDescription": "自定义播放器设置",
|
||||||
|
"autoTurnOnSleepTimer": "自动开启睡眠定时器",
|
||||||
|
"automaticallyDescription": "根据一天中的时间自动打开睡眠定时器",
|
||||||
|
|
||||||
|
"shakeDetector": "抖动检测器",
|
||||||
|
"shakeDetectorDescription": "自定义抖动检测器设置",
|
||||||
|
"appearance": "外观",
|
||||||
|
"themeSettings": "主题设置",
|
||||||
|
"themeSettingsDescription": "自定义应用主题",
|
||||||
|
"notificationMediaPlayer": "通知媒体播放器",
|
||||||
|
"notificationMediaPlayerDescription": "在通知中自定义媒体播放器",
|
||||||
|
"homePageSettings": "主页设置",
|
||||||
|
"homePageSettingsDescription": "自定义主页",
|
||||||
|
"backupAndRestore": "备份与恢复",
|
||||||
|
"copyToClipboard": "复制到剪贴板",
|
||||||
|
"copyToClipboardDescription": "将应用程序设置复制到剪贴板",
|
||||||
|
"copyToClipboardToast": "设置已复制到剪贴板",
|
||||||
|
"restore": "恢复",
|
||||||
|
"restoreDescription": "从备份中还原应用程序设置",
|
||||||
|
"restoreBackup": "恢复备份",
|
||||||
|
"backup": "备份",
|
||||||
|
"restoreBackupHint": "将备份粘贴到此处",
|
||||||
|
"restoreBackupValidator": "请将备份粘贴到此处",
|
||||||
|
"restoreBackupInvalid": "无效备份",
|
||||||
|
"restoreBackupSuccess": "设置已恢复",
|
||||||
|
|
||||||
|
"resetAppSettings": "重置应用程序设置",
|
||||||
|
"resetAppSettingsDescription": "将应用程序设置重置为默认值",
|
||||||
|
"resetAppSettingsDialog": "您确定要重置应用程序设置吗?"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import 'package:dynamic_color/dynamic_color.dart';
|
import 'package:dynamic_color/dynamic_color.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:vaani/api/server_provider.dart';
|
import 'package:vaani/api/server_provider.dart';
|
||||||
|
|
@ -12,6 +13,7 @@ import 'package:vaani/features/player/providers/audiobook_player.dart'
|
||||||
show audiobookPlayerProvider, simpleAudiobookPlayerProvider;
|
show audiobookPlayerProvider, simpleAudiobookPlayerProvider;
|
||||||
import 'package:vaani/features/shake_detection/providers/shake_detector.dart';
|
import 'package:vaani/features/shake_detection/providers/shake_detector.dart';
|
||||||
import 'package:vaani/features/sleep_timer/providers/sleep_timer_provider.dart';
|
import 'package:vaani/features/sleep_timer/providers/sleep_timer_provider.dart';
|
||||||
|
import 'package:vaani/generated/l10n.dart';
|
||||||
import 'package:vaani/router/router.dart';
|
import 'package:vaani/router/router.dart';
|
||||||
import 'package:vaani/settings/api_settings_provider.dart';
|
import 'package:vaani/settings/api_settings_provider.dart';
|
||||||
import 'package:vaani/settings/app_settings_provider.dart';
|
import 'package:vaani/settings/app_settings_provider.dart';
|
||||||
|
|
@ -126,6 +128,15 @@ class MyApp extends ConsumerWidget {
|
||||||
try {
|
try {
|
||||||
return MaterialApp.router(
|
return MaterialApp.router(
|
||||||
// debugShowCheckedModeBanner: false,
|
// debugShowCheckedModeBanner: false,
|
||||||
|
locale: Locale(appSettings.language),
|
||||||
|
localizationsDelegates: [
|
||||||
|
// 以下是其他代理
|
||||||
|
S.delegate, //String 资源的 本地化
|
||||||
|
GlobalMaterialLocalizations.delegate, //Material Widgets 的本地化
|
||||||
|
GlobalWidgetsLocalizations.delegate, //基础 Widgets 的本地化
|
||||||
|
GlobalCupertinoLocalizations.delegate,
|
||||||
|
],
|
||||||
|
supportedLocales: S.delegate.supportedLocales,
|
||||||
theme: appThemeLight,
|
theme: appThemeLight,
|
||||||
darkTheme: appThemeDark,
|
darkTheme: appThemeDark,
|
||||||
themeMode: themeSettings.themeMode,
|
themeMode: themeSettings.themeMode,
|
||||||
|
|
|
||||||
|
|
@ -25,8 +25,7 @@ import 'transitions/slide.dart';
|
||||||
|
|
||||||
part 'constants.dart';
|
part 'constants.dart';
|
||||||
|
|
||||||
final GlobalKey<NavigatorState> rootNavigatorKey =
|
final GlobalKey<NavigatorState> rootNavigatorKey = GlobalKey<NavigatorState>(debugLabel: 'root');
|
||||||
GlobalKey<NavigatorState>(debugLabel: 'root');
|
|
||||||
final GlobalKey<NavigatorState> sectionHomeNavigatorKey =
|
final GlobalKey<NavigatorState> sectionHomeNavigatorKey =
|
||||||
GlobalKey<NavigatorState>(debugLabel: 'HomeNavigator');
|
GlobalKey<NavigatorState>(debugLabel: 'HomeNavigator');
|
||||||
|
|
||||||
|
|
@ -94,10 +93,8 @@ class MyAppRouter {
|
||||||
// itemId: itemId, extra: state.extra);
|
// itemId: itemId, extra: state.extra);
|
||||||
// },
|
// },
|
||||||
pageBuilder: (context, state) {
|
pageBuilder: (context, state) {
|
||||||
final itemId = state
|
final itemId = state.pathParameters[Routes.libraryItem.pathParamName]!;
|
||||||
.pathParameters[Routes.libraryItem.pathParamName]!;
|
final child = LibraryItemPage(itemId: itemId, extra: state.extra);
|
||||||
final child =
|
|
||||||
LibraryItemPage(itemId: itemId, extra: state.extra);
|
|
||||||
return buildPageWithDefaultTransition(
|
return buildPageWithDefaultTransition(
|
||||||
context: context,
|
context: context,
|
||||||
state: state,
|
state: state,
|
||||||
|
|
@ -204,8 +201,7 @@ class MyAppRouter {
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: Routes.playerSettings.pathName,
|
path: Routes.playerSettings.pathName,
|
||||||
name: Routes.playerSettings.name,
|
name: Routes.playerSettings.name,
|
||||||
pageBuilder:
|
pageBuilder: defaultPageBuilder(const PlayerSettingsPage()),
|
||||||
defaultPageBuilder(const PlayerSettingsPage()),
|
|
||||||
),
|
),
|
||||||
GoRoute(
|
GoRoute(
|
||||||
path: Routes.shakeDetectorSettings.pathName,
|
path: Routes.shakeDetectorSettings.pathName,
|
||||||
|
|
@ -253,8 +249,7 @@ class MyAppRouter {
|
||||||
final stateParam = state.uri.queryParameters['state'];
|
final stateParam = state.uri.queryParameters['state'];
|
||||||
appLogger.fine('deep linking callback: code: $code, state: $stateParam');
|
appLogger.fine('deep linking callback: code: $code, state: $stateParam');
|
||||||
|
|
||||||
var callbackPage =
|
var callbackPage = CallbackPage(code: code, state: stateParam, key: ValueKey(stateParam));
|
||||||
CallbackPage(code: code, state: stateParam, key: ValueKey(stateParam));
|
|
||||||
return buildPageWithDefaultTransition(
|
return buildPageWithDefaultTransition(
|
||||||
context: context,
|
context: context,
|
||||||
state: state,
|
state: state,
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import 'package:vaani/features/player/providers/player_form.dart';
|
||||||
import 'package:vaani/features/player/view/audiobook_player.dart';
|
import 'package:vaani/features/player/view/audiobook_player.dart';
|
||||||
import 'package:vaani/features/player/view/player_when_expanded.dart';
|
import 'package:vaani/features/player/view/player_when_expanded.dart';
|
||||||
import 'package:vaani/features/you/view/widgets/library_switch_chip.dart';
|
import 'package:vaani/features/you/view/widgets/library_switch_chip.dart';
|
||||||
|
import 'package:vaani/generated/l10n.dart';
|
||||||
import 'package:vaani/main.dart';
|
import 'package:vaani/main.dart';
|
||||||
import 'package:vaani/router/router.dart';
|
import 'package:vaani/router/router.dart';
|
||||||
import 'package:vaani/shared/icons/abs_icons.dart' show AbsIcons;
|
import 'package:vaani/shared/icons/abs_icons.dart' show AbsIcons;
|
||||||
|
|
@ -36,7 +37,7 @@ class ScaffoldWithNavBar extends HookConsumerWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final playerProgress = ref.watch(playerHeightProvider);
|
final playerProgress = ref.watch(playerHeightProvider);
|
||||||
final isMobile = Platform.isAndroid || Platform.isIOS;
|
final isMobile = Platform.isFuchsia || Platform.isAndroid || Platform.isIOS;
|
||||||
onBackButtonPressed() async {
|
onBackButtonPressed() async {
|
||||||
final isPlayerExpanded = playerProgress != playerMinHeight;
|
final isPlayerExpanded = playerProgress != playerMinHeight;
|
||||||
|
|
||||||
|
|
@ -90,75 +91,72 @@ class ScaffoldWithNavBar extends HookConsumerWidget {
|
||||||
return BackButtonListener(
|
return BackButtonListener(
|
||||||
onBackButtonPressed: onBackButtonPressed,
|
onBackButtonPressed: onBackButtonPressed,
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
body: isMobile
|
body: Stack(
|
||||||
? Stack(
|
children: [
|
||||||
children: [
|
isMobile ? navigationShell : buildNavLeft(context, ref),
|
||||||
navigationShell,
|
const AudiobookPlayer(),
|
||||||
const AudiobookPlayer(),
|
],
|
||||||
],
|
),
|
||||||
)
|
|
||||||
: buildNavLeft(context, ref),
|
|
||||||
bottomNavigationBar: isMobile ? buildNavBottom(context, ref) : null,
|
bottomNavigationBar: isMobile ? buildNavBottom(context, ref) : null,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget buildNavLeft(BuildContext context, WidgetRef ref) {
|
Widget buildNavLeft(BuildContext context, WidgetRef ref) {
|
||||||
return Row(
|
final isPlayerActive = ref.watch(isPlayerActiveProvider);
|
||||||
children: [
|
return Padding(
|
||||||
SafeArea(
|
padding: EdgeInsets.only(bottom: isPlayerActive ? playerMinHeight : 0),
|
||||||
child: NavigationRail(
|
child: Row(
|
||||||
minWidth: 60,
|
children: [
|
||||||
minExtendedWidth: 120,
|
SafeArea(
|
||||||
extended: MediaQuery.of(context).size.width > 640,
|
child: NavigationRail(
|
||||||
// extended: false,
|
minWidth: 60,
|
||||||
destinations: _navigationItems.map((item) {
|
minExtendedWidth: 120,
|
||||||
final isDestinationLibrary = item.name == 'Library';
|
extended: MediaQuery.of(context).size.width > 640,
|
||||||
var currentLibrary = ref.watch(currentLibraryProvider).valueOrNull;
|
// extended: false,
|
||||||
final libraryIcon = AbsIcons.getIconByName(
|
destinations: _navigationItems(context).map((item) {
|
||||||
currentLibrary?.icon,
|
final isDestinationLibrary = item.name == S.of(context).library;
|
||||||
);
|
var currentLibrary = ref.watch(currentLibraryProvider).valueOrNull;
|
||||||
final destinationWidget = NavigationRailDestination(
|
final libraryIcon = AbsIcons.getIconByName(
|
||||||
icon: Icon(
|
currentLibrary?.icon,
|
||||||
isDestinationLibrary ? libraryIcon ?? item.icon : item.icon,
|
);
|
||||||
),
|
final destinationWidget = NavigationRailDestination(
|
||||||
selectedIcon: Icon(
|
icon: Icon(
|
||||||
isDestinationLibrary ? libraryIcon ?? item.activeIcon : item.activeIcon,
|
isDestinationLibrary ? libraryIcon ?? item.icon : item.icon,
|
||||||
),
|
),
|
||||||
label: Text(isDestinationLibrary ? currentLibrary?.name ?? item.name : item.name),
|
selectedIcon: Icon(
|
||||||
// tooltip: item.tooltip,
|
isDestinationLibrary ? libraryIcon ?? item.activeIcon : item.activeIcon,
|
||||||
);
|
),
|
||||||
// if (isDestinationLibrary) {
|
label: Text(isDestinationLibrary ? currentLibrary?.name ?? item.name : item.name),
|
||||||
// return GestureDetector(
|
// tooltip: item.tooltip,
|
||||||
// onSecondaryTap: () => showLibrarySwitcher(context, ref),
|
);
|
||||||
// onDoubleTap: () => showLibrarySwitcher(context, ref),
|
// if (isDestinationLibrary) {
|
||||||
// child:
|
// return GestureDetector(
|
||||||
// destinationWidget, // Wrap the actual NavigationDestination
|
// onSecondaryTap: () => showLibrarySwitcher(context, ref),
|
||||||
// );
|
// onDoubleTap: () => showLibrarySwitcher(context, ref),
|
||||||
// } else {
|
// child:
|
||||||
// // Return the unwrapped destination for other items
|
// destinationWidget, // Wrap the actual NavigationDestination
|
||||||
// return destinationWidget;
|
// );
|
||||||
// }
|
// } else {
|
||||||
return destinationWidget;
|
// // Return the unwrapped destination for other items
|
||||||
// return NavigationRailDestination(icon: Icon(nav.icon), label: Text(nav.name));
|
// return destinationWidget;
|
||||||
}).toList(),
|
// }
|
||||||
selectedIndex: navigationShell.currentIndex,
|
return destinationWidget;
|
||||||
onDestinationSelected: (int index) {
|
// return NavigationRailDestination(icon: Icon(nav.icon), label: Text(nav.name));
|
||||||
print(index);
|
}).toList(),
|
||||||
_onTap(context, index, ref);
|
selectedIndex: navigationShell.currentIndex,
|
||||||
},
|
onDestinationSelected: (int index) {
|
||||||
|
print(index);
|
||||||
|
_onTap(context, index, ref);
|
||||||
|
},
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
VerticalDivider(width: 0.5, thickness: 0.5),
|
||||||
VerticalDivider(width: 0.5, thickness: 0.5),
|
Expanded(
|
||||||
Expanded(
|
child: navigationShell,
|
||||||
child: Stack(
|
|
||||||
children: [
|
|
||||||
navigationShell,
|
|
||||||
const AudiobookPlayer(),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -184,8 +182,8 @@ class ScaffoldWithNavBar extends HookConsumerWidget {
|
||||||
// world scenario, the items would most likely be generated from the
|
// world scenario, the items would most likely be generated from the
|
||||||
// branches of the shell route, which can be fetched using
|
// branches of the shell route, which can be fetched using
|
||||||
// `navigationShell.route.branches`.
|
// `navigationShell.route.branches`.
|
||||||
destinations: _navigationItems.map((item) {
|
destinations: _navigationItems(context).map((item) {
|
||||||
final isDestinationLibrary = item.name == 'Library';
|
final isDestinationLibrary = item.name == S.of(context).library;
|
||||||
var currentLibrary = ref.watch(currentLibraryProvider).valueOrNull;
|
var currentLibrary = ref.watch(currentLibraryProvider).valueOrNull;
|
||||||
final libraryIcon = AbsIcons.getIconByName(
|
final libraryIcon = AbsIcons.getIconByName(
|
||||||
currentLibrary?.icon,
|
currentLibrary?.icon,
|
||||||
|
|
@ -217,6 +215,42 @@ class ScaffoldWithNavBar extends HookConsumerWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
List<_NavigationItem> _navigationItems(BuildContext context) {
|
||||||
|
return [
|
||||||
|
_NavigationItem(
|
||||||
|
// name: 'Home',
|
||||||
|
name: S.of(context).home,
|
||||||
|
icon: Icons.home_outlined,
|
||||||
|
activeIcon: Icons.home,
|
||||||
|
),
|
||||||
|
// Library
|
||||||
|
_NavigationItem(
|
||||||
|
// name: 'Library',
|
||||||
|
name: S.of(context).library,
|
||||||
|
icon: Icons.book_outlined,
|
||||||
|
activeIcon: Icons.book,
|
||||||
|
// tooltip: 'Browse your library',
|
||||||
|
tooltip: S.of(context).exploreTooltip,
|
||||||
|
),
|
||||||
|
_NavigationItem(
|
||||||
|
// name: 'Explore',
|
||||||
|
name: S.of(context).explore,
|
||||||
|
icon: Icons.search_outlined,
|
||||||
|
activeIcon: Icons.search,
|
||||||
|
// tooltip: 'Search and Explore',
|
||||||
|
tooltip: S.of(context).exploreTooltip,
|
||||||
|
),
|
||||||
|
_NavigationItem(
|
||||||
|
// name: 'You',
|
||||||
|
name: S.of(context).you,
|
||||||
|
icon: Icons.account_circle_outlined,
|
||||||
|
activeIcon: Icons.account_circle,
|
||||||
|
// tooltip: 'Your Profile and Settings',
|
||||||
|
tooltip: S.of(context).youTooltip,
|
||||||
|
),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
/// Navigate to the current location of the branch at the provided index when
|
/// Navigate to the current location of the branch at the provided index when
|
||||||
/// tapping an item in the BottomNavigationBar.
|
/// tapping an item in the BottomNavigationBar.
|
||||||
void _onTap(BuildContext context, int index, WidgetRef ref) {
|
void _onTap(BuildContext context, int index, WidgetRef ref) {
|
||||||
|
|
@ -267,32 +301,32 @@ class ScaffoldWithNavBar extends HookConsumerWidget {
|
||||||
|
|
||||||
// list of constants with names and icons so that they can be used in the bottom navigation bar
|
// list of constants with names and icons so that they can be used in the bottom navigation bar
|
||||||
// and reused for nav rail and other places
|
// and reused for nav rail and other places
|
||||||
const _navigationItems = [
|
// const _navigationItems = [
|
||||||
_NavigationItem(
|
// _NavigationItem(
|
||||||
name: 'Home',
|
// name: 'Home',
|
||||||
icon: Icons.home_outlined,
|
// icon: Icons.home_outlined,
|
||||||
activeIcon: Icons.home,
|
// activeIcon: Icons.home,
|
||||||
),
|
// ),
|
||||||
// Library
|
// // Library
|
||||||
_NavigationItem(
|
// _NavigationItem(
|
||||||
name: 'Library',
|
// name: 'Library',
|
||||||
icon: Icons.book_outlined,
|
// icon: Icons.book_outlined,
|
||||||
activeIcon: Icons.book,
|
// activeIcon: Icons.book,
|
||||||
tooltip: 'Browse your library',
|
// tooltip: 'Browse your library',
|
||||||
),
|
// ),
|
||||||
_NavigationItem(
|
// _NavigationItem(
|
||||||
name: 'Explore',
|
// name: 'Explore',
|
||||||
icon: Icons.search_outlined,
|
// icon: Icons.search_outlined,
|
||||||
activeIcon: Icons.search,
|
// activeIcon: Icons.search,
|
||||||
tooltip: 'Search and Explore',
|
// tooltip: 'Search and Explore',
|
||||||
),
|
// ),
|
||||||
_NavigationItem(
|
// _NavigationItem(
|
||||||
name: 'You',
|
// name: 'You',
|
||||||
icon: Icons.account_circle_outlined,
|
// icon: Icons.account_circle_outlined,
|
||||||
activeIcon: Icons.account_circle,
|
// activeIcon: Icons.account_circle,
|
||||||
tooltip: 'Your Profile and Settings',
|
// tooltip: 'Your Profile and Settings',
|
||||||
),
|
// ),
|
||||||
];
|
// ];
|
||||||
|
|
||||||
class _NavigationItem {
|
class _NavigationItem {
|
||||||
const _NavigationItem({
|
const _NavigationItem({
|
||||||
|
|
|
||||||
|
|
@ -12,18 +12,17 @@ part 'app_settings.g.dart';
|
||||||
@freezed
|
@freezed
|
||||||
class AppSettings with _$AppSettings {
|
class AppSettings with _$AppSettings {
|
||||||
const factory AppSettings({
|
const factory AppSettings({
|
||||||
|
@Default('zh') String language,
|
||||||
@Default(ThemeSettings()) ThemeSettings themeSettings,
|
@Default(ThemeSettings()) ThemeSettings themeSettings,
|
||||||
@Default(PlayerSettings()) PlayerSettings playerSettings,
|
@Default(PlayerSettings()) PlayerSettings playerSettings,
|
||||||
@Default(SleepTimerSettings()) SleepTimerSettings sleepTimerSettings,
|
@Default(SleepTimerSettings()) SleepTimerSettings sleepTimerSettings,
|
||||||
@Default(DownloadSettings()) DownloadSettings downloadSettings,
|
@Default(DownloadSettings()) DownloadSettings downloadSettings,
|
||||||
@Default(NotificationSettings()) NotificationSettings notificationSettings,
|
@Default(NotificationSettings()) NotificationSettings notificationSettings,
|
||||||
@Default(ShakeDetectionSettings())
|
@Default(ShakeDetectionSettings()) ShakeDetectionSettings shakeDetectionSettings,
|
||||||
ShakeDetectionSettings shakeDetectionSettings,
|
|
||||||
@Default(HomePageSettings()) HomePageSettings homePageSettings,
|
@Default(HomePageSettings()) HomePageSettings homePageSettings,
|
||||||
}) = _AppSettings;
|
}) = _AppSettings;
|
||||||
|
|
||||||
factory AppSettings.fromJson(Map<String, dynamic> json) =>
|
factory AppSettings.fromJson(Map<String, dynamic> json) => _$AppSettingsFromJson(json);
|
||||||
_$AppSettingsFromJson(json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
|
|
@ -37,17 +36,14 @@ class ThemeSettings with _$ThemeSettings {
|
||||||
@Default(true) bool useCurrentPlayerThemeThroughoutApp,
|
@Default(true) bool useCurrentPlayerThemeThroughoutApp,
|
||||||
}) = _ThemeSettings;
|
}) = _ThemeSettings;
|
||||||
|
|
||||||
factory ThemeSettings.fromJson(Map<String, dynamic> json) =>
|
factory ThemeSettings.fromJson(Map<String, dynamic> json) => _$ThemeSettingsFromJson(json);
|
||||||
_$ThemeSettingsFromJson(json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
class PlayerSettings with _$PlayerSettings {
|
class PlayerSettings with _$PlayerSettings {
|
||||||
const factory PlayerSettings({
|
const factory PlayerSettings({
|
||||||
@Default(MinimizedPlayerSettings())
|
@Default(MinimizedPlayerSettings()) MinimizedPlayerSettings miniPlayerSettings,
|
||||||
MinimizedPlayerSettings miniPlayerSettings,
|
@Default(ExpandedPlayerSettings()) ExpandedPlayerSettings expandedPlayerSettings,
|
||||||
@Default(ExpandedPlayerSettings())
|
|
||||||
ExpandedPlayerSettings expandedPlayerSettings,
|
|
||||||
@Default(1) double preferredDefaultVolume,
|
@Default(1) double preferredDefaultVolume,
|
||||||
@Default(1) double preferredDefaultSpeed,
|
@Default(1) double preferredDefaultSpeed,
|
||||||
@Default([1, 1.25, 1.5, 1.75, 2]) List<double> speedOptions,
|
@Default([1, 1.25, 1.5, 1.75, 2]) List<double> speedOptions,
|
||||||
|
|
@ -60,8 +56,7 @@ class PlayerSettings with _$PlayerSettings {
|
||||||
@Default(true) bool configurePlayerForEveryBook,
|
@Default(true) bool configurePlayerForEveryBook,
|
||||||
}) = _PlayerSettings;
|
}) = _PlayerSettings;
|
||||||
|
|
||||||
factory PlayerSettings.fromJson(Map<String, dynamic> json) =>
|
factory PlayerSettings.fromJson(Map<String, dynamic> json) => _$PlayerSettingsFromJson(json);
|
||||||
_$PlayerSettingsFromJson(json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
|
|
@ -144,8 +139,7 @@ class DownloadSettings with _$DownloadSettings {
|
||||||
@Default(3) int maxConcurrentByGroup,
|
@Default(3) int maxConcurrentByGroup,
|
||||||
}) = _DownloadSettings;
|
}) = _DownloadSettings;
|
||||||
|
|
||||||
factory DownloadSettings.fromJson(Map<String, dynamic> json) =>
|
factory DownloadSettings.fromJson(Map<String, dynamic> json) => _$DownloadSettingsFromJson(json);
|
||||||
_$DownloadSettingsFromJson(json);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@freezed
|
@freezed
|
||||||
|
|
@ -202,8 +196,7 @@ class ShakeDetectionSettings with _$ShakeDetectionSettings {
|
||||||
@Default(ShakeDirection.horizontal) ShakeDirection direction,
|
@Default(ShakeDirection.horizontal) ShakeDirection direction,
|
||||||
@Default(5) double threshold,
|
@Default(5) double threshold,
|
||||||
@Default(ShakeAction.resetSleepTimer) ShakeAction shakeAction,
|
@Default(ShakeAction.resetSleepTimer) ShakeAction shakeAction,
|
||||||
@Default({ShakeDetectedFeedback.vibrate})
|
@Default({ShakeDetectedFeedback.vibrate}) Set<ShakeDetectedFeedback> feedback,
|
||||||
Set<ShakeDetectedFeedback> feedback,
|
|
||||||
@Default(0.5) double beepVolume,
|
@Default(0.5) double beepVolume,
|
||||||
|
|
||||||
/// the duration to wait before the shake detection is enabled again
|
/// the duration to wait before the shake detection is enabled again
|
||||||
|
|
@ -241,6 +234,5 @@ class HomePageSettings with _$HomePageSettings {
|
||||||
@Default(false) bool showPlayButtonOnListenAgainShelf,
|
@Default(false) bool showPlayButtonOnListenAgainShelf,
|
||||||
}) = _HomePageSettings;
|
}) = _HomePageSettings;
|
||||||
|
|
||||||
factory HomePageSettings.fromJson(Map<String, dynamic> json) =>
|
factory HomePageSettings.fromJson(Map<String, dynamic> json) => _$HomePageSettingsFromJson(json);
|
||||||
_$HomePageSettingsFromJson(json);
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ AppSettings _$AppSettingsFromJson(Map<String, dynamic> json) {
|
||||||
|
|
||||||
/// @nodoc
|
/// @nodoc
|
||||||
mixin _$AppSettings {
|
mixin _$AppSettings {
|
||||||
|
String get language => throw _privateConstructorUsedError;
|
||||||
ThemeSettings get themeSettings => throw _privateConstructorUsedError;
|
ThemeSettings get themeSettings => throw _privateConstructorUsedError;
|
||||||
PlayerSettings get playerSettings => throw _privateConstructorUsedError;
|
PlayerSettings get playerSettings => throw _privateConstructorUsedError;
|
||||||
SleepTimerSettings get sleepTimerSettings =>
|
SleepTimerSettings get sleepTimerSettings =>
|
||||||
|
|
@ -48,7 +49,8 @@ abstract class $AppSettingsCopyWith<$Res> {
|
||||||
_$AppSettingsCopyWithImpl<$Res, AppSettings>;
|
_$AppSettingsCopyWithImpl<$Res, AppSettings>;
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call(
|
||||||
{ThemeSettings themeSettings,
|
{String language,
|
||||||
|
ThemeSettings themeSettings,
|
||||||
PlayerSettings playerSettings,
|
PlayerSettings playerSettings,
|
||||||
SleepTimerSettings sleepTimerSettings,
|
SleepTimerSettings sleepTimerSettings,
|
||||||
DownloadSettings downloadSettings,
|
DownloadSettings downloadSettings,
|
||||||
|
|
@ -80,6 +82,7 @@ class _$AppSettingsCopyWithImpl<$Res, $Val extends AppSettings>
|
||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
@override
|
@override
|
||||||
$Res call({
|
$Res call({
|
||||||
|
Object? language = null,
|
||||||
Object? themeSettings = null,
|
Object? themeSettings = null,
|
||||||
Object? playerSettings = null,
|
Object? playerSettings = null,
|
||||||
Object? sleepTimerSettings = null,
|
Object? sleepTimerSettings = null,
|
||||||
|
|
@ -89,6 +92,10 @@ class _$AppSettingsCopyWithImpl<$Res, $Val extends AppSettings>
|
||||||
Object? homePageSettings = null,
|
Object? homePageSettings = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_value.copyWith(
|
return _then(_value.copyWith(
|
||||||
|
language: null == language
|
||||||
|
? _value.language
|
||||||
|
: language // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
themeSettings: null == themeSettings
|
themeSettings: null == themeSettings
|
||||||
? _value.themeSettings
|
? _value.themeSettings
|
||||||
: themeSettings // ignore: cast_nullable_to_non_nullable
|
: themeSettings // ignore: cast_nullable_to_non_nullable
|
||||||
|
|
@ -203,7 +210,8 @@ abstract class _$$AppSettingsImplCopyWith<$Res>
|
||||||
@override
|
@override
|
||||||
@useResult
|
@useResult
|
||||||
$Res call(
|
$Res call(
|
||||||
{ThemeSettings themeSettings,
|
{String language,
|
||||||
|
ThemeSettings themeSettings,
|
||||||
PlayerSettings playerSettings,
|
PlayerSettings playerSettings,
|
||||||
SleepTimerSettings sleepTimerSettings,
|
SleepTimerSettings sleepTimerSettings,
|
||||||
DownloadSettings downloadSettings,
|
DownloadSettings downloadSettings,
|
||||||
|
|
@ -240,6 +248,7 @@ class __$$AppSettingsImplCopyWithImpl<$Res>
|
||||||
@pragma('vm:prefer-inline')
|
@pragma('vm:prefer-inline')
|
||||||
@override
|
@override
|
||||||
$Res call({
|
$Res call({
|
||||||
|
Object? language = null,
|
||||||
Object? themeSettings = null,
|
Object? themeSettings = null,
|
||||||
Object? playerSettings = null,
|
Object? playerSettings = null,
|
||||||
Object? sleepTimerSettings = null,
|
Object? sleepTimerSettings = null,
|
||||||
|
|
@ -249,6 +258,10 @@ class __$$AppSettingsImplCopyWithImpl<$Res>
|
||||||
Object? homePageSettings = null,
|
Object? homePageSettings = null,
|
||||||
}) {
|
}) {
|
||||||
return _then(_$AppSettingsImpl(
|
return _then(_$AppSettingsImpl(
|
||||||
|
language: null == language
|
||||||
|
? _value.language
|
||||||
|
: language // ignore: cast_nullable_to_non_nullable
|
||||||
|
as String,
|
||||||
themeSettings: null == themeSettings
|
themeSettings: null == themeSettings
|
||||||
? _value.themeSettings
|
? _value.themeSettings
|
||||||
: themeSettings // ignore: cast_nullable_to_non_nullable
|
: themeSettings // ignore: cast_nullable_to_non_nullable
|
||||||
|
|
@ -285,7 +298,8 @@ class __$$AppSettingsImplCopyWithImpl<$Res>
|
||||||
@JsonSerializable()
|
@JsonSerializable()
|
||||||
class _$AppSettingsImpl implements _AppSettings {
|
class _$AppSettingsImpl implements _AppSettings {
|
||||||
const _$AppSettingsImpl(
|
const _$AppSettingsImpl(
|
||||||
{this.themeSettings = const ThemeSettings(),
|
{this.language = 'zh',
|
||||||
|
this.themeSettings = const ThemeSettings(),
|
||||||
this.playerSettings = const PlayerSettings(),
|
this.playerSettings = const PlayerSettings(),
|
||||||
this.sleepTimerSettings = const SleepTimerSettings(),
|
this.sleepTimerSettings = const SleepTimerSettings(),
|
||||||
this.downloadSettings = const DownloadSettings(),
|
this.downloadSettings = const DownloadSettings(),
|
||||||
|
|
@ -296,6 +310,9 @@ class _$AppSettingsImpl implements _AppSettings {
|
||||||
factory _$AppSettingsImpl.fromJson(Map<String, dynamic> json) =>
|
factory _$AppSettingsImpl.fromJson(Map<String, dynamic> json) =>
|
||||||
_$$AppSettingsImplFromJson(json);
|
_$$AppSettingsImplFromJson(json);
|
||||||
|
|
||||||
|
@override
|
||||||
|
@JsonKey()
|
||||||
|
final String language;
|
||||||
@override
|
@override
|
||||||
@JsonKey()
|
@JsonKey()
|
||||||
final ThemeSettings themeSettings;
|
final ThemeSettings themeSettings;
|
||||||
|
|
@ -320,7 +337,7 @@ class _$AppSettingsImpl implements _AppSettings {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String toString() {
|
String toString() {
|
||||||
return 'AppSettings(themeSettings: $themeSettings, playerSettings: $playerSettings, sleepTimerSettings: $sleepTimerSettings, downloadSettings: $downloadSettings, notificationSettings: $notificationSettings, shakeDetectionSettings: $shakeDetectionSettings, homePageSettings: $homePageSettings)';
|
return 'AppSettings(language: $language, themeSettings: $themeSettings, playerSettings: $playerSettings, sleepTimerSettings: $sleepTimerSettings, downloadSettings: $downloadSettings, notificationSettings: $notificationSettings, shakeDetectionSettings: $shakeDetectionSettings, homePageSettings: $homePageSettings)';
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
@ -328,6 +345,8 @@ class _$AppSettingsImpl implements _AppSettings {
|
||||||
return identical(this, other) ||
|
return identical(this, other) ||
|
||||||
(other.runtimeType == runtimeType &&
|
(other.runtimeType == runtimeType &&
|
||||||
other is _$AppSettingsImpl &&
|
other is _$AppSettingsImpl &&
|
||||||
|
(identical(other.language, language) ||
|
||||||
|
other.language == language) &&
|
||||||
(identical(other.themeSettings, themeSettings) ||
|
(identical(other.themeSettings, themeSettings) ||
|
||||||
other.themeSettings == themeSettings) &&
|
other.themeSettings == themeSettings) &&
|
||||||
(identical(other.playerSettings, playerSettings) ||
|
(identical(other.playerSettings, playerSettings) ||
|
||||||
|
|
@ -348,6 +367,7 @@ class _$AppSettingsImpl implements _AppSettings {
|
||||||
@override
|
@override
|
||||||
int get hashCode => Object.hash(
|
int get hashCode => Object.hash(
|
||||||
runtimeType,
|
runtimeType,
|
||||||
|
language,
|
||||||
themeSettings,
|
themeSettings,
|
||||||
playerSettings,
|
playerSettings,
|
||||||
sleepTimerSettings,
|
sleepTimerSettings,
|
||||||
|
|
@ -374,7 +394,8 @@ class _$AppSettingsImpl implements _AppSettings {
|
||||||
|
|
||||||
abstract class _AppSettings implements AppSettings {
|
abstract class _AppSettings implements AppSettings {
|
||||||
const factory _AppSettings(
|
const factory _AppSettings(
|
||||||
{final ThemeSettings themeSettings,
|
{final String language,
|
||||||
|
final ThemeSettings themeSettings,
|
||||||
final PlayerSettings playerSettings,
|
final PlayerSettings playerSettings,
|
||||||
final SleepTimerSettings sleepTimerSettings,
|
final SleepTimerSettings sleepTimerSettings,
|
||||||
final DownloadSettings downloadSettings,
|
final DownloadSettings downloadSettings,
|
||||||
|
|
@ -385,6 +406,8 @@ abstract class _AppSettings implements AppSettings {
|
||||||
factory _AppSettings.fromJson(Map<String, dynamic> json) =
|
factory _AppSettings.fromJson(Map<String, dynamic> json) =
|
||||||
_$AppSettingsImpl.fromJson;
|
_$AppSettingsImpl.fromJson;
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get language;
|
||||||
@override
|
@override
|
||||||
ThemeSettings get themeSettings;
|
ThemeSettings get themeSettings;
|
||||||
@override
|
@override
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ part of 'app_settings.dart';
|
||||||
|
|
||||||
_$AppSettingsImpl _$$AppSettingsImplFromJson(Map<String, dynamic> json) =>
|
_$AppSettingsImpl _$$AppSettingsImplFromJson(Map<String, dynamic> json) =>
|
||||||
_$AppSettingsImpl(
|
_$AppSettingsImpl(
|
||||||
|
language: json['language'] as String? ?? 'zh',
|
||||||
themeSettings: json['themeSettings'] == null
|
themeSettings: json['themeSettings'] == null
|
||||||
? const ThemeSettings()
|
? const ThemeSettings()
|
||||||
: ThemeSettings.fromJson(
|
: ThemeSettings.fromJson(
|
||||||
|
|
@ -40,6 +41,7 @@ _$AppSettingsImpl _$$AppSettingsImplFromJson(Map<String, dynamic> json) =>
|
||||||
|
|
||||||
Map<String, dynamic> _$$AppSettingsImplToJson(_$AppSettingsImpl instance) =>
|
Map<String, dynamic> _$$AppSettingsImplToJson(_$AppSettingsImpl instance) =>
|
||||||
<String, dynamic>{
|
<String, dynamic>{
|
||||||
|
'language': instance.language,
|
||||||
'themeSettings': instance.themeSettings,
|
'themeSettings': instance.themeSettings,
|
||||||
'playerSettings': instance.playerSettings,
|
'playerSettings': instance.playerSettings,
|
||||||
'sleepTimerSettings': instance.sleepTimerSettings,
|
'sleepTimerSettings': instance.sleepTimerSettings,
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import 'package:flutter_settings_ui/flutter_settings_ui.dart';
|
||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:material_symbols_icons/symbols.dart';
|
import 'package:material_symbols_icons/symbols.dart';
|
||||||
|
import 'package:vaani/generated/l10n.dart';
|
||||||
import 'package:vaani/router/router.dart';
|
import 'package:vaani/router/router.dart';
|
||||||
import 'package:vaani/settings/app_settings_provider.dart';
|
import 'package:vaani/settings/app_settings_provider.dart';
|
||||||
import 'package:vaani/settings/models/app_settings.dart' as model;
|
import 'package:vaani/settings/models/app_settings.dart' as model;
|
||||||
|
|
@ -18,14 +19,13 @@ class AppSettingsPage extends HookConsumerWidget {
|
||||||
const AppSettingsPage({
|
const AppSettingsPage({
|
||||||
super.key,
|
super.key,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final appSettings = ref.watch(appSettingsProvider);
|
final appSettings = ref.watch(appSettingsProvider);
|
||||||
final sleepTimerSettings = appSettings.sleepTimerSettings;
|
final sleepTimerSettings = appSettings.sleepTimerSettings;
|
||||||
|
final locales = {'en': 'English', 'zh': '中文'};
|
||||||
return SimpleSettingsPage(
|
return SimpleSettingsPage(
|
||||||
title: const Text('App Settings'),
|
title: Text(S.of(context).appSettings),
|
||||||
sections: [
|
sections: [
|
||||||
// General section
|
// General section
|
||||||
SettingsSection(
|
SettingsSection(
|
||||||
|
|
@ -34,25 +34,42 @@ class AppSettingsPage extends HookConsumerWidget {
|
||||||
vertical: 8.0,
|
vertical: 8.0,
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
'General',
|
S.of(context).general,
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
tiles: [
|
tiles: [
|
||||||
SettingsTile(
|
SettingsTile(
|
||||||
title: const Text('Player Settings'),
|
title: Text(S.of(context).language),
|
||||||
leading: const Icon(Icons.play_arrow),
|
leading: const Icon(Icons.play_arrow),
|
||||||
description: const Text(
|
trailing: DropdownButton(
|
||||||
'Customize the player settings',
|
value: appSettings.language,
|
||||||
|
items: S.delegate.supportedLocales.map((locale) {
|
||||||
|
return DropdownMenuItem(
|
||||||
|
value: locale.languageCode,
|
||||||
|
child: Text(locales[locale.languageCode] ?? 'unknown'),
|
||||||
|
);
|
||||||
|
}).toList(),
|
||||||
|
onChanged: (value) {
|
||||||
|
ref.read(appSettingsProvider.notifier).update(
|
||||||
|
appSettings.copyWith(
|
||||||
|
language: value!,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
|
description: Text(S.of(context).languageDescription),
|
||||||
|
),
|
||||||
|
SettingsTile(
|
||||||
|
title: Text(S.of(context).playerSettings),
|
||||||
|
leading: const Icon(Icons.play_arrow),
|
||||||
|
description: Text(S.of(context).playerSettingsDescription),
|
||||||
onPressed: (context) {
|
onPressed: (context) {
|
||||||
context.pushNamed(Routes.playerSettings.name);
|
context.pushNamed(Routes.playerSettings.name);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
NavigationWithSwitchTile(
|
NavigationWithSwitchTile(
|
||||||
title: const Text('Auto Turn On Sleep Timer'),
|
title: Text(S.of(context).autoTurnOnSleepTimer),
|
||||||
description: const Text(
|
description: Text(S.of(context).automaticallyDescription),
|
||||||
'Automatically turn on the sleep timer based on the time of day',
|
|
||||||
),
|
|
||||||
leading: sleepTimerSettings.autoTurnOnTimer
|
leading: sleepTimerSettings.autoTurnOnTimer
|
||||||
? const Icon(Symbols.time_auto, fill: 1)
|
? const Icon(Symbols.time_auto, fill: 1)
|
||||||
: const Icon(Symbols.timer_off, fill: 1),
|
: const Icon(Symbols.timer_off, fill: 1),
|
||||||
|
|
@ -69,10 +86,10 @@ class AppSettingsPage extends HookConsumerWidget {
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
NavigationWithSwitchTile(
|
NavigationWithSwitchTile(
|
||||||
title: const Text('Shake Detector'),
|
title: Text(S.of(context).shakeDetector),
|
||||||
leading: const Icon(Icons.vibration),
|
leading: const Icon(Icons.vibration),
|
||||||
description: const Text(
|
description: Text(
|
||||||
'Customize the shake detector settings',
|
S.of(context).shakeDetectorDescription,
|
||||||
),
|
),
|
||||||
value: appSettings.shakeDetectionSettings.isEnabled,
|
value: appSettings.shakeDetectionSettings.isEnabled,
|
||||||
onPressed: (context) {
|
onPressed: (context) {
|
||||||
|
|
@ -96,36 +113,30 @@ class AppSettingsPage extends HookConsumerWidget {
|
||||||
vertical: 8.0,
|
vertical: 8.0,
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
'Appearance',
|
S.of(context).appearance,
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
tiles: [
|
tiles: [
|
||||||
SettingsTile.navigation(
|
SettingsTile.navigation(
|
||||||
leading: const Icon(Icons.color_lens),
|
leading: const Icon(Icons.color_lens),
|
||||||
title: const Text('Theme Settings'),
|
title: Text(S.of(context).themeSettings),
|
||||||
description: const Text(
|
description: Text(S.of(context).themeSettingsDescription),
|
||||||
'Customize the app theme',
|
|
||||||
),
|
|
||||||
onPressed: (context) {
|
onPressed: (context) {
|
||||||
context.pushNamed(Routes.themeSettings.name);
|
context.pushNamed(Routes.themeSettings.name);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SettingsTile(
|
SettingsTile(
|
||||||
title: const Text('Notification Media Player'),
|
title: Text(S.of(context).notificationMediaPlayer),
|
||||||
leading: const Icon(Icons.play_lesson),
|
leading: const Icon(Icons.play_lesson),
|
||||||
description: const Text(
|
description: Text(S.of(context).notificationMediaPlayerDescription),
|
||||||
'Customize the media player in notifications',
|
|
||||||
),
|
|
||||||
onPressed: (context) {
|
onPressed: (context) {
|
||||||
context.pushNamed(Routes.notificationSettings.name);
|
context.pushNamed(Routes.notificationSettings.name);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SettingsTile.navigation(
|
SettingsTile.navigation(
|
||||||
leading: const Icon(Icons.home_filled),
|
leading: const Icon(Icons.home_filled),
|
||||||
title: const Text('Home Page Settings'),
|
title: Text(S.of(context).homePageSettings),
|
||||||
description: const Text(
|
description: Text(S.of(context).homePageSettingsDescription),
|
||||||
'Customize the home page',
|
|
||||||
),
|
|
||||||
onPressed: (context) {
|
onPressed: (context) {
|
||||||
context.pushNamed(Routes.homePageSettings.name);
|
context.pushNamed(Routes.homePageSettings.name);
|
||||||
},
|
},
|
||||||
|
|
@ -140,15 +151,15 @@ class AppSettingsPage extends HookConsumerWidget {
|
||||||
vertical: 8.0,
|
vertical: 8.0,
|
||||||
),
|
),
|
||||||
title: Text(
|
title: Text(
|
||||||
'Backup and Restore',
|
S.of(context).backupAndRestore,
|
||||||
style: Theme.of(context).textTheme.titleLarge,
|
style: Theme.of(context).textTheme.titleLarge,
|
||||||
),
|
),
|
||||||
tiles: [
|
tiles: [
|
||||||
SettingsTile(
|
SettingsTile(
|
||||||
title: const Text('Copy to Clipboard'),
|
title: Text(S.of(context).copyToClipboard),
|
||||||
leading: const Icon(Icons.copy),
|
leading: const Icon(Icons.copy),
|
||||||
description: const Text(
|
description: Text(
|
||||||
'Copy the app settings to the clipboard',
|
S.of(context).copyToClipboardDescription,
|
||||||
),
|
),
|
||||||
onPressed: (context) async {
|
onPressed: (context) async {
|
||||||
// copy to clipboard
|
// copy to clipboard
|
||||||
|
|
@ -159,18 +170,16 @@ class AppSettingsPage extends HookConsumerWidget {
|
||||||
);
|
);
|
||||||
// show toast
|
// show toast
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
SnackBar(
|
||||||
content: Text('Settings copied to clipboard'),
|
content: Text(S.of(context).copyToClipboardToast),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
SettingsTile(
|
SettingsTile(
|
||||||
title: const Text('Restore'),
|
title: Text(S.of(context).restore),
|
||||||
leading: const Icon(Icons.restore),
|
leading: const Icon(Icons.restore),
|
||||||
description: const Text(
|
description: Text(S.of(context).restoreDescription),
|
||||||
'Restore the app settings from the backup',
|
|
||||||
),
|
|
||||||
onPressed: (context) {
|
onPressed: (context) {
|
||||||
// show a dialog to get the backup
|
// show a dialog to get the backup
|
||||||
showDialog(
|
showDialog(
|
||||||
|
|
@ -184,33 +193,29 @@ class AppSettingsPage extends HookConsumerWidget {
|
||||||
|
|
||||||
// a button to reset the app settings
|
// a button to reset the app settings
|
||||||
SettingsTile(
|
SettingsTile(
|
||||||
title: const Text('Reset App Settings'),
|
title: Text(S.of(context).resetAppSettings),
|
||||||
leading: const Icon(Icons.settings_backup_restore),
|
leading: const Icon(Icons.settings_backup_restore),
|
||||||
description: const Text(
|
description: Text(S.of(context).resetAppSettingsDescription),
|
||||||
'Reset the app settings to the default values',
|
|
||||||
),
|
|
||||||
onPressed: (context) async {
|
onPressed: (context) async {
|
||||||
// confirm the reset
|
// confirm the reset
|
||||||
final res = await showDialog(
|
final res = await showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) {
|
builder: (context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('Reset App Settings'),
|
title: Text(S.of(context).resetAppSettings),
|
||||||
content: const Text(
|
content: Text(S.of(context).resetAppSettingsDialog),
|
||||||
'Are you sure you want to reset the app settings?',
|
|
||||||
),
|
|
||||||
actions: [
|
actions: [
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop(false);
|
Navigator.of(context).pop(false);
|
||||||
},
|
},
|
||||||
child: const Text('Cancel'),
|
child: Text(S.of(context).cancel),
|
||||||
),
|
),
|
||||||
TextButton(
|
TextButton(
|
||||||
onPressed: () {
|
onPressed: () {
|
||||||
Navigator.of(context).pop(true);
|
Navigator.of(context).pop(true);
|
||||||
},
|
},
|
||||||
child: const Text('Reset'),
|
child: Text(S.of(context).reset),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
@ -242,14 +247,14 @@ class RestoreDialogue extends HookConsumerWidget {
|
||||||
|
|
||||||
final settingsInputController = useTextEditingController();
|
final settingsInputController = useTextEditingController();
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
title: const Text('Restore Backup'),
|
title: Text(S.of(context).restoreBackup),
|
||||||
content: Form(
|
content: Form(
|
||||||
key: formKey,
|
key: formKey,
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
autofocus: true,
|
autofocus: true,
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
labelText: 'Backup',
|
labelText: S.of(context).backup,
|
||||||
hintText: 'Paste the backup here',
|
hintText: S.of(context).restoreBackupHint,
|
||||||
// clear button
|
// clear button
|
||||||
suffixIcon: IconButton(
|
suffixIcon: IconButton(
|
||||||
icon: Icon(Icons.clear),
|
icon: Icon(Icons.clear),
|
||||||
|
|
@ -260,7 +265,7 @@ class RestoreDialogue extends HookConsumerWidget {
|
||||||
),
|
),
|
||||||
validator: (value) {
|
validator: (value) {
|
||||||
if (value == null || value.isEmpty) {
|
if (value == null || value.isEmpty) {
|
||||||
return 'Please paste the backup here';
|
return S.of(context).restoreBackupValidator;
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
// try to decode the backup
|
// try to decode the backup
|
||||||
|
|
@ -268,7 +273,7 @@ class RestoreDialogue extends HookConsumerWidget {
|
||||||
jsonDecode(value),
|
jsonDecode(value),
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return 'Invalid backup';
|
return S.of(context).restoreBackupInvalid;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
},
|
},
|
||||||
|
|
@ -281,8 +286,8 @@ class RestoreDialogue extends HookConsumerWidget {
|
||||||
if (formKey.currentState!.validate()) {
|
if (formKey.currentState!.validate()) {
|
||||||
if (settings.value == null) {
|
if (settings.value == null) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
SnackBar(
|
||||||
content: Text('Invalid backup'),
|
content: Text(S.of(context).restoreBackupInvalid),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
return;
|
return;
|
||||||
|
|
@ -291,19 +296,19 @@ class RestoreDialogue extends HookConsumerWidget {
|
||||||
settingsInputController.clear();
|
settingsInputController.clear();
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
SnackBar(
|
||||||
content: Text('Settings restored'),
|
content: Text(S.of(context).restoreBackupSuccess),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
SnackBar(
|
||||||
content: Text('Invalid backup'),
|
content: Text(S.of(context).restoreBackupInvalid),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: const Text('Restore'),
|
child: Text(S.of(context).restore),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:vaani/generated/l10n.dart';
|
||||||
|
|
||||||
class OkButton<T> extends StatelessWidget {
|
class OkButton<T> extends StatelessWidget {
|
||||||
const OkButton({
|
const OkButton({
|
||||||
|
|
@ -12,7 +13,7 @@ class OkButton<T> extends StatelessWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return TextButton(
|
return TextButton(
|
||||||
onPressed: onPressed,
|
onPressed: onPressed,
|
||||||
child: const Text('OK'),
|
child: Text(S.of(context).ok),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -32,7 +33,7 @@ class CancelButton extends StatelessWidget {
|
||||||
onPressed?.call();
|
onPressed?.call();
|
||||||
Navigator.of(context).pop();
|
Navigator.of(context).pop();
|
||||||
},
|
},
|
||||||
child: const Text('Cancel'),
|
child: Text(S.of(context).cancel),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,7 @@ class BookOnShelf extends HookConsumerWidget {
|
||||||
onTap: handleTapOnBook,
|
onTap: handleTapOnBook,
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding:
|
padding: const EdgeInsets.only(bottom: 8.0, right: 4.0, left: 4.0),
|
||||||
const EdgeInsets.only(bottom: 8.0, right: 4.0, left: 4.0),
|
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
|
|
@ -112,9 +111,7 @@ class BookOnShelf extends HookConsumerWidget {
|
||||||
alignment: Alignment.bottomRight,
|
alignment: Alignment.bottomRight,
|
||||||
children: [
|
children: [
|
||||||
Hero(
|
Hero(
|
||||||
tag: HeroTagPrefixes.bookCover +
|
tag: HeroTagPrefixes.bookCover + item.id + heroTagSuffix,
|
||||||
item.id +
|
|
||||||
heroTagSuffix,
|
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.circular(10),
|
borderRadius: BorderRadius.circular(10),
|
||||||
child: AnimatedSwitcher(
|
child: AnimatedSwitcher(
|
||||||
|
|
@ -128,17 +125,13 @@ class BookOnShelf extends HookConsumerWidget {
|
||||||
var imageWidget = Image.memory(
|
var imageWidget = Image.memory(
|
||||||
image,
|
image,
|
||||||
fit: BoxFit.fill,
|
fit: BoxFit.fill,
|
||||||
cacheWidth: (height *
|
cacheWidth:
|
||||||
1.2 *
|
(height * 1.2 * MediaQuery.of(context).devicePixelRatio)
|
||||||
MediaQuery.of(context)
|
.round(),
|
||||||
.devicePixelRatio)
|
|
||||||
.round(),
|
|
||||||
);
|
);
|
||||||
return Container(
|
return Container(
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Theme.of(context)
|
color: Theme.of(context).colorScheme.onPrimaryContainer,
|
||||||
.colorScheme
|
|
||||||
.onPrimaryContainer,
|
|
||||||
),
|
),
|
||||||
child: imageWidget,
|
child: imageWidget,
|
||||||
);
|
);
|
||||||
|
|
@ -213,8 +206,7 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
|
||||||
Widget build(BuildContext context, WidgetRef ref) {
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
final me = ref.watch(meProvider);
|
final me = ref.watch(meProvider);
|
||||||
final player = ref.watch(audiobookPlayerProvider);
|
final player = ref.watch(audiobookPlayerProvider);
|
||||||
final isCurrentBookSetInPlayer =
|
final isCurrentBookSetInPlayer = player.book?.libraryItemId == libraryItemId;
|
||||||
player.book?.libraryItemId == libraryItemId;
|
|
||||||
final isPlayingThisBook = player.playing && isCurrentBookSetInPlayer;
|
final isPlayingThisBook = player.playing && isCurrentBookSetInPlayer;
|
||||||
|
|
||||||
final userProgress = me.valueOrNull?.mediaProgress
|
final userProgress = me.valueOrNull?.mediaProgress
|
||||||
|
|
@ -242,8 +234,7 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
|
||||||
return Theme(
|
return Theme(
|
||||||
// if current book is set in player, get theme from the cover image
|
// if current book is set in player, get theme from the cover image
|
||||||
data: ThemeData(
|
data: ThemeData(
|
||||||
colorScheme:
|
colorScheme: coverColorScheme.valueOrNull ?? Theme.of(context).colorScheme,
|
||||||
coverColorScheme.valueOrNull ?? Theme.of(context).colorScheme,
|
|
||||||
),
|
),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(strokeWidth / 2 + 2),
|
padding: EdgeInsets.all(strokeWidth / 2 + 2),
|
||||||
|
|
@ -258,10 +249,7 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
|
||||||
child: CircularProgressIndicator(
|
child: CircularProgressIndicator(
|
||||||
value: userProgress.progress,
|
value: userProgress.progress,
|
||||||
strokeWidth: strokeWidth,
|
strokeWidth: strokeWidth,
|
||||||
backgroundColor: Theme.of(context)
|
backgroundColor: Theme.of(context).colorScheme.onPrimary.withValues(alpha: 0.8),
|
||||||
.colorScheme
|
|
||||||
.onPrimary
|
|
||||||
.withValues(alpha: 0.8),
|
|
||||||
valueColor: AlwaysStoppedAnimation<Color>(
|
valueColor: AlwaysStoppedAnimation<Color>(
|
||||||
Theme.of(context).colorScheme.primary,
|
Theme.of(context).colorScheme.primary,
|
||||||
),
|
),
|
||||||
|
|
@ -279,15 +267,11 @@ class _BookOnShelfPlayButton extends HookConsumerWidget {
|
||||||
const Size(size, size),
|
const Size(size, size),
|
||||||
),
|
),
|
||||||
backgroundColor: WidgetStateProperty.all(
|
backgroundColor: WidgetStateProperty.all(
|
||||||
Theme.of(context)
|
Theme.of(context).colorScheme.onPrimary.withValues(alpha: 0.9),
|
||||||
.colorScheme
|
|
||||||
.onPrimary
|
|
||||||
.withValues(alpha: 0.9),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
final book =
|
final book = await ref.watch(libraryItemProvider(libraryItemId).future);
|
||||||
await ref.watch(libraryItemProvider(libraryItemId).future);
|
|
||||||
|
|
||||||
libraryItemPlayButtonOnPressed(
|
libraryItemPlayButtonOnPressed(
|
||||||
ref: ref,
|
ref: ref,
|
||||||
|
|
@ -324,10 +308,8 @@ class BookCoverSkeleton extends StatelessWidget {
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
width: 150,
|
width: 150,
|
||||||
child: Shimmer.fromColors(
|
child: Shimmer.fromColors(
|
||||||
baseColor:
|
baseColor: Theme.of(context).colorScheme.surface.withValues(alpha: 0.3),
|
||||||
Theme.of(context).colorScheme.surface.withValues(alpha: 0.3),
|
highlightColor: Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.1),
|
||||||
highlightColor:
|
|
||||||
Theme.of(context).colorScheme.onSurface.withValues(alpha: 0.1),
|
|
||||||
child: Container(
|
child: Container(
|
||||||
color: Theme.of(context).colorScheme.surface,
|
color: Theme.of(context).colorScheme.surface,
|
||||||
),
|
),
|
||||||
|
|
|
||||||
|
|
@ -507,6 +507,11 @@ packages:
|
||||||
url: "https://pub.flutter-io.cn"
|
url: "https://pub.flutter-io.cn"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "5.0.0"
|
version: "5.0.0"
|
||||||
|
flutter_localizations:
|
||||||
|
dependency: "direct main"
|
||||||
|
description: flutter
|
||||||
|
source: sdk
|
||||||
|
version: "0.0.0"
|
||||||
flutter_plugin_android_lifecycle:
|
flutter_plugin_android_lifecycle:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,8 @@ dependencies:
|
||||||
shimmer: ^3.0.0
|
shimmer: ^3.0.0
|
||||||
url_launcher: ^6.2.6
|
url_launcher: ^6.2.6
|
||||||
vibration: ^3.1.3
|
vibration: ^3.1.3
|
||||||
|
flutter_localizations:
|
||||||
|
sdk: flutter
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
build_runner: ^2.4.9
|
build_runner: ^2.4.9
|
||||||
custom_lint: ^0.7.0
|
custom_lint: ^0.7.0
|
||||||
|
|
@ -152,3 +154,5 @@ flutter:
|
||||||
- family: AbsIcons
|
- family: AbsIcons
|
||||||
fonts:
|
fonts:
|
||||||
- asset: assets/fonts/AbsIcons.ttf
|
- asset: assets/fonts/AbsIcons.ttf
|
||||||
|
flutter_intl:
|
||||||
|
enabled: true
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue