diff --git a/.flutter b/.flutter
index 54e66469a..761747bfc 160000
--- a/.flutter
+++ b/.flutter
@@ -1 +1 @@
-Subproject commit 54e66469a933b60ddf175f858f82eaeb97e48c8d
+Subproject commit 761747bfc538b5af34aa0d3fac380f1bc331ec49
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b09bea370..8c359adab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,27 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
+## [v1.11.2] - 2024-06-11
+
+### Added
+
+- Albums / Countries / Tags: show selection in Collection
+- allow shifting dates by seconds
+
+### Changed
+
+- opening app from launcher shows home page only when exited by back button
+- Screen saver: black background, consistent with slideshow
+- upgraded Flutter to stable v3.22.2
+
+### Removed
+
+- support for Android KitKat (API 19)
+
+### Fixed
+
+- crash when cataloguing large images
+
## [v1.11.1] - 2024-05-03
### Added
diff --git a/android/app/build.gradle b/android/app/build.gradle
index aad98560d..7175da16d 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -66,9 +66,6 @@ android {
defaultConfig {
applicationId packageName
- // minSdk constraints:
- // - Flutter & other plugins: 19 (cf `flutter.minSdkVersion`)
- // - google_maps_flutter v2.1.1: 20
minSdk flutter.minSdkVersion
targetSdk 34
versionCode flutterVersionCode.toInteger()
@@ -197,11 +194,11 @@ repositories {
}
dependencies {
- implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0'
+ implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1'
implementation "androidx.appcompat:appcompat:1.6.1"
- implementation 'androidx.core:core-ktx:1.12.0'
- implementation 'androidx.lifecycle:lifecycle-process:2.7.0'
+ implementation 'androidx.core:core-ktx:1.13.1'
+ implementation 'androidx.lifecycle:lifecycle-process:2.8.0'
implementation 'androidx.media:media:1.7.0'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.security:security-crypto:1.1.0-alpha06'
@@ -211,9 +208,9 @@ dependencies {
implementation 'com.commonsware.cwac:document:0.5.0'
implementation 'com.drewnoakes:metadata-extractor:2.19.0'
implementation "com.github.bumptech.glide:glide:$glide_version"
- implementation 'com.google.android.material:material:1.11.0'
+ implementation 'com.google.android.material:material:1.12.0'
// SLF4J implementation for `mp4parser`
- implementation 'org.slf4j:slf4j-simple:2.0.12'
+ implementation 'org.slf4j:slf4j-simple:2.0.13'
// forked, built by JitPack:
// - https://jitpack.io/p/deckerst/Android-TiffBitmapFactory
@@ -229,7 +226,7 @@ dependencies {
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.2'
- kapt 'androidx.annotation:annotation:1.7.1'
+ kapt 'androidx.annotation:annotation:1.8.0'
ksp "com.github.bumptech.glide:ksp:$glide_version"
compileOnly rootProject.findProject(':streams_channel')
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 5e2437a2b..e0a82ddb2 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -72,12 +72,10 @@
-->
-
+
@@ -321,8 +319,8 @@
-
+
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/AnalysisWorker.kt b/android/app/src/main/kotlin/deckers/thibault/aves/AnalysisWorker.kt
index d7be83a8c..a0c1a0ab9 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/AnalysisWorker.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/AnalysisWorker.kt
@@ -161,13 +161,12 @@ class AnalysisWorker(context: Context, parameters: WorkerParameters) : Coroutine
applicationContext.getString(R.string.analysis_notification_action_stop),
WorkManager.getInstance(applicationContext).createCancelPendingIntent(id)
).build()
- val icon = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) R.drawable.ic_notification else R.mipmap.ic_launcher_round
val contentTitle = title ?: applicationContext.getText(R.string.analysis_notification_default_title)
val notification = NotificationCompat.Builder(applicationContext, NOTIFICATION_CHANNEL)
.setContentTitle(contentTitle)
.setTicker(contentTitle)
.setContentText(message)
- .setSmallIcon(icon)
+ .setSmallIcon(R.drawable.ic_notification)
.setOngoing(true)
.setContentIntent(openAppIntent)
.addAction(stopAction)
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/HomeWidgetConfigureActivity.kt b/android/app/src/main/kotlin/deckers/thibault/aves/HomeWidgetConfigureActivity.kt
index 5d1518fcc..dad5ed84e 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/HomeWidgetConfigureActivity.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/HomeWidgetConfigureActivity.kt
@@ -4,8 +4,6 @@ import android.appwidget.AppWidgetManager
import android.content.Intent
import android.os.Bundle
import deckers.thibault.aves.model.FieldMap
-import deckers.thibault.aves.utils.FlutterUtils
-import deckers.thibault.aves.utils.FlutterUtils.enableSoftwareRendering
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodChannel
@@ -13,9 +11,6 @@ class HomeWidgetSettingsActivity : MainActivity() {
private var appWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID
public override fun onCreate(savedInstanceState: Bundle?) {
- if (FlutterUtils.isSoftwareRenderingRequired()) {
- intent.enableSoftwareRendering()
- }
super.onCreate(savedInstanceState)
// cancel if user does not complete widget setup
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt b/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt
index c54b6820c..5c4903840 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/MainActivity.kt
@@ -17,13 +17,37 @@ import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat
import app.loup.streams_channel.StreamsChannel
import deckers.thibault.aves.channel.AvesByteSendingMethodCodec
-import deckers.thibault.aves.channel.calls.*
+import deckers.thibault.aves.channel.calls.AccessibilityHandler
+import deckers.thibault.aves.channel.calls.AnalysisHandler
+import deckers.thibault.aves.channel.calls.AppAdapterHandler
+import deckers.thibault.aves.channel.calls.DebugHandler
+import deckers.thibault.aves.channel.calls.DeviceHandler
+import deckers.thibault.aves.channel.calls.EmbeddedDataHandler
+import deckers.thibault.aves.channel.calls.GeocodingHandler
+import deckers.thibault.aves.channel.calls.GlobalSearchHandler
+import deckers.thibault.aves.channel.calls.HomeWidgetHandler
+import deckers.thibault.aves.channel.calls.MediaEditHandler
+import deckers.thibault.aves.channel.calls.MediaFetchBytesHandler
+import deckers.thibault.aves.channel.calls.MediaFetchObjectHandler
+import deckers.thibault.aves.channel.calls.MediaSessionHandler
+import deckers.thibault.aves.channel.calls.MediaStoreHandler
+import deckers.thibault.aves.channel.calls.MetadataEditHandler
+import deckers.thibault.aves.channel.calls.MetadataFetchHandler
+import deckers.thibault.aves.channel.calls.SecurityHandler
+import deckers.thibault.aves.channel.calls.StorageHandler
import deckers.thibault.aves.channel.calls.window.ActivityWindowHandler
import deckers.thibault.aves.channel.calls.window.WindowHandler
-import deckers.thibault.aves.channel.streams.*
+import deckers.thibault.aves.channel.streams.ActivityResultStreamHandler
+import deckers.thibault.aves.channel.streams.AnalysisStreamHandler
+import deckers.thibault.aves.channel.streams.ErrorStreamHandler
+import deckers.thibault.aves.channel.streams.ImageByteStreamHandler
+import deckers.thibault.aves.channel.streams.ImageOpStreamHandler
+import deckers.thibault.aves.channel.streams.IntentStreamHandler
+import deckers.thibault.aves.channel.streams.MediaCommandStreamHandler
+import deckers.thibault.aves.channel.streams.MediaStoreChangeStreamHandler
+import deckers.thibault.aves.channel.streams.MediaStoreStreamHandler
+import deckers.thibault.aves.channel.streams.SettingsChangeStreamHandler
import deckers.thibault.aves.model.FieldMap
-import deckers.thibault.aves.utils.FlutterUtils.enableSoftwareRendering
-import deckers.thibault.aves.utils.FlutterUtils.isSoftwareRenderingRequired
import deckers.thibault.aves.utils.LogUtils
import deckers.thibault.aves.utils.getParcelableExtraCompat
import io.flutter.embedding.android.FlutterFragmentActivity
@@ -52,13 +76,6 @@ open class MainActivity : FlutterFragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Log.i(LOG_TAG, "onCreate intent=$intent")
- if (isSoftwareRenderingRequired()) {
- intent.enableSoftwareRendering()
- // running the app from Android Studio automatically adds to the intent the `start-paused` flag
- // so the IDE can connect to the app, but launching on KitKat emulators fails because of a timeout
- intent.removeExtra("start-paused")
- }
-
intent.extras?.takeUnless { it.isEmpty }?.let {
Log.i(LOG_TAG, "onCreate intent extras=$it")
}
@@ -168,11 +185,9 @@ open class MainActivity : FlutterFragmentActivity() {
// as of Flutter v3.0.1, the window `viewInsets` and `viewPadding`
// are incorrect on startup in some environments (e.g. API 29 emulator),
// so we manually request to apply the insets to update the window metrics
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
- Handler(Looper.getMainLooper()).postDelayed({
- window.decorView.requestApplyInsets()
- }, 100)
- }
+ Handler(Looper.getMainLooper()).postDelayed({
+ window.decorView.requestApplyInsets()
+ }, 100)
}
override fun onStop() {
@@ -270,10 +285,10 @@ open class MainActivity : FlutterFragmentActivity() {
open fun extractIntentData(intent: Intent?): FieldMap {
when (val action = intent?.action) {
Intent.ACTION_MAIN -> {
- val fields = hashMapOf(
- INTENT_DATA_KEY_LAUNCHER to intent.hasCategory(Intent.CATEGORY_LAUNCHER),
- INTENT_DATA_KEY_SAFE_MODE to intent.getBooleanExtra(EXTRA_KEY_SAFE_MODE, false),
- )
+ val fields = HashMap()
+ if (intent.getBooleanExtra(EXTRA_KEY_SAFE_MODE, false)) {
+ fields[INTENT_DATA_KEY_SAFE_MODE] = true
+ }
intent.getStringExtra(EXTRA_KEY_PAGE)?.let { page ->
val filters = extractFiltersFromIntent(intent)
fields[INTENT_DATA_KEY_PAGE] = page
@@ -482,7 +497,6 @@ open class MainActivity : FlutterFragmentActivity() {
const val INTENT_DATA_KEY_ACTION = "action"
const val INTENT_DATA_KEY_ALLOW_MULTIPLE = "allowMultiple"
const val INTENT_DATA_KEY_FILTERS = "filters"
- const val INTENT_DATA_KEY_LAUNCHER = "launcher"
const val INTENT_DATA_KEY_MIME_TYPE = "mimeType"
const val INTENT_DATA_KEY_PAGE = "page"
const val INTENT_DATA_KEY_QUERY = "query"
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/ScreenSaverService.kt b/android/app/src/main/kotlin/deckers/thibault/aves/ScreenSaverService.kt
index be332d36f..931fe9560 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/ScreenSaverService.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/ScreenSaverService.kt
@@ -9,6 +9,7 @@ import deckers.thibault.aves.channel.calls.*
import deckers.thibault.aves.channel.calls.window.ServiceWindowHandler
import deckers.thibault.aves.channel.calls.window.WindowHandler
import deckers.thibault.aves.channel.streams.ImageByteStreamHandler
+import deckers.thibault.aves.channel.streams.MediaCommandStreamHandler
import deckers.thibault.aves.channel.streams.MediaStoreStreamHandler
import deckers.thibault.aves.utils.LogUtils
import io.flutter.FlutterInjector
@@ -18,12 +19,14 @@ import io.flutter.embedding.android.FlutterView
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.dart.DartExecutor.DartEntrypoint
import io.flutter.embedding.engine.plugins.util.GeneratedPluginRegister
+import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodChannel
// for FlutterView-level integration, cf https://docs.flutter.dev/development/add-to-app/android/add-flutter-view
class ScreenSaverService : DreamService() {
private var flutterEngine: FlutterEngine? = null
private var flutterView: FlutterView? = null
+ private lateinit var mediaSessionHandler: MediaSessionHandler
override fun onAttachedToWindow() {
Log.i(LOG_TAG, "onAttachedToWindow")
@@ -77,6 +80,7 @@ class ScreenSaverService : DreamService() {
private fun release() {
destroyView()
+ mediaSessionHandler.dispose()
flutterEngine = null
flutterView = null
}
@@ -96,12 +100,19 @@ class ScreenSaverService : DreamService() {
private fun initChannels() {
val messenger = flutterEngine!!.dartExecutor
+ // notification: platform -> dart
+ val mediaCommandStreamHandler = MediaCommandStreamHandler().apply {
+ EventChannel(messenger, MediaCommandStreamHandler.CHANNEL).setStreamHandler(this)
+ }
+
// dart -> platform -> dart
// - need Context
+ mediaSessionHandler = MediaSessionHandler(this, mediaCommandStreamHandler)
MethodChannel(messenger, DeviceHandler.CHANNEL).setMethodCallHandler(DeviceHandler(this))
MethodChannel(messenger, EmbeddedDataHandler.CHANNEL).setMethodCallHandler(EmbeddedDataHandler(this))
MethodChannel(messenger, MediaFetchBytesHandler.CHANNEL, AvesByteSendingMethodCodec.INSTANCE).setMethodCallHandler(MediaFetchBytesHandler(this))
MethodChannel(messenger, MediaFetchObjectHandler.CHANNEL).setMethodCallHandler(MediaFetchObjectHandler(this))
+ MethodChannel(messenger, MediaSessionHandler.CHANNEL).setMethodCallHandler(mediaSessionHandler)
MethodChannel(messenger, MediaStoreHandler.CHANNEL).setMethodCallHandler(MediaStoreHandler(this))
MethodChannel(messenger, MetadataFetchHandler.CHANNEL).setMethodCallHandler(MetadataFetchHandler(this))
MethodChannel(messenger, StorageHandler.CHANNEL).setMethodCallHandler(StorageHandler(this))
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/SearchSuggestionsProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/SearchSuggestionsProvider.kt
index 4429cd878..c7f2f8fd4 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/SearchSuggestionsProvider.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/SearchSuggestionsProvider.kt
@@ -34,7 +34,7 @@ class SearchSuggestionsProvider : ContentProvider() {
val columns = arrayOf(
SearchManager.SUGGEST_COLUMN_INTENT_DATA,
SearchManager.SUGGEST_COLUMN_INTENT_EXTRA_DATA,
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) SearchManager.SUGGEST_COLUMN_CONTENT_TYPE else "mimeType",
+ SearchManager.SUGGEST_COLUMN_CONTENT_TYPE,
SearchManager.SUGGEST_COLUMN_TEXT_1,
SearchManager.SUGGEST_COLUMN_TEXT_2,
SearchManager.SUGGEST_COLUMN_ICON_1,
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/WallpaperActivity.kt b/android/app/src/main/kotlin/deckers/thibault/aves/WallpaperActivity.kt
index c37539172..4aa32be1a 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/WallpaperActivity.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/WallpaperActivity.kt
@@ -2,21 +2,26 @@ package deckers.thibault.aves
import android.content.Intent
import android.net.Uri
-import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log
import app.loup.streams_channel.StreamsChannel
import deckers.thibault.aves.channel.AvesByteSendingMethodCodec
-import deckers.thibault.aves.channel.calls.*
+import deckers.thibault.aves.channel.calls.AccessibilityHandler
+import deckers.thibault.aves.channel.calls.DeviceHandler
+import deckers.thibault.aves.channel.calls.EmbeddedDataHandler
+import deckers.thibault.aves.channel.calls.MediaFetchBytesHandler
+import deckers.thibault.aves.channel.calls.MediaFetchObjectHandler
+import deckers.thibault.aves.channel.calls.MediaSessionHandler
+import deckers.thibault.aves.channel.calls.MetadataFetchHandler
+import deckers.thibault.aves.channel.calls.StorageHandler
+import deckers.thibault.aves.channel.calls.WallpaperHandler
import deckers.thibault.aves.channel.calls.window.ActivityWindowHandler
import deckers.thibault.aves.channel.calls.window.WindowHandler
import deckers.thibault.aves.channel.streams.ImageByteStreamHandler
import deckers.thibault.aves.channel.streams.MediaCommandStreamHandler
import deckers.thibault.aves.model.FieldMap
-import deckers.thibault.aves.utils.FlutterUtils
-import deckers.thibault.aves.utils.FlutterUtils.enableSoftwareRendering
import deckers.thibault.aves.utils.LogUtils
import deckers.thibault.aves.utils.getParcelableExtraCompat
import io.flutter.embedding.android.FlutterFragmentActivity
@@ -30,9 +35,6 @@ class WallpaperActivity : FlutterFragmentActivity() {
private lateinit var mediaSessionHandler: MediaSessionHandler
override fun onCreate(savedInstanceState: Bundle?) {
- if (FlutterUtils.isSoftwareRenderingRequired()) {
- intent.enableSoftwareRendering()
- }
super.onCreate(savedInstanceState)
Log.i(LOG_TAG, "onCreate intent=$intent")
@@ -83,11 +85,9 @@ class WallpaperActivity : FlutterFragmentActivity() {
// as of Flutter v3.0.1, the window `viewInsets` and `viewPadding`
// are incorrect on startup in some environments (e.g. API 29 emulator),
// so we manually request to apply the insets to update the window metrics
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
- Handler(Looper.getMainLooper()).postDelayed({
- window.decorView.requestApplyInsets()
- }, 100)
- }
+ Handler(Looper.getMainLooper()).postDelayed({
+ window.decorView.requestApplyInsets()
+ }, 100)
}
override fun onDestroy() {
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AnalysisHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AnalysisHandler.kt
index c640d31c7..21b7dd13e 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AnalysisHandler.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AnalysisHandler.kt
@@ -1,7 +1,7 @@
package deckers.thibault.aves.channel.calls
import android.content.Context
-import androidx.core.app.ComponentActivity
+import androidx.activity.ComponentActivity
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.OneTimeWorkRequestBuilder
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt
index 3292bb715..9f223655f 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt
@@ -79,15 +79,9 @@ class DebugHandler(private val context: Context) : MethodCallHandler {
"obbDir" to context.obbDir,
"externalCacheDir" to context.externalCacheDir,
"externalFilesDir" to context.getExternalFilesDir(null),
+ "codeCacheDir" to context.codeCacheDir,
+ "noBackupFilesDir" to context.noBackupFilesDir,
).apply {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- putAll(
- hashMapOf(
- "codeCacheDir" to context.codeCacheDir,
- "noBackupFilesDir" to context.noBackupFilesDir,
- )
- )
- }
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
put("dataDir", context.dataDir)
}
@@ -108,8 +102,6 @@ class DebugHandler(private val context: Context) : MethodCallHandler {
}
private fun getCodecs(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
- val codecs = ArrayList()
-
fun getFields(info: MediaCodecInfo): FieldMap {
val fields: FieldMap = hashMapOf(
"name" to info.name,
@@ -126,18 +118,7 @@ class DebugHandler(private val context: Context) : MethodCallHandler {
return fields
}
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- codecs.addAll(MediaCodecList(MediaCodecList.REGULAR_CODECS).codecInfos.map(::getFields))
- } else {
- @Suppress("deprecation")
- val count = MediaCodecList.getCodecCount()
- for (i in 0 until count) {
- @Suppress("deprecation")
- val info = MediaCodecList.getCodecInfoAt(i)
- codecs.add(getFields(info))
- }
- }
-
+ val codecs = MediaCodecList(MediaCodecList.REGULAR_CODECS).codecInfos.map(::getFields).toList()
result.success(codecs)
}
@@ -294,7 +275,7 @@ class DebugHandler(private val context: Context) : MethodCallHandler {
if (canReadWithMetadataExtractor(mimeType)) {
try {
Metadata.openSafeInputStream(context, uri, mimeType, sizeBytes)?.use { input ->
- val metadata = Helper.safeRead(input)
+ val metadata = Helper.safeRead(input, sizeBytes)
metadataMap["mimeType"] = metadata.getDirectoriesOfType(FileTypeDirectory::class.java).joinToString { dir ->
if (dir.containsTag(FileTypeDirectory.TAG_DETECTED_FILE_MIME_TYPE)) {
dir.getString(FileTypeDirectory.TAG_DETECTED_FILE_MIME_TYPE)
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt
index 3b1306a99..603739bde 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt
@@ -69,16 +69,11 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
}
private fun getLocales(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
- fun toMap(locale: Locale): FieldMap {
- val fields: HashMap = hashMapOf(
- "language" to locale.language,
- "country" to locale.country,
- )
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- fields["script"] = locale.script
- }
- return fields
- }
+ fun toMap(locale: Locale): FieldMap = hashMapOf(
+ "language" to locale.language,
+ "country" to locale.country,
+ "script" to locale.script,
+ )
val locales = ArrayList()
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
@@ -106,11 +101,7 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
}
private fun isSystemFilePickerEnabled(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
- val enabled = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).resolveActivity(context.packageManager) != null
- } else {
- false
- }
+ val enabled = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).resolveActivity(context.packageManager) != null
result.success(enabled)
}
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/EmbeddedDataHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/EmbeddedDataHandler.kt
index e46d31a46..a96f84e90 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/EmbeddedDataHandler.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/EmbeddedDataHandler.kt
@@ -102,7 +102,7 @@ class EmbeddedDataHandler(private val context: Context) : MethodCallHandler {
if (canReadWithMetadataExtractor(mimeType)) {
try {
Metadata.openSafeInputStream(context, uri, mimeType, sizeBytes)?.use { input ->
- val metadata = Helper.safeRead(input)
+ val metadata = Helper.safeRead(input, sizeBytes)
// data can be large and stored in "Extended XMP",
// which is returned as a second XMP directory
val xmpDirs = metadata.getDirectoriesOfType(XmpDirectory::class.java)
@@ -272,7 +272,7 @@ class EmbeddedDataHandler(private val context: Context) : MethodCallHandler {
if (canReadWithMetadataExtractor(mimeType)) {
try {
Metadata.openSafeInputStream(context, uri, mimeType, sizeBytes)?.use { input ->
- val metadata = Helper.safeRead(input)
+ val metadata = Helper.safeRead(input, sizeBytes)
// data can be large and stored in "Extended XMP",
// which is returned as a second XMP directory
val xmpDirs = metadata.getDirectoriesOfType(XmpDirectory::class.java)
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataEditHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataEditHandler.kt
index 9d1704b27..36ac14ff2 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataEditHandler.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataEditHandler.kt
@@ -74,7 +74,7 @@ class MetadataEditHandler(private val contextWrapper: ContextWrapper) : MethodCa
private fun editDate(call: MethodCall, result: MethodChannel.Result) {
val dateMillis = call.argument("dateMillis")?.toLong()
- val shiftMinutes = call.argument("shiftMinutes")?.toLong()
+ val shiftSeconds = call.argument("shiftSeconds")?.toLong()
val fields = call.argument>("fields")
val entryMap = call.argument("entry")
if (entryMap == null || fields == null) {
@@ -97,7 +97,7 @@ class MetadataEditHandler(private val contextWrapper: ContextWrapper) : MethodCa
}
val callback = MetadataOpCallback("editDate", entryMap, result)
- provider.editDate(contextWrapper, path, uri, mimeType, dateMillis, shiftMinutes, fields, callback)
+ provider.editDate(contextWrapper, path, uri, mimeType, dateMillis, shiftSeconds, fields, callback)
}
private fun editMetadata(call: MethodCall, result: MethodChannel.Result) {
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataFetchHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataFetchHandler.kt
index 8819f24ff..06c64cd50 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataFetchHandler.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataFetchHandler.kt
@@ -229,7 +229,7 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
if (canReadWithMetadataExtractor(mimeType)) {
try {
Metadata.openSafeInputStream(context, uri, mimeType, sizeBytes)?.use { input ->
- val metadata = Helper.safeRead(input)
+ val metadata = Helper.safeRead(input, sizeBytes)
foundExif = metadata.directories.any { it is ExifDirectoryBase && it.tagCount > 0 }
foundMp4Uuid = metadata.directories.any { it is Mp4UuidBoxDirectory && it.tagCount > 0 }
@@ -296,7 +296,7 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
byGeoTiff[false]?.map { exifTagMapper(it) }?.let { dirMap.putAll(it) }
}
- mimeType == MimeTypes.DNG -> {
+ mimeType == MimeTypes.DNG || mimeType == MimeTypes.DNG_ADOBE -> {
// split DNG tags in their own directory
val dngDirMap = metadataMap[DIR_DNG] ?: HashMap()
metadataMap[DIR_DNG] = dngDirMap
@@ -599,7 +599,7 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
if (canReadWithMetadataExtractor(mimeType)) {
try {
Metadata.openSafeInputStream(context, uri, mimeType, sizeBytes)?.use { input ->
- val metadata = Helper.safeRead(input)
+ val metadata = Helper.safeRead(input, sizeBytes)
foundExif = metadata.directories.any { it is ExifDirectoryBase && it.tagCount > 0 }
foundMp4Uuid = metadata.directories.any { it is Mp4UuidBoxDirectory && it.tagCount > 0 }
@@ -803,7 +803,7 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
}
StorageUtils.openInputStream(context, uri)?.let { input ->
input.skip(dataOffset)
- val pageMetadata = Helper.safeRead(input)
+ val pageMetadata = Helper.safeRead(input, sizeBytes)
if (pageMetadata.getDirectoriesOfType(XmpDirectory::class.java).any { it.xmpMeta.hasHdrGainMap() }) {
return true
}
@@ -897,7 +897,7 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
if (canReadWithMetadataExtractor(mimeType)) {
try {
Metadata.openSafeInputStream(context, uri, mimeType, sizeBytes)?.use { input ->
- val metadata = Helper.safeRead(input)
+ val metadata = Helper.safeRead(input, sizeBytes)
for (dir in metadata.getDirectoriesOfType(ExifSubIFDDirectory::class.java)) {
foundExif = true
if (fields.contains(KEY_APERTURE)) {
@@ -1007,7 +1007,7 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
if (canReadWithMetadataExtractor(mimeType)) {
try {
Metadata.openSafeInputStream(context, uri, mimeType, sizeBytes)?.use { input ->
- val metadata = Helper.safeRead(input)
+ val metadata = Helper.safeRead(input, sizeBytes)
val fields = HashMap()
for (dir in metadata.getDirectoriesOfType(ExifIFD0Directory::class.java)) {
if (dir.containsGeoTiffTags()) {
@@ -1084,7 +1084,7 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
if (canReadWithMetadataExtractor(mimeType) && !isLargeMp4(mimeType, sizeBytes)) {
try {
Metadata.openSafeInputStream(context, uri, mimeType, sizeBytes)?.use { input ->
- val metadata = Helper.safeRead(input)
+ val metadata = Helper.safeRead(input, sizeBytes)
metadata.getDirectoriesOfType(XmpDirectory::class.java).map { it.xmpMeta }.forEach {
processXmp(it, allowMultiple = true)
}
@@ -1166,7 +1166,7 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
if (canReadWithMetadataExtractor(mimeType) && !isLargeMp4(mimeType, sizeBytes)) {
try {
Metadata.openSafeInputStream(context, uri, mimeType, sizeBytes)?.use { input ->
- val metadata = Helper.safeRead(input)
+ val metadata = Helper.safeRead(input, sizeBytes)
metadata.getDirectoriesOfType(XmpDirectory::class.java).map { it.xmpMeta }.forEach {
processXmp(it, allowMultiple = true)
}
@@ -1244,7 +1244,7 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
if (canReadWithMetadataExtractor(mimeType)) {
try {
Metadata.openSafeInputStream(context, uri, mimeType, sizeBytes)?.use { input ->
- val metadata = Helper.safeRead(input)
+ val metadata = Helper.safeRead(input, sizeBytes)
val tag = when (field) {
ExifInterface.TAG_DATETIME -> ExifIFD0Directory.TAG_DATETIME
ExifInterface.TAG_DATETIME_DIGITIZED -> ExifSubIFDDirectory.TAG_DATETIME_DIGITIZED
@@ -1345,7 +1345,7 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
if (canReadWithMetadataExtractor(mimeType)) {
try {
Metadata.openSafeInputStream(context, uri, mimeType, sizeBytes)?.use { input ->
- val metadata = Helper.safeRead(input)
+ val metadata = Helper.safeRead(input, sizeBytes)
for (dir in metadata.getDirectoriesOfType(ExifDirectoryBase::class.java)) {
foundExif = true
val allTags = ExifInterfaceHelper.allTags
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/StorageHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/StorageHandler.kt
index 81da9f0b0..4d0001ce0 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/StorageHandler.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/StorageHandler.kt
@@ -47,9 +47,7 @@ class StorageHandler(private val context: Context) : MethodCallHandler {
private fun getDataUsage(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
var internalCache = getFolderSize(context.cacheDir)
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- internalCache += getFolderSize(context.codeCacheDir)
- }
+ internalCache += getFolderSize(context.codeCacheDir)
val externalCache = context.externalCacheDirs.map(::getFolderSize).sum()
val dataDir = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) context.dataDir else File(context.applicationInfo.dataDir)
@@ -105,12 +103,7 @@ class StorageHandler(private val context: Context) : MethodCallHandler {
val volumeFile = File(volumePath)
try {
val isPrimary = volumePath == primaryVolumePath
- val isRemovable = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- Environment.isExternalStorageRemovable(volumeFile)
- } else {
- // random guess
- !isPrimary
- }
+ val isRemovable = Environment.isExternalStorageRemovable(volumeFile)
volumes.add(
hashMapOf(
"path" to volumePath,
@@ -202,11 +195,6 @@ class StorageHandler(private val context: Context) : MethodCallHandler {
return
}
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
- result.error("revokeDirectoryAccess-unsupported", "volume access is not allowed before Android Lollipop", null)
- return
- }
-
val success = PermissionManager.revokeDirectoryAccess(context, path)
result.success(success)
}
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/RegionFetcher.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/RegionFetcher.kt
index 9d319e7db..b8f3c5740 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/RegionFetcher.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/RegionFetcher.kt
@@ -14,10 +14,12 @@ import deckers.thibault.aves.decoder.MultiPageImage
import deckers.thibault.aves.utils.BitmapRegionDecoderCompat
import deckers.thibault.aves.utils.BitmapUtils.ARGB_8888_BYTE_SIZE
import deckers.thibault.aves.utils.BitmapUtils.getBytes
+import deckers.thibault.aves.utils.MathUtils
import deckers.thibault.aves.utils.MemoryUtils
import deckers.thibault.aves.utils.MimeTypes
import deckers.thibault.aves.utils.StorageUtils
import io.flutter.plugin.common.MethodChannel
+import kotlin.math.max
import kotlin.math.roundToInt
// As of Android 14 (API 34), `BitmapRegionDecoder` documentation states
@@ -60,10 +62,6 @@ class RegionFetcher internal constructor(
return
}
- val options = BitmapFactory.Options().apply {
- inSampleSize = sampleSize
- }
-
var currentDecoderRef = lastDecoderRef
if (currentDecoderRef != null && currentDecoderRef.uri != uri) {
currentDecoderRef = null
@@ -85,27 +83,35 @@ class RegionFetcher internal constructor(
// with raw images, the known image size may not match the decoded image size
// so we scale the requested region accordingly
- val effectiveRect = if (imageWidth != decoder.width || imageHeight != decoder.height) {
+ var effectiveRect = regionRect
+ var effectiveSampleSize = sampleSize
+
+ if (imageWidth != decoder.width || imageHeight != decoder.height) {
val xf = decoder.width.toDouble() / imageWidth
val yf = decoder.height.toDouble() / imageHeight
- Rect(
+ effectiveRect = Rect(
(regionRect.left * xf).roundToInt(),
(regionRect.top * yf).roundToInt(),
(regionRect.right * xf).roundToInt(),
(regionRect.bottom * yf).roundToInt(),
)
- } else {
- regionRect
+ val factor = MathUtils.highestPowerOf2((1 / max(xf, yf)).roundToInt())
+ if (factor > 1) {
+ effectiveSampleSize = max(1, effectiveSampleSize / factor)
+ }
}
// use `Long` as rect size could be unexpectedly large and go beyond `Int` max
- val targetBitmapSizeBytes: Long = ARGB_8888_BYTE_SIZE.toLong() * effectiveRect.width() * effectiveRect.height() / sampleSize
+ val targetBitmapSizeBytes: Long = ARGB_8888_BYTE_SIZE.toLong() * effectiveRect.width() * effectiveRect.height() / effectiveSampleSize
if (!MemoryUtils.canAllocate(targetBitmapSizeBytes)) {
// decoding a region that large would yield an OOM when creating the bitmap
result.error("fetch-large-region", "Region too large for uri=$uri regionRect=$regionRect", null)
return
}
+ val options = BitmapFactory.Options().apply {
+ inSampleSize = effectiveSampleSize
+ }
val bitmap = decoder.decodeRegion(effectiveRect, options)
if (bitmap != null) {
result.success(bitmap.getBytes(MimeTypes.canHaveAlpha(mimeType), recycle = true))
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/ActivityResultStreamHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/ActivityResultStreamHandler.kt
index 7d442e98b..29005f9ae 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/ActivityResultStreamHandler.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/streams/ActivityResultStreamHandler.kt
@@ -61,11 +61,6 @@ class ActivityResultStreamHandler(private val activity: Activity, arguments: Any
return
}
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
- error("requestDirectoryAccess-unsupported", "directory access is not allowed before Android Lollipop", null)
- return
- }
-
PermissionManager.requestDirectoryAccess(activity, ensureTrailingSeparator(path), {
success(true)
endOfStream()
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/ExifInterfaceHelper.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/ExifInterfaceHelper.kt
index ce361ea32..f0d28fb3e 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/ExifInterfaceHelper.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/ExifInterfaceHelper.kt
@@ -4,14 +4,18 @@ import android.util.Log
import androidx.exifinterface.media.ExifInterface
import com.drew.lang.Rational
import com.drew.metadata.Directory
-import com.drew.metadata.exif.*
+import com.drew.metadata.exif.ExifDirectoryBase
+import com.drew.metadata.exif.ExifIFD0Directory
+import com.drew.metadata.exif.ExifThumbnailDirectory
+import com.drew.metadata.exif.GpsDirectory
+import com.drew.metadata.exif.PanasonicRawIFD0Directory
import com.drew.metadata.exif.makernotes.OlympusCameraSettingsMakernoteDirectory
import com.drew.metadata.exif.makernotes.OlympusImageProcessingMakernoteDirectory
import com.drew.metadata.exif.makernotes.OlympusMakernoteDirectory
import deckers.thibault.aves.utils.LogUtils
import java.text.ParseException
import java.text.SimpleDateFormat
-import java.util.*
+import java.util.Locale
import kotlin.math.abs
import kotlin.math.floor
import kotlin.math.roundToLong
@@ -22,7 +26,7 @@ object ExifInterfaceHelper {
val GPS_DATE_FORMAT = SimpleDateFormat("yyyy:MM:dd", Locale.ROOT)
val GPS_TIME_FORMAT = SimpleDateFormat("HH:mm:ss", Locale.ROOT)
- private const val precisionErrorTolerance = 1e-10
+ private const val PRECISION_ERROR_TOLERANCE = 1e-10
// ExifInterface always states it has the following attributes
// and returns "0" instead of "null" when they are actually missing
@@ -220,7 +224,7 @@ object ExifInterfaceHelper {
// initialize metadata-extractor directories that we will fill
// by tags converted from the ExifInterface attributes
// so that we can rely on metadata-extractor descriptions
- val dirs = DirType.values().associateWith { it.createDirectory() }
+ val dirs = DirType.entries.associateWith { it.createDirectory() }
// exclude Exif directory when it only includes image size
val isUselessExif = fun(it: Map): Boolean {
@@ -308,7 +312,7 @@ object ExifInterfaceHelper {
val numerator = 1L
val f = numerator / d
val denominator = f.roundToLong()
- if (abs(f - denominator) < precisionErrorTolerance) {
+ if (abs(f - denominator) < PRECISION_ERROR_TOLERANCE) {
return Rational(numerator, denominator)
}
}
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt
index 4553b2c4f..04a21de0a 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/Metadata.kt
@@ -120,7 +120,7 @@ object Metadata {
return date.time + parseSubSecond(subSecond)
}
- // Opening large PSD/TIFF files yields an OOM (both with `metadata-extractor` v2.15.0 and `ExifInterface` v1.3.1),
+ // Opening some large files yields an OOM (both with `metadata-extractor` v2.15.0 and `ExifInterface` v1.3.1),
// so we define an arbitrary threshold to avoid a crash on launch.
// It is not clear whether it is because of the file itself or its metadata.
private const val FILE_SIZE_MAX = 100 * (1 shl 20) // MB
@@ -134,30 +134,24 @@ object Metadata {
private val previewFiles = HashMap()
private fun getSafeUri(context: Context, uri: Uri, mimeType: String, sizeBytes: Long?): Uri {
- return when (mimeType) {
- // formats known to yield OOM for large files
- MimeTypes.HEIC,
- MimeTypes.HEIF,
- MimeTypes.MP4,
- MimeTypes.PSD_VND,
- MimeTypes.PSD_X,
- MimeTypes.TIFF -> {
- if (isDangerouslyLarge(sizeBytes)) {
- // make a preview from the beginning of the file,
- // hoping the metadata is accessible in the copied chunk
- var previewFile = previewFiles[uri]
- if (previewFile == null) {
- previewFile = createPreviewFile(context, uri)
- previewFiles[uri] = previewFile
- }
- Uri.fromFile(previewFile)
- } else {
- // small enough to be safe as it is
- uri
+ // formats known to yield OOM for large files
+ return if ((MimeTypes.isImage(mimeType) || mimeType == MimeTypes.MP4)) {
+ if (isDangerouslyLarge(sizeBytes)) {
+ // make a preview from the beginning of the file,
+ // hoping the metadata is accessible in the copied chunk
+ var previewFile = previewFiles[uri]
+ if (previewFile == null) {
+ previewFile = createPreviewFile(context, uri)
+ previewFiles[uri] = previewFile
}
+ Uri.fromFile(previewFile)
+ } else {
+ // small enough to be safe as it is
+ uri
}
+ } else {
// *probably* safe
- else -> uri
+ uri
}
}
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MultiPage.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MultiPage.kt
index 2a7d679ac..113562fee 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MultiPage.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MultiPage.kt
@@ -97,7 +97,7 @@ object MultiPage {
if (canReadWithMetadataExtractor(mimeType)) {
try {
Metadata.openSafeInputStream(context, uri, mimeType, sizeBytes)?.use { input ->
- val metadata = Helper.safeRead(input)
+ val metadata = Helper.safeRead(input, sizeBytes)
foundExif = metadata.directories.any { it is ExifDirectoryBase && it.tagCount > 0 }
for (dir in metadata.getDirectoriesOfType(ExifIFD0Directory::class.java)) {
dir.getSafeInt(ExifDirectoryBase.TAG_ORIENTATION) {
@@ -168,7 +168,7 @@ object MultiPage {
val mimeType = MimeTypes.JPEG
try {
Metadata.openSafeInputStream(context, uri, mimeType, sizeBytes)?.use { input ->
- val metadata = Helper.safeRead(input)
+ val metadata = Helper.safeRead(input, sizeBytes)
return metadata.getDirectoriesOfType(MpEntryDirectory::class.java).map { it.entry }
}
} catch (e: Exception) {
@@ -332,7 +332,7 @@ object MultiPage {
try {
Metadata.openSafeInputStream(context, uri, mimeType, sizeBytes)?.use { input ->
- val metadata = Helper.safeRead(input)
+ val metadata = Helper.safeRead(input, sizeBytes)
foundXmp = metadata.directories.any { it is XmpDirectory && it.tagCount > 0 }
metadata.getDirectoriesOfType(XmpDirectory::class.java).map { it.xmpMeta }.forEach(::processXmp)
}
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/Helper.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/Helper.kt
index 5ebe7537a..36afac482 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/Helper.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/Helper.kt
@@ -29,6 +29,7 @@ import deckers.thibault.aves.metadata.GeoTiffKeys
import deckers.thibault.aves.metadata.Metadata
import deckers.thibault.aves.metadata.metadataextractor.mpf.MpfReader
import deckers.thibault.aves.utils.LogUtils
+import deckers.thibault.aves.utils.MemoryUtils
import java.io.BufferedInputStream
import java.io.IOException
import java.io.InputStream
@@ -59,19 +60,21 @@ object Helper {
// e.g. "exif [...] 134 [...] 4578696600004949[...]"
private val PNG_RAW_PROFILE_PATTERN = Regex("^\\n(.*?)\\n\\s*(\\d+)\\n(.*)", RegexOption.DOT_MATCHES_ALL)
- // providing the stream length is risky, as it may crash if it is incorrect
- private const val safeReadStreamLength = -1L
-
fun readMimeType(input: InputStream): String? {
val bufferedInputStream = if (input is BufferedInputStream) input else BufferedInputStream(input)
return FileTypeDetector.detectFileType(bufferedInputStream).mimeType
}
@Throws(IOException::class, ImageProcessingException::class)
- fun safeRead(input: InputStream): com.drew.metadata.Metadata {
+ fun safeRead(input: InputStream, @Suppress("unused_parameter") sizeBytes: Long?): com.drew.metadata.Metadata {
val inputStream = if (input is BufferedInputStream) input else BufferedInputStream(input)
val fileType = FileTypeDetector.detectFileType(inputStream)
+ // Providing the stream length is risky, as it may crash if it is incorrect.
+ // Not providing the stream length is also risky, as it may lead to OOM
+ // when `RandomAccessStreamReader` reads the entire stream to validate offsets.
+ val undefinedStreamLength = -1L
+
val metadata = when (fileType) {
FileType.Jpeg -> safeReadJpeg(inputStream)
FileType.Mp4 -> safeReadMp4(inputStream)
@@ -82,9 +85,9 @@ object Helper {
FileType.Cr2,
FileType.Nef,
FileType.Orf,
- FileType.Rw2 -> safeReadTiff(inputStream)
+ FileType.Rw2 -> safeReadTiff(inputStream, undefinedStreamLength)
- else -> ImageMetadataReader.readMetadata(inputStream, safeReadStreamLength, fileType)
+ else -> ImageMetadataReader.readMetadata(inputStream, undefinedStreamLength, fileType)
}
metadata.addDirectory(FileTypeDirectory(fileType))
@@ -115,8 +118,8 @@ object Helper {
}
@Throws(IOException::class, TiffProcessingException::class)
- fun safeReadTiff(input: InputStream): com.drew.metadata.Metadata {
- val reader = RandomAccessStreamReader(input, RandomAccessStreamReader.DEFAULT_CHUNK_LENGTH, safeReadStreamLength)
+ fun safeReadTiff(input: InputStream, streamLength: Long): com.drew.metadata.Metadata {
+ val reader = RandomAccessStreamReader(input, RandomAccessStreamReader.DEFAULT_CHUNK_LENGTH, streamLength)
val metadata = com.drew.metadata.Metadata()
val handler = SafeExifTiffHandler(metadata, null, 0)
TiffReader().processTiff(reader, handler, 0)
@@ -294,9 +297,7 @@ object Helper {
if (!modelTiePoints && !modelTransformation) return false
val modelPixelScale = this.containsTag(ExifGeoTiffTags.TAG_MODEL_PIXEL_SCALE)
- if ((modelTransformation && modelPixelScale) || (modelPixelScale && !modelTiePoints)) return false
-
- return true
+ return !((modelTransformation && modelPixelScale) || (modelPixelScale && !modelTiePoints))
}
// TODO TLAD use `GeoTiffDirectory` from the Java version of `metadata-extractor` when available
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafePngMetadataReader.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafePngMetadataReader.kt
index 97498c0e4..719f08a95 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafePngMetadataReader.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafePngMetadataReader.kt
@@ -31,6 +31,7 @@ import deckers.thibault.aves.utils.LogUtils
import java.io.ByteArrayInputStream
import java.io.IOException
import java.io.InputStream
+import java.util.Locale
import java.util.zip.InflaterInputStream
import java.util.zip.ZipException
@@ -42,7 +43,7 @@ object SafePngMetadataReader {
private val LOG_TAG = LogUtils.createTag()
// arbitrary size to detect chunks that may yield an OOM
- private const val chunkSizeDangerThreshold = SafeXmpReader.SEGMENT_TYPE_SIZE_DANGER_THRESHOLD
+ private const val CHUNK_SIZE_DANGER_THRESHOLD = SafeXmpReader.SEGMENT_TYPE_SIZE_DANGER_THRESHOLD
private val latin1Encoding = Charsets.ISO_8859_1
private val utf8Encoding = Charsets.UTF_8
@@ -85,7 +86,7 @@ object SafePngMetadataReader {
val bytes = chunk.bytes
// TLAD insert start
- if (bytes.size > chunkSizeDangerThreshold) {
+ if (bytes.size > CHUNK_SIZE_DANGER_THRESHOLD) {
Log.w(LOG_TAG, "PNG chunk $chunkType is too large, with a size of ${bytes.size} B")
return
}
@@ -290,11 +291,12 @@ object SafePngMetadataReader {
val second = reader.uInt8.toInt()
val directory = PngDirectory(PngChunkType.tIME)
if (DateUtil.isValidDate(year, month - 1, day) && DateUtil.isValidTime(hour, minute, second)) {
- val dateString = String.format("%04d:%02d:%02d %02d:%02d:%02d", year, month, day, hour, minute, second)
+ val dateString = String.format(Locale.ROOT, "%04d:%02d:%02d %02d:%02d:%02d", year, month, day, hour, minute, second)
directory.setString(PngDirectory.TAG_LAST_MODIFICATION_TIME, dateString)
} else {
directory.addError(
String.format(
+ Locale.ROOT,
"PNG tIME data describes an invalid date/time: year=%d month=%d day=%d hour=%d minute=%d second=%d",
year, month, day, hour, minute, second
)
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafeXmpReader.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafeXmpReader.kt
index e36402907..603e3edba 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafeXmpReader.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafeXmpReader.kt
@@ -16,6 +16,7 @@ import com.drew.metadata.xmp.XmpDirectory
import com.drew.metadata.xmp.XmpReader
import deckers.thibault.aves.utils.LogUtils
import java.io.IOException
+import java.util.Locale
class SafeXmpReader : XmpReader() {
// adapted from `XmpReader` to detect and skip large extended XMP
@@ -133,7 +134,7 @@ class SafeXmpReader : XmpReader() {
System.arraycopy(segmentBytes, totalOffset, extendedXMPBuffer, chunkOffset, segmentLength - totalOffset)
} else {
val directory = XmpDirectory()
- directory.addError(String.format("Inconsistent length for the Extended XMP buffer: %d instead of %d", fullLength, extendedXMPBuffer.size))
+ directory.addError(String.format(Locale.ROOT, "Inconsistent length for the Extended XMP buffer: %d instead of %d", fullLength, extendedXMPBuffer.size))
metadata.addDirectory(directory)
}
}
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/mpf/MpfDirectory.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/mpf/MpfDirectory.kt
index 53904b56f..c0ea08c38 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/mpf/MpfDirectory.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/mpf/MpfDirectory.kt
@@ -16,8 +16,6 @@ class MpfDirectory : Directory() {
return _tagNameMap
}
- fun getNumberOfImages() = getInt(TAG_NUMBER_OF_IMAGES)
-
companion object {
const val TAG_MPF_VERSION = 0xb000
const val TAG_NUMBER_OF_IMAGES = 0xb001
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/AvesEntry.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/AvesEntry.kt
index 424fdb08a..8d41846ab 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/model/AvesEntry.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/AvesEntry.kt
@@ -15,7 +15,7 @@ class AvesEntry(map: FieldMap) {
val trashed = map["trashed"] as Boolean
val trashPath = map["trashPath"] as String?
- val isRotated: Boolean
+ private val isRotated: Boolean
get() = rotationDegrees % 180 == 90
val displayWidth: Int
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt
index 3fbb68b33..d339fda96 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt
@@ -163,7 +163,7 @@ class SourceEntry {
try {
Metadata.openSafeInputStream(context, uri, sourceMimeType, sizeBytes)?.use { input ->
- val metadata = Helper.safeRead(input)
+ val metadata = Helper.safeRead(input, sizeBytes)
// do not switch on specific MIME types, as the reported MIME type could be wrong
// (e.g. PNG registered as JPG)
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt
index cc54bd459..d406f12fd 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt
@@ -1026,7 +1026,7 @@ abstract class ImageProvider {
uri: Uri,
mimeType: String,
dateMillis: Long?,
- shiftMinutes: Long?,
+ shiftSeconds: Long?,
fields: List,
callback: ImageOpCallback,
) {
@@ -1057,9 +1057,9 @@ abstract class ImageProvider {
}
}
- shiftMinutes != null -> {
+ shiftSeconds != null -> {
// shift
- val shiftMillis = shiftMinutes * 60000
+ val shiftMillis = shiftSeconds * 1000
listOf(
ExifInterface.TAG_DATETIME,
ExifInterface.TAG_DATETIME_ORIGINAL,
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/Compat.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/Compat.kt
index 90a9b154f..eea1b08cb 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/Compat.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/Compat.kt
@@ -56,13 +56,7 @@ fun Geocoder.getFromLocationCompat(
onError: (errorCode: String, errorMessage: String?, errorDetails: Any?) -> Unit,
) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
- getFromLocation(latitude, longitude, maxResults, object : Geocoder.GeocodeListener {
- override fun onGeocode(addresses: List) = processAddresses(addresses.filterNotNull())
-
- override fun onError(errorMessage: String?) {
- onError("getAddress-asyncerror", "failed to get address", errorMessage)
- }
- })
+ Compat33.geocoderGetFromLocation(this, latitude, longitude, maxResults, processAddresses, onError)
} else {
try {
@Suppress("deprecation")
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/Compat33.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/Compat33.kt
new file mode 100644
index 000000000..537773205
--- /dev/null
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/Compat33.kt
@@ -0,0 +1,30 @@
+package deckers.thibault.aves.utils
+
+import android.location.Address
+import android.location.Geocoder
+import android.os.Build
+import androidx.annotation.RequiresApi
+
+/**
+ * Compatibility layer in a separate object to avoid class loading issues on older Android versions.
+ * e.g. `ClassNotFoundException` for `android.location.Geocoder$GeocodeListener`
+ */
+@RequiresApi(Build.VERSION_CODES.TIRAMISU)
+object Compat33 {
+ fun geocoderGetFromLocation(
+ geocoder: Geocoder,
+ latitude: Double,
+ longitude: Double,
+ maxResults: Int,
+ processAddresses: (addresses: List) -> Unit,
+ onError: (errorCode: String, errorMessage: String?, errorDetails: Any?) -> Unit,
+ ) {
+ geocoder.getFromLocation(latitude, longitude, maxResults, object : Geocoder.GeocodeListener {
+ override fun onGeocode(addresses: List) = processAddresses(addresses.filterNotNull())
+
+ override fun onError(errorMessage: String?) {
+ onError("getAddress-asyncerror", "failed to get address", errorMessage)
+ }
+ })
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/FlutterUtils.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/FlutterUtils.kt
index d3c56d23c..e7b179259 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/FlutterUtils.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/FlutterUtils.kt
@@ -1,8 +1,6 @@
package deckers.thibault.aves.utils
import android.content.Context
-import android.content.Intent
-import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Log
@@ -71,29 +69,4 @@ object FlutterUtils {
r.run()
}
}
-
- fun Intent.enableSoftwareRendering() {
- putExtra("enable-software-rendering", true)
- Log.i(LOG_TAG, "Enable software rendering")
- }
-
- fun isSoftwareRenderingRequired() = Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT && isEmulator
-
- private val isEmulator: Boolean
- get() = (Build.BRAND.startsWith("generic") && Build.DEVICE.startsWith("generic")
- || Build.FINGERPRINT.startsWith("generic")
- || Build.FINGERPRINT.startsWith("unknown")
- || Build.HARDWARE.contains("goldfish")
- || Build.HARDWARE.contains("ranchu")
- || Build.MODEL.contains("google_sdk")
- || Build.MODEL.contains("Emulator")
- || Build.MODEL.contains("Android SDK built for x86")
- || Build.MANUFACTURER.contains("Genymotion")
- || Build.PRODUCT.contains("sdk_google")
- || Build.PRODUCT.contains("google_sdk")
- || Build.PRODUCT.contains("sdk")
- || Build.PRODUCT.contains("sdk_x86")
- || Build.PRODUCT.contains("vbox86p")
- || Build.PRODUCT.contains("emulator")
- || Build.PRODUCT.contains("simulator"))
}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MathUtils.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MathUtils.kt
new file mode 100644
index 000000000..7002c07f0
--- /dev/null
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MathUtils.kt
@@ -0,0 +1,9 @@
+package deckers.thibault.aves.utils
+
+import kotlin.math.log2
+import kotlin.math.pow
+
+object MathUtils {
+ fun highestPowerOf2(x: Int): Int = highestPowerOf2(x.toDouble())
+ fun highestPowerOf2(x: Double): Int = if (x < 1) 0 else 2.toDouble().pow(log2(x).toInt()).toInt()
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt
index 645fa47a1..1430472b0 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt
@@ -28,7 +28,8 @@ object MimeTypes {
private const val CR2 = "image/x-canon-cr2"
private const val CRW = "image/x-canon-crw"
private const val DCR = "image/x-kodak-dcr"
- const val DNG = "image/x-adobe-dng"
+ const val DNG = "image/dng"
+ const val DNG_ADOBE = "image/x-adobe-dng"
private const val ERF = "image/x-epson-erf"
private const val K25 = "image/x-kodak-k25"
private const val KDC = "image/x-kodak-kdc"
@@ -71,7 +72,7 @@ object MimeTypes {
fun isRaw(mimeType: String): Boolean {
return when (mimeType) {
- ARW, CR2, CRW, DCR, DNG, ERF, K25, KDC, MRW, NEF, NRW, ORF, PEF, RAF, RAW, RW2, SR2, SRF, SRW, X3F -> true
+ ARW, CR2, CRW, DCR, DNG, DNG_ADOBE, ERF, K25, KDC, MRW, NEF, NRW, ORF, PEF, RAF, RAW, RW2, SR2, SRF, SRW, X3F -> true
else -> false
}
}
@@ -142,7 +143,7 @@ object MimeTypes {
return if (pageId != null && MultiPageImage.isSupported(mimeType)) {
true
} else when (mimeType) {
- DNG, HEIC, HEIF, PNG, WEBP -> true
+ DNG, DNG_ADOBE, HEIC, HEIF, PNG, WEBP -> true
else -> false
}
}
@@ -151,7 +152,7 @@ object MimeTypes {
// according to EXIF orientation when decoding images of known formats
// but we need to rotate the decoded bitmap for the other formats
fun needRotationAfterContentResolverThumbnail(mimeType: String) = when (mimeType) {
- DNG, PNG -> true
+ DNG, DNG_ADOBE, PNG -> true
else -> false
}
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/PermissionManager.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/PermissionManager.kt
index 91e5dd4d5..4ae662b5f 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/PermissionManager.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/PermissionManager.kt
@@ -28,7 +28,6 @@ object PermissionManager {
Environment.DIRECTORY_PICTURES,
)
- @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
suspend fun requestDirectoryAccess(activity: Activity, path: String, onGranted: (uri: Uri) -> Unit, onDenied: () -> Unit) {
Log.i(LOG_TAG, "request user to select and grant access permission to path=$path")
@@ -151,7 +150,6 @@ object PermissionManager {
}
}
- @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun revokeDirectoryAccess(context: Context, path: String): Boolean {
return StorageUtils.convertDirPathToTreeDocumentUri(context, path)?.let {
releaseUriPermission(context, it)
@@ -162,11 +160,9 @@ object PermissionManager {
// returns paths matching directory URIs granted by the user
fun getGrantedDirs(context: Context): Set {
val grantedDirs = HashSet()
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- for (uriPermission in context.contentResolver.persistedUriPermissions) {
- val dirPath = StorageUtils.convertTreeDocumentUriToDirPath(context, uriPermission.uri)
- dirPath?.let { grantedDirs.add(it) }
- }
+ for (uriPermission in context.contentResolver.persistedUriPermissions) {
+ val dirPath = StorageUtils.convertTreeDocumentUriToDirPath(context, uriPermission.uri)
+ dirPath?.let { grantedDirs.add(it) }
}
return grantedDirs
}
@@ -216,19 +212,6 @@ object PermissionManager {
)
})
}
- } else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT
- || Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT_WATCH
- ) {
- // removable storage requires access permission, at the file level
- // without directory access, we consider the whole volume restricted
- val primaryVolume = StorageUtils.getPrimaryVolumePath(context)
- val nonPrimaryVolumes = StorageUtils.getVolumePaths(context).filter { it != primaryVolume }
- dirs.addAll(nonPrimaryVolumes.map {
- hashMapOf(
- "volumePath" to it,
- "relativeDir" to "",
- )
- })
}
return dirs
}
@@ -236,7 +219,6 @@ object PermissionManager {
// As of Android 11, `MediaStore.getDocumentUri` fails if any of the persisted
// URI permissions we hold points to a folder that no longer exists,
// so we should remove these obsolete URIs before proceeding.
- @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun sanitizePersistedUriPermissions(context: Context) {
try {
for (uriPermission in context.contentResolver.persistedUriPermissions) {
@@ -252,7 +234,6 @@ object PermissionManager {
}
}
- @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun releaseUriPermission(context: Context, it: Uri) {
val flags = Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
context.contentResolver.releasePersistableUriPermission(it, flags)
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt
index a24cceda8..ff888a95f 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt
@@ -16,7 +16,6 @@ import android.provider.DocumentsContract
import android.provider.MediaStore
import android.text.TextUtils
import android.util.Log
-import androidx.annotation.RequiresApi
import com.commonsware.cwac.document.DocumentFileCompat
import deckers.thibault.aves.model.provider.ImageProvider
import deckers.thibault.aves.utils.FileUtils.transferFrom
@@ -29,7 +28,7 @@ import java.io.FileInputStream
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
-import java.util.*
+import java.util.Locale
import java.util.regex.Pattern
object StorageUtils {
@@ -381,7 +380,6 @@ object StorageUtils {
// e.g.
// /storage/emulated/0/ -> content://com.android.externalstorage.documents/tree/primary%3A
// /storage/10F9-3F13/Pictures/ -> content://com.android.externalstorage.documents/tree/10F9-3F13%3APictures
- @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun convertDirPathToTreeDocumentUri(context: Context, dirPath: String): Uri? {
val uuid = getVolumeUuidForDocumentUri(context, dirPath)
if (uuid != null) {
@@ -446,7 +444,7 @@ object StorageUtils {
fun getDocumentFile(context: Context, anyPath: String, mediaUri: Uri): DocumentFileCompat? {
try {
- if (requireAccessPermission(context, anyPath) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ if (requireAccessPermission(context, anyPath)) {
// need a document URI (not a media content URI) to open a `DocumentFile` output stream
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && isMediaStoreContentUri(mediaUri)) {
// cleanest API to get it
@@ -485,7 +483,7 @@ object StorageUtils {
fun createDirectoryDocIfAbsent(context: Context, dirPath: String): DocumentFileCompat? {
try {
val cleanDirPath = ensureTrailingSeparator(dirPath)
- return if (requireAccessPermission(context, cleanDirPath) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ return if (requireAccessPermission(context, cleanDirPath)) {
val grantedDir = getGrantedDirForPath(context, cleanDirPath) ?: return null
val rootTreeDocumentUri = convertDirPathToTreeDocumentUri(context, grantedDir) ?: return null
var parentFile: DocumentFileCompat? = DocumentFileCompat.fromTreeUri(context, rootTreeDocumentUri) ?: return null
diff --git a/android/app/src/test/kotlin/deckers/thibault/aves/model/provider/ImageProviderTest.kt b/android/app/src/test/kotlin/deckers/thibault/aves/model/provider/ImageProviderTest.kt
index 705503c62..14a126fef 100644
--- a/android/app/src/test/kotlin/deckers/thibault/aves/model/provider/ImageProviderTest.kt
+++ b/android/app/src/test/kotlin/deckers/thibault/aves/model/provider/ImageProviderTest.kt
@@ -10,9 +10,9 @@ class ImageProviderTest {
@Test
fun imageProvider_CorrectEmailSimple_ReturnsTrue() {
val date = LocalDate.of(1990, Month.FEBRUARY, 11).toEpochDay()
- assertEquals(ImageProvider.getTimeZoneString(TimeZone.getTimeZone("Europe/Paris"), date), "+01:00")
- assertEquals(ImageProvider.getTimeZoneString(TimeZone.getTimeZone("UTC"), date), "+00:00")
- assertEquals(ImageProvider.getTimeZoneString(TimeZone.getTimeZone("Asia/Kolkata"), date), "+05:30")
- assertEquals(ImageProvider.getTimeZoneString(TimeZone.getTimeZone("America/Chicago"), date), "-06:00")
+ assertEquals("+01:00", ImageProvider.getTimeZoneString(TimeZone.getTimeZone("Europe/Paris"), date))
+ assertEquals("+00:00", ImageProvider.getTimeZoneString(TimeZone.getTimeZone("UTC"), date))
+ assertEquals("+05:30", ImageProvider.getTimeZoneString(TimeZone.getTimeZone("Asia/Kolkata"), date))
+ assertEquals("-06:00", ImageProvider.getTimeZoneString(TimeZone.getTimeZone("America/Chicago"), date))
}
}
diff --git a/android/app/src/test/kotlin/deckers/thibault/aves/utils/MathUtilsTest.kt b/android/app/src/test/kotlin/deckers/thibault/aves/utils/MathUtilsTest.kt
new file mode 100644
index 000000000..48382cca3
--- /dev/null
+++ b/android/app/src/test/kotlin/deckers/thibault/aves/utils/MathUtilsTest.kt
@@ -0,0 +1,17 @@
+package deckers.thibault.aves.utils
+
+import deckers.thibault.aves.utils.MathUtils.highestPowerOf2
+import org.junit.jupiter.api.Assertions.assertEquals
+import org.junit.jupiter.api.Test
+
+class MathUtilsTest {
+ @Test
+ fun mathUtils_highestPowerOf2() {
+ assertEquals(1024, highestPowerOf2(1024))
+ assertEquals(32, highestPowerOf2(42))
+ assertEquals(0, highestPowerOf2(0))
+ assertEquals(0, highestPowerOf2(-42))
+ assertEquals(0, highestPowerOf2(.5))
+ assertEquals(1, highestPowerOf2(1.5))
+ }
+}
diff --git a/android/build.gradle b/android/build.gradle
index 814e9fc98..87bd1337b 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,6 +1,6 @@
buildscript {
ext {
- agp_version = '8.4.0-rc02' // same as `settings.ext.agp_version` in `/android/settings.gradle`
+ agp_version = '8.4.1' // same as `settings.ext.agp_version` in `/android/settings.gradle`
glide_version = '4.16.0'
// AppGallery Connect plugin versions: https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agc-sdk-changenotes-0000001058732550
huawei_agconnect_version = '1.9.1.300'
@@ -25,7 +25,7 @@ buildscript {
if (useCrashlytics) {
// GMS & Firebase Crashlytics (used by some flavors only)
- classpath 'com.google.gms:google-services:4.4.0'
+ classpath 'com.google.gms:google-services:4.4.1'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.9'
}
diff --git a/android/exifinterface/build.gradle b/android/exifinterface/build.gradle
index 76ba4c97f..88a67339e 100644
--- a/android/exifinterface/build.gradle
+++ b/android/exifinterface/build.gradle
@@ -26,5 +26,5 @@ android {
}
dependencies {
- implementation 'androidx.annotation:annotation:1.7.1'
+ implementation 'androidx.annotation:annotation:1.8.0'
}
\ No newline at end of file
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index 11fce01a1..45181329e 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
diff --git a/android/settings.gradle b/android/settings.gradle
index fe6428dc3..293bc6423 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -8,9 +8,9 @@ pluginManagement {
}
settings.ext.flutterSdkPath = flutterSdkPath()
- settings.ext.kotlin_version = '1.9.21'
- settings.ext.ksp_version = "$kotlin_version-1.0.15"
- settings.ext.agp_version = '8.4.0-rc02'
+ settings.ext.kotlin_version = '1.9.24'
+ settings.ext.ksp_version = "$kotlin_version-1.0.20"
+ settings.ext.agp_version = '8.4.1'
includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
diff --git a/devtools_options.yaml b/devtools_options.yaml
new file mode 100644
index 000000000..fa0b357c4
--- /dev/null
+++ b/devtools_options.yaml
@@ -0,0 +1,3 @@
+description: This file stores settings for Dart & Flutter DevTools.
+documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
+extensions:
diff --git a/fastlane/metadata/android/en-US/changelogs/121.txt b/fastlane/metadata/android/en-US/changelogs/121.txt
new file mode 100644
index 000000000..24d17aed2
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/121.txt
@@ -0,0 +1,3 @@
+In v1.11.2:
+- show selected albums together in Collection
+Full changelog available on GitHub
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/changelogs/12101.txt b/fastlane/metadata/android/en-US/changelogs/12101.txt
new file mode 100644
index 000000000..24d17aed2
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/12101.txt
@@ -0,0 +1,3 @@
+In v1.11.2:
+- show selected albums together in Collection
+Full changelog available on GitHub
\ No newline at end of file
diff --git a/fastlane/metadata/android/fa/full_description.txt b/fastlane/metadata/android/fa/full_description.txt
index 37cba69c7..87c81b673 100644
--- a/fastlane/metadata/android/fa/full_description.txt
+++ b/fastlane/metadata/android/fa/full_description.txt
@@ -2,4 +2,4 @@
پیمایش و جستجویک بخش مهم از اِیوْز است. هدف این است که کاربران به راحتی از آلبوم ها به عکس ها به برچسب ها به نقشه ها و غیره دست پیدا کنند.
-اِیوْز با اندروید سازگار است (از نسخه KitKat تا اندروید 14, شامل تلوزیون اندرویدی) با قابلیت هایی مانند ابزارک ها, میانبر ها, ذخیره نیرو و جستجو عمومی و همچنین میتوان از آن به عنوان نمایشگر و انتخابگز رسانه استفاده کرد.
+اِیوْز با اندروید سازگار است (از نسخه KitKat تا اندروید 14, شامل تلوزیون اندرویدی) با قابلیت هایی مانند ابزارک ها, میانبر ها, ذخیره نیرو و جستجو عمومی و همچنین میتوان از آن به عنوان نمایشگر و انتخابگر رسانه استفاده کرد.
diff --git a/fastlane/metadata/android/fa/images/featureGraphic.png b/fastlane/metadata/android/fa/images/featureGraphic.png
index bfbfdaa7f..7dcf92379 100644
Binary files a/fastlane/metadata/android/fa/images/featureGraphic.png and b/fastlane/metadata/android/fa/images/featureGraphic.png differ
diff --git a/fastlane/metadata/android/fa/images/phoneScreenshots/1.png b/fastlane/metadata/android/fa/images/phoneScreenshots/1.png
index 0e854daa6..69d9359e8 100644
Binary files a/fastlane/metadata/android/fa/images/phoneScreenshots/1.png and b/fastlane/metadata/android/fa/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/fa/images/phoneScreenshots/2.png b/fastlane/metadata/android/fa/images/phoneScreenshots/2.png
index c11fd661b..ab6f62ff6 100644
Binary files a/fastlane/metadata/android/fa/images/phoneScreenshots/2.png and b/fastlane/metadata/android/fa/images/phoneScreenshots/2.png differ
diff --git a/fastlane/metadata/android/fa/images/phoneScreenshots/3.png b/fastlane/metadata/android/fa/images/phoneScreenshots/3.png
index 3a8799c14..ab81876aa 100644
Binary files a/fastlane/metadata/android/fa/images/phoneScreenshots/3.png and b/fastlane/metadata/android/fa/images/phoneScreenshots/3.png differ
diff --git a/fastlane/metadata/android/fa/images/phoneScreenshots/4.png b/fastlane/metadata/android/fa/images/phoneScreenshots/4.png
index eda2a264b..71eb26632 100644
Binary files a/fastlane/metadata/android/fa/images/phoneScreenshots/4.png and b/fastlane/metadata/android/fa/images/phoneScreenshots/4.png differ
diff --git a/fastlane/metadata/android/fa/images/phoneScreenshots/5.png b/fastlane/metadata/android/fa/images/phoneScreenshots/5.png
index 2ff60ebb0..172f0d533 100644
Binary files a/fastlane/metadata/android/fa/images/phoneScreenshots/5.png and b/fastlane/metadata/android/fa/images/phoneScreenshots/5.png differ
diff --git a/fastlane/metadata/android/fa/images/phoneScreenshots/6.png b/fastlane/metadata/android/fa/images/phoneScreenshots/6.png
index 82669ab28..ec1eff3e0 100644
Binary files a/fastlane/metadata/android/fa/images/phoneScreenshots/6.png and b/fastlane/metadata/android/fa/images/phoneScreenshots/6.png differ
diff --git a/fastlane/metadata/android/fa/images/phoneScreenshots/7.png b/fastlane/metadata/android/fa/images/phoneScreenshots/7.png
index 3efaf9736..988e02308 100644
Binary files a/fastlane/metadata/android/fa/images/phoneScreenshots/7.png and b/fastlane/metadata/android/fa/images/phoneScreenshots/7.png differ
diff --git a/lib/app_mode.dart b/lib/app_mode.dart
index 478afd8b9..34b16f9b5 100644
--- a/lib/app_mode.dart
+++ b/lib/app_mode.dart
@@ -1,4 +1,5 @@
enum AppMode {
+ initialization,
main,
pickCollectionFiltersExternal,
pickSingleMediaExternal,
@@ -31,7 +32,10 @@ extension ExtraAppMode on AppMode {
AppMode.pickMultipleMediaExternal,
}.contains(this);
- bool get canSelectFilter => this == AppMode.main;
+ bool get canSelectFilter => {
+ AppMode.main,
+ AppMode.pickCollectionFiltersExternal,
+ }.contains(this);
bool get canCreateFilter => {
AppMode.main,
diff --git a/lib/l10n/app_ar.arb b/lib/l10n/app_ar.arb
index 5774273ee..025a59a6a 100644
--- a/lib/l10n/app_ar.arb
+++ b/lib/l10n/app_ar.arb
@@ -887,7 +887,7 @@
"@placePageTitle": {},
"filterOnThisDayLabel": "في هذا اليوم",
"@filterOnThisDayLabel": {},
- "columnCount": "{count, plural, =1{1 عمود} other{{count} أعمدة}}{count}",
+ "columnCount": "{count, plural, =1{{count} عمود} other{{count} أعمدة}}{count}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -941,7 +941,7 @@
"@albumMimeTypeMixed": {},
"settingsViewerQuickActionEditorAvailableButtonsSectionTitle": "الأزرار المتاحة",
"@settingsViewerQuickActionEditorAvailableButtonsSectionTitle": {},
- "itemCount": "{count, plural, =1{1 عنصر} other{{count} عناصر}}",
+ "itemCount": "{count, plural, =1{{count} عنصر} other{{count} عناصر}}",
"@itemCount": {
"placeholders": {
"count": {}
@@ -1247,7 +1247,7 @@
"@drawerCollectionImages": {},
"sortOrderSmallestFirst": "الأصغر أولاً",
"@sortOrderSmallestFirst": {},
- "timeSeconds": "{seconds, plural, =1{1 ثانية} other{{seconds} ثواني}}",
+ "timeSeconds": "{count, plural, =1{{count} ثانية} other{{count} ثواني}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
@@ -1393,7 +1393,7 @@
"@subtitlePositionBottom": {},
"castDialogTitle": "أجهزة البث",
"@castDialogTitle": {},
- "timeDays": "{days, plural, =1{1 يوم} other{{days} أيام}}",
+ "timeDays": "{count, plural, =1{{count} يوم} other{{count} أيام}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -1425,7 +1425,7 @@
"@newVaultDialogTitle": {},
"entryInfoActionEditRating": "تحرير التقييم",
"@entryInfoActionEditRating": {},
- "timeMinutes": "{minutes, plural, =1{1 دقيقة} other{{minutes} دقائق}}",
+ "timeMinutes": "{count, plural, =1{{count} دقيقة} other{{count} دقائق}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
@@ -1536,5 +1536,7 @@
"settingsForceWesternArabicNumeralsTile": "فرض الأرقام العربية",
"@settingsForceWesternArabicNumeralsTile": {},
"renameProcessorHash": "تجزئة",
- "@renameProcessorHash": {}
+ "@renameProcessorHash": {},
+ "chipActionShowCollection": "عرض في المجموعة",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_be.arb b/lib/l10n/app_be.arb
index 19fb1cc6f..181c2f428 100644
--- a/lib/l10n/app_be.arb
+++ b/lib/l10n/app_be.arb
@@ -7,7 +7,7 @@
"@welcomeOptional": {},
"welcomeMessage": "Сардэчна запрашаем ў Aves",
"@welcomeMessage": {},
- "itemCount": "{count, plural, =1{1 элемент} other{{count} элементаў}}",
+ "itemCount": "{count, plural, =1{{count} элемент} other{{count} элементаў}}",
"@itemCount": {
"placeholders": {
"count": {}
@@ -1495,25 +1495,25 @@
"@settingsViewerShowHistogram": {},
"settingsVideoPlaybackTile": "Прайграванне",
"@settingsVideoPlaybackTile": {},
- "columnCount": "{count, plural, =1{1 слупок} few{{count} слупкі} other{{count} слупкоў}}",
+ "columnCount": "{count, plural, =1{{count} слупок} few{{count} слупкі} other{{count} слупкоў}}",
"@columnCount": {
"placeholders": {
"count": {}
}
},
- "timeSeconds": "{seconds, plural, =1{1 секунда} few{{seconds} секунды} other{{seconds} секунд}}",
+ "timeSeconds": "{count, plural, =1{{count} секунда} few{{count} секунды} other{{count} секунд}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
}
},
- "timeDays": "{days, plural, =1{1 дзень} few{{days} дні} other{{days} дзён}}",
+ "timeDays": "{count, plural, =1{{count} дзень} few{{count} дні} other{{count} дзён}}",
"@timeDays": {
"placeholders": {
"days": {}
}
},
- "timeMinutes": "{minutes, plural, =1{1 хвіліна} few{{minutes} хвіліны} other{{minutes} хвілін}}",
+ "timeMinutes": "{count, plural, =1{{count} хвіліна} few{{count} хвіліны} other{{count} хвілін}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
@@ -1536,5 +1536,7 @@
"renameProcessorHash": "Хэш",
"@renameProcessorHash": {},
"settingsForceWesternArabicNumeralsTile": "Прымусовыя арабскія лічбы",
- "@settingsForceWesternArabicNumeralsTile": {}
+ "@settingsForceWesternArabicNumeralsTile": {},
+ "chipActionShowCollection": "Паказаць ў Калекцыі",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_ca.arb b/lib/l10n/app_ca.arb
index e34d3a686..32677eb23 100644
--- a/lib/l10n/app_ca.arb
+++ b/lib/l10n/app_ca.arb
@@ -7,13 +7,13 @@
"@welcomeOptional": {},
"welcomeTermsToggle": "Estic d’acord amb els termes i condicions",
"@welcomeTermsToggle": {},
- "itemCount": "{count, plural, =1{1 element} other{{count} elements}}",
+ "itemCount": "{count, plural, =1{{count} element} other{{count} elements}}",
"@itemCount": {
"placeholders": {
"count": {}
}
},
- "columnCount": "{count, plural, =1{1 columna} other{{count} columnes}}",
+ "columnCount": "{count, plural, =1{{count} columna} other{{count} columnes}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -479,19 +479,19 @@
"@policyPageTitle": {},
"collectionPageTitle": "Coŀlecció",
"@collectionPageTitle": {},
- "timeSeconds": "{seconds, plural, =1{1 segon} other{{seconds} segons}}",
+ "timeSeconds": "{count, plural, =1{{count} segon} other{{count} segons}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
}
},
- "timeMinutes": "{minutes, plural, =1{1 minut} other{{minutes} minuts}}",
+ "timeMinutes": "{count, plural, =1{{count} minut} other{{count} minuts}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
}
},
- "timeDays": "{days, plural, =1{1 dia} other{{days} dies}}",
+ "timeDays": "{count, plural, =1{{count} dia} other{{count} dies}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -1524,5 +1524,7 @@
"placeholders": {
"count": {}
}
- }
+ },
+ "chipActionShowCollection": "Mostrar a Coŀlecció",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_ckb.arb b/lib/l10n/app_ckb.arb
index 668de373a..5ffd61aa5 100644
--- a/lib/l10n/app_ckb.arb
+++ b/lib/l10n/app_ckb.arb
@@ -4,13 +4,13 @@
"@welcomeOptional": {},
"welcomeTermsToggle": "ڕازیم بە مەرج و یاساکانی بەکارهێنان",
"@welcomeTermsToggle": {},
- "columnCount": "{count, plural, =1{١ ڕیز} other{ڕیز {count}}}",
+ "columnCount": "{count, plural, other{ڕیز {count}}}",
"@columnCount": {
"placeholders": {
"count": {}
}
},
- "timeSeconds": "{seconds, plural, =1{١ چرکە} other{چرکە {seconds}}}",
+ "timeSeconds": "{count, plural, other{چرکە {count}}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
@@ -92,7 +92,7 @@
"@entryActionInfo": {},
"entryActionRestore": "گێڕانەوە",
"@entryActionRestore": {},
- "timeDays": "{days, plural, =1{١ ڕۆژ} other{ڕۆژ {days}}}",
+ "timeDays": "{count, plural, other{ڕۆژ {count}}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -131,7 +131,7 @@
"@videoActionPause": {},
"videoActionPlay": "لێدان",
"@videoActionPlay": {},
- "itemCount": "{count, plural, =1{١ دانە} other{دانە {count}}}",
+ "itemCount": "{count, plural, other{دانە {count}}}",
"@itemCount": {
"placeholders": {
"count": {}
@@ -155,7 +155,7 @@
"@chipActionCreateAlbum": {},
"entryActionRename": "ناوگۆڕین",
"@entryActionRename": {},
- "timeMinutes": "{minutes, plural, =1{١ خولەک} other{خولەک {minutes}}}",
+ "timeMinutes": "{count, plural, other{خولەک {count}}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
diff --git a/lib/l10n/app_cs.arb b/lib/l10n/app_cs.arb
index 42d9c2f31..d8b08617d 100644
--- a/lib/l10n/app_cs.arb
+++ b/lib/l10n/app_cs.arb
@@ -44,19 +44,19 @@
}
}
},
- "timeDays": "{days, plural, =1{1 den} few{{days} dny} other{{days} dnů}}",
+ "timeDays": "{count, plural, =1{{count} den} few{{count} dny} other{{count} dnů}}",
"@timeDays": {
"placeholders": {
"days": {}
}
},
- "itemCount": "{count, plural, =1{1 položka} few{{count} položky} other{{count} položek}}",
+ "itemCount": "{count, plural, =1{{count} položka} few{{count} položky} other{{count} položek}}",
"@itemCount": {
"placeholders": {
"count": {}
}
},
- "columnCount": "{count, plural, =1{1 sloupec} few{{count} sloupce} other{{count} sloupců}}",
+ "columnCount": "{count, plural, =1{{count} sloupec} few{{count} sloupce} other{{count} sloupců}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -1279,13 +1279,13 @@
"@settingsThumbnailShowVideoDuration": {},
"settingsCollectionQuickActionsTile": "Rychlé akce",
"@settingsCollectionQuickActionsTile": {},
- "timeMinutes": "{minutes, plural, =1{1 minuta} few{{minutes} minuty} other{{minutes} minut}}",
+ "timeMinutes": "{count, plural, =1{{count} minuta} few{{count} minuty} other{{count} minut}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
}
},
- "timeSeconds": "{seconds, plural, =1{1 sekunda} few{{seconds} sekundy} other{{seconds} sekund}}",
+ "timeSeconds": "{count, plural, =1{{count} sekunda} few{{count} sekundy} other{{count} sekund}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
@@ -1532,5 +1532,7 @@
"videoRepeatActionSetEnd": "Nastavit konec",
"@videoRepeatActionSetEnd": {},
"videoActionABRepeat": "Opakování A-B",
- "@videoActionABRepeat": {}
+ "@videoActionABRepeat": {},
+ "chipActionShowCollection": "Zobrazit ve sbírce",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb
index 2adf015db..1e4ea37d3 100644
--- a/lib/l10n/app_de.arb
+++ b/lib/l10n/app_de.arb
@@ -7,13 +7,13 @@
"@welcomeOptional": {},
"welcomeTermsToggle": "Ich stimme den Bedingungen und Konditionen zu",
"@welcomeTermsToggle": {},
- "itemCount": "{count, plural, =1{1 Element} other{{count} Elemente}}",
+ "itemCount": "{count, plural, =1{{count} Element} other{{count} Elemente}}",
"@itemCount": {},
- "timeSeconds": "{seconds, plural, =1{1 Sekunde} other{{seconds} Sekunde}}",
+ "timeSeconds": "{count, plural, =1{{count} Sekunde} other{{count} Sekunde}}",
"@timeSeconds": {},
- "timeMinutes": "{minutes, plural, =1{1 Minute} other{{minutes} Minuten}}",
+ "timeMinutes": "{count, plural, =1{{count} Minute} other{{count} Minuten}}",
"@timeMinutes": {},
- "timeDays": "{days, plural, =1{1 Tag} other{{days} Tage}}",
+ "timeDays": "{count, plural, =1{{count} Tag} other{{count} Tage}}",
"@timeDays": {},
"focalLength": "{length} mm",
"@focalLength": {},
@@ -1193,7 +1193,7 @@
"@settingsAccessibilityShowPinchGestureAlternatives": {},
"settingsDisplayUseTvInterface": "Android-TV Oberfläche",
"@settingsDisplayUseTvInterface": {},
- "columnCount": "{count, plural, =1{1 Spalte} other{{count} Spalten}}",
+ "columnCount": "{count, plural, =1{{count} Spalte} other{{count} Spalten}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -1366,5 +1366,19 @@
"entryActionCast": "Übertragen",
"@entryActionCast": {},
"castDialogTitle": "Geräte zur Übertragung",
- "@castDialogTitle": {}
+ "@castDialogTitle": {},
+ "renameProcessorHash": "Raute",
+ "@renameProcessorHash": {},
+ "settingsForceWesternArabicNumeralsTile": "Arabische Ziffern erzwingen",
+ "@settingsForceWesternArabicNumeralsTile": {},
+ "videoActionABRepeat": "A-B-Wiederholung",
+ "@videoActionABRepeat": {},
+ "videoRepeatActionSetStart": "Start festlegen",
+ "@videoRepeatActionSetStart": {},
+ "stopTooltip": "Stop",
+ "@stopTooltip": {},
+ "videoRepeatActionSetEnd": "Ende festlegen",
+ "@videoRepeatActionSetEnd": {},
+ "chipActionShowCollection": "In Sammlung anzeigen",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_el.arb b/lib/l10n/app_el.arb
index 26e285185..664acecf6 100644
--- a/lib/l10n/app_el.arb
+++ b/lib/l10n/app_el.arb
@@ -7,13 +7,13 @@
"@welcomeOptional": {},
"welcomeTermsToggle": "Συμφωνώ με τους όρους και τις προυποθέσεις",
"@welcomeTermsToggle": {},
- "itemCount": "{count, plural, =1{1 στοιχείο} other{{count} στοιχεία}}",
+ "itemCount": "{count, plural, =1{{count} στοιχείο} other{{count} στοιχεία}}",
"@itemCount": {},
- "timeSeconds": "{seconds, plural, =1{1 δευτερόλεπτο} other{{seconds} δευτερόλεπτα}}",
+ "timeSeconds": "{count, plural, =1{{count} δευτερόλεπτο} other{{count} δευτερόλεπτα}}",
"@timeSeconds": {},
- "timeMinutes": "{minutes, plural, =1{1 λεπτό} other{{minutes} λεπτά}}",
+ "timeMinutes": "{count, plural, =1{{count} λεπτό} other{{count} λεπτά}}",
"@timeMinutes": {},
- "timeDays": "{days, plural, =1{1 ημέρα} other{{days} ημέρες}}",
+ "timeDays": "{count, plural, =1{{count} ημέρα} other{{count} ημέρες}}",
"@timeDays": {},
"focalLength": "{length} mm",
"@focalLength": {},
@@ -1169,7 +1169,7 @@
"@settingsSubtitleThemeTextPositionDialogTitle": {},
"entryInfoActionExportMetadata": "Εξαγωγή μεταδεδομένων",
"@entryInfoActionExportMetadata": {},
- "columnCount": "{count, plural, =1{1 στήλη} other{{count} στήλες}}",
+ "columnCount": "{count, plural, =1{{count} στήλη} other{{count} στήλες}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -1338,5 +1338,7 @@
"overlayHistogramRGB": "RGB",
"@overlayHistogramRGB": {},
"overlayHistogramLuminance": "Φωτεινότητα",
- "@overlayHistogramLuminance": {}
+ "@overlayHistogramLuminance": {},
+ "chipActionShowCollection": "Εμφάνιση στη Συλλογή",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb
index d803fb63e..4ceb286db 100644
--- a/lib/l10n/app_en.arb
+++ b/lib/l10n/app_en.arb
@@ -3,35 +3,45 @@
"welcomeMessage": "Welcome to Aves",
"welcomeOptional": "Optional",
"welcomeTermsToggle": "I agree to the terms and conditions",
- "itemCount": "{count, plural, =1{1 item} other{{count} items}}",
+ "itemCount": "{count, plural, =1{{count} item} other{{count} items}}",
"@itemCount": {
"placeholders": {
- "count": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
- "columnCount": "{count, plural, =1{1 column} other{{count} columns}}",
+ "columnCount": "{count, plural, =1{{count} column} other{{count} columns}}",
"@columnCount": {
"placeholders": {
- "count": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
- "timeSeconds": "{seconds, plural, =1{1 second} other{{seconds} seconds}}",
+ "timeSeconds": "{count, plural, =1{{count} second} other{{count} seconds}}",
"@timeSeconds": {
"placeholders": {
- "seconds": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
- "timeMinutes": "{minutes, plural, =1{1 minute} other{{minutes} minutes}}",
+ "timeMinutes": "{count, plural, =1{{count} minute} other{{count} minutes}}",
"@timeMinutes": {
"placeholders": {
- "minutes": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
- "timeDays": "{days, plural, =1{1 day} other{{days} days}}",
+ "timeDays": "{count, plural, =1{{count} day} other{{count} days}}",
"@timeDays": {
"placeholders": {
- "days": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
"focalLength": "{length} mm",
@@ -75,6 +85,7 @@
"sourceStateLocatingPlaces": "Locating places",
"chipActionDelete": "Delete",
+ "chipActionShowCollection": "Show in Collection",
"chipActionGoToAlbumPage": "Show in Albums",
"chipActionGoToCountryPage": "Show in Countries",
"chipActionGoToPlacePage": "Show in Places",
@@ -376,13 +387,17 @@
"binEntriesConfirmationDialogMessage": "{count, plural, =1{Move this item to the recycle bin?} other{Move these {count} items to the recycle bin?}}",
"@binEntriesConfirmationDialogMessage": {
"placeholders": {
- "count": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
"deleteEntriesConfirmationDialogMessage": "{count, plural, =1{Delete this item?} other{Delete these {count} items?}}",
"@deleteEntriesConfirmationDialogMessage": {
"placeholders": {
- "count": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
"moveUndatedConfirmationDialogMessage": "Save item dates before proceeding?",
@@ -446,13 +461,17 @@
"deleteSingleAlbumConfirmationDialogMessage": "{count, plural, =1{Delete this album and the item in it?} other{Delete this album and the {count} items in it?}}",
"@deleteSingleAlbumConfirmationDialogMessage": {
"placeholders": {
- "count": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
"deleteMultiAlbumConfirmationDialogMessage": "{count, plural, =1{Delete these albums and the item in them?} other{Delete these albums and the {count} items in them?}}",
"@deleteMultiAlbumConfirmationDialogMessage": {
"placeholders": {
- "count": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
@@ -597,61 +616,81 @@
"collectionDeleteFailureFeedback": "{count, plural, =1{Failed to delete 1 item} other{Failed to delete {count} items}}",
"@collectionDeleteFailureFeedback": {
"placeholders": {
- "count": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
"collectionCopyFailureFeedback": "{count, plural, =1{Failed to copy 1 item} other{Failed to copy {count} items}}",
"@collectionCopyFailureFeedback": {
"placeholders": {
- "count": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
"collectionMoveFailureFeedback": "{count, plural, =1{Failed to move 1 item} other{Failed to move {count} items}}",
"@collectionMoveFailureFeedback": {
"placeholders": {
- "count": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
"collectionRenameFailureFeedback": "{count, plural, =1{Failed to rename 1 item} other{Failed to rename {count} items}}",
"@collectionRenameFailureFeedback": {
"placeholders": {
- "count": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
"collectionEditFailureFeedback": "{count, plural, =1{Failed to edit 1 item} other{Failed to edit {count} items}}",
"@collectionEditFailureFeedback": {
"placeholders": {
- "count": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
"collectionExportFailureFeedback": "{count, plural, =1{Failed to export 1 page} other{Failed to export {count} pages}}",
"@collectionExportFailureFeedback": {
"placeholders": {
- "count": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
"collectionCopySuccessFeedback": "{count, plural, =1{Copied 1 item} other{Copied {count} items}}",
"@collectionCopySuccessFeedback": {
"placeholders": {
- "count": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
"collectionMoveSuccessFeedback": "{count, plural, =1{Moved 1 item} other{Moved {count} items}}",
"@collectionMoveSuccessFeedback": {
"placeholders": {
- "count": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
"collectionRenameSuccessFeedback": "{count, plural, =1{Renamed 1 item} other{Renamed {count} items}}",
"@collectionRenameSuccessFeedback": {
"placeholders": {
- "count": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
"collectionEditSuccessFeedback": "{count, plural, =1{Edited 1 item} other{Edited {count} items}}",
"@collectionEditSuccessFeedback": {
"placeholders": {
- "count": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
@@ -949,7 +988,9 @@
"statsWithGps": "{count, plural, =1{1 item with location} other{{count} items with location}}",
"@statsWithGps": {
"placeholders": {
- "count": {}
+ "count": {
+ "format": "decimalPattern"
+ }
}
},
"statsTopCountriesSectionTitle": "Top Countries",
diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb
index 249ee65da..94a36e8d2 100644
--- a/lib/l10n/app_es.arb
+++ b/lib/l10n/app_es.arb
@@ -7,13 +7,13 @@
"@welcomeOptional": {},
"welcomeTermsToggle": "Acepto los términos y condiciones",
"@welcomeTermsToggle": {},
- "itemCount": "{count, plural, =1{1 elemento} other{{count} elementos}}",
+ "itemCount": "{count, plural, =1{{count} elemento} other{{count} elementos}}",
"@itemCount": {},
- "timeSeconds": "{seconds, plural, =1{1 segundo} other{{seconds} segundos}}",
+ "timeSeconds": "{count, plural, =1{{count} segundo} other{{count} segundos}}",
"@timeSeconds": {},
- "timeMinutes": "{minutes, plural, =1{1 minuto} other{{minutes} minutos}}",
+ "timeMinutes": "{count, plural, =1{{count} minuto} other{{count} minutos}}",
"@timeMinutes": {},
- "timeDays": "{days, plural, =1{1 día} other{{days} días}}",
+ "timeDays": "{count, plural, =1{{count} día} other{{count} días}}",
"@timeDays": {},
"focalLength": "{length} mm",
"@focalLength": {},
@@ -1187,7 +1187,7 @@
"@keepScreenOnVideoPlayback": {},
"settingsAccessibilityShowPinchGestureAlternatives": "Mostrar alternativas a gestos multitáctiles",
"@settingsAccessibilityShowPinchGestureAlternatives": {},
- "columnCount": "{count, plural, =1{1 columna} other{{count} columnas}}",
+ "columnCount": "{count, plural, =1{{count} columna} other{{count} columnas}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -1378,5 +1378,7 @@
"settingsForceWesternArabicNumeralsTile": "Forzar números arábigos",
"@settingsForceWesternArabicNumeralsTile": {},
"renameProcessorHash": "Hash",
- "@renameProcessorHash": {}
+ "@renameProcessorHash": {},
+ "chipActionShowCollection": "Mostrar en Colección",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_eu.arb b/lib/l10n/app_eu.arb
index ac714af6b..6b823d6a6 100644
--- a/lib/l10n/app_eu.arb
+++ b/lib/l10n/app_eu.arb
@@ -1,25 +1,25 @@
{
"saveTooltip": "Gorde",
"@saveTooltip": {},
- "columnCount": "{count, plural, =1{zutabe 1} other{{count} zutabe}}",
+ "columnCount": "{count, plural, =1{zutabe {count}} other{{count} zutabe}}",
"@columnCount": {
"placeholders": {
"count": {}
}
},
- "timeSeconds": "{seconds, plural, =1{segundu 1} other{{seconds} segundu}}",
+ "timeSeconds": "{count, plural, =1{segundu {count}} other{{count} segundu}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
}
},
- "timeMinutes": "{minutes, plural, =1{minutu 1} other{{minutes} minutu}}",
+ "timeMinutes": "{count, plural, =1{minutu {count}} other{{count} minutu}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
}
},
- "timeDays": "{days, plural, =1{egun 1} other{{days} egun}}",
+ "timeDays": "{count, plural, =1{egun {count}} other{{count} egun}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -50,7 +50,7 @@
"@welcomeMessage": {},
"welcomeOptional": "Aukerazkoa",
"@welcomeOptional": {},
- "itemCount": "{count, plural, =1{elementu 1} other{{count} elementu}}",
+ "itemCount": "{count, plural, =1{elementu {count}} other{{count} elementu}}",
"@itemCount": {
"placeholders": {
"count": {}
@@ -1536,5 +1536,7 @@
"videoActionABRepeat": "Atik Brako errepikapena",
"@videoActionABRepeat": {},
"videoRepeatActionSetEnd": "Ezarri amaiera",
- "@videoRepeatActionSetEnd": {}
+ "@videoRepeatActionSetEnd": {},
+ "chipActionShowCollection": "Erakutsi bilduman",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_fa.arb b/lib/l10n/app_fa.arb
index 15870aa9d..e82cac816 100644
--- a/lib/l10n/app_fa.arb
+++ b/lib/l10n/app_fa.arb
@@ -65,9 +65,9 @@
"@hideTooltip": {},
"chipActionCreateAlbum": "ایجاد البوم",
"@chipActionCreateAlbum": {},
- "filterNoRatingLabel": "بدون رتبه",
+ "filterNoRatingLabel": "بدون امتیاز",
"@filterNoRatingLabel": {},
- "timeSeconds": "{seconds, plural, =1{1 ثانیع} other{{seconds} ثانیه}}",
+ "timeSeconds": "{count, plural, =1{{count} ثانیع} other{{count} ثانیه}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
@@ -75,7 +75,7 @@
},
"deleteButtonLabel": "پاک کردن",
"@deleteButtonLabel": {},
- "itemCount": "{count, plural, =1{1 فایل} other{{count} فایل}}",
+ "itemCount": "{count, plural, =1{{count} فایل} other{{count} فایل}}",
"@itemCount": {
"placeholders": {
"count": {}
@@ -99,7 +99,7 @@
"@doNotAskAgain": {},
"entryActionDelete": "پاککردن",
"@entryActionDelete": {},
- "timeMinutes": "{minutes, plural, =1{1 دقیقه} other{{minutes} دقیقه}}",
+ "timeMinutes": "{count, plural, other{{count} دقیقه}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
@@ -123,7 +123,7 @@
"@entryActionRotateCW": {},
"entryInfoActionEditLocation": "ویرایش مکان",
"@entryInfoActionEditLocation": {},
- "entryActionFlip": "ایینه کردن افقی",
+ "entryActionFlip": "آینه کردن افقی",
"@entryActionFlip": {},
"entryActionShare": "اشتراک گذاری",
"@entryActionShare": {},
@@ -141,7 +141,7 @@
"@filterBinLabel": {},
"filterNoDateLabel": "بدون تاریخ",
"@filterNoDateLabel": {},
- "timeDays": "{days, plural, =1{1 روز} other{{days} روز}}",
+ "timeDays": "{count, plural, other{{count} روز}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -163,7 +163,7 @@
"@videoActionSkip10": {},
"viewerActionSettings": "تنظیمات",
"@viewerActionSettings": {},
- "entryInfoActionEditRating": "ویرایش رتبه",
+ "entryInfoActionEditRating": "ویرایش امتیاز",
"@entryInfoActionEditRating": {},
"entryInfoActionEditTags": "ویرایش برچسب ها",
"@entryInfoActionEditTags": {},
@@ -333,7 +333,7 @@
"@videoPlaybackMuted": {},
"storageVolumeDescriptionFallbackPrimary": "حافظه داخلی",
"@storageVolumeDescriptionFallbackPrimary": {},
- "columnCount": "{count, plural, =1{1 ستون} other{{count} ستون}}",
+ "columnCount": "{count, plural, other{{count} ستون}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -363,7 +363,7 @@
"@nameConflictStrategyReplace": {},
"displayRefreshRatePreferHighest": "بیشترین مقدار",
"@displayRefreshRatePreferHighest": {},
- "storageAccessDialogMessage": "لطفا شاخهٔ {directory} در «{volume}» را در صفحه بعد انتخاب کنید و اجازه را به برنامه بدهید.",
+ "storageAccessDialogMessage": "لطفا شاخهٔ {directory} در «{volume}» را در صفحه بعد انتخاب کنید و به برنامه اجازه بدهید.",
"@storageAccessDialogMessage": {
"placeholders": {
"directory": {
@@ -571,9 +571,9 @@
"@setCoverDialogCustom": {},
"nameConflictDialogSingleSourceMessage": "برخی از پرونده های موجود در پوشه مقصد به همین نام هستند.",
"@nameConflictDialogSingleSourceMessage": {},
- "missingSystemFilePickerDialogMessage": "انتخابگر پرونده سیستم وجود ندارد یا غیرفعال است. لطفا آن را فعال کنید و دوباره امتحان کنید",
+ "missingSystemFilePickerDialogMessage": "انتخابگر پرونده سامانه وجود ندارد یا غیرفعال است. لطفا آن را فعال کنید و دوباره امتحان کنید.",
"@missingSystemFilePickerDialogMessage": {},
- "nameConflictDialogMultipleSourceMessage": "برخی پرونده ها نام های یکسانی دارند",
+ "nameConflictDialogMultipleSourceMessage": "برخی پرونده ها نام های یکسانی دارند.",
"@nameConflictDialogMultipleSourceMessage": {},
"noMatchingAppDialogMessage": "هیچ کاره ای وجود ندارد که بتواند این موضوع را مدیریت کند.",
"@noMatchingAppDialogMessage": {},
@@ -605,7 +605,7 @@
"@drawerAlbumPage": {},
"drawerTagPage": "برچسب ها",
"@drawerTagPage": {},
- "sortByRating": "با رتبهبندی",
+ "sortByRating": "با امتیازبندی",
"@sortByRating": {},
"sortByAlbumFileName": "با آلبوم و نام پرونده",
"@sortByAlbumFileName": {},
@@ -621,9 +621,9 @@
"@settingsViewerQuickActionEditorAvailableButtonsSectionTitle": {},
"settingsViewerQuickActionEmpty": "بدون دکمه",
"@settingsViewerQuickActionEmpty": {},
- "settingsSlideshowVideoPlaybackTile": "بازپخش ویدیو",
+ "settingsSlideshowVideoPlaybackTile": "پخش ویدیو",
"@settingsSlideshowVideoPlaybackTile": {},
- "settingsSlideshowVideoPlaybackDialogTitle": "بازپخش ویدیو",
+ "settingsSlideshowVideoPlaybackDialogTitle": "پخش ویدیو",
"@settingsSlideshowVideoPlaybackDialogTitle": {},
"settingsVideoResumptionModeTile": "ادامه پخش",
"@settingsVideoResumptionModeTile": {},
@@ -639,9 +639,9 @@
"@viewerInfoSearchSuggestionDate": {},
"settingsSlideshowShuffle": "در هم",
"@settingsSlideshowShuffle": {},
- "settingsVideoPlaybackTile": "بازپخش",
+ "settingsVideoPlaybackTile": "پخش",
"@settingsVideoPlaybackTile": {},
- "settingsVideoPlaybackPageTitle": "بازپخش",
+ "settingsVideoPlaybackPageTitle": "پخش",
"@settingsVideoPlaybackPageTitle": {},
"viewerInfoLabelResolution": "وضوح",
"@viewerInfoLabelResolution": {},
@@ -668,7 +668,7 @@
"@settingsViewerSlideshowPageTitle": {},
"viewerInfoLabelUri": "URI",
"@viewerInfoLabelUri": {},
- "moveUndatedConfirmationDialogSetDate": "ذخیره تاریخ ها",
+ "moveUndatedConfirmationDialogSetDate": "نگهداری تاریخ ها",
"@moveUndatedConfirmationDialogSetDate": {},
"editEntryLocationDialogLongitude": "طول جغرافیایی",
"@editEntryLocationDialogLongitude": {},
@@ -865,7 +865,7 @@
},
"settingsConfirmationBeforeMoveToBinItems": "پیش از هدایت موارد به سطل زباله بپرسید",
"@settingsConfirmationBeforeMoveToBinItems": {},
- "pinDialogEnter": "وارد کردن شماره",
+ "pinDialogEnter": "وارد کردن PIN",
"@pinDialogEnter": {},
"collectionGroupDay": "با روز",
"@collectionGroupDay": {},
@@ -881,7 +881,7 @@
"@filePickerNoItems": {},
"settingsHomeDialogTitle": "خانه",
"@settingsHomeDialogTitle": {},
- "settingsThumbnailShowRating": "نمایش رتبهبندی",
+ "settingsThumbnailShowRating": "نمایش امتیازبندی",
"@settingsThumbnailShowRating": {},
"settingsThumbnailShowTagIcon": "نمایش نماد برچسب",
"@settingsThumbnailShowTagIcon": {},
@@ -1067,7 +1067,7 @@
"@drawerPlacePage": {},
"sortOrderNewestFirst": "اول جدیدترین",
"@sortOrderNewestFirst": {},
- "albumGroupTier": "با رتبه",
+ "albumGroupTier": "با امتیاز",
"@albumGroupTier": {},
"sortOrderZtoA": "ی تا ا",
"@sortOrderZtoA": {},
@@ -1129,7 +1129,7 @@
"@settingsThumbnailShowHdrIcon": {},
"settingsCollectionQuickActionTabBrowsing": "مرور کردن",
"@settingsCollectionQuickActionTabBrowsing": {},
- "settingsCollectionQuickActionEditorPageTitle": "کنش سریع",
+ "settingsCollectionQuickActionEditorPageTitle": "کنش های سریع",
"@settingsCollectionQuickActionEditorPageTitle": {},
"settingsCollectionQuickActionTabSelecting": "انتخاب کردن",
"@settingsCollectionQuickActionTabSelecting": {},
@@ -1153,7 +1153,7 @@
"@settingsSlideshowTransitionTile": {},
"settingsSlideshowIntervalTile": "فاصله",
"@settingsSlideshowIntervalTile": {},
- "settingsVideoEnableHardwareAcceleration": "شتاب سختافزاری",
+ "settingsVideoEnableHardwareAcceleration": "شتاب سختافزاری",
"@settingsVideoEnableHardwareAcceleration": {},
"settingsVideoAutoPlay": "پخش خودکار",
"@settingsVideoAutoPlay": {},
@@ -1163,7 +1163,7 @@
"@settingsVideoControlsPageTitle": {},
"settingsVideoBackgroundModeDialogTitle": "حالت پسزمینه",
"@settingsVideoBackgroundModeDialogTitle": {},
- "settingsVideoGestureDoubleTapTogglePlay": "برای پخش/ایست دوبار ضربه ردن",
+ "settingsVideoGestureDoubleTapTogglePlay": "برای پخش/ایست دوبار ضربه زدن",
"@settingsVideoGestureDoubleTapTogglePlay": {},
"settingsVideoGestureSideDoubleTapSeek": "روی لبه های صفحه دوبار ضربه بزنید تا به عقب/جلو بروید",
"@settingsVideoGestureSideDoubleTapSeek": {},
@@ -1249,7 +1249,7 @@
"@renameEntrySetPageInsertTooltip": {},
"renameEntrySetPageTitle": "تغییرنام",
"@renameEntrySetPageTitle": {},
- "videoSpeedDialogLabel": "سرعت پازپخش",
+ "videoSpeedDialogLabel": "سرعت پخش",
"@videoSpeedDialogLabel": {},
"collectionActionAddShortcut": "افزودن میانبر",
"@collectionActionAddShortcut": {},
@@ -1293,7 +1293,7 @@
"@settingsCollectionBurstPatternsTile": {},
"settingsCollectionBurstPatternsNone": "هیچکدام",
"@settingsCollectionBurstPatternsNone": {},
- "settingsCollectionQuickActionsTile": "کنش سریع",
+ "settingsCollectionQuickActionsTile": "کنش های سریع",
"@settingsCollectionQuickActionsTile": {},
"settingsUnitSystemTile": "واحد ها",
"@settingsUnitSystemTile": {},
@@ -1315,9 +1315,9 @@
"@settingsViewerSectionTitle": {},
"settingsViewerShowInformationSubtitle": "نمایش عنوان، تاریخ، مکان، و...",
"@settingsViewerShowInformationSubtitle": {},
- "settingsViewerQuickActionsTile": "کنش های سزیع",
+ "settingsViewerQuickActionsTile": "کنش های سریع",
"@settingsViewerQuickActionsTile": {},
- "settingsViewerQuickActionEditorPageTitle": "کنش های سزیع",
+ "settingsViewerQuickActionEditorPageTitle": "کنش های سریع",
"@settingsViewerQuickActionEditorPageTitle": {},
"settingsViewerQuickActionEditorBanner": "لمس کنید و نگه دارید تا دکمه ها را حرکت دهید و انتخاب کنید که کدام کنش ها در بیننده نمایش داده می شود.",
"@settingsViewerQuickActionEditorBanner": {},
@@ -1440,5 +1440,103 @@
"settingsDisplayRefreshRateModeTile": "نمایش نرخ تازهسازی",
"@settingsDisplayRefreshRateModeTile": {},
"settingsSubtitleThemeShowOutline": "نمایش نوار حاشیه و سایه",
- "@settingsSubtitleThemeShowOutline": {}
+ "@settingsSubtitleThemeShowOutline": {},
+ "collectionCopySuccessFeedback": "{count, plural, =1{۱ مورد رونوشت شد} other{{count} مورد رونوشت شد}}",
+ "@collectionCopySuccessFeedback": {
+ "placeholders": {
+ "count": {}
+ }
+ },
+ "statsWithGps": "{count, plural, =1{1 مورد دارای مکان} other{{count} مورد دارای مکان}}",
+ "@statsWithGps": {
+ "placeholders": {
+ "count": {}
+ }
+ },
+ "editEntryDateDialogShift": "جابهجایی",
+ "@editEntryDateDialogShift": {},
+ "collectionDeleteFailureFeedback": "{count, plural, =1{پاککردن ۱ مورد ناموفق بود} other{پاککردن {count} مورد ناموفق بود}}",
+ "@collectionDeleteFailureFeedback": {
+ "placeholders": {
+ "count": {}
+ }
+ },
+ "settingsDisablingBinWarningDialogMessage": "موارد موجود در سطل زباله برای همیشه نابود خواهند شد.",
+ "@settingsDisablingBinWarningDialogMessage": {},
+ "settingsAllowInstalledAppAccessSubtitle": "برای بهبود نمایش آلبوم بهرهوری میشود",
+ "@settingsAllowInstalledAppAccessSubtitle": {},
+ "settingsHiddenPathsBanner": "تصاویر و ویدیوهای موجود در این پوشهها یا هر یک از زیرپوشههای آنها در مجموعه شما دیده نمیشوند.",
+ "@settingsHiddenPathsBanner": {},
+ "settingsStorageAccessBanner": "برخی پوشه ها برای اصلاح پرونپه های موجود در آنها به یک مجوز دسترسی نیاز دارند. در اینجا می توانید پوشه هایی را که قبلاً به آنها دسترسی داشته اید، مرور کنید.",
+ "@settingsStorageAccessBanner": {},
+ "collectionMoveFailureFeedback": "{count, plural, =1{هدایت ۱ مورد ناموفق بود} other{هدایت {count} مورد ناموفق بود}}",
+ "@collectionMoveFailureFeedback": {
+ "placeholders": {
+ "count": {}
+ }
+ },
+ "collectionRenameFailureFeedback": "{count, plural, =1{تغییرنام ۱ مورد ناموفق بود} other{تغییرنام {count} مورد ناموفق بود}}",
+ "@collectionRenameFailureFeedback": {
+ "placeholders": {
+ "count": {}
+ }
+ },
+ "aboutCreditsWorldAtlas1": "این برنامه یک پرونده TopoJSON استفاده میکند از",
+ "@aboutCreditsWorldAtlas1": {},
+ "collectionCopyFailureFeedback": "{count, plural, =1{رونوشت ۱ مورد ناموفق بود} other{رونوشت {count} مورد ناموفق بود}}",
+ "@collectionCopyFailureFeedback": {
+ "placeholders": {
+ "count": {}
+ }
+ },
+ "collectionMoveSuccessFeedback": "{count, plural, =1{۱ مورد هدایت شد} other{{count} مورد هدایت شد}}",
+ "@collectionMoveSuccessFeedback": {
+ "placeholders": {
+ "count": {}
+ }
+ },
+ "collectionRenameSuccessFeedback": "{count, plural, =1{۱ مورد تغییرنام یافت} other{{count} مورد تغییرنام یافت}}",
+ "@collectionRenameSuccessFeedback": {
+ "placeholders": {
+ "count": {}
+ }
+ },
+ "collectionEditSuccessFeedback": "{count, plural, =1{۱ مورد ویرایش شد} other{{count} مورد ویرایش شد}}",
+ "@collectionEditSuccessFeedback": {
+ "placeholders": {
+ "count": {}
+ }
+ },
+ "settingsViewerUseCutout": "ناحیه برش را بکار بگیر",
+ "@settingsViewerUseCutout": {},
+ "settingsViewerShowHistogram": "نمایش نمودار",
+ "@settingsViewerShowHistogram": {},
+ "settingsVideoGestureVerticalDragBrightnessVolume": "برای تنظیم نور/صدا به بالا و پایین بکشید",
+ "@settingsVideoGestureVerticalDragBrightnessVolume": {},
+ "mapEmptyRegion": "تصویری در این ناحیه وجود ندارد",
+ "@mapEmptyRegion": {},
+ "mapAttributionOsmHot": "دادههای نقشه © [OpenStreetMap](https:www.openstreetmap.orgcopyright) مشارکتکنندگان • کاشی ها توسط [HOT](https:www.hotosm.org) • میزبانی شده به دست [OSM France](https:openstreetmap.fr)",
+ "@mapAttributionOsmHot": {},
+ "mapAttributionStamen": "دادههای نقشه © [OpenStreetMap](https:www.openstreetmap.org/copyright) مشارکتکنندگان • کاشی ها بدست [Stamen Design](https:stamen.com)، [CC BY 3.0](https:creativecommons.orglicensesby3.0)",
+ "@mapAttributionStamen": {},
+ "addShortcutButtonLabel": "افزودن",
+ "@addShortcutButtonLabel": {},
+ "aboutBugReportInstruction": "در گیتهاب با گزارش ها و ریزگان دستگاه گزارش کنید",
+ "@aboutBugReportInstruction": {},
+ "settingsViewerShowShootingDetails": "نمایش ریزگان تصویربرداری",
+ "@settingsViewerShowShootingDetails": {},
+ "settingsHiddenFiltersBanner": "تصاویر و ویدیوهایی که با پالایشهای مخفی سازگار نیستند در مجموعه شما دیده نمیشوند.",
+ "@settingsHiddenFiltersBanner": {},
+ "moveUndatedConfirmationDialogMessage": "پیش از ادامه دادن تاریخ موارد نگهداری شود؟",
+ "@moveUndatedConfirmationDialogMessage": {},
+ "settingsAllowInstalledAppAccess": "اجازه دسترسی به دارایی برنامه",
+ "@settingsAllowInstalledAppAccess": {},
+ "collectionEditFailureFeedback": "{count, plural, =1{ویرایش ۱ مورد ناموفق بود} other{ویرایش {count} مورد ناموفق بود}}",
+ "@collectionEditFailureFeedback": {
+ "placeholders": {
+ "count": {}
+ }
+ },
+ "chipActionShowCollection": "نمایش در مجموعه",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_fi.arb b/lib/l10n/app_fi.arb
index ca50038ca..f36e81669 100644
--- a/lib/l10n/app_fi.arb
+++ b/lib/l10n/app_fi.arb
@@ -137,7 +137,7 @@
"@viewerActionSettings": {},
"filterOnThisDayLabel": "Tänä päivänä",
"@filterOnThisDayLabel": {},
- "columnCount": "{count, plural, =1{1 sarake} other{{count} saraketta}}",
+ "columnCount": "{count, plural, =1{{count} sarake} other{{count} saraketta}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -149,7 +149,7 @@
"@maxBrightnessNever": {},
"videoActionReplay10": "kelaa taaksepäin 10 sekuntia",
"@videoActionReplay10": {},
- "itemCount": "{count, plural, =1{1 kohde} other{{count} kohdetta}}",
+ "itemCount": "{count, plural, =1{{count} kohde} other{{count} kohdetta}}",
"@itemCount": {
"placeholders": {
"count": {}
@@ -231,7 +231,7 @@
"@keepScreenOnNever": {},
"mapStyleOsmHot": "Humanitaarinen OSM",
"@mapStyleOsmHot": {},
- "timeSeconds": "{seconds, plural, =1{1 sekunti} other{{seconds} sekuntia}}",
+ "timeSeconds": "{count, plural, =1{{count} sekunti} other{{count} sekuntia}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
@@ -265,7 +265,7 @@
"@entryActionRotateCW": {},
"entryActionFlip": "Käännä vaakasuorassa",
"@entryActionFlip": {},
- "timeDays": "{days, plural, =1{1 Päivä} other{{days} Päivää}}",
+ "timeDays": "{count, plural, =1{{count} Päivä} other{{count} Päivää}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -279,7 +279,7 @@
"@entryActionEdit": {},
"entryInfoActionEditRating": "Muokkaa luokitusta",
"@entryInfoActionEditRating": {},
- "timeMinutes": "{minutes, plural, =1{1 minuutti} other{{minutes} minuuttia}}",
+ "timeMinutes": "{count, plural, =1{{count} minuutti} other{{count} minuuttia}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
@@ -300,5 +300,7 @@
"entryActionShareImageOnly": "Jaa vain kuva",
"@entryActionShareImageOnly": {},
"filterNoDateLabel": "Päiväämätön",
- "@filterNoDateLabel": {}
+ "@filterNoDateLabel": {},
+ "chipActionShowCollection": "Näytä kokoelmassa",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb
index 3dc8fece3..f0e39d841 100644
--- a/lib/l10n/app_fr.arb
+++ b/lib/l10n/app_fr.arb
@@ -7,13 +7,13 @@
"@welcomeOptional": {},
"welcomeTermsToggle": "J’accepte les conditions d’utilisation",
"@welcomeTermsToggle": {},
- "itemCount": "{count, plural, =1{1 élément} other{{count} éléments}}",
+ "itemCount": "{count, plural, =1{{count} élément} other{{count} éléments}}",
"@itemCount": {},
- "timeSeconds": "{seconds, plural, =0{0 seconde} =1{1 seconde} other{{seconds} secondes}}",
+ "timeSeconds": "{count, plural, =0{{count} seconde} =1{{count} seconde} other{{count} secondes}}",
"@timeSeconds": {},
- "timeMinutes": "{minutes, plural, =0{0 minute} =1{1 minute} other{{minutes} minutes}}",
+ "timeMinutes": "{count, plural, =0{{count} minute} =1{{count} minute} other{{count} minutes}}",
"@timeMinutes": {},
- "timeDays": "{days, plural, =0{0 jour} =1{1 jour} other{{days} jours}}",
+ "timeDays": "{count, plural, =0{{count} jour} =1{{count} jour} other{{count} jours}}",
"@timeDays": {},
"focalLength": "{length} mm",
"@focalLength": {},
@@ -1185,7 +1185,7 @@
"@entryInfoActionRemoveLocation": {},
"keepScreenOnVideoPlayback": "Pendant la lecture de vidéos",
"@keepScreenOnVideoPlayback": {},
- "columnCount": "{count, plural, =1{1 colonne} other{{count} colonnes}}",
+ "columnCount": "{count, plural, =1{{count} colonne} other{{count} colonnes}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -1378,5 +1378,7 @@
"renameProcessorHash": "Hash",
"@renameProcessorHash": {},
"settingsForceWesternArabicNumeralsTile": "Toujours utiliser les chiffres arabes",
- "@settingsForceWesternArabicNumeralsTile": {}
+ "@settingsForceWesternArabicNumeralsTile": {},
+ "chipActionShowCollection": "Afficher dans Collection",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_gl.arb b/lib/l10n/app_gl.arb
index 814720b1a..966b77f1e 100644
--- a/lib/l10n/app_gl.arb
+++ b/lib/l10n/app_gl.arb
@@ -177,25 +177,25 @@
"@appName": {},
"welcomeMessage": "Benvido a Aves",
"@welcomeMessage": {},
- "itemCount": "{count, plural, =1{1 elementos} other{{count} elementos}}",
+ "itemCount": "{count, plural, =1{{count} elementos} other{{count} elementos}}",
"@itemCount": {
"placeholders": {
"count": {}
}
},
- "timeSeconds": "{seconds, plural, =1{1 segundo} other{{seconds} segundos}}",
+ "timeSeconds": "{count, plural, =1{{count} segundo} other{{count} segundos}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
}
},
- "timeMinutes": "{minutes, plural, =1{1 minuto} other{{minutes} minutos}}",
+ "timeMinutes": "{count, plural, =1{{count} minuto} other{{count} minutos}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
}
},
- "timeDays": "{days, plural, =1{1 día} other{{days} días}}",
+ "timeDays": "{count, plural, =1{{count} día} other{{count} días}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -296,5 +296,7 @@
"mapStyleHuaweiTerrain": "Petal Maps (Terreo)",
"@mapStyleHuaweiTerrain": {},
"mapStyleOsmHot": "Humanitarian OpenStreetMap Team",
- "@mapStyleOsmHot": {}
+ "@mapStyleOsmHot": {},
+ "chipActionShowCollection": "Mostrar na colección",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_hi.arb b/lib/l10n/app_hi.arb
index 6e72e81ab..8d12567fa 100644
--- a/lib/l10n/app_hi.arb
+++ b/lib/l10n/app_hi.arb
@@ -3,19 +3,19 @@
"@welcomeOptional": {},
"welcomeTermsToggle": "मैं नियमों और शर्तों पर सहमत हुं",
"@welcomeTermsToggle": {},
- "columnCount": "{count, plural, =1{१ कॉलम} other{{count} कॉलम}}",
+ "columnCount": "{count, plural, other{{count} कॉलम}}",
"@columnCount": {
"placeholders": {
"count": {}
}
},
- "timeSeconds": "{seconds, plural, =1{ १ सेकंड} other{{seconds} सेकंडस}}",
+ "timeSeconds": "{count, plural, =1{{count} सेकंड} other{{count} सेकंडस}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
}
},
- "timeDays": "{days, plural, =1{ १ दिन} other{{days} दिन}}",
+ "timeDays": "{count, plural, other{{count} दिन}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -35,7 +35,7 @@
"@clearTooltip": {},
"actionRemove": "हटाएं",
"@actionRemove": {},
- "itemCount": "{count, plural, =1{१ चीज} other{{count} चीजे}}",
+ "itemCount": "{count, plural, =1{{count} चीज} other{{count} चीजे}}",
"@itemCount": {
"placeholders": {
"count": {}
@@ -43,7 +43,7 @@
},
"deleteButtonLabel": "डिलीट",
"@deleteButtonLabel": {},
- "timeMinutes": "{minutes, plural, =1{ १ मिनट} other{{minutes} मिनट}}",
+ "timeMinutes": "{count, plural, other{{count} मिनट}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
diff --git a/lib/l10n/app_hu.arb b/lib/l10n/app_hu.arb
index eb73d4c11..173481f0e 100644
--- a/lib/l10n/app_hu.arb
+++ b/lib/l10n/app_hu.arb
@@ -811,7 +811,7 @@
"@widgetOpenPageCollection": {},
"renameEntrySetPageInsertTooltip": "Mező beszúrása",
"@renameEntrySetPageInsertTooltip": {},
- "timeDays": "{days, plural, other{{days} nap}}",
+ "timeDays": "{count, plural, other{{count} nap}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -829,13 +829,13 @@
"count": {}
}
},
- "timeSeconds": "{seconds, plural, other{{seconds} másodperc}}",
+ "timeSeconds": "{count, plural, other{{count} másodperc}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
}
},
- "timeMinutes": "{minutes, plural, other{{minutes} perc}}",
+ "timeMinutes": "{count, plural, other{{count} perc}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
@@ -1532,5 +1532,7 @@
"videoActionABRepeat": "A-B ismétlés",
"@videoActionABRepeat": {},
"videoRepeatActionSetEnd": "Végpont beállítása",
- "@videoRepeatActionSetEnd": {}
+ "@videoRepeatActionSetEnd": {},
+ "chipActionShowCollection": "Megjelenítés a gyűjteményekben",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb
index 8ef064d2e..648a7a706 100644
--- a/lib/l10n/app_id.arb
+++ b/lib/l10n/app_id.arb
@@ -9,11 +9,11 @@
"@welcomeTermsToggle": {},
"itemCount": "{count, plural, other{{count} benda}}",
"@itemCount": {},
- "timeSeconds": "{seconds, plural, other{{seconds} detik}}",
+ "timeSeconds": "{count, plural, other{{count} detik}}",
"@timeSeconds": {},
- "timeMinutes": "{minutes, plural, other{{minutes} menit}}",
+ "timeMinutes": "{count, plural, other{{count} menit}}",
"@timeMinutes": {},
- "timeDays": "{days, plural, other{{days} hari}}",
+ "timeDays": "{count, plural, other{{count} hari}}",
"@timeDays": {},
"focalLength": "{length} mm",
"@focalLength": {},
@@ -1378,5 +1378,7 @@
"settingsForceWesternArabicNumeralsTile": "Paksa angka Arab",
"@settingsForceWesternArabicNumeralsTile": {},
"renameProcessorHash": "Hash",
- "@renameProcessorHash": {}
+ "@renameProcessorHash": {},
+ "chipActionShowCollection": "Tampilkan di Koleksi",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_is.arb b/lib/l10n/app_is.arb
index 15ece4e21..71c37c23c 100644
--- a/lib/l10n/app_is.arb
+++ b/lib/l10n/app_is.arb
@@ -782,7 +782,7 @@
"@placePageTitle": {},
"filterOnThisDayLabel": "Á þessum degi",
"@filterOnThisDayLabel": {},
- "columnCount": "{count, plural, =1{1 dálkur} other{{count} dálkar}}",
+ "columnCount": "{count, plural, =1{{count} dálkur} other{{count} dálkar}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -840,7 +840,7 @@
"@albumMimeTypeMixed": {},
"settingsViewerQuickActionEditorAvailableButtonsSectionTitle": "Tiltækir hnappar",
"@settingsViewerQuickActionEditorAvailableButtonsSectionTitle": {},
- "itemCount": "{count, plural, =1{1 atriði} other{{count} atriðum}}",
+ "itemCount": "{count, plural, =1{{count} atriði} other{{count} atriðum}}",
"@itemCount": {
"placeholders": {
"count": {}
@@ -1136,7 +1136,7 @@
"@drawerCollectionImages": {},
"sortOrderSmallestFirst": "Minnsta fyrst",
"@sortOrderSmallestFirst": {},
- "timeSeconds": "{seconds, plural, =1{1 sekúnda} other{{seconds} sekúndur}}",
+ "timeSeconds": "{count, plural, =1{{count} sekúnda} other{{count} sekúndur}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
@@ -1292,7 +1292,7 @@
"@subtitlePositionBottom": {},
"castDialogTitle": "Útvörpunartæki",
"@castDialogTitle": {},
- "timeDays": "{days, plural, =1{1 dagur} other{{days} dagar}}",
+ "timeDays": "{count, plural, =1{{count} dagur} other{{count} dagar}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -1322,7 +1322,7 @@
"@entryActionEdit": {},
"entryInfoActionEditRating": "Breyta einkunn",
"@entryInfoActionEditRating": {},
- "timeMinutes": "{minutes, plural, =1{1 mínúta} other{{minutes} mínútur}}",
+ "timeMinutes": "{count, plural, =1{{count} mínúta} other{{count} mínútur}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
@@ -1536,5 +1536,7 @@
"videoActionABRepeat": "Endurtekning A-B",
"@videoActionABRepeat": {},
"videoRepeatActionSetEnd": "Stilla endi",
- "@videoRepeatActionSetEnd": {}
+ "@videoRepeatActionSetEnd": {},
+ "chipActionShowCollection": "Sýna í safni",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb
index 7157352ab..1d98c5c2d 100644
--- a/lib/l10n/app_it.arb
+++ b/lib/l10n/app_it.arb
@@ -7,13 +7,13 @@
"@welcomeOptional": {},
"welcomeTermsToggle": "Accetto i termini e le condizioni",
"@welcomeTermsToggle": {},
- "itemCount": "{count, plural, =1{1 elemento} other{{count} elementi}}",
+ "itemCount": "{count, plural, =1{{count} elemento} other{{count} elementi}}",
"@itemCount": {},
- "timeSeconds": "{seconds, plural, =1{1 secondo} other{{seconds} secondi}}",
+ "timeSeconds": "{count, plural, =1{{count} secondo} other{{count} secondi}}",
"@timeSeconds": {},
- "timeMinutes": "{minutes, plural, =1{1 minuto} other{{minutes} minuti}}",
+ "timeMinutes": "{count, plural, =1{{count} minuto} other{{count} minuti}}",
"@timeMinutes": {},
- "timeDays": "{days, plural, =1{1 giorno} other{{days} giorni}}",
+ "timeDays": "{count, plural, =1{{count} giorno} other{{count} giorni}}",
"@timeDays": {},
"focalLength": "{length} mm",
"@focalLength": {},
@@ -1171,7 +1171,7 @@
"@settingsWidgetDisplayedItem": {},
"settingsViewerShowRatingTags": "Mostra valutazione & etichette",
"@settingsViewerShowRatingTags": {},
- "columnCount": "{count, plural, =1{1 colonna} other{{count} colonne}}",
+ "columnCount": "{count, plural, =1{{count} colonna} other{{count} colonne}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -1374,5 +1374,11 @@
"collectionActionSetHome": "Imposta come pagina iniziale",
"@collectionActionSetHome": {},
"setHomeCustomCollection": "Collezione personalizzata",
- "@setHomeCustomCollection": {}
+ "@setHomeCustomCollection": {},
+ "chipActionShowCollection": "Mostra nella Collezione",
+ "@chipActionShowCollection": {},
+ "renameProcessorHash": "Hash",
+ "@renameProcessorHash": {},
+ "settingsForceWesternArabicNumeralsTile": "Forza numeri arabi",
+ "@settingsForceWesternArabicNumeralsTile": {}
}
diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb
index 717587375..cff859e99 100644
--- a/lib/l10n/app_ja.arb
+++ b/lib/l10n/app_ja.arb
@@ -9,11 +9,11 @@
"@welcomeTermsToggle": {},
"itemCount": "{count, plural, other{{count} 件のアイテム}}",
"@itemCount": {},
- "timeSeconds": "{seconds, plural, other{{seconds} 秒}}",
+ "timeSeconds": "{count, plural, other{{count} 秒}}",
"@timeSeconds": {},
- "timeMinutes": "{minutes, plural, other{{minutes} 分}}",
+ "timeMinutes": "{count, plural, other{{count} 分}}",
"@timeMinutes": {},
- "timeDays": "{days, plural, other{{days} 日}}",
+ "timeDays": "{count, plural, other{{count} 日}}",
"@timeDays": {},
"focalLength": "{length} mm",
"@focalLength": {},
@@ -1264,5 +1264,117 @@
"settingsVideoBackgroundModeDialogTitle": "バックグラウンド再生",
"@settingsVideoBackgroundModeDialogTitle": {},
"maxBrightnessAlways": "常時有効",
- "@maxBrightnessAlways": {}
+ "@maxBrightnessAlways": {},
+ "chipActionShowCollection": "コレクションで表示",
+ "@chipActionShowCollection": {},
+ "aboutDataUsageData": "データ",
+ "@aboutDataUsageData": {},
+ "searchStatesSectionTitle": "地域",
+ "@searchStatesSectionTitle": {},
+ "settingsViewerShowDescription": "説明を表示",
+ "@settingsViewerShowDescription": {},
+ "chipActionConfigureVault": "保管庫を設定",
+ "@chipActionConfigureVault": {},
+ "videoActionABRepeat": "A-B リピート",
+ "@videoActionABRepeat": {},
+ "videoRepeatActionSetStart": "開始点を設定",
+ "@videoRepeatActionSetStart": {},
+ "videoRepeatActionSetEnd": "終点を設定",
+ "@videoRepeatActionSetEnd": {},
+ "cropAspectRatioSquare": "正方形",
+ "@cropAspectRatioSquare": {},
+ "filterTaggedLabel": "タグ付き",
+ "@filterTaggedLabel": {},
+ "albumTierVaults": "保管庫",
+ "@albumTierVaults": {},
+ "overlayHistogramRGB": "RGB",
+ "@overlayHistogramRGB": {},
+ "subtitlePositionBottom": "下",
+ "@subtitlePositionBottom": {},
+ "statsTopStatesSectionTitle": "トップ地域",
+ "@statsTopStatesSectionTitle": {},
+ "applyTooltip": "適用する",
+ "@applyTooltip": {},
+ "viewerActionUnlock": "ビューをアンロック",
+ "@viewerActionUnlock": {},
+ "editorActionTransform": "変換",
+ "@editorActionTransform": {},
+ "settingsVideoPlaybackPageTitle": "再生",
+ "@settingsVideoPlaybackPageTitle": {},
+ "editorTransformCrop": "切り取り",
+ "@editorTransformCrop": {},
+ "exportEntryDialogWriteMetadata": "メタデータを書き込む",
+ "@exportEntryDialogWriteMetadata": {},
+ "videoResumptionModeNever": "無効",
+ "@videoResumptionModeNever": {},
+ "aboutDataUsageSectionTitle": "データ使用量",
+ "@aboutDataUsageSectionTitle": {},
+ "videoResumptionModeAlways": "常にオン",
+ "@videoResumptionModeAlways": {},
+ "settingsCollectionBurstPatternsTile": "バーストパターン",
+ "@settingsCollectionBurstPatternsTile": {},
+ "settingsDisplayUseTvInterface": "Android TV インターフェイス",
+ "@settingsDisplayUseTvInterface": {},
+ "tagEditorDiscardDialogMessage": "変更を破棄しますか?",
+ "@tagEditorDiscardDialogMessage": {},
+ "widgetTapUpdateWidget": "ウィジェットを更新",
+ "@widgetTapUpdateWidget": {},
+ "vaultBinUsageDialogMessage": "ゴミ箱を使用している保管庫があります。",
+ "@vaultBinUsageDialogMessage": {},
+ "settingsConfirmationVaultDataLoss": "保管庫のデータ損失警告を表示する",
+ "@settingsConfirmationVaultDataLoss": {},
+ "settingsWidgetDisplayedItem": "表示アイテム",
+ "@settingsWidgetDisplayedItem": {},
+ "renameProcessorHash": "ハッシュ",
+ "@renameProcessorHash": {},
+ "exportEntryDialogQuality": "画質",
+ "@exportEntryDialogQuality": {},
+ "castDialogTitle": "キャストデバイス",
+ "@castDialogTitle": {},
+ "aboutDataUsageCache": "キャッシュ",
+ "@aboutDataUsageCache": {},
+ "aboutDataUsageMisc": "その他",
+ "@aboutDataUsageMisc": {},
+ "aboutDataUsageInternal": "内部ストレージ",
+ "@aboutDataUsageInternal": {},
+ "aboutDataUsageDatabase": "データベース",
+ "@aboutDataUsageDatabase": {},
+ "aboutDataUsageExternal": "外部ストレージ",
+ "@aboutDataUsageExternal": {},
+ "collectionActionSetHome": "ホームに設定",
+ "@collectionActionSetHome": {},
+ "placeEmpty": "場所なし",
+ "@placeEmpty": {},
+ "settingsThumbnailShowHdrIcon": "HDRアイコンを表示",
+ "@settingsThumbnailShowHdrIcon": {},
+ "settingsCollectionBurstPatternsNone": "なし",
+ "@settingsCollectionBurstPatternsNone": {},
+ "settingsVideoPlaybackTile": "再生",
+ "@settingsVideoPlaybackTile": {},
+ "settingsDisablingBinWarningDialogMessage": "ごみ箱の中のアイテムは永久に削除されます。",
+ "@settingsDisablingBinWarningDialogMessage": {},
+ "settingsForceWesternArabicNumeralsTile": "アラビア数字を強制する",
+ "@settingsForceWesternArabicNumeralsTile": {},
+ "overlayHistogramNone": "なし",
+ "@overlayHistogramNone": {},
+ "overlayHistogramLuminance": "明るさ",
+ "@overlayHistogramLuminance": {},
+ "settingsModificationWarningDialogMessage": "他の設定は変更されます。",
+ "@settingsModificationWarningDialogMessage": {},
+ "setHomeCustomCollection": "カスタムコレクション",
+ "@setHomeCustomCollection": {},
+ "settingsAccessibilityShowPinchGestureAlternatives": "マルチタッチジェスチャーの選択肢を表示する",
+ "@settingsAccessibilityShowPinchGestureAlternatives": {},
+ "chipActionCreateVault": "保管庫を作成",
+ "@chipActionCreateVault": {},
+ "aboutDataUsageClearCache": "キャッシュを削除",
+ "@aboutDataUsageClearCache": {},
+ "viewerActionLock": "ビューをロック",
+ "@viewerActionLock": {},
+ "cropAspectRatioFree": "フリー",
+ "@cropAspectRatioFree": {},
+ "cropAspectRatioOriginal": "オリジナル",
+ "@cropAspectRatioOriginal": {},
+ "stopTooltip": "停止",
+ "@stopTooltip": {}
}
diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb
index db3489eb0..5705a155d 100644
--- a/lib/l10n/app_ko.arb
+++ b/lib/l10n/app_ko.arb
@@ -9,11 +9,11 @@
"@welcomeTermsToggle": {},
"itemCount": "{count, plural, other{{count}개}}",
"@itemCount": {},
- "timeSeconds": "{seconds, plural, other{{seconds}초}}",
+ "timeSeconds": "{count, plural, other{{count}초}}",
"@timeSeconds": {},
- "timeMinutes": "{minutes, plural, other{{minutes}분}}",
+ "timeMinutes": "{count, plural, other{{count}분}}",
"@timeMinutes": {},
- "timeDays": "{days, plural, other{{days}일}}",
+ "timeDays": "{count, plural, other{{count}일}}",
"@timeDays": {},
"focalLength": "{length} mm",
"@focalLength": {},
@@ -1378,5 +1378,7 @@
"renameProcessorHash": "해시",
"@renameProcessorHash": {},
"settingsForceWesternArabicNumeralsTile": "아라비아 숫자 항상 사용",
- "@settingsForceWesternArabicNumeralsTile": {}
+ "@settingsForceWesternArabicNumeralsTile": {},
+ "chipActionShowCollection": "미디어 페이지에서 보기",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_lt.arb b/lib/l10n/app_lt.arb
index 05a009605..6d53fa15f 100644
--- a/lib/l10n/app_lt.arb
+++ b/lib/l10n/app_lt.arb
@@ -13,13 +13,13 @@
"@welcomeOptional": {},
"welcomeTermsToggle": "Sutinku su taisyklėmis ir sąlygomis",
"@welcomeTermsToggle": {},
- "timeSeconds": "{seconds, plural, =1{1 sekundė} few{{seconds} sekundės} other{{seconds} sekundžių}}",
+ "timeSeconds": "{count, plural, =1{{count} sekundė} few{{count} sekundės} other{{count} sekundžių}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
}
},
- "timeDays": "{days, plural, =1{1 diena} few{{days} dienos} other{{days} dienų}}",
+ "timeDays": "{count, plural, =1{{count} diena} few{{count} dienos} other{{count} dienų}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -285,13 +285,13 @@
"@collectionActionEdit": {},
"welcomeMessage": "Sveiki atvykę į Aves",
"@welcomeMessage": {},
- "itemCount": "{count, plural, =1{1 elementas} few{{count} elementai} other{{count} elementų}}",
+ "itemCount": "{count, plural, =1{{count} elementas} few{{count} elementai} other{{count} elementų}}",
"@itemCount": {
"placeholders": {
"count": {}
}
},
- "timeMinutes": "{minutes, plural, =1{1 minutė} few{{minutes} minutės} other{{minutes} minučių}}",
+ "timeMinutes": "{count, plural, =1{{count} minutė} few{{count} minutės} other{{count} minučių}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
@@ -1340,5 +1340,7 @@
"entryActionShareImageOnly": "Bendrinti tik paveikslėlį",
"@entryActionShareImageOnly": {},
"entryInfoActionRemoveLocation": "Pašalinti vietą",
- "@entryInfoActionRemoveLocation": {}
+ "@entryInfoActionRemoveLocation": {},
+ "chipActionShowCollection": "Rodyti kolekcijoje",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_my.arb b/lib/l10n/app_my.arb
index f32afc13a..27d367996 100644
--- a/lib/l10n/app_my.arb
+++ b/lib/l10n/app_my.arb
@@ -23,19 +23,19 @@
"count": {}
}
},
- "timeSeconds": "{seconds, plural, other{{seconds} စက္ကန့်}}",
+ "timeSeconds": "{count, plural, other{{count} စက္ကန့်}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
}
},
- "timeMinutes": "{minutes, plural, other{{minutes} မိနစ်}}",
+ "timeMinutes": "{count, plural, other{{count} မိနစ်}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
}
},
- "timeDays": "{days, plural, other{{days} ရက်}}",
+ "timeDays": "{count, plural, other{{count} ရက်}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -1296,5 +1296,7 @@
"settingsViewerShowHistogram": "Histogram ကို ပြရန်",
"@settingsViewerShowHistogram": {},
"settingsVideoPlaybackTile": "ဖွင့်ကြည့်ခြင်း",
- "@settingsVideoPlaybackTile": {}
+ "@settingsVideoPlaybackTile": {},
+ "chipActionShowCollection": "စုစည်းမှုထဲမှာ ပြရန်",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_nb.arb b/lib/l10n/app_nb.arb
index 81eac5dc0..2951ed1ac 100644
--- a/lib/l10n/app_nb.arb
+++ b/lib/l10n/app_nb.arb
@@ -751,25 +751,25 @@
"@settingsConfirmationTile": {},
"settingsThumbnailSectionTitle": "Miniatyrbilder",
"@settingsThumbnailSectionTitle": {},
- "timeMinutes": "{minutes, plural, =1{1 minutt} other{{minutes} minutter}}",
+ "timeMinutes": "{count, plural, =1{{count} minutt} other{{count} minutter}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
}
},
- "timeDays": "{days, plural, =1{1 dag} other{{days} dager}}",
+ "timeDays": "{count, plural, =1{{count} dag} other{{count} dager}}",
"@timeDays": {
"placeholders": {
"days": {}
}
},
- "itemCount": "{count, plural, =1{1 element} other{{count} elementer}}",
+ "itemCount": "{count, plural, =1{{count} element} other{{count} elementer}}",
"@itemCount": {
"placeholders": {
"count": {}
}
},
- "timeSeconds": "{seconds, plural, =1{1 sekund} other{{seconds} sekunder}}",
+ "timeSeconds": "{count, plural, =1{{count} sekund} other{{count} sekunder}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
@@ -1343,7 +1343,7 @@
"@settingsDisplayUseTvInterface": {},
"settingsAccessibilityShowPinchGestureAlternatives": "Vis multi-trykkhåndvendingsalternativer",
"@settingsAccessibilityShowPinchGestureAlternatives": {},
- "columnCount": "{count, plural, =1{1 kolonne} other{{count} kolonner}}",
+ "columnCount": "{count, plural, =1{{count} kolonne} other{{count} kolonner}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -1478,5 +1478,7 @@
"searchStatesSectionTitle": "Tilstander",
"@searchStatesSectionTitle": {},
"vaultLockTypePattern": "Mønster",
- "@vaultLockTypePattern": {}
+ "@vaultLockTypePattern": {},
+ "chipActionShowCollection": "Vis i samling",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb
index 2721728f3..3c24fed18 100644
--- a/lib/l10n/app_nl.arb
+++ b/lib/l10n/app_nl.arb
@@ -7,13 +7,13 @@
"@welcomeOptional": {},
"welcomeTermsToggle": "Ik ga akkoord met de voorwaarden",
"@welcomeTermsToggle": {},
- "itemCount": "{count, plural, =1{1 item} other{{count} items}}",
+ "itemCount": "{count, plural, =1{{count} item} other{{count} items}}",
"@itemCount": {},
- "timeSeconds": "{seconds, plural, =1{1 seconde} other{{seconds} seconden}}",
+ "timeSeconds": "{count, plural, =1{{count} seconde} other{{count} seconden}}",
"@timeSeconds": {},
- "timeMinutes": "{minutes, plural, =1{1 minuut} other{{minutes} minuten}}",
+ "timeMinutes": "{count, plural, =1{{count} minuut} other{{count} minuten}}",
"@timeMinutes": {},
- "timeDays": "{days, plural, =1{1 dag} other{{days} dagen}}",
+ "timeDays": "{count, plural, =1{{count} dag} other{{count} dagen}}",
"@timeDays": {},
"focalLength": "{length} mm",
"@focalLength": {},
@@ -387,7 +387,7 @@
"@renameProcessorName": {},
"deleteSingleAlbumConfirmationDialogMessage": "{count, plural, =1{Verwijder dit album en het item binnen dit album?} other{Verwijder dit album en de {count} items binnen dit album?}}",
"@deleteSingleAlbumConfirmationDialogMessage": {},
- "deleteMultiAlbumConfirmationDialogMessage": "{count, plural, =1{Verwijder deze albums en het item binnen deze albums?} other{Verwijder dit album en de {count} items binnen deze albums?}}",
+ "deleteMultiAlbumConfirmationDialogMessage": "{count, plural, =1{Verwijder deze albums en het item binnen deze albums?} other{Verwijder deze albums en de {count} items binnen deze albums?}}",
"@deleteMultiAlbumConfirmationDialogMessage": {},
"exportEntryDialogFormat": "Formaat:",
"@exportEntryDialogFormat": {},
@@ -1190,5 +1190,91 @@
"applyTooltip": "Toepassen",
"@applyTooltip": {},
"tagPlaceholderState": "Staat",
- "@tagPlaceholderState": {}
+ "@tagPlaceholderState": {},
+ "stopTooltip": "Stop",
+ "@stopTooltip": {},
+ "chipActionLock": "Vergrendel",
+ "@chipActionLock": {},
+ "chipActionShowCountryStates": "Status laten xien",
+ "@chipActionShowCountryStates": {},
+ "chipActionGoToPlacePage": "Laat zien in plaatsen",
+ "@chipActionGoToPlacePage": {},
+ "subtitlePositionTop": "Boven",
+ "@subtitlePositionTop": {},
+ "subtitlePositionBottom": "Onder",
+ "@subtitlePositionBottom": {},
+ "settingsThumbnailShowHdrIcon": "HDR icoon zichtbaar",
+ "@settingsThumbnailShowHdrIcon": {},
+ "editorTransformCrop": "Bijsnijden",
+ "@editorTransformCrop": {},
+ "patternDialogConfirm": "Bevestig patroon",
+ "@patternDialogConfirm": {},
+ "pinDialogEnter": "Voer PIN in",
+ "@pinDialogEnter": {},
+ "settingsAskEverytime": "Vraag elke keer",
+ "@settingsAskEverytime": {},
+ "aboutDataUsageExternal": "Extern",
+ "@aboutDataUsageExternal": {},
+ "tooManyItemsErrorDialogMessage": "Probeer opnieuw met minder items.",
+ "@tooManyItemsErrorDialogMessage": {},
+ "maxBrightnessAlways": "Altijd",
+ "@maxBrightnessAlways": {},
+ "patternDialogEnter": "Voer patroon in",
+ "@patternDialogEnter": {},
+ "settingsViewerShowDescription": "Laat beschrijving zien",
+ "@settingsViewerShowDescription": {},
+ "exportEntryDialogQuality": "Kwaliteit",
+ "@exportEntryDialogQuality": {},
+ "aboutDataUsageDatabase": "Database",
+ "@aboutDataUsageDatabase": {},
+ "aboutDataUsageMisc": "Overig",
+ "@aboutDataUsageMisc": {},
+ "settingsModificationWarningDialogMessage": "Andere instellingen zullen worden aangepast.",
+ "@settingsModificationWarningDialogMessage": {},
+ "vaultDialogLockModeWhenScreenOff": "Vergrendel als scherm uitgaat",
+ "@vaultDialogLockModeWhenScreenOff": {},
+ "aboutDataUsageData": "Data",
+ "@aboutDataUsageData": {},
+ "aboutDataUsageCache": "Cache",
+ "@aboutDataUsageCache": {},
+ "aboutDataUsageInternal": "Intern",
+ "@aboutDataUsageInternal": {},
+ "overlayHistogramRGB": "RGB",
+ "@overlayHistogramRGB": {},
+ "overlayHistogramLuminance": "Helderheid",
+ "@overlayHistogramLuminance": {},
+ "videoResumptionModeNever": "Nooit",
+ "@videoResumptionModeNever": {},
+ "pinDialogConfirm": "Bevestig PIN",
+ "@pinDialogConfirm": {},
+ "passwordDialogEnter": "Voer wachtwoord in",
+ "@passwordDialogEnter": {},
+ "passwordDialogConfirm": "Bevestig wachtwoord",
+ "@passwordDialogConfirm": {},
+ "settingsViewerShowHistogram": "Laat histogram zien",
+ "@settingsViewerShowHistogram": {},
+ "settingsVideoGestureVerticalDragBrightnessVolume": "Veeg omhoog of naar beneden om helderheid/volume aan te passen",
+ "@settingsVideoGestureVerticalDragBrightnessVolume": {},
+ "settingsSubtitleThemeTextPositionTile": "Tekstpositie",
+ "@settingsSubtitleThemeTextPositionTile": {},
+ "settingsSubtitleThemeTextPositionDialogTitle": "Tekstpositie",
+ "@settingsSubtitleThemeTextPositionDialogTitle": {},
+ "vaultLockTypePattern": "Patroon",
+ "@vaultLockTypePattern": {},
+ "entryActionShareImageOnly": "Enkel afbeelding delen",
+ "@entryActionShareImageOnly": {},
+ "entryActionShareVideoOnly": "Enkel video delen",
+ "@entryActionShareVideoOnly": {},
+ "cropAspectRatioOriginal": "Origineel",
+ "@cropAspectRatioOriginal": {},
+ "cropAspectRatioSquare": "Vierkant",
+ "@cropAspectRatioSquare": {},
+ "maxBrightnessNever": "Nooit",
+ "@maxBrightnessNever": {},
+ "videoResumptionModeAlways": "Altijd",
+ "@videoResumptionModeAlways": {},
+ "exportEntryDialogWriteMetadata": "Schrijf metadata",
+ "@exportEntryDialogWriteMetadata": {},
+ "chipActionShowCollection": "Tonen in Collectie",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_nn.arb b/lib/l10n/app_nn.arb
index 8a8db7aa4..9f74b6576 100644
--- a/lib/l10n/app_nn.arb
+++ b/lib/l10n/app_nn.arb
@@ -1,5 +1,5 @@
{
- "timeSeconds": "{seconds, plural, other{{seconds} sekund}}",
+ "timeSeconds": "{count, plural, other{{count} sekund}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
@@ -9,19 +9,19 @@
"@welcomeOptional": {},
"welcomeTermsToggle": "Eg samtykkjer til krava og føresetnadane",
"@welcomeTermsToggle": {},
- "itemCount": "{count, plural, =1{1 er vald} other{{count} er valde}}",
+ "itemCount": "{count, plural, =1{{count} er vald} other{{count} er valde}}",
"@itemCount": {
"placeholders": {
"count": {}
}
},
- "timeMinutes": "{minutes, plural, other{{minutes} minutt}}",
+ "timeMinutes": "{count, plural, other{{count} minutt}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
}
},
- "timeDays": "{days, plural, =1{1 dag} other{{days} dagar}}",
+ "timeDays": "{count, plural, =1{{count} dag} other{{count} dagar}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -1421,7 +1421,7 @@
"@mapAttributionOsmHot": {},
"mapAttributionStamen": "Kartdata © [OpenStreetMap](https://www.openstreetmap.org/copyright)-medverkarar • Fliser av [Stamen Design](https://stamen.com), [CC BY 3.0](https://creativecommons.org/licenses/by/3.0)",
"@mapAttributionStamen": {},
- "columnCount": "{count, plural, =1{1 kolonne} other{{count} kolonnar}}",
+ "columnCount": "{count, plural, =1{{count} kolonne} other{{count} kolonnar}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -1444,5 +1444,7 @@
"filterNoLocationLabel": "Ustadsette",
"@filterNoLocationLabel": {},
"settingsNavigationSectionTitle": "Finn fram",
- "@settingsNavigationSectionTitle": {}
+ "@settingsNavigationSectionTitle": {},
+ "chipActionShowCollection": "Vis i Samling",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb
index 1f898ac01..5ab5253b2 100644
--- a/lib/l10n/app_pl.arb
+++ b/lib/l10n/app_pl.arb
@@ -233,31 +233,31 @@
"@displayRefreshRatePreferLowest": {},
"videoPlaybackMuted": "Odtwarzaj bez dźwięku",
"@videoPlaybackMuted": {},
- "itemCount": "{count, plural, =1{1 element} few{{count} elementy} other{{count} elelmentów}}",
+ "itemCount": "{count, plural, =1{{count} element} few{{count} elementy} other{{count} elelmentów}}",
"@itemCount": {
"placeholders": {
"count": {}
}
},
- "columnCount": "{count, plural, =1{1 rząd} few{{count} rzędy} other{{count} rzędów}}",
+ "columnCount": "{count, plural, =1{{count} rząd} few{{count} rzędy} other{{count} rzędów}}",
"@columnCount": {
"placeholders": {
"count": {}
}
},
- "timeSeconds": "{seconds, plural, =1{1 sekunda} few{{seconds} sekundy} other{{seconds} sekund}}",
+ "timeSeconds": "{count, plural, =1{{count} sekunda} few{{count} sekundy} other{{count} sekund}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
}
},
- "timeMinutes": "{minutes, plural, =1{1 minuta} few{{minutes} minuty} other{{minutes} minut}}",
+ "timeMinutes": "{count, plural, =1{{count} minuta} few{{count} minuty} other{{count} minut}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
}
},
- "timeDays": "{days, plural, =1{1 dzień} few{{days} dni} other{{days} dni}}",
+ "timeDays": "{count, plural, =1{{count} dzień} few{{count} dni} other{{count} dni}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -1536,5 +1536,7 @@
"settingsForceWesternArabicNumeralsTile": "Wymuszaj cyfry arabskie",
"@settingsForceWesternArabicNumeralsTile": {},
"renameProcessorHash": "Skrót",
- "@renameProcessorHash": {}
+ "@renameProcessorHash": {},
+ "chipActionShowCollection": "Pokaż w Kolekcji",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb
index 4196bcce1..c4a462eaa 100644
--- a/lib/l10n/app_pt.arb
+++ b/lib/l10n/app_pt.arb
@@ -7,13 +7,13 @@
"@welcomeOptional": {},
"welcomeTermsToggle": "Eu concordo com os Termos e Condições",
"@welcomeTermsToggle": {},
- "itemCount": "{count, plural, =1{1 item} other{{count} itens}}",
+ "itemCount": "{count, plural, =1{{count} item} other{{count} itens}}",
"@itemCount": {},
- "timeSeconds": "{seconds, plural, =1{1 segundo} other{{seconds} segundos}}",
+ "timeSeconds": "{count, plural, =1{{count} segundo} other{{count} segundos}}",
"@timeSeconds": {},
- "timeMinutes": "{minutes, plural, =1{1 minuto} other{{minutes} minutos}}",
+ "timeMinutes": "{count, plural, =1{{count} minuto} other{{count} minutos}}",
"@timeMinutes": {},
- "timeDays": "{days, plural, =1{1 dia} other{{days} dias}}",
+ "timeDays": "{count, plural, =1{{count} dia} other{{count} dias}}",
"@timeDays": {},
"focalLength": "{length} mm",
"@focalLength": {},
@@ -1215,7 +1215,7 @@
"@pinDialogConfirm": {},
"passwordDialogEnter": "Digite a senha",
"@passwordDialogEnter": {},
- "columnCount": "{count, plural, =1{1 coluna} other{{count} colunas}}",
+ "columnCount": "{count, plural, =1{{count} coluna} other{{count} colunas}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -1374,5 +1374,7 @@
"stopTooltip": "Parar",
"@stopTooltip": {},
"videoRepeatActionSetStart": "Definir início",
- "@videoRepeatActionSetStart": {}
+ "@videoRepeatActionSetStart": {},
+ "chipActionShowCollection": "Mostrar na Coleção",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_ro.arb b/lib/l10n/app_ro.arb
index 78a29d2b4..96470a656 100644
--- a/lib/l10n/app_ro.arb
+++ b/lib/l10n/app_ro.arb
@@ -5,19 +5,19 @@
"@welcomeOptional": {},
"welcomeTermsToggle": "Sunt de acord cu Termenii și condițiile",
"@welcomeTermsToggle": {},
- "timeSeconds": "{seconds, plural, =1{1 secundă} other{{seconds} secunde}}",
+ "timeSeconds": "{count, plural, =1{{count} secundă} other{{count} secunde}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
}
},
- "timeMinutes": "{minutes, plural, =1{1 minut} other{{minutes} minute}}",
+ "timeMinutes": "{count, plural, =1{{count} minut} other{{count} minute}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
}
},
- "timeDays": "{days, plural, =1{1 zi} other{{days} zile}}",
+ "timeDays": "{count, plural, =1{{count} zi} other{{count} zile}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -444,9 +444,9 @@
},
"exportEntryDialogFormat": "Format:",
"@exportEntryDialogFormat": {},
- "exportEntryDialogWidth": "Lăţime",
+ "exportEntryDialogWidth": "Lățime",
"@exportEntryDialogWidth": {},
- "exportEntryDialogHeight": "Înălţime",
+ "exportEntryDialogHeight": "Înălțime",
"@exportEntryDialogHeight": {},
"renameEntryDialogLabel": "Nume nou",
"@renameEntryDialogLabel": {},
@@ -648,7 +648,7 @@
},
"appName": "Aves",
"@appName": {},
- "itemCount": "{count, plural, =1{1 element} other{{count} elemente}}",
+ "itemCount": "{count, plural, =1{{count} element} other{{count} elemente}}",
"@itemCount": {
"placeholders": {
"count": {}
@@ -1329,7 +1329,7 @@
"@statsTopCountriesSectionTitle": {},
"settingsAccessibilityShowPinchGestureAlternatives": "Afișare alternative de gesturi atingeri-multiple",
"@settingsAccessibilityShowPinchGestureAlternatives": {},
- "columnCount": "{count, plural, =1{1 coloană} other{{count} coloane}}",
+ "columnCount": "{count, plural, =1{{count} coloană} other{{count} coloane}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -1524,5 +1524,19 @@
"maxBrightnessNever": "Niciodată",
"@maxBrightnessNever": {},
"maxBrightnessAlways": "Mereu",
- "@maxBrightnessAlways": {}
+ "@maxBrightnessAlways": {},
+ "renameProcessorHash": "Hash",
+ "@renameProcessorHash": {},
+ "settingsForceWesternArabicNumeralsTile": "Cifre arabe forțat",
+ "@settingsForceWesternArabicNumeralsTile": {},
+ "stopTooltip": "Oprește",
+ "@stopTooltip": {},
+ "videoActionABRepeat": "Repetă de la A la B",
+ "@videoActionABRepeat": {},
+ "videoRepeatActionSetStart": "Setează începutul",
+ "@videoRepeatActionSetStart": {},
+ "videoRepeatActionSetEnd": "Setează sfârșitul",
+ "@videoRepeatActionSetEnd": {},
+ "chipActionShowCollection": "Afișați în colecție",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb
index 25e3f10f4..9424307c2 100644
--- a/lib/l10n/app_ru.arb
+++ b/lib/l10n/app_ru.arb
@@ -7,13 +7,13 @@
"@welcomeOptional": {},
"welcomeTermsToggle": "Я согласен с условиями и положениями",
"@welcomeTermsToggle": {},
- "itemCount": "{count, plural, =1{1 объект} few{{count} объекта} other{{count} объектов}}",
+ "itemCount": "{count, plural, =1{{count} объект} few{{count} объекта} other{{count} объектов}}",
"@itemCount": {},
- "timeSeconds": "{seconds, plural, =1{1 секунда} few{{seconds} секунды} other{{seconds} секунд}}",
+ "timeSeconds": "{count, plural, =1{{count} секунда} few{{count} секунды} other{{count} секунд}}",
"@timeSeconds": {},
- "timeMinutes": "{minutes, plural, =1{1 минута} few{{minutes} минуты} other{{minutes} минут}}",
+ "timeMinutes": "{count, plural, =1{{count} минута} few{{count} минуты} other{{count} минут}}",
"@timeMinutes": {},
- "timeDays": "{days, plural, =1{1 день} few{{days} дня} other{{days} дней}}",
+ "timeDays": "{count, plural, =1{{count} день} few{{count} дня} other{{count} дней}}",
"@timeDays": {},
"focalLength": "{length} mm",
"@focalLength": {},
@@ -1177,7 +1177,7 @@
"@filterAspectRatioPortraitLabel": {},
"settingsViewerShowRatingTags": "Показать рейтинг и теги",
"@settingsViewerShowRatingTags": {},
- "columnCount": "{count, plural, =1{1 столбец} few{{count} столбца} other{{count} столбцов}}",
+ "columnCount": "{count, plural, =1{{count} столбец} few{{count} столбца} other{{count} столбцов}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -1378,5 +1378,7 @@
"renameProcessorHash": "Хэш",
"@renameProcessorHash": {},
"settingsForceWesternArabicNumeralsTile": "Принудительные арабские цифры",
- "@settingsForceWesternArabicNumeralsTile": {}
+ "@settingsForceWesternArabicNumeralsTile": {},
+ "chipActionShowCollection": "Показать в Коллекции",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb
index eda652986..28f294611 100644
--- a/lib/l10n/app_sk.arb
+++ b/lib/l10n/app_sk.arb
@@ -27,7 +27,7 @@
"@chipActionDelete": {},
"welcomeTermsToggle": "Súhlasím s pravidlami a podmienkami",
"@welcomeTermsToggle": {},
- "timeDays": "{days, plural, other{{days} dni}}",
+ "timeDays": "{count, plural, other{{count} dni}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -50,7 +50,7 @@
"@doubleBackExitMessage": {},
"welcomeOptional": "Voliteľné",
"@welcomeOptional": {},
- "timeMinutes": "{minutes, plural, =1{1 minúta} few{{minutes} minúty} other{{minutes} minút}}",
+ "timeMinutes": "{count, plural, =1{{count} minúta} few{{count} minúty} other{{count} minút}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
@@ -638,19 +638,19 @@
"@filePickerOpenFrom": {},
"filePickerUseThisFolder": "Použiť tento priečinok",
"@filePickerUseThisFolder": {},
- "itemCount": "{count, plural, =1{1 položka} other{{count} položiek}}",
+ "itemCount": "{count, plural, =1{{count} položka} other{{count} položiek}}",
"@itemCount": {
"placeholders": {
"count": {}
}
},
- "columnCount": "{count, plural, =1{1 stĺpec} other{{count} stĺpcov}}",
+ "columnCount": "{count, plural, =1{{count} stĺpec} other{{count} stĺpcov}}",
"@columnCount": {
"placeholders": {
"count": {}
}
},
- "timeSeconds": "{seconds, plural, =1{1 sekunda} other{{seconds} sekúnd}}",
+ "timeSeconds": "{count, plural, =1{{count} sekunda} other{{count} sekúnd}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
@@ -1524,5 +1524,7 @@
"setHomeCustomCollection": "Kolekcia na mieru",
"@setHomeCustomCollection": {},
"settingsThumbnailShowHdrIcon": "Zobraziť ikonu HDR",
- "@settingsThumbnailShowHdrIcon": {}
+ "@settingsThumbnailShowHdrIcon": {},
+ "chipActionShowCollection": "Zobraziť v kolekcií",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_sv.arb b/lib/l10n/app_sv.arb
index 5f3f40eb4..e1282e8a1 100644
--- a/lib/l10n/app_sv.arb
+++ b/lib/l10n/app_sv.arb
@@ -106,25 +106,25 @@
"count": {}
}
},
- "columnCount": "{count, plural, =1{1 kolumn} other{{count} kolumner}}",
+ "columnCount": "{count, plural, =1{{count} kolumn} other{{count} kolumner}}",
"@columnCount": {
"placeholders": {
"count": {}
}
},
- "timeSeconds": "{seconds, plural, =1{1 sekund} other{{seconds} sekunder}}",
+ "timeSeconds": "{count, plural, =1{{count} sekund} other{{count} sekunder}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
}
},
- "timeMinutes": "{minutes, plural, =1{1 minut} other{{minutes} minuter}}",
+ "timeMinutes": "{count, plural, =1{{count} minut} other{{count} minuter}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
}
},
- "timeDays": "{days, plural, =1{1 dag} other{{days} dagar}}",
+ "timeDays": "{count, plural, =1{{count} dag} other{{count} dagar}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -835,5 +835,7 @@
"filePickerUseThisFolder": "Använd den har mappen",
"@filePickerUseThisFolder": {},
"chipActionUnpin": "Släpp från fästet",
- "@chipActionUnpin": {}
+ "@chipActionUnpin": {},
+ "chipActionShowCollection": "Visa i samling",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_th.arb b/lib/l10n/app_th.arb
index 574bca11f..4ca6bf8b6 100644
--- a/lib/l10n/app_th.arb
+++ b/lib/l10n/app_th.arb
@@ -649,5 +649,7 @@
"description": "a list of unsupported types"
}
}
- }
+ },
+ "chipActionShowCollection": "แสดงคอลเลกชัน",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_tr.arb b/lib/l10n/app_tr.arb
index 5a6e9e6b3..0ac680d5b 100644
--- a/lib/l10n/app_tr.arb
+++ b/lib/l10n/app_tr.arb
@@ -7,13 +7,13 @@
"@welcomeOptional": {},
"welcomeTermsToggle": "Hüküm ve koşulları kabul ediyorum",
"@welcomeTermsToggle": {},
- "itemCount": "{count, plural, =1{1 öğe} other{{count} öğe}}",
+ "itemCount": "{count, plural, other{{count} öğe}}",
"@itemCount": {},
- "timeSeconds": "{seconds, plural, =1{1 saniye} other{{seconds} saniye}}",
+ "timeSeconds": "{count, plural, other{{count} saniye}}",
"@timeSeconds": {},
- "timeMinutes": "{minutes, plural, =1{1 dakika} other{{minutes} dakika}}",
+ "timeMinutes": "{count, plural, other{{count} dakika}}",
"@timeMinutes": {},
- "timeDays": "{days, plural, =1{1 gün} other{{days} gün}}",
+ "timeDays": "{count, plural, other{{count} gün}}",
"@timeDays": {},
"focalLength": "{length} mm",
"@focalLength": {},
@@ -1378,5 +1378,7 @@
"settingsForceWesternArabicNumeralsTile": "Arap rakamlarını zorla",
"@settingsForceWesternArabicNumeralsTile": {},
"renameProcessorHash": "Sağlama",
- "@renameProcessorHash": {}
+ "@renameProcessorHash": {},
+ "chipActionShowCollection": "Koleksiyonda göster",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb
index 3864c6776..bd1bcf700 100644
--- a/lib/l10n/app_uk.arb
+++ b/lib/l10n/app_uk.arb
@@ -7,7 +7,7 @@
"@welcomeOptional": {},
"welcomeTermsToggle": "Я погоджуюсь з умовами та положеннями",
"@welcomeTermsToggle": {},
- "timeDays": "{days, plural, =1{1 день} few{{days} дні} other{{days} днів}}",
+ "timeDays": "{count, plural, =1{{count} день} few{{count} дні} other{{count} днів}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -637,19 +637,19 @@
"@entryActionShareVideoOnly": {},
"continueButtonLabel": "ПРОДОВЖИТИ",
"@continueButtonLabel": {},
- "itemCount": "{count, plural, =1{1 елемент} few{{count} елементи} other{{count} елементів}}",
+ "itemCount": "{count, plural, =1{{count} елемент} few{{count} елементи} other{{count} елементів}}",
"@itemCount": {
"placeholders": {
"count": {}
}
},
- "timeSeconds": "{seconds, plural, =1{1 секунда} few{{seconds} секунди} other{{seconds} секунд}}",
+ "timeSeconds": "{count, plural, =1{{count} секунда} few{{count} секунди} other{{count} секунд}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
}
},
- "timeMinutes": "{minutes, plural, =1{1 хвилина} few{{minutes} хвилини} other{{minutes} хвилин}}",
+ "timeMinutes": "{count, plural, =1{{count} хвилина} few{{count} хвилини} other{{count} хвилин}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
@@ -1345,7 +1345,7 @@
"@mapAttributionOsmHot": {},
"settingsAccessibilityShowPinchGestureAlternatives": "Показувати альтернативи мультисенсорним жестам",
"@settingsAccessibilityShowPinchGestureAlternatives": {},
- "columnCount": "{count, plural, =1{1 стовпець} few{{count} стовпці} other{{count} стовпців}}",
+ "columnCount": "{count, plural, =1{{count} стовпець} few{{count} стовпці} other{{count} стовпців}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -1532,5 +1532,11 @@
"stopTooltip": "Зупинити",
"@stopTooltip": {},
"videoActionABRepeat": "Повторити від А до Б",
- "@videoActionABRepeat": {}
+ "@videoActionABRepeat": {},
+ "renameProcessorHash": "Хеш",
+ "@renameProcessorHash": {},
+ "settingsForceWesternArabicNumeralsTile": "Примусові арабські цифри",
+ "@settingsForceWesternArabicNumeralsTile": {},
+ "chipActionShowCollection": "Показати у Колекції",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_vi.arb b/lib/l10n/app_vi.arb
index a79f07b43..cc823dc07 100644
--- a/lib/l10n/app_vi.arb
+++ b/lib/l10n/app_vi.arb
@@ -951,7 +951,7 @@
"@placePageTitle": {},
"filterOnThisDayLabel": "Vào ngày này",
"@filterOnThisDayLabel": {},
- "columnCount": "{count, plural, =1{1 cột} other{{count} cột}}",
+ "columnCount": "{count, plural, other{{count} cột}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -1007,7 +1007,7 @@
"@albumMimeTypeMixed": {},
"settingsViewerQuickActionEditorAvailableButtonsSectionTitle": "Các nút có sẵn",
"@settingsViewerQuickActionEditorAvailableButtonsSectionTitle": {},
- "itemCount": "{count, plural, =1{1 mục} other{{count} mục}}",
+ "itemCount": "{count, plural, other{{count} mục}}",
"@itemCount": {
"placeholders": {
"count": {}
@@ -1271,7 +1271,7 @@
"@drawerCollectionImages": {},
"sortOrderSmallestFirst": "Nhỏ trước",
"@sortOrderSmallestFirst": {},
- "timeSeconds": "{seconds, plural, =1{1 giây} other{{seconds} giây}}",
+ "timeSeconds": "{count, plural, other{{count} giây}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
@@ -1405,7 +1405,7 @@
"@settingsNavigationDrawerTabAlbums": {},
"subtitlePositionBottom": "Dưới",
"@subtitlePositionBottom": {},
- "timeDays": "{days, plural, =1{1 ngày} other{{days} ngày}}",
+ "timeDays": "{count, plural, other{{count} ngày}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -1429,7 +1429,7 @@
"@settingsViewerQuickActionsTile": {},
"newVaultDialogTitle": "Két sắt mới",
"@newVaultDialogTitle": {},
- "timeMinutes": "{minutes, plural, =1{1 phút} other{{minutes} phút}}",
+ "timeMinutes": "{count, plural, other{{count} phút}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
@@ -1536,5 +1536,7 @@
"renameProcessorHash": "Băm",
"@renameProcessorHash": {},
"settingsForceWesternArabicNumeralsTile": "Buộc chữ số Ả Rập",
- "@settingsForceWesternArabicNumeralsTile": {}
+ "@settingsForceWesternArabicNumeralsTile": {},
+ "chipActionShowCollection": "Hiển thị trong Bộ sưu tập",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb
index 7630a3e57..80d0c3c6e 100644
--- a/lib/l10n/app_zh.arb
+++ b/lib/l10n/app_zh.arb
@@ -9,11 +9,11 @@
"@welcomeTermsToggle": {},
"itemCount": "{count, plural, other{{count} 项}}",
"@itemCount": {},
- "timeSeconds": "{seconds, plural, other{{seconds} 秒}}",
+ "timeSeconds": "{count, plural, other{{count} 秒}}",
"@timeSeconds": {},
- "timeMinutes": "{minutes, plural, other{{minutes} 分}}",
+ "timeMinutes": "{count, plural, other{{count} 分}}",
"@timeMinutes": {},
- "timeDays": "{days, plural, other{{days} 天}}",
+ "timeDays": "{count, plural, other{{count} 天}}",
"@timeDays": {},
"focalLength": "{length} mm",
"@focalLength": {},
@@ -723,7 +723,7 @@
"@searchMetadataSectionTitle": {},
"settingsPageTitle": "设置",
"@settingsPageTitle": {},
- "settingsSystemDefault": "系统",
+ "settingsSystemDefault": "系统默认",
"@settingsSystemDefault": {},
"settingsDefault": "默认",
"@settingsDefault": {},
@@ -1377,6 +1377,8 @@
"@videoRepeatActionSetEnd": {},
"renameProcessorHash": "哈希",
"@renameProcessorHash": {},
- "settingsForceWesternArabicNumeralsTile": "强制阿拉伯数数字",
- "@settingsForceWesternArabicNumeralsTile": {}
+ "settingsForceWesternArabicNumeralsTile": "强制使用阿拉伯数字",
+ "@settingsForceWesternArabicNumeralsTile": {},
+ "chipActionShowCollection": "在媒体集中显示",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/l10n/app_zh_Hant.arb b/lib/l10n/app_zh_Hant.arb
index de8052229..d847d39e0 100644
--- a/lib/l10n/app_zh_Hant.arb
+++ b/lib/l10n/app_zh_Hant.arb
@@ -13,7 +13,7 @@
"count": {}
}
},
- "timeDays": "{days, plural, other{{days} 天}}",
+ "timeDays": "{count, plural, other{{count} 天}}",
"@timeDays": {
"placeholders": {
"days": {}
@@ -919,13 +919,13 @@
"@mapZoomOutTooltip": {},
"mapPointNorthUpTooltip": "北方向上",
"@mapPointNorthUpTooltip": {},
- "timeSeconds": "{seconds, plural, other{{seconds} 秒}}",
+ "timeSeconds": "{count, plural, other{{count} 秒}}",
"@timeSeconds": {
"placeholders": {
"seconds": {}
}
},
- "timeMinutes": "{minutes, plural, other{{minutes} 分}}",
+ "timeMinutes": "{count, plural, other{{count} 分}}",
"@timeMinutes": {
"placeholders": {
"minutes": {}
@@ -1419,7 +1419,7 @@
"@settingsVideoResumptionModeDialogTitle": {},
"tagEditorDiscardDialogMessage": "是否要放棄更改?",
"@tagEditorDiscardDialogMessage": {},
- "columnCount": "{count, plural, =1{1 列} other{{count} 列}}",
+ "columnCount": "{count, plural, other{{count} 列}}",
"@columnCount": {
"placeholders": {
"count": {}
@@ -1532,5 +1532,11 @@
"videoRepeatActionSetStart": "設置起點",
"@videoRepeatActionSetStart": {},
"videoRepeatActionSetEnd": "設置終點",
- "@videoRepeatActionSetEnd": {}
+ "@videoRepeatActionSetEnd": {},
+ "renameProcessorHash": "雜湊",
+ "@renameProcessorHash": {},
+ "settingsForceWesternArabicNumeralsTile": "強制使用阿拉伯數字",
+ "@settingsForceWesternArabicNumeralsTile": {},
+ "chipActionShowCollection": "在收藏品中顯示",
+ "@chipActionShowCollection": {}
}
diff --git a/lib/model/app/contributors.dart b/lib/model/app/contributors.dart
index 0bd8fe21e..5614d2139 100644
--- a/lib/model/app/contributors.dart
+++ b/lib/model/app/contributors.dart
@@ -87,6 +87,10 @@ class Contributors {
Contributor('slasb37', 'p84haghi@gmail.com'),
Contributor('mimvahedi', 'vahedi0vahedi@gmail.com'),
Contributor('Alireza Rashidi', 'alirezarashidigoorabi@gmail.com'),
+ Contributor('何意挽秋風', '94283631+RejectVanity@users.noreply.github.com'),
+ Contributor('cheese', 'deanlemans5646@gmail.com'),
+ Contributor('Owen Elderbroek', 'o.elderbroek@gmail.com'),
+ Contributor('Maxi', 'maxitendo01@proton.me'),
// Contributor('Alvi Khan', 'aveenalvi@gmail.com'), // Bengali
// Contributor('Htet Oo Hlaing', 'htetoh2006@outlook.com'), // Burmese
// Contributor('Khant', 'khant@users.noreply.hosted.weblate.org'), // Burmese
diff --git a/lib/model/entry/extensions/metadata_edition.dart b/lib/model/entry/extensions/metadata_edition.dart
index 29d1d447d..6ad3b2dce 100644
--- a/lib/model/entry/extensions/metadata_edition.dart
+++ b/lib/model/entry/extensions/metadata_edition.dart
@@ -23,6 +23,9 @@ import 'package:latlong2/latlong.dart';
import 'package:xml/xml.dart';
extension ExtraAvesEntryMetadataEdition on AvesEntry {
+ static final _iso6709LatitudeFormatter = NumberFormat('00.0000', asciiLocale);
+ static final _iso6709LongitudeFormatter = NumberFormat('000.0000', asciiLocale);
+
Future> editDate(DateModifier userModifier) async {
final dataTypes = {};
@@ -59,7 +62,7 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
final date = DateTime.tryParse(xmpDate);
if (date != null) {
// TODO TLAD [date] DateTime.tryParse converts to UTC time, losing the time zone offset
- final shiftedDate = date.add(Duration(minutes: appliedModifier.shiftMinutes!));
+ final shiftedDate = date.add(Duration(seconds: appliedModifier.shiftSeconds!));
editCreateDateXmp(descriptions, shiftedDate);
} else {
reportService.recordError('failed to parse XMP date=$xmpDate', null);
@@ -122,9 +125,8 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
if (latLng != null && latLng != removalLocation) {
final latitude = latLng.latitude;
final longitude = latLng.longitude;
- const locale = asciiLocale;
- final isoLat = '${latitude >= 0 ? '+' : '-'}${NumberFormat('00.0000', locale).format(latitude.abs())}';
- final isoLon = '${longitude >= 0 ? '+' : '-'}${NumberFormat('000.0000', locale).format(longitude.abs())}';
+ final isoLat = '${latitude >= 0 ? '+' : '-'}${_iso6709LatitudeFormatter.format(latitude.abs())}';
+ final isoLon = '${longitude >= 0 ? '+' : '-'}${_iso6709LongitudeFormatter.format(longitude.abs())}';
iso6709String = '$isoLat$isoLon/';
}
mp4Fields[MetadataField.mp4GpsCoordinates] = iso6709String;
diff --git a/lib/model/entry/extensions/props.dart b/lib/model/entry/extensions/props.dart
index 1b231ea59..88d8b5b1a 100644
--- a/lib/model/entry/extensions/props.dart
+++ b/lib/model/entry/extensions/props.dart
@@ -11,6 +11,7 @@ import 'package:aves/services/common/services.dart';
import 'package:aves/theme/text.dart';
import 'package:aves/utils/android_file_utils.dart';
import 'package:aves/utils/time_utils.dart';
+import 'package:intl/intl.dart';
extension ExtraAvesEntryProps on AvesEntry {
bool get isValid => !isMissingAtPath && sizeBytes != 0 && width > 0 && height > 0;
@@ -51,9 +52,10 @@ extension ExtraAvesEntryProps on AvesEntry {
// text
- String get resolutionText {
- final ws = width;
- final hs = height;
+ String getResolutionText(String locale) {
+ final dimensionFormatter = NumberFormat('0', locale);
+ final ws = dimensionFormatter.format(width);
+ final hs = dimensionFormatter.format(height);
return isRotated ? '$hs${AText.resolutionSeparator}$ws' : '$ws${AText.resolutionSeparator}$hs';
}
diff --git a/lib/model/filters/aspect_ratio.dart b/lib/model/filters/aspect_ratio.dart
index f21f48280..b638ef7e4 100644
--- a/lib/model/filters/aspect_ratio.dart
+++ b/lib/model/filters/aspect_ratio.dart
@@ -68,9 +68,7 @@ class AspectRatioFilter extends CollectionFilter {
}
@override
- Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) {
- return Icon(AIcons.aspectRatio, size: size);
- }
+ Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(AIcons.aspectRatio, size: size);
@override
String get category => type;
diff --git a/lib/model/filters/coordinate.dart b/lib/model/filters/coordinate.dart
index 57b7b4ebe..fff9df429 100644
--- a/lib/model/filters/coordinate.dart
+++ b/lib/model/filters/coordinate.dart
@@ -69,7 +69,7 @@ class CoordinateFilter extends CollectionFilter {
}
@override
- Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(AIcons.geoBounds, size: size);
+ Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(AIcons.geoBounds, size: size);
@override
String get category => type;
diff --git a/lib/model/filters/date.dart b/lib/model/filters/date.dart
index 4749fcd4d..2756de2e0 100644
--- a/lib/model/filters/date.dart
+++ b/lib/model/filters/date.dart
@@ -100,8 +100,7 @@ class DateFilter extends CollectionFilter {
@override
String getLabel(BuildContext context) {
- final l10n = context.l10n;
- final locale = l10n.localeName;
+ final locale = context.locale;
switch (level) {
case DateLevel.y:
return DateFormat.y(locale).format(_effectiveDate);
@@ -113,7 +112,7 @@ class DateFilter extends CollectionFilter {
if (date != null) {
return DateFormat.MMMd(locale).format(_effectiveDate);
} else {
- return l10n.filterOnThisDayLabel;
+ return context.l10n.filterOnThisDayLabel;
}
case DateLevel.m:
return DateFormat.MMMM(locale).format(_effectiveDate);
@@ -123,9 +122,7 @@ class DateFilter extends CollectionFilter {
}
@override
- Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) {
- return Icon(AIcons.date, size: size);
- }
+ Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(AIcons.date, size: size);
@override
String get category => type;
diff --git a/lib/model/filters/favourite.dart b/lib/model/filters/favourite.dart
index 3b5ff0333..2bd813b89 100644
--- a/lib/model/filters/favourite.dart
+++ b/lib/model/filters/favourite.dart
@@ -45,7 +45,7 @@ class FavouriteFilter extends CollectionFilter {
String getLabel(BuildContext context) => context.l10n.filterFavouriteLabel;
@override
- Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(AIcons.favourite, size: size);
+ Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(AIcons.favourite, size: size);
@override
Future color(BuildContext context) {
diff --git a/lib/model/filters/filters.dart b/lib/model/filters/filters.dart
index b6e08e2b9..f22270c88 100644
--- a/lib/model/filters/filters.dart
+++ b/lib/model/filters/filters.dart
@@ -10,6 +10,7 @@ import 'package:aves/model/filters/favourite.dart';
import 'package:aves/model/filters/location.dart';
import 'package:aves/model/filters/mime.dart';
import 'package:aves/model/filters/missing.dart';
+import 'package:aves/model/filters/or.dart';
import 'package:aves/model/filters/path.dart';
import 'package:aves/model/filters/placeholder.dart';
import 'package:aves/model/filters/query.dart';
@@ -43,6 +44,7 @@ abstract class CollectionFilter extends Equatable implements Comparable _isUnlocated ? context.l10n.filterNoLocationLabel : _location;
@override
- Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) {
+ Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) {
if (_isUnlocated) {
return Icon(AIcons.locationUnlocated, size: size);
}
diff --git a/lib/model/filters/mime.dart b/lib/model/filters/mime.dart
index 341780c53..86d4840d2 100644
--- a/lib/model/filters/mime.dart
+++ b/lib/model/filters/mime.dart
@@ -77,7 +77,7 @@ class MimeFilter extends CollectionFilter {
}
@override
- Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(_icon, size: size);
+ Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(_icon, size: size);
@override
Future color(BuildContext context) {
diff --git a/lib/model/filters/missing.dart b/lib/model/filters/missing.dart
index 9d83f82c3..d785bc3c2 100644
--- a/lib/model/filters/missing.dart
+++ b/lib/model/filters/missing.dart
@@ -70,7 +70,7 @@ class MissingFilter extends CollectionFilter {
}
@override
- Widget iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(_icon, size: size);
+ Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) => Icon(_icon, size: size);
@override
String get category => type;
diff --git a/lib/model/filters/or.dart b/lib/model/filters/or.dart
new file mode 100644
index 000000000..89e62751f
--- /dev/null
+++ b/lib/model/filters/or.dart
@@ -0,0 +1,72 @@
+import 'package:aves/model/filters/album.dart';
+import 'package:aves/model/filters/filters.dart';
+import 'package:aves/model/filters/location.dart';
+import 'package:aves/theme/icons.dart';
+import 'package:collection/collection.dart';
+import 'package:flutter/widgets.dart';
+
+class OrFilter extends CollectionFilter {
+ static const type = 'or';
+
+ late final List _filters;
+
+ late final EntryFilter _test;
+ late final IconData? _genericIcon;
+
+ @override
+ List