Merge branch 'develop'
2
.flutter
|
@ -1 +1 @@
|
|||
Subproject commit 54e66469a933b60ddf175f858f82eaeb97e48c8d
|
||||
Subproject commit 761747bfc538b5af34aa0d3fac380f1bc331ec49
|
21
CHANGELOG.md
|
@ -4,6 +4,27 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
## <a id="unreleased"></a>[Unreleased]
|
||||
|
||||
## <a id="v1.11.2"></a>[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
|
||||
|
||||
## <a id="v1.11.1"></a>[v1.11.1] - 2024-05-03
|
||||
|
||||
### Added
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -72,12 +72,10 @@
|
|||
-->
|
||||
|
||||
<!--
|
||||
allow install on API 19, despite the `minSdk` declared in dependencies:
|
||||
- the Security library is from API 21
|
||||
allow install on API 21, despite the `minSdk` declared in dependencies:
|
||||
- FFmpegKit for Flutter is from API 24 (when not LTS)
|
||||
- Google Maps is from API 20
|
||||
-->
|
||||
<uses-sdk tools:overrideLibrary="androidx.security, com.arthenica.ffmpegkit.flutter, io.flutter.plugins.googlemaps" />
|
||||
<uses-sdk tools:overrideLibrary="com.arthenica.ffmpegkit.flutter" />
|
||||
|
||||
<!-- from Android 11, we should define <queries> to make other apps visible to this app -->
|
||||
<queries>
|
||||
|
@ -321,8 +319,8 @@
|
|||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
<!-- as of Flutter v3.19.0 (stable),
|
||||
Impeller fails to render videos & platform views, has poor performance -->
|
||||
<!-- as of Flutter v3.22.0 (stable),
|
||||
Impeller fails to render videos (via `ffmpegkit`), and has random glitches -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.EnableImpeller"
|
||||
android:value="false" />
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<String, Any?>(
|
||||
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<String, Any?>()
|
||||
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"
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<FieldMap>()
|
||||
|
||||
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)
|
||||
|
|
|
@ -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<String, Any?> = 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<FieldMap>()
|
||||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -74,7 +74,7 @@ class MetadataEditHandler(private val contextWrapper: ContextWrapper) : MethodCa
|
|||
|
||||
private fun editDate(call: MethodCall, result: MethodChannel.Result) {
|
||||
val dateMillis = call.argument<Number>("dateMillis")?.toLong()
|
||||
val shiftMinutes = call.argument<Number>("shiftMinutes")?.toLong()
|
||||
val shiftSeconds = call.argument<Number>("shiftSeconds")?.toLong()
|
||||
val fields = call.argument<List<String>>("fields")
|
||||
val entryMap = call.argument<FieldMap>("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) {
|
||||
|
|
|
@ -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<Int, Any?>()
|
||||
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
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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<String, String>): 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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<Uri, File>()
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<SafePngMetadataReader>()
|
||||
|
||||
// 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
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1026,7 +1026,7 @@ abstract class ImageProvider {
|
|||
uri: Uri,
|
||||
mimeType: String,
|
||||
dateMillis: Long?,
|
||||
shiftMinutes: Long?,
|
||||
shiftSeconds: Long?,
|
||||
fields: List<String>,
|
||||
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,
|
||||
|
|
|
@ -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<Address?>) = 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")
|
||||
|
|
|
@ -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<Address>) -> Unit,
|
||||
onError: (errorCode: String, errorMessage: String?, errorDetails: Any?) -> Unit,
|
||||
) {
|
||||
geocoder.getFromLocation(latitude, longitude, maxResults, object : Geocoder.GeocodeListener {
|
||||
override fun onGeocode(addresses: List<Address?>) = processAddresses(addresses.filterNotNull())
|
||||
|
||||
override fun onError(errorMessage: String?) {
|
||||
onError("getAddress-asyncerror", "failed to get address", errorMessage)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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"))
|
||||
}
|
|
@ -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()
|
||||
}
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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<String> {
|
||||
val grantedDirs = HashSet<String>()
|
||||
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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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'
|
||||
}
|
||||
|
||||
|
|
|
@ -26,5 +26,5 @@ android {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.annotation:annotation:1.7.1'
|
||||
implementation 'androidx.annotation:annotation:1.8.0'
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
3
devtools_options.yaml
Normal file
|
@ -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:
|
3
fastlane/metadata/android/en-US/changelogs/121.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
In v1.11.2:
|
||||
- show selected albums together in Collection
|
||||
Full changelog available on GitHub
|
3
fastlane/metadata/android/en-US/changelogs/12101.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
In v1.11.2:
|
||||
- show selected albums together in Collection
|
||||
Full changelog available on GitHub
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
<b>پیمایش و جستجو</b>یک بخش مهم از <i>اِیوْز</i> است. هدف این است که کاربران به راحتی از آلبوم ها به عکس ها به برچسب ها به نقشه ها و غیره دست پیدا کنند.
|
||||
|
||||
<i>اِیوْز</i> با اندروید سازگار است (از نسخه KitKat تا اندروید 14, شامل تلوزیون اندرویدی) با قابلیت هایی مانند <b>ابزارک ها</b>, <b>میانبر ها</b>, <b>ذخیره نیرو</b> و <b>جستجو عمومی</b> و همچنین میتوان از آن به عنوان <b>نمایشگر و انتخابگز رسانه</b> استفاده کرد.
|
||||
<i>اِیوْز</i> با اندروید سازگار است (از نسخه KitKat تا اندروید 14, شامل تلوزیون اندرویدی) با قابلیت هایی مانند <b>ابزارک ها</b>, <b>میانبر ها</b>, <b>ذخیره نیرو</b> و <b>جستجو عمومی</b> و همچنین میتوان از آن به عنوان <b>نمایشگر و انتخابگر رسانه</b> استفاده کرد.
|
||||
|
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 17 KiB |
Before Width: | Height: | Size: 278 KiB After Width: | Height: | Size: 279 KiB |
Before Width: | Height: | Size: 495 KiB After Width: | Height: | Size: 495 KiB |
Before Width: | Height: | Size: 189 KiB After Width: | Height: | Size: 189 KiB |
Before Width: | Height: | Size: 120 KiB After Width: | Height: | Size: 119 KiB |
Before Width: | Height: | Size: 72 KiB After Width: | Height: | Size: 71 KiB |
Before Width: | Height: | Size: 322 KiB After Width: | Height: | Size: 322 KiB |
Before Width: | Height: | Size: 333 KiB After Width: | Height: | Size: 333 KiB |
|
@ -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,
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -649,5 +649,7 @@
|
|||
"description": "a list of unsupported types"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"chipActionShowCollection": "แสดงคอลเลกชัน",
|
||||
"@chipActionShowCollection": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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": {}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<Set<EntryDataType>> editDate(DateModifier userModifier) async {
|
||||
final dataTypes = <EntryDataType>{};
|
||||
|
||||
|
@ -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;
|
||||
|
|
|
@ -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';
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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> color(BuildContext context) {
|
||||
|
|