poc with dlna_dart / shelf
This commit is contained in:
parent
d19b9ad4c6
commit
59582483e3
13 changed files with 322 additions and 2 deletions
|
@ -112,6 +112,7 @@
|
|||
"entryActionEdit": "Edit",
|
||||
"entryActionOpen": "Open with",
|
||||
"entryActionSetAs": "Set as",
|
||||
"entryActionCast": "Cast",
|
||||
"entryActionOpenMap": "Show in map app",
|
||||
"entryActionRotateScreen": "Rotate screen",
|
||||
"entryActionAddFavourite": "Add to favorites",
|
||||
|
@ -518,6 +519,8 @@
|
|||
"tileLayoutGrid": "Grid",
|
||||
"tileLayoutList": "List",
|
||||
|
||||
"castDialogTitle": "Cast Devices",
|
||||
|
||||
"coverDialogTabCover": "Cover",
|
||||
"coverDialogTabApp": "App",
|
||||
"coverDialogTabColor": "Color",
|
||||
|
|
|
@ -76,6 +76,7 @@ class AIcons {
|
|||
static const addShortcut = Icons.add_to_home_screen_outlined;
|
||||
static const cancel = Icons.cancel_outlined;
|
||||
static const captureFrame = Icons.screenshot_outlined;
|
||||
static const cast = Icons.cast_outlined;
|
||||
static const clear = Icons.clear_outlined;
|
||||
static const clipboard = Icons.content_copy_outlined;
|
||||
static const convert = Icons.transform_outlined;
|
||||
|
|
|
@ -47,6 +47,7 @@ extension ExtraEntryActionView on EntryAction {
|
|||
EntryAction.open || EntryAction.openVideo => l10n.entryActionOpen,
|
||||
EntryAction.openMap => l10n.entryActionOpenMap,
|
||||
EntryAction.setAs => l10n.entryActionSetAs,
|
||||
EntryAction.cast => l10n.entryActionCast,
|
||||
// platform
|
||||
EntryAction.rotateScreen => l10n.entryActionRotateScreen,
|
||||
// metadata
|
||||
|
@ -120,6 +121,7 @@ extension ExtraEntryActionView on EntryAction {
|
|||
EntryAction.open || EntryAction.openVideo => AIcons.openOutside,
|
||||
EntryAction.openMap => AIcons.map,
|
||||
EntryAction.setAs => AIcons.setAs,
|
||||
EntryAction.cast => AIcons.cast,
|
||||
// platform
|
||||
EntryAction.rotateScreen => AIcons.rotateScreen,
|
||||
// metadata
|
||||
|
|
61
lib/widgets/dialogs/cast_dialog.dart
Normal file
61
lib/widgets/dialogs/cast_dialog.dart
Normal file
|
@ -0,0 +1,61 @@
|
|||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/dialogs/aves_dialog.dart';
|
||||
import 'package:dlna_dart/dlna.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CastDialog extends StatefulWidget {
|
||||
static const routeName = '/dialog/cast';
|
||||
|
||||
const CastDialog({super.key});
|
||||
|
||||
@override
|
||||
State<CastDialog> createState() => _CastDialogState();
|
||||
}
|
||||
|
||||
class _CastDialogState extends State<CastDialog> {
|
||||
final DLNAManager _dlnaManager = DLNAManager();
|
||||
final Map<String, DLNADevice> _seenRenderers = {};
|
||||
|
||||
static const String upnpDeviceTypeMediaRenderer = 'urn:schemas-upnp-org:device:MediaRenderer:1';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
_dlnaManager.start().then((deviceManager) {
|
||||
deviceManager.devices.stream.listen((devices) {
|
||||
_seenRenderers.addAll(Map.fromEntries(devices.entries.where((kv) => kv.value.info.deviceType == upnpDeviceTypeMediaRenderer)));
|
||||
setState(() {});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_dlnaManager.stop();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AvesDialog(
|
||||
title: context.l10n.castDialogTitle,
|
||||
scrollableContent: [
|
||||
if (_seenRenderers.isEmpty)
|
||||
const Padding(
|
||||
padding: EdgeInsets.all(16),
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
),
|
||||
..._seenRenderers.values.map((dev) => ListTile(
|
||||
title: Text(dev.info.friendlyName),
|
||||
onTap: () => Navigator.maybeOf(context)?.pop<DLNADevice>(dev),
|
||||
)),
|
||||
],
|
||||
actions: const [
|
||||
CancelButton(),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
|
@ -108,6 +108,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
|||
case EntryAction.copyToClipboard:
|
||||
case EntryAction.open:
|
||||
case EntryAction.setAs:
|
||||
case EntryAction.cast:
|
||||
return !settings.useTvLayout;
|
||||
case EntryAction.info:
|
||||
case EntryAction.share:
|
||||
|
@ -256,6 +257,8 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
|||
appService.setAs(targetEntry.uri, targetEntry.mimeType).then((success) {
|
||||
if (!success) showNoMatchingAppDialog(context);
|
||||
});
|
||||
case EntryAction.cast:
|
||||
const CastNotification(true).dispatch(context);
|
||||
// platform
|
||||
case EntryAction.rotateScreen:
|
||||
_rotateScreen(context);
|
||||
|
|
103
lib/widgets/viewer/controls/cast.dart
Normal file
103
lib/widgets/viewer/controls/cast.dart
Normal file
|
@ -0,0 +1,103 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/model/entry/extensions/props.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves/widgets/dialogs/cast_dialog.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:dlna_dart/dlna.dart';
|
||||
import 'package:dlna_dart/xmlParser.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:network_info_plus/network_info_plus.dart';
|
||||
import 'package:shelf/shelf.dart';
|
||||
import 'package:shelf/shelf_io.dart' as shelf_io;
|
||||
|
||||
mixin CastMixin {
|
||||
DLNADevice? _renderer;
|
||||
HttpServer? _mediaServer;
|
||||
|
||||
bool get isCasting => _renderer != null && _mediaServer != null;
|
||||
|
||||
Future<void> initCast(BuildContext context, List<AvesEntry> entries) async {
|
||||
await stopCast();
|
||||
|
||||
final renderer = await _selectRenderer(context);
|
||||
_renderer = renderer;
|
||||
if (renderer == null) return;
|
||||
debugPrint('cast: select renderer `${renderer.info.friendlyName}` at ${renderer.info.URLBase}');
|
||||
|
||||
final ip = await NetworkInfo().getWifiIP();
|
||||
if (ip == null) return;
|
||||
|
||||
final handler = const Pipeline().addHandler((request) async {
|
||||
final id = int.tryParse(request.url.path);
|
||||
if (id != null) {
|
||||
final entry = entries.firstWhereOrNull((v) => v.id == id);
|
||||
if (entry != null) {
|
||||
final bytes = await mediaFetchService.getImage(
|
||||
entry.uri,
|
||||
entry.mimeType,
|
||||
rotationDegrees: entry.rotationDegrees,
|
||||
isFlipped: entry.isFlipped,
|
||||
pageId: entry.pageId,
|
||||
sizeBytes: entry.sizeBytes,
|
||||
);
|
||||
debugPrint('cast: send ${bytes.length} bytes for entry=$entry');
|
||||
return Response.ok(
|
||||
bytes,
|
||||
headers: {
|
||||
'Content-Type': entry.mimeType,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
return Response.notFound('no resource for url=${request.url}');
|
||||
});
|
||||
_mediaServer = await shelf_io.serve(handler, ip, 8080);
|
||||
debugPrint('cast: serving media on $_serverBaseUrl}');
|
||||
}
|
||||
|
||||
Future<void> stopCast() async {
|
||||
if (isCasting) {
|
||||
debugPrint('cast: stop');
|
||||
}
|
||||
|
||||
await _mediaServer?.close();
|
||||
_mediaServer = null;
|
||||
|
||||
await _renderer?.stop();
|
||||
_renderer = null;
|
||||
}
|
||||
|
||||
Future<DLNADevice?> _selectRenderer(BuildContext context) async {
|
||||
return await showDialog<DLNADevice?>(
|
||||
context: context,
|
||||
builder: (context) => const CastDialog(),
|
||||
routeSettings: const RouteSettings(name: CastDialog.routeName),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> castEntry(AvesEntry entry) async {
|
||||
final server = _mediaServer;
|
||||
final renderer = _renderer;
|
||||
if (server == null || renderer == null) return;
|
||||
|
||||
debugPrint('cast: set entry=$entry');
|
||||
try {
|
||||
await renderer.setUrl(
|
||||
'$_serverBaseUrl/${entry.id}',
|
||||
title: entry.bestTitle ?? '',
|
||||
type: entry.isVideo ? PlayType.Video : PlayType.Image,
|
||||
);
|
||||
await renderer.play();
|
||||
} catch (error, stack) {
|
||||
await reportService.recordError(error, stack);
|
||||
}
|
||||
}
|
||||
|
||||
String? get _serverBaseUrl {
|
||||
final server = _mediaServer;
|
||||
return server != null ? 'http://${server.address.host}:${server.port}' : null;
|
||||
}
|
||||
}
|
|
@ -3,13 +3,14 @@ import 'dart:math';
|
|||
|
||||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
import 'package:aves/widgets/viewer/controls/cast.dart';
|
||||
import 'package:aves/widgets/viewer/controls/events.dart';
|
||||
import 'package:aves_magnifier/aves_magnifier.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
class ViewerController {
|
||||
class ViewerController with CastMixin {
|
||||
final ValueNotifier<AvesEntry?> entryNotifier = ValueNotifier(null);
|
||||
final ViewerTransition transition;
|
||||
final Duration? autopilotInterval;
|
||||
|
|
|
@ -78,6 +78,16 @@ class VideoActionNotification extends Notification {
|
|||
});
|
||||
}
|
||||
|
||||
@immutable
|
||||
class CastNotification extends Notification with EquatableMixin {
|
||||
final bool enabled;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [enabled];
|
||||
|
||||
const CastNotification(this.enabled);
|
||||
}
|
||||
|
||||
@immutable
|
||||
class FilterSelectedNotification extends Notification with EquatableMixin {
|
||||
final CollectionFilter filter;
|
||||
|
|
|
@ -516,6 +516,8 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
|
|||
bool _handleNotification(dynamic notification) {
|
||||
if (notification is FilterSelectedNotification) {
|
||||
_goToCollection(notification.filter);
|
||||
} else if (notification is CastNotification) {
|
||||
_cast(notification.enabled);
|
||||
} else if (notification is FullImageLoadedNotification) {
|
||||
final viewStateController = context.read<ViewStateConductor>().getOrCreateController(notification.entry);
|
||||
// microtask so that listeners do not trigger during build
|
||||
|
@ -581,6 +583,21 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
|
|||
return true;
|
||||
}
|
||||
|
||||
Future<void> _cast(bool enabled) async {
|
||||
if (enabled) {
|
||||
final entries = collection?.sortedEntries;
|
||||
if (entries != null) {
|
||||
await viewerController.initCast(context, entries);
|
||||
final entry = entryNotifier.value;
|
||||
if (entry != null) {
|
||||
await viewerController.castEntry(entry);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
await viewerController.stopCast();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onVideoAction({
|
||||
required BuildContext context,
|
||||
required AvesEntry entry,
|
||||
|
@ -756,6 +773,13 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
|
|||
_isEntryTracked = false;
|
||||
await pauseVideoControllers();
|
||||
await initEntryControllers(newEntry);
|
||||
|
||||
if (viewerController.isCasting) {
|
||||
final entry = entryNotifier.value;
|
||||
if (entry != null) {
|
||||
await viewerController.castEntry(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _onWillPop() {
|
||||
|
@ -817,6 +841,8 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
|
|||
// to be unmounted after the other async steps
|
||||
final theme = Theme.of(context);
|
||||
|
||||
await viewerController.stopCast();
|
||||
|
||||
switch (settings.maxBrightness) {
|
||||
case MaxBrightness.never:
|
||||
case MaxBrightness.viewerOnly:
|
||||
|
|
|
@ -33,6 +33,7 @@ enum EntryAction {
|
|||
openVideo,
|
||||
openMap,
|
||||
setAs,
|
||||
cast,
|
||||
// platform
|
||||
rotateScreen,
|
||||
// metadata
|
||||
|
@ -82,6 +83,7 @@ class EntryActions {
|
|||
EntryAction.open,
|
||||
EntryAction.openMap,
|
||||
EntryAction.setAs,
|
||||
EntryAction.cast,
|
||||
];
|
||||
|
||||
static const pageActions = {
|
||||
|
|
26
pubspec.lock
26
pubspec.lock
|
@ -302,6 +302,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.0"
|
||||
dlna_dart:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: dlna_dart
|
||||
sha256: ae07c1c53077bbf58756fa589f936968719b0085441981d33e74f82f89d1d281
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.8"
|
||||
dynamic_color:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -920,6 +928,22 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
network_info_plus:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: network_info_plus
|
||||
sha256: "2d9e88b9a459e5d4e224f828d26cc38ea140511e89b943116939994324be5c96"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
network_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: network_info_plus_platform_interface
|
||||
sha256: "881f5029c5edaf19c616c201d3d8b366c5b1384afd5c1da5a49e4345de82fb8b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.3"
|
||||
nm:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1314,7 +1338,7 @@ packages:
|
|||
source: hosted
|
||||
version: "2.3.2"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: shelf
|
||||
sha256: ad29c505aee705f41a4d8963641f91ac4cee3c8fad5947e033390a7bd8180fa4
|
||||
|
|
|
@ -117,6 +117,10 @@ dependencies:
|
|||
volume_controller:
|
||||
xml:
|
||||
|
||||
dlna_dart:
|
||||
network_info_plus:
|
||||
shelf:
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
|
|
|
@ -51,6 +51,7 @@
|
|||
"entryActionEdit",
|
||||
"entryActionOpen",
|
||||
"entryActionSetAs",
|
||||
"entryActionCast",
|
||||
"entryActionOpenMap",
|
||||
"entryActionRotateScreen",
|
||||
"entryActionAddFavourite",
|
||||
|
@ -286,6 +287,7 @@
|
|||
"tileLayoutMosaic",
|
||||
"tileLayoutGrid",
|
||||
"tileLayoutList",
|
||||
"castDialogTitle",
|
||||
"coverDialogTabCover",
|
||||
"coverDialogTabApp",
|
||||
"coverDialogTabColor",
|
||||
|
@ -644,6 +646,7 @@
|
|||
],
|
||||
|
||||
"be": [
|
||||
"entryActionCast",
|
||||
"binEntriesConfirmationDialogMessage",
|
||||
"deleteEntriesConfirmationDialogMessage",
|
||||
"setCoverDialogCustom",
|
||||
|
@ -712,6 +715,7 @@
|
|||
"tileLayoutMosaic",
|
||||
"tileLayoutGrid",
|
||||
"tileLayoutList",
|
||||
"castDialogTitle",
|
||||
"coverDialogTabCover",
|
||||
"coverDialogTabApp",
|
||||
"coverDialogTabColor",
|
||||
|
@ -1077,6 +1081,7 @@
|
|||
"entryActionEdit",
|
||||
"entryActionOpen",
|
||||
"entryActionSetAs",
|
||||
"entryActionCast",
|
||||
"entryActionOpenMap",
|
||||
"entryActionRotateScreen",
|
||||
"entryActionAddFavourite",
|
||||
|
@ -1312,6 +1317,7 @@
|
|||
"tileLayoutMosaic",
|
||||
"tileLayoutGrid",
|
||||
"tileLayoutList",
|
||||
"castDialogTitle",
|
||||
"coverDialogTabCover",
|
||||
"coverDialogTabApp",
|
||||
"coverDialogTabColor",
|
||||
|
@ -1689,6 +1695,7 @@
|
|||
"entryActionFlip",
|
||||
"entryActionShowGeoTiffOnMap",
|
||||
"entryActionConvertMotionPhotoToStillImage",
|
||||
"entryActionCast",
|
||||
"videoActionCaptureFrame",
|
||||
"videoActionSelectStreams",
|
||||
"viewerActionLock",
|
||||
|
@ -1846,6 +1853,7 @@
|
|||
"tileLayoutMosaic",
|
||||
"tileLayoutGrid",
|
||||
"tileLayoutList",
|
||||
"castDialogTitle",
|
||||
"coverDialogTabCover",
|
||||
"coverDialogTabApp",
|
||||
"coverDialogTabColor",
|
||||
|
@ -2218,22 +2226,28 @@
|
|||
],
|
||||
|
||||
"cs": [
|
||||
"entryActionCast",
|
||||
"overlayHistogramLuminance",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"de": [
|
||||
"entryActionCast",
|
||||
"overlayHistogramNone",
|
||||
"overlayHistogramRGB",
|
||||
"overlayHistogramLuminance",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageClearCache",
|
||||
"settingsViewerShowHistogram"
|
||||
],
|
||||
|
||||
"el": [
|
||||
"entryActionCast",
|
||||
"overlayHistogramNone",
|
||||
"overlayHistogramRGB",
|
||||
"overlayHistogramLuminance",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageSectionTitle",
|
||||
"aboutDataUsageData",
|
||||
"aboutDataUsageCache",
|
||||
|
@ -2246,10 +2260,14 @@
|
|||
],
|
||||
|
||||
"es": [
|
||||
"entryActionCast",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"eu": [
|
||||
"entryActionCast",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
|
@ -2262,6 +2280,7 @@
|
|||
"chipActionShowCountryStates",
|
||||
"chipActionCreateVault",
|
||||
"chipActionConfigureVault",
|
||||
"entryActionCast",
|
||||
"videoActionPause",
|
||||
"videoActionPlay",
|
||||
"videoActionSelectStreams",
|
||||
|
@ -2423,6 +2442,7 @@
|
|||
"tileLayoutMosaic",
|
||||
"tileLayoutGrid",
|
||||
"tileLayoutList",
|
||||
"castDialogTitle",
|
||||
"coverDialogTabCover",
|
||||
"appPickDialogNone",
|
||||
"aboutLinkLicense",
|
||||
|
@ -2774,6 +2794,7 @@
|
|||
"clearTooltip",
|
||||
"chipActionFilterIn",
|
||||
"entryActionSetAs",
|
||||
"entryActionCast",
|
||||
"videoActionUnmute",
|
||||
"videoActionSelectStreams",
|
||||
"filterTypeRawLabel",
|
||||
|
@ -2937,6 +2958,7 @@
|
|||
"tileLayoutMosaic",
|
||||
"tileLayoutGrid",
|
||||
"tileLayoutList",
|
||||
"castDialogTitle",
|
||||
"coverDialogTabCover",
|
||||
"coverDialogTabApp",
|
||||
"coverDialogTabColor",
|
||||
|
@ -3309,6 +3331,8 @@
|
|||
],
|
||||
|
||||
"fr": [
|
||||
"entryActionCast",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
|
@ -3323,6 +3347,7 @@
|
|||
"chipActionConfigureVault",
|
||||
"entryActionShareImageOnly",
|
||||
"entryActionShareVideoOnly",
|
||||
"entryActionCast",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
"entryInfoActionExportMetadata",
|
||||
|
@ -3489,6 +3514,7 @@
|
|||
"tileLayoutMosaic",
|
||||
"tileLayoutGrid",
|
||||
"tileLayoutList",
|
||||
"castDialogTitle",
|
||||
"coverDialogTabCover",
|
||||
"coverDialogTabApp",
|
||||
"coverDialogTabColor",
|
||||
|
@ -3930,6 +3956,7 @@
|
|||
"entryActionEdit",
|
||||
"entryActionOpen",
|
||||
"entryActionSetAs",
|
||||
"entryActionCast",
|
||||
"entryActionOpenMap",
|
||||
"entryActionRotateScreen",
|
||||
"entryActionAddFavourite",
|
||||
|
@ -4165,6 +4192,7 @@
|
|||
"tileLayoutMosaic",
|
||||
"tileLayoutGrid",
|
||||
"tileLayoutList",
|
||||
"castDialogTitle",
|
||||
"coverDialogTabCover",
|
||||
"coverDialogTabApp",
|
||||
"coverDialogTabColor",
|
||||
|
@ -4586,6 +4614,7 @@
|
|||
"entryActionEdit",
|
||||
"entryActionOpen",
|
||||
"entryActionSetAs",
|
||||
"entryActionCast",
|
||||
"entryActionOpenMap",
|
||||
"entryActionRotateScreen",
|
||||
"entryActionAddFavourite",
|
||||
|
@ -4821,6 +4850,7 @@
|
|||
"tileLayoutMosaic",
|
||||
"tileLayoutGrid",
|
||||
"tileLayoutList",
|
||||
"castDialogTitle",
|
||||
"coverDialogTabCover",
|
||||
"coverDialogTabApp",
|
||||
"coverDialogTabColor",
|
||||
|
@ -5193,14 +5223,20 @@
|
|||
],
|
||||
|
||||
"hu": [
|
||||
"entryActionCast",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"id": [
|
||||
"entryActionCast",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"it": [
|
||||
"entryActionCast",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
|
@ -5211,6 +5247,7 @@
|
|||
"chipActionShowCountryStates",
|
||||
"chipActionCreateVault",
|
||||
"chipActionConfigureVault",
|
||||
"entryActionCast",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
"editorActionTransform",
|
||||
|
@ -5234,6 +5271,7 @@
|
|||
"vaultBinUsageDialogMessage",
|
||||
"exportEntryDialogQuality",
|
||||
"exportEntryDialogWriteMetadata",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageSectionTitle",
|
||||
"aboutDataUsageData",
|
||||
"aboutDataUsageCache",
|
||||
|
@ -5310,6 +5348,7 @@
|
|||
"entryActionEdit",
|
||||
"entryActionOpen",
|
||||
"entryActionSetAs",
|
||||
"entryActionCast",
|
||||
"entryActionOpenMap",
|
||||
"entryActionRotateScreen",
|
||||
"entryActionAddFavourite",
|
||||
|
@ -5545,6 +5584,7 @@
|
|||
"tileLayoutMosaic",
|
||||
"tileLayoutGrid",
|
||||
"tileLayoutList",
|
||||
"castDialogTitle",
|
||||
"coverDialogTabCover",
|
||||
"coverDialogTabApp",
|
||||
"coverDialogTabColor",
|
||||
|
@ -5917,6 +5957,8 @@
|
|||
],
|
||||
|
||||
"ko": [
|
||||
"entryActionCast",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
|
@ -5929,6 +5971,7 @@
|
|||
"chipActionShowCountryStates",
|
||||
"chipActionCreateVault",
|
||||
"chipActionConfigureVault",
|
||||
"entryActionCast",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
"editorActionTransform",
|
||||
|
@ -5972,6 +6015,7 @@
|
|||
"exportEntryDialogQuality",
|
||||
"exportEntryDialogWriteMetadata",
|
||||
"tooManyItemsErrorDialogMessage",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageSectionTitle",
|
||||
"aboutDataUsageData",
|
||||
"aboutDataUsageCache",
|
||||
|
@ -6082,6 +6126,7 @@
|
|||
"entryActionEdit",
|
||||
"entryActionOpen",
|
||||
"entryActionSetAs",
|
||||
"entryActionCast",
|
||||
"entryActionOpenMap",
|
||||
"entryActionRotateScreen",
|
||||
"entryActionAddFavourite",
|
||||
|
@ -6317,6 +6362,7 @@
|
|||
"tileLayoutMosaic",
|
||||
"tileLayoutGrid",
|
||||
"tileLayoutList",
|
||||
"castDialogTitle",
|
||||
"coverDialogTabCover",
|
||||
"coverDialogTabApp",
|
||||
"coverDialogTabColor",
|
||||
|
@ -6689,6 +6735,7 @@
|
|||
],
|
||||
|
||||
"my": [
|
||||
"entryActionCast",
|
||||
"accessibilityAnimationsRemove",
|
||||
"accessibilityAnimationsKeep",
|
||||
"overlayHistogramLuminance",
|
||||
|
@ -6696,6 +6743,7 @@
|
|||
"widgetOpenPageCollection",
|
||||
"widgetOpenPageViewer",
|
||||
"menuActionConfigureView",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageClearCache",
|
||||
"newFilterBanner",
|
||||
"settingsDefault",
|
||||
|
@ -6799,6 +6847,7 @@
|
|||
],
|
||||
|
||||
"nb": [
|
||||
"entryActionCast",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
"editorActionTransform",
|
||||
|
@ -6810,6 +6859,7 @@
|
|||
"settingsVideoEnablePip",
|
||||
"widgetTapUpdateWidget",
|
||||
"patternDialogEnter",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
|
@ -6826,6 +6876,7 @@
|
|||
"chipActionShowCountryStates",
|
||||
"entryActionShareImageOnly",
|
||||
"entryActionShareVideoOnly",
|
||||
"entryActionCast",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
"editorActionTransform",
|
||||
|
@ -6864,6 +6915,7 @@
|
|||
"exportEntryDialogQuality",
|
||||
"exportEntryDialogWriteMetadata",
|
||||
"tooManyItemsErrorDialogMessage",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageSectionTitle",
|
||||
"aboutDataUsageData",
|
||||
"aboutDataUsageCache",
|
||||
|
@ -6905,6 +6957,7 @@
|
|||
|
||||
"nn": [
|
||||
"sourceStateCataloguing",
|
||||
"entryActionCast",
|
||||
"accessibilityAnimationsKeep",
|
||||
"overlayHistogramNone",
|
||||
"overlayHistogramRGB",
|
||||
|
@ -6915,6 +6968,7 @@
|
|||
"authenticateToUnlockVault",
|
||||
"viewDialogSortSectionTitle",
|
||||
"viewDialogReverseSortOrder",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageSectionTitle",
|
||||
"aboutDataUsageData",
|
||||
"aboutDataUsageCache",
|
||||
|
@ -7006,6 +7060,7 @@
|
|||
"entryActionViewMotionPhotoVideo",
|
||||
"entryActionOpen",
|
||||
"entryActionSetAs",
|
||||
"entryActionCast",
|
||||
"entryActionOpenMap",
|
||||
"entryActionRotateScreen",
|
||||
"entryActionAddFavourite",
|
||||
|
@ -7213,6 +7268,7 @@
|
|||
"tileLayoutMosaic",
|
||||
"tileLayoutGrid",
|
||||
"tileLayoutList",
|
||||
"castDialogTitle",
|
||||
"coverDialogTabCover",
|
||||
"appPickDialogTitle",
|
||||
"appPickDialogNone",
|
||||
|
@ -7552,16 +7608,21 @@
|
|||
],
|
||||
|
||||
"pl": [
|
||||
"entryActionCast",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"pt": [
|
||||
"entryActionCast",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"ro": [
|
||||
"saveCopyButtonLabel",
|
||||
"applyTooltip",
|
||||
"entryActionCast",
|
||||
"editorActionTransform",
|
||||
"editorTransformCrop",
|
||||
"editorTransformRotate",
|
||||
|
@ -7577,6 +7638,7 @@
|
|||
"videoResumptionModeAlways",
|
||||
"widgetTapUpdateWidget",
|
||||
"exportEntryDialogQuality",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageSectionTitle",
|
||||
"aboutDataUsageData",
|
||||
"aboutDataUsageCache",
|
||||
|
@ -7595,10 +7657,14 @@
|
|||
],
|
||||
|
||||
"ru": [
|
||||
"entryActionCast",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"sk": [
|
||||
"entryActionCast",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
|
@ -7672,6 +7738,7 @@
|
|||
"entryActionEdit",
|
||||
"entryActionOpen",
|
||||
"entryActionSetAs",
|
||||
"entryActionCast",
|
||||
"entryActionOpenMap",
|
||||
"entryActionRotateScreen",
|
||||
"entryActionAddFavourite",
|
||||
|
@ -7907,6 +7974,7 @@
|
|||
"tileLayoutMosaic",
|
||||
"tileLayoutGrid",
|
||||
"tileLayoutList",
|
||||
"castDialogTitle",
|
||||
"coverDialogTabCover",
|
||||
"coverDialogTabApp",
|
||||
"coverDialogTabColor",
|
||||
|
@ -8293,6 +8361,7 @@
|
|||
"chipActionShowCountryStates",
|
||||
"chipActionCreateVault",
|
||||
"chipActionConfigureVault",
|
||||
"entryActionCast",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
"editorActionTransform",
|
||||
|
@ -8336,6 +8405,7 @@
|
|||
"editEntryDateDialogShift",
|
||||
"removeEntryMetadataDialogTitle",
|
||||
"tooManyItemsErrorDialogMessage",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageSectionTitle",
|
||||
"aboutDataUsageData",
|
||||
"aboutDataUsageCache",
|
||||
|
@ -8686,6 +8756,7 @@
|
|||
"chipActionShowCountryStates",
|
||||
"chipActionCreateVault",
|
||||
"chipActionConfigureVault",
|
||||
"entryActionCast",
|
||||
"viewerActionLock",
|
||||
"viewerActionUnlock",
|
||||
"editorActionTransform",
|
||||
|
@ -8725,6 +8796,7 @@
|
|||
"vaultBinUsageDialogMessage",
|
||||
"exportEntryDialogQuality",
|
||||
"exportEntryDialogWriteMetadata",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageSectionTitle",
|
||||
"aboutDataUsageData",
|
||||
"aboutDataUsageCache",
|
||||
|
@ -8757,16 +8829,21 @@
|
|||
],
|
||||
|
||||
"uk": [
|
||||
"entryActionCast",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"vi": [
|
||||
"entryActionCast",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"zh": [
|
||||
"saveCopyButtonLabel",
|
||||
"chipActionGoToPlacePage",
|
||||
"entryActionCast",
|
||||
"editorTransformCrop",
|
||||
"cropAspectRatioFree",
|
||||
"cropAspectRatioOriginal",
|
||||
|
@ -8798,6 +8875,7 @@
|
|||
"exportEntryDialogQuality",
|
||||
"exportEntryDialogWriteMetadata",
|
||||
"tooManyItemsErrorDialogMessage",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageSectionTitle",
|
||||
"aboutDataUsageData",
|
||||
"aboutDataUsageCache",
|
||||
|
@ -8833,8 +8911,10 @@
|
|||
],
|
||||
|
||||
"zh_Hant": [
|
||||
"entryActionCast",
|
||||
"overlayHistogramNone",
|
||||
"overlayHistogramLuminance",
|
||||
"castDialogTitle",
|
||||
"aboutDataUsageSectionTitle",
|
||||
"aboutDataUsageData",
|
||||
"aboutDataUsageCache",
|
||||
|
|
Loading…
Reference in a new issue