improved temp file cleanup
This commit is contained in:
parent
e883be1787
commit
0a482e6ccf
12 changed files with 153 additions and 26 deletions
|
@ -9,6 +9,10 @@ All notable changes to this project will be documented in this file.
|
|||
- target Android 14 (API 34)
|
||||
- upgraded Flutter to stable v3.13.8
|
||||
|
||||
### Fixed
|
||||
|
||||
- temporary files remaining in the cache directory forever
|
||||
|
||||
## <a id="v1.9.7"></a>[v1.9.7] - 2023-10-17
|
||||
|
||||
### Added
|
||||
|
|
|
@ -33,7 +33,6 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
|
||||
class EmbeddedDataHandler(private val context: Context) : MethodCallHandler {
|
||||
|
@ -279,8 +278,7 @@ class EmbeddedDataHandler(private val context: Context) : MethodCallHandler {
|
|||
embeddedByteLength: Long,
|
||||
) {
|
||||
val extension = extensionFor(mimeType)
|
||||
val targetFile = File.createTempFile("aves", extension, context.cacheDir).apply {
|
||||
deleteOnExit()
|
||||
val targetFile = StorageUtils.createTempFile(context, extension).apply {
|
||||
transferFrom(embeddedByteStream, embeddedByteLength)
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@ class StorageHandler(private val context: Context) : MethodCallHandler {
|
|||
"getRestrictedDirectories" -> ioScope.launch { safe(call, result, ::getRestrictedDirectories) }
|
||||
"revokeDirectoryAccess" -> safe(call, result, ::revokeDirectoryAccess)
|
||||
"deleteEmptyDirectories" -> ioScope.launch { safe(call, result, ::deleteEmptyDirectories) }
|
||||
"deleteTempDirectory" -> ioScope.launch { safe(call, result, ::deleteTempDirectory) }
|
||||
"canRequestMediaFileBulkAccess" -> safe(call, result, ::canRequestMediaFileBulkAccess)
|
||||
"canInsertMedia" -> safe(call, result, ::canInsertMedia)
|
||||
else -> result.notImplemented()
|
||||
|
@ -200,6 +201,10 @@ class StorageHandler(private val context: Context) : MethodCallHandler {
|
|||
result.success(deleted)
|
||||
}
|
||||
|
||||
private fun deleteTempDirectory(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||
result.success(StorageUtils.deleteTempDirectory(context))
|
||||
}
|
||||
|
||||
private fun canRequestMediaFileBulkAccess(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||
result.success(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R)
|
||||
}
|
||||
|
|
|
@ -16,7 +16,6 @@ import deckers.thibault.aves.utils.BitmapUtils.getBytes
|
|||
import deckers.thibault.aves.utils.MimeTypes
|
||||
import deckers.thibault.aves.utils.StorageUtils
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import java.io.File
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
class RegionFetcher internal constructor(
|
||||
|
@ -113,8 +112,7 @@ class RegionFetcher internal constructor(
|
|||
.submit()
|
||||
try {
|
||||
val bitmap = target.get()
|
||||
val tempFile = File.createTempFile("aves", null, context.cacheDir).apply {
|
||||
deleteOnExit()
|
||||
val tempFile = StorageUtils.createTempFile(context).apply {
|
||||
outputStream().use { output ->
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, output)
|
||||
}
|
||||
|
|
|
@ -160,8 +160,7 @@ object Metadata {
|
|||
}
|
||||
|
||||
fun createPreviewFile(context: Context, uri: Uri): File {
|
||||
return File.createTempFile("aves", null, context.cacheDir).apply {
|
||||
deleteOnExit()
|
||||
return StorageUtils.createTempFile(context).apply {
|
||||
transferFrom(StorageUtils.openInputStream(context, uri), PREVIEW_SIZE)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -381,8 +381,7 @@ abstract class ImageProvider {
|
|||
targetUri: Uri,
|
||||
targetPath: String,
|
||||
) {
|
||||
val editableFile = File.createTempFile("aves", null).apply {
|
||||
deleteOnExit()
|
||||
val editableFile = StorageUtils.createTempFile(context).apply {
|
||||
// copy original file to a temporary file for editing
|
||||
val inputStream = StorageUtils.openInputStream(context, targetUri)
|
||||
transferFrom(inputStream, File(targetPath).length())
|
||||
|
@ -514,8 +513,7 @@ abstract class ImageProvider {
|
|||
output.write(bytes)
|
||||
}
|
||||
} else {
|
||||
val editableFile = withContext(Dispatchers.IO) { File.createTempFile("aves", null) }.apply {
|
||||
deleteOnExit()
|
||||
val editableFile = withContext(Dispatchers.IO) { StorageUtils.createTempFile(contextWrapper) }.apply {
|
||||
transferFrom(ByteArrayInputStream(bytes), bytes.size.toLong())
|
||||
}
|
||||
|
||||
|
@ -649,8 +647,7 @@ abstract class ImageProvider {
|
|||
val originalFileSize = File(path).length()
|
||||
val videoSize = MultiPage.getMotionPhotoOffset(context, uri, mimeType, originalFileSize)?.let { it.toInt() + trailerDiff }
|
||||
var videoBytes: ByteArray? = null
|
||||
val editableFile = File.createTempFile("aves", null).apply {
|
||||
deleteOnExit()
|
||||
val editableFile = StorageUtils.createTempFile(context).apply {
|
||||
try {
|
||||
if (videoSize != null) {
|
||||
// handle motion photo and embedded video separately
|
||||
|
@ -733,8 +730,7 @@ abstract class ImageProvider {
|
|||
val originalFileSize = File(path).length()
|
||||
val videoSize = MultiPage.getMotionPhotoOffset(context, uri, mimeType, originalFileSize)?.let { it.toInt() + trailerDiff }
|
||||
var videoBytes: ByteArray? = null
|
||||
val editableFile = File.createTempFile("aves", null).apply {
|
||||
deleteOnExit()
|
||||
val editableFile = StorageUtils.createTempFile(context).apply {
|
||||
try {
|
||||
if (videoSize != null) {
|
||||
// handle motion photo and embedded video separately
|
||||
|
@ -898,8 +894,7 @@ abstract class ImageProvider {
|
|||
|
||||
val originalFileSize = File(path).length()
|
||||
val videoSize = MultiPage.getMotionPhotoOffset(context, uri, mimeType, originalFileSize)?.let { it.toInt() + trailerDiff }
|
||||
val editableFile = File.createTempFile("aves", null).apply {
|
||||
deleteOnExit()
|
||||
val editableFile = StorageUtils.createTempFile(context).apply {
|
||||
try {
|
||||
editXmpWithPixy(
|
||||
context = context,
|
||||
|
@ -1275,8 +1270,7 @@ abstract class ImageProvider {
|
|||
return
|
||||
}
|
||||
|
||||
val editableFile = File.createTempFile("aves", null).apply {
|
||||
deleteOnExit()
|
||||
val editableFile = StorageUtils.createTempFile(context).apply {
|
||||
try {
|
||||
val inputStream = StorageUtils.openInputStream(context, uri)
|
||||
// partial copy
|
||||
|
@ -1316,8 +1310,7 @@ abstract class ImageProvider {
|
|||
|
||||
val originalFileSize = File(path).length()
|
||||
val videoSize = MultiPage.getMotionPhotoOffset(context, uri, mimeType, originalFileSize)?.toInt()
|
||||
val editableFile = File.createTempFile("aves", null).apply {
|
||||
deleteOnExit()
|
||||
val editableFile = StorageUtils.createTempFile(context).apply {
|
||||
try {
|
||||
outputStream().use { output ->
|
||||
// reopen input to read from start
|
||||
|
|
|
@ -26,6 +26,7 @@ import deckers.thibault.aves.utils.PermissionManager.getGrantedDirForPath
|
|||
import deckers.thibault.aves.utils.UriUtils.tryParseId
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.util.*
|
||||
|
@ -593,8 +594,7 @@ object StorageUtils {
|
|||
uriPath?.contains("/file/") == true -> {
|
||||
// e.g. `content://media/external/file/...`
|
||||
// create an ad-hoc temporary file for decoding only
|
||||
File.createTempFile("aves", null).apply {
|
||||
deleteOnExit()
|
||||
createTempFile(context).apply {
|
||||
try {
|
||||
transferFrom(openInputStream(context, uri), sizeBytes)
|
||||
return Uri.fromFile(this)
|
||||
|
@ -714,6 +714,25 @@ object StorageUtils {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getTempDirectory(context: Context): File = File(context.cacheDir, "temp")
|
||||
|
||||
fun createTempFile(context: Context, extension: String? = null): File {
|
||||
val directory = getTempDirectory(context)
|
||||
if (!directory.exists() && !directory.mkdirs()) {
|
||||
throw IOException("failed to create directories at path=$directory")
|
||||
}
|
||||
val tempFile = File.createTempFile("aves", extension, directory)
|
||||
// `deleteOnExit` is unreliable, but it does not hurt
|
||||
tempFile.deleteOnExit()
|
||||
return tempFile
|
||||
}
|
||||
|
||||
fun deleteTempDirectory(context: Context): Boolean {
|
||||
val directory = getTempDirectory(context)
|
||||
if (!directory.exists()) return false
|
||||
return directory.deleteRecursively()
|
||||
}
|
||||
|
||||
// convenience methods
|
||||
|
||||
fun getFolderSize(f: File): Long {
|
||||
|
|
|
@ -543,6 +543,7 @@
|
|||
"aboutDataUsageMisc": "Misc",
|
||||
"aboutDataUsageInternal": "Internal",
|
||||
"aboutDataUsageExternal": "External",
|
||||
"aboutDataUsageClearCache": "Clear Cache",
|
||||
|
||||
"aboutCreditsSectionTitle": "Credits",
|
||||
"aboutCreditsWorldAtlas1": "This app uses a TopoJSON file from",
|
||||
|
|
|
@ -27,6 +27,8 @@ abstract class StorageService {
|
|||
// returns number of deleted directories
|
||||
Future<int> deleteEmptyRegularDirectories(Set<String> dirPaths);
|
||||
|
||||
Future<bool> deleteTempDirectory();
|
||||
|
||||
// returns whether user granted access to a directory of his choosing
|
||||
Future<bool> requestDirectoryAccess(String path);
|
||||
|
||||
|
@ -158,6 +160,17 @@ class PlatformStorageService implements StorageService {
|
|||
return 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> deleteTempDirectory() async {
|
||||
try {
|
||||
final result = await _platform.invokeMethod('deleteTempDirectory');
|
||||
if (result != null) return result as bool;
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> canRequestMediaFileBulkAccess() async {
|
||||
try {
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:aves/utils/file_utils.dart';
|
|||
import 'package:aves/widgets/common/action_mixins/feedback.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/common/identity/aves_donut.dart';
|
||||
import 'package:aves/widgets/common/identity/buttons/outlined_button.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
|
@ -24,7 +25,7 @@ class _AboutDataUsageState extends State<AboutDataUsage> with FeedbackMixin {
|
|||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loader = storageService.getDataUsage();
|
||||
_reload();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -81,6 +82,18 @@ class _AboutDataUsageState extends State<AboutDataUsage> with FeedbackMixin {
|
|||
byTypes: cacheMap,
|
||||
animationDuration: animationDuration,
|
||||
),
|
||||
Center(
|
||||
child: AvesOutlinedButton(
|
||||
label: context.l10n.aboutDataUsageClearCache,
|
||||
onPressed: () async {
|
||||
await storageService.deleteTempDirectory();
|
||||
await mediaFetchService.clearSizedThumbnailDiskCache();
|
||||
imageCache.clear();
|
||||
_reload();
|
||||
setState(() {});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
|
@ -92,6 +105,10 @@ class _AboutDataUsageState extends State<AboutDataUsage> with FeedbackMixin {
|
|||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _reload() {
|
||||
_loader = storageService.getDataUsage();
|
||||
}
|
||||
}
|
||||
|
||||
class DataUsageDonut extends StatelessWidget {
|
||||
|
|
|
@ -458,6 +458,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
_monitorSettings();
|
||||
videoControllerFactory.init();
|
||||
|
||||
unawaited(storageService.deleteTempDirectory());
|
||||
unawaited(_setupErrorReporting());
|
||||
|
||||
debugPrint('App setup in ${stopwatch.elapsed.inMilliseconds}ms');
|
||||
|
|
|
@ -307,6 +307,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"aboutCreditsSectionTitle",
|
||||
"aboutCreditsWorldAtlas1",
|
||||
"aboutCreditsWorldAtlas2",
|
||||
|
@ -732,6 +733,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"aboutCreditsSectionTitle",
|
||||
"aboutCreditsWorldAtlas1",
|
||||
"aboutCreditsWorldAtlas2",
|
||||
|
@ -1331,6 +1333,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"aboutCreditsSectionTitle",
|
||||
"aboutCreditsWorldAtlas1",
|
||||
"aboutCreditsWorldAtlas2",
|
||||
|
@ -1864,6 +1867,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"aboutCreditsSectionTitle",
|
||||
"aboutCreditsWorldAtlas1",
|
||||
"aboutCreditsWorldAtlas2",
|
||||
|
@ -2214,13 +2218,15 @@
|
|||
],
|
||||
|
||||
"cs": [
|
||||
"overlayHistogramLuminance"
|
||||
"overlayHistogramLuminance",
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"de": [
|
||||
"overlayHistogramNone",
|
||||
"overlayHistogramRGB",
|
||||
"overlayHistogramLuminance",
|
||||
"aboutDataUsageClearCache",
|
||||
"settingsViewerShowHistogram"
|
||||
],
|
||||
|
||||
|
@ -2235,9 +2241,18 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"settingsViewerShowHistogram"
|
||||
],
|
||||
|
||||
"es": [
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"eu": [
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"fa": [
|
||||
"saveCopyButtonLabel",
|
||||
"applyTooltip",
|
||||
|
@ -2421,6 +2436,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"aboutCreditsSectionTitle",
|
||||
"aboutCreditsWorldAtlas1",
|
||||
"aboutCreditsWorldAtlas2",
|
||||
|
@ -2942,6 +2958,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"aboutCreditsSectionTitle",
|
||||
"aboutCreditsWorldAtlas1",
|
||||
"aboutCreditsWorldAtlas2",
|
||||
|
@ -3291,6 +3308,10 @@
|
|||
"filePickerUseThisFolder"
|
||||
],
|
||||
|
||||
"fr": [
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"gl": [
|
||||
"columnCount",
|
||||
"saveCopyButtonLabel",
|
||||
|
@ -3489,6 +3510,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"aboutCreditsSectionTitle",
|
||||
"aboutCreditsWorldAtlas1",
|
||||
"aboutCreditsWorldAtlas2",
|
||||
|
@ -4164,6 +4186,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"aboutCreditsSectionTitle",
|
||||
"aboutCreditsWorldAtlas1",
|
||||
"aboutCreditsWorldAtlas2",
|
||||
|
@ -4819,6 +4842,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"aboutCreditsSectionTitle",
|
||||
"aboutCreditsWorldAtlas1",
|
||||
"aboutCreditsWorldAtlas2",
|
||||
|
@ -5168,6 +5192,18 @@
|
|||
"filePickerUseThisFolder"
|
||||
],
|
||||
|
||||
"hu": [
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"id": [
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"it": [
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"ja": [
|
||||
"columnCount",
|
||||
"saveCopyButtonLabel",
|
||||
|
@ -5205,6 +5241,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"stateEmpty",
|
||||
"placeEmpty",
|
||||
"searchStatesSectionTitle",
|
||||
|
@ -5529,6 +5566,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"aboutCreditsSectionTitle",
|
||||
"aboutCreditsWorldAtlas1",
|
||||
"aboutCreditsWorldAtlas2",
|
||||
|
@ -5878,6 +5916,10 @@
|
|||
"filePickerUseThisFolder"
|
||||
],
|
||||
|
||||
"ko": [
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"lt": [
|
||||
"columnCount",
|
||||
"saveCopyButtonLabel",
|
||||
|
@ -5937,6 +5979,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"drawerPlacePage",
|
||||
"statePageTitle",
|
||||
"stateEmpty",
|
||||
|
@ -6295,6 +6338,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"aboutCreditsSectionTitle",
|
||||
"aboutCreditsWorldAtlas1",
|
||||
"aboutCreditsWorldAtlas2",
|
||||
|
@ -6652,6 +6696,7 @@
|
|||
"widgetOpenPageCollection",
|
||||
"widgetOpenPageViewer",
|
||||
"menuActionConfigureView",
|
||||
"aboutDataUsageClearCache",
|
||||
"newFilterBanner",
|
||||
"settingsDefault",
|
||||
"settingsNavigationDrawerTile",
|
||||
|
@ -6767,6 +6812,7 @@
|
|||
"patternDialogEnter",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"settingsCollectionBurstPatternsTile",
|
||||
"settingsCollectionBurstPatternsNone",
|
||||
"settingsViewerShowHistogram",
|
||||
|
@ -6825,6 +6871,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"drawerPlacePage",
|
||||
"statePageTitle",
|
||||
"stateEmpty",
|
||||
|
@ -6875,6 +6922,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"aboutCreditsSectionTitle",
|
||||
"aboutLicensesBanner",
|
||||
"aboutLicensesAndroidLibrariesSectionTitle",
|
||||
|
@ -7182,6 +7230,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"aboutCreditsWorldAtlas1",
|
||||
"aboutCreditsWorldAtlas2",
|
||||
"aboutLicensesSectionTitle",
|
||||
|
@ -7502,6 +7551,14 @@
|
|||
"filePickerUseThisFolder"
|
||||
],
|
||||
|
||||
"pl": [
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"pt": [
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"ro": [
|
||||
"saveCopyButtonLabel",
|
||||
"applyTooltip",
|
||||
|
@ -7527,6 +7584,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"settingsAskEverytime",
|
||||
"settingsViewerShowHistogram",
|
||||
"settingsVideoPlaybackTile",
|
||||
|
@ -7536,6 +7594,14 @@
|
|||
"tagEditorDiscardDialogMessage"
|
||||
],
|
||||
|
||||
"ru": [
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"sk": [
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"sl": [
|
||||
"itemCount",
|
||||
"columnCount",
|
||||
|
@ -7862,6 +7928,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"aboutCreditsSectionTitle",
|
||||
"aboutCreditsWorldAtlas1",
|
||||
"aboutCreditsWorldAtlas2",
|
||||
|
@ -8276,6 +8343,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"collectionActionShowTitleSearch",
|
||||
"collectionActionHideTitleSearch",
|
||||
"collectionActionAddShortcut",
|
||||
|
@ -8664,6 +8732,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"drawerPlacePage",
|
||||
"statePageTitle",
|
||||
"stateEmpty",
|
||||
|
@ -8687,6 +8756,14 @@
|
|||
"tagPlaceholderState"
|
||||
],
|
||||
|
||||
"uk": [
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"vi": [
|
||||
"aboutDataUsageClearCache"
|
||||
],
|
||||
|
||||
"zh": [
|
||||
"saveCopyButtonLabel",
|
||||
"chipActionGoToPlacePage",
|
||||
|
@ -8728,6 +8805,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"drawerPlacePage",
|
||||
"statePageTitle",
|
||||
"stateEmpty",
|
||||
|
@ -8764,6 +8842,7 @@
|
|||
"aboutDataUsageMisc",
|
||||
"aboutDataUsageInternal",
|
||||
"aboutDataUsageExternal",
|
||||
"aboutDataUsageClearCache",
|
||||
"settingsViewerShowHistogram"
|
||||
]
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue