Merge branch 'develop'

This commit is contained in:
Thibault Deckers 2024-08-07 22:01:05 +02:00
commit 00681cbaee
208 changed files with 2132 additions and 1877 deletions

@ -1 +1 @@
Subproject commit b0850beeb25f6d5b10426284f506557f66181b36 Subproject commit 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819

View file

@ -53,6 +53,8 @@ jobs:
./flutterw build apk -t lib/main_izzy.dart --flavor izzy --split-per-abi ./flutterw build apk -t lib/main_izzy.dart --flavor izzy --split-per-abi
cp build/app/outputs/apk/izzy/release/*.apk outputs cp build/app/outputs/apk/izzy/release/*.apk outputs
scripts/apply_flavor_libre.sh scripts/apply_flavor_libre.sh
./flutterw build appbundle -t lib/main_libre.dart --flavor libre
cp build/app/outputs/bundle/libreRelease/*.aab outputs
./flutterw build apk -t lib/main_libre.dart --flavor libre --split-per-abi ./flutterw build apk -t lib/main_libre.dart --flavor libre --split-per-abi
cp build/app/outputs/apk/libre/release/*.apk outputs cp build/app/outputs/apk/libre/release/*.apk outputs
rm $AVES_STORE_FILE rm $AVES_STORE_FILE

View file

@ -4,6 +4,27 @@ All notable changes to this project will be documented in this file.
## <a id="unreleased"></a>[Unreleased] ## <a id="unreleased"></a>[Unreleased]
## <a id="v1.11.9"></a>[v1.11.9] - 2024-08-07
### Added
- Viewer: display more items in tag/copy/move quick action choosers
- Viewer: long descriptions are scrollable when overlay is expanded by tap
- Collection: sort by duration
- Map: open external map app from map views
- Explorer: stats
### Changed
- Accessibility: more animations and effects are suppressed when animations are disabled
- upgraded Flutter to stable v3.24.0
### Fixed
- opening app from launcher always showing home page
- collection quick actions not showing in the top bar nor the menu
- multiple widget setup after device reboot
## <a id="v1.11.8"></a>[v1.11.8] - 2024-07-19 ## <a id="v1.11.8"></a>[v1.11.8] - 2024-07-19
### Added ### Added

View file

@ -189,7 +189,7 @@ dependencies {
implementation "androidx.appcompat:appcompat:1.7.0" implementation "androidx.appcompat:appcompat:1.7.0"
implementation 'androidx.core:core-ktx:1.13.1' implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.lifecycle:lifecycle-process:2.8.3' implementation 'androidx.lifecycle:lifecycle-process:2.8.4'
implementation 'androidx.media:media:1.7.0' implementation 'androidx.media:media:1.7.0'
implementation 'androidx.multidex:multidex:2.0.1' implementation 'androidx.multidex:multidex:2.0.1'
implementation 'androidx.security:security-crypto:1.1.0-alpha06' implementation 'androidx.security:security-crypto:1.1.0-alpha06'
@ -201,7 +201,7 @@ dependencies {
implementation "com.github.bumptech.glide:glide:$glide_version" implementation "com.github.bumptech.glide:glide:$glide_version"
implementation 'com.google.android.material:material:1.12.0' implementation 'com.google.android.material:material:1.12.0'
// SLF4J implementation for `mp4parser` // SLF4J implementation for `mp4parser`
implementation 'org.slf4j:slf4j-simple:2.0.13' implementation 'org.slf4j:slf4j-simple:2.0.14'
// forked, built by JitPack: // forked, built by JitPack:
// - https://jitpack.io/p/deckerst/Android-TiffBitmapFactory // - https://jitpack.io/p/deckerst/Android-TiffBitmapFactory
@ -213,9 +213,9 @@ dependencies {
implementation 'com.github.deckerst:pixymeta-android:9ec7097f17' implementation 'com.github.deckerst:pixymeta-android:9ec7097f17'
implementation project(':exifinterface') implementation project(':exifinterface')
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.2' testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.3'
kapt 'androidx.annotation:annotation:1.8.0' kapt 'androidx.annotation:annotation:1.8.1'
ksp "com.github.bumptech.glide:ksp:$glide_version" ksp "com.github.bumptech.glide:ksp:$glide_version"
compileOnly rootProject.findProject(':streams_channel') compileOnly rootProject.findProject(':streams_channel')

View file

@ -323,8 +323,10 @@
<meta-data <meta-data
android:name="flutterEmbedding" android:name="flutterEmbedding"
android:value="2" /> android:value="2" />
<!-- as of Flutter v3.22.0 (stable), <!--
Impeller fails to render videos (via `ffmpegkit`), and has random glitches --> Impeller is not supported by `media_kit` v1.1.10+1:
https://github.com/media-kit/media-kit/issues/707
-->
<meta-data <meta-data
android:name="io.flutter.embedding.android.EnableImpeller" android:name="io.flutter.embedding.android.EnableImpeller"
android:value="false" /> android:value="false" />

View file

@ -11,12 +11,18 @@ import android.graphics.Bitmap
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.util.Log import android.util.Log
import android.util.SizeF import android.util.SizeF
import android.widget.RemoteViews import android.widget.RemoteViews
import app.loup.streams_channel.StreamsChannel import app.loup.streams_channel.StreamsChannel
import deckers.thibault.aves.channel.AvesByteSendingMethodCodec import deckers.thibault.aves.channel.AvesByteSendingMethodCodec
import deckers.thibault.aves.channel.calls.* import deckers.thibault.aves.channel.calls.DeviceHandler
import deckers.thibault.aves.channel.calls.MediaFetchBytesHandler
import deckers.thibault.aves.channel.calls.MediaFetchObjectHandler
import deckers.thibault.aves.channel.calls.MediaStoreHandler
import deckers.thibault.aves.channel.calls.StorageHandler
import deckers.thibault.aves.channel.streams.ImageByteStreamHandler import deckers.thibault.aves.channel.streams.ImageByteStreamHandler
import deckers.thibault.aves.channel.streams.MediaStoreStreamHandler import deckers.thibault.aves.channel.streams.MediaStoreStreamHandler
import deckers.thibault.aves.model.FieldMap import deckers.thibault.aves.model.FieldMap
@ -26,8 +32,14 @@ import io.flutter.FlutterInjector
import io.flutter.embedding.engine.FlutterEngine import io.flutter.embedding.engine.FlutterEngine
import io.flutter.embedding.engine.dart.DartExecutor import io.flutter.embedding.engine.dart.DartExecutor
import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.* import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.nio.ByteBuffer import java.nio.ByteBuffer
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume import kotlin.coroutines.resume
import kotlin.coroutines.resumeWithException import kotlin.coroutines.resumeWithException
import kotlin.coroutines.suspendCoroutine import kotlin.coroutines.suspendCoroutine
@ -108,37 +120,25 @@ class HomeWidgetProvider : AppWidgetProvider() {
val isNightModeOn = (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES val isNightModeOn = (context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES
val params = hashMapOf(
"widgetId" to widgetId,
"sizesDip" to sizesDip,
"devicePixelRatio" to getDevicePixelRatio(),
"drawEntryImage" to drawEntryImage,
"reuseEntry" to reuseEntry,
"isSystemThemeDark" to isNightModeOn,
).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
put("cornerRadiusPx", context.resources.getDimension(android.R.dimen.system_app_widget_background_radius))
}
}
initFlutterEngine(context) initFlutterEngine(context)
val messenger = flutterEngine!!.dartExecutor
val channel = MethodChannel(messenger, WIDGET_DRAW_CHANNEL)
try { try {
val props = suspendCoroutine<Any?> { cont -> val props = suspendCoroutine { cont ->
defaultScope.launch { defaultScope.launch {
FlutterUtils.runOnUiThread { FlutterUtils.runOnUiThread {
channel.invokeMethod("drawWidget", hashMapOf( tryDrawWidget(params, cont, 0)
"widgetId" to widgetId,
"sizesDip" to sizesDip,
"devicePixelRatio" to getDevicePixelRatio(),
"drawEntryImage" to drawEntryImage,
"reuseEntry" to reuseEntry,
"isSystemThemeDark" to isNightModeOn,
).apply {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
put("cornerRadiusPx", context.resources.getDimension(android.R.dimen.system_app_widget_background_radius))
}
}, object : MethodChannel.Result {
override fun success(result: Any?) {
cont.resume(result)
}
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
cont.resumeWithException(Exception("$errorCode: $errorMessage\n$errorDetails"))
}
override fun notImplemented() {
cont.resumeWithException(Exception("not implemented"))
}
})
} }
} }
} }
@ -150,6 +150,30 @@ class HomeWidgetProvider : AppWidgetProvider() {
return null return null
} }
private fun tryDrawWidget(params: HashMap<String, Any>, cont: Continuation<Any?>, drawRetry: Int) {
val messenger = flutterEngine!!.dartExecutor
val channel = MethodChannel(messenger, WIDGET_DRAW_CHANNEL)
channel.invokeMethod("drawWidget", params, object : MethodChannel.Result {
override fun success(result: Any?) {
cont.resume(result)
}
override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) {
cont.resumeWithException(Exception("$errorCode: $errorMessage\n$errorDetails"))
}
override fun notImplemented() {
if (drawRetry > DRAW_RETRY_MAX) {
cont.resumeWithException(Exception("not implemented"))
} else {
Handler(Looper.getMainLooper()).postDelayed({
tryDrawWidget(params, cont, drawRetry + 1)
}, 2000L)
}
}
})
}
private fun updateWidgetImage( private fun updateWidgetImage(
context: Context, context: Context,
appWidgetManager: AppWidgetManager, appWidgetManager: AppWidgetManager,
@ -271,6 +295,7 @@ class HomeWidgetProvider : AppWidgetProvider() {
private val LOG_TAG = LogUtils.createTag<HomeWidgetProvider>() private val LOG_TAG = LogUtils.createTag<HomeWidgetProvider>()
private const val WIDGET_DART_ENTRYPOINT = "widgetMain" private const val WIDGET_DART_ENTRYPOINT = "widgetMain"
private const val WIDGET_DRAW_CHANNEL = "deckers.thibault/aves/widget_draw" private const val WIDGET_DRAW_CHANNEL = "deckers.thibault/aves/widget_draw"
private const val DRAW_RETRY_MAX = 5
private var flutterEngine: FlutterEngine? = null private var flutterEngine: FlutterEngine? = null
private var imageByteFetchJob: Job? = null private var imageByteFetchJob: Job? = null

View file

@ -59,7 +59,14 @@ class MediaStoreHandler(private val context: Context) : MethodCallHandler {
private fun getGeneration(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) { private fun getGeneration(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
val generation = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { val generation = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
MediaStore.getGeneration(context, MediaStore.VOLUME_EXTERNAL_PRIMARY) try {
MediaStore.getGeneration(context, MediaStore.VOLUME_EXTERNAL_PRIMARY)
} catch (e: Exception) {
// may yield `IllegalArgumentException: Volume external_primary not found`
val volumes = MediaStore.getExternalVolumeNames(context).joinToString(", ")
result.error("getGeneration-primary", e.message + " (available volumes are $volumes)", e)
return
}
} else { } else {
null null
} }

View file

@ -6,7 +6,7 @@
<string name="search_shortcut_short_label">Zoeken</string> <string name="search_shortcut_short_label">Zoeken</string>
<string name="videos_shortcut_short_label">Videos</string> <string name="videos_shortcut_short_label">Videos</string>
<string name="analysis_channel_name">Media indexeren</string> <string name="analysis_channel_name">Media indexeren</string>
<string name="analysis_notification_default_title">Indexeren van media</string> <string name="analysis_notification_default_title">Media indexeren</string>
<string name="analysis_notification_action_stop">Stoppen</string> <string name="analysis_notification_action_stop">Stoppen</string>
<string name="safe_mode_shortcut_short_label">Veilige modus</string> <string name="safe_mode_shortcut_short_label">Veilige modus</string>
</resources> </resources>

View file

@ -7,6 +7,6 @@
<string name="videos_shortcut_short_label">Videor</string> <string name="videos_shortcut_short_label">Videor</string>
<string name="analysis_channel_name">Media scanning</string> <string name="analysis_channel_name">Media scanning</string>
<string name="analysis_notification_default_title">Scannar media</string> <string name="analysis_notification_default_title">Scannar media</string>
<string name="analysis_notification_action_stop">Stop</string> <string name="analysis_notification_action_stop">Stopp</string>
<string name="search_shortcut_short_label">Sök</string> <string name="search_shortcut_short_label">Sök</string>
</resources> </resources>

View file

@ -1,4 +0,0 @@
In v1.11.1:
- watch videos with SRT subtitle files
- enjoy the app in Persian
Full changelog available on GitHub

View file

@ -1,4 +0,0 @@
In v1.11.1:
- watch videos with SRT subtitle files
- enjoy the app in Persian
Full changelog available on GitHub

View file

@ -1,3 +0,0 @@
In v1.11.2:
- show selected albums together in Collection
Full changelog available on GitHub

View file

@ -1,3 +0,0 @@
In v1.11.2:
- show selected albums together in Collection
Full changelog available on GitHub

View file

@ -1,3 +0,0 @@
In v1.11.3:
- show selected albums together in Collection
Full changelog available on GitHub

View file

@ -1,3 +0,0 @@
In v1.11.3:
- show selected albums together in Collection
Full changelog available on GitHub

View file

@ -1,4 +0,0 @@
In v1.11.4:
- explore your collection with the... explorer
- convert your motion photos to stills in bulk
Full changelog available on GitHub

View file

@ -1,4 +0,0 @@
In v1.11.4:
- explore your collection with the... explorer
- convert your motion photos to stills in bulk
Full changelog available on GitHub

View file

@ -0,0 +1,5 @@
In v1.11.9:
- peruse more options to tag or move via quick actions
- read long descriptions right from the overlay
- sort videos by duration
Full changelog available on GitHub

View file

@ -0,0 +1,5 @@
In v1.11.9:
- peruse more options to tag or move via quick actions
- read long descriptions right from the overlay
- sort videos by duration
Full changelog available on GitHub

View file

@ -1536,5 +1536,17 @@
"chipActionGoToExplorerPage": "عرض في المستكشف", "chipActionGoToExplorerPage": "عرض في المستكشف",
"@chipActionGoToExplorerPage": {}, "@chipActionGoToExplorerPage": {},
"explorerPageTitle": "المستكشف", "explorerPageTitle": "المستكشف",
"@explorerPageTitle": {} "@explorerPageTitle": {},
"explorerActionSelectStorageVolume": "حدد التخزين",
"@explorerActionSelectStorageVolume": {},
"setHomeCustom": "مخصص",
"@setHomeCustom": {},
"selectStorageVolumeDialogTitle": "حدد التَخزين",
"@selectStorageVolumeDialogTitle": {},
"sortOrderShortestFirst": "الأقصر أولاً",
"@sortOrderShortestFirst": {},
"sortOrderLongestFirst": "الأطول أولاً",
"@sortOrderLongestFirst": {},
"sortByDuration": "حسب المدة",
"@sortByDuration": {}
} }

View file

@ -1536,5 +1536,17 @@
"chipActionGoToExplorerPage": "Паказаць у Правадыру", "chipActionGoToExplorerPage": "Паказаць у Правадыру",
"@chipActionGoToExplorerPage": {}, "@chipActionGoToExplorerPage": {},
"explorerPageTitle": "Правадыр", "explorerPageTitle": "Правадыр",
"@explorerPageTitle": {} "@explorerPageTitle": {},
"sortByDuration": "Па працягласці",
"@sortByDuration": {},
"sortOrderShortestFirst": "Спачатку самы кароткі",
"@sortOrderShortestFirst": {},
"sortOrderLongestFirst": "Спачатку самы доўгі",
"@sortOrderLongestFirst": {},
"explorerActionSelectStorageVolume": "Выбраць сховішча",
"@explorerActionSelectStorageVolume": {},
"selectStorageVolumeDialogTitle": "Выбраць сховішча",
"@selectStorageVolumeDialogTitle": {},
"setHomeCustom": "Па-свойму",
"@setHomeCustom": {}
} }

View file

@ -723,6 +723,7 @@
"sortBySize": "By size", "sortBySize": "By size",
"sortByAlbumFileName": "By album & file name", "sortByAlbumFileName": "By album & file name",
"sortByRating": "By rating", "sortByRating": "By rating",
"sortByDuration": "By duration",
"sortOrderNewestFirst": "Newest first", "sortOrderNewestFirst": "Newest first",
"sortOrderOldestFirst": "Oldest first", "sortOrderOldestFirst": "Oldest first",
@ -732,6 +733,8 @@
"sortOrderLowestFirst": "Lowest first", "sortOrderLowestFirst": "Lowest first",
"sortOrderLargestFirst": "Largest first", "sortOrderLargestFirst": "Largest first",
"sortOrderSmallestFirst": "Smallest first", "sortOrderSmallestFirst": "Smallest first",
"sortOrderShortestFirst": "Shortest first",
"sortOrderLongestFirst": "Longest first",
"albumGroupTier": "By tier", "albumGroupTier": "By tier",
"albumGroupType": "By type", "albumGroupType": "By type",

View file

@ -1384,5 +1384,11 @@
"setHomeCustom": "Personalizado", "setHomeCustom": "Personalizado",
"@setHomeCustom": {}, "@setHomeCustom": {},
"explorerActionSelectStorageVolume": "Seleccionar almacenamiento", "explorerActionSelectStorageVolume": "Seleccionar almacenamiento",
"@explorerActionSelectStorageVolume": {} "@explorerActionSelectStorageVolume": {},
"sortByDuration": "Por duración",
"@sortByDuration": {},
"sortOrderShortestFirst": "El más corto primero",
"@sortOrderShortestFirst": {},
"sortOrderLongestFirst": "El más largo primero",
"@sortOrderLongestFirst": {}
} }

View file

@ -1384,5 +1384,11 @@
"explorerActionSelectStorageVolume": "Choisir le stockage", "explorerActionSelectStorageVolume": "Choisir le stockage",
"@explorerActionSelectStorageVolume": {}, "@explorerActionSelectStorageVolume": {},
"selectStorageVolumeDialogTitle": "Volumes de stockage", "selectStorageVolumeDialogTitle": "Volumes de stockage",
"@selectStorageVolumeDialogTitle": {} "@selectStorageVolumeDialogTitle": {},
"sortByDuration": "par durée",
"@sortByDuration": {},
"sortOrderShortestFirst": "Plus courts dabord",
"@sortOrderShortestFirst": {},
"sortOrderLongestFirst": "Plus longs dabord",
"@sortOrderLongestFirst": {}
} }

View file

@ -101,5 +101,448 @@
"chipActionGoToCountryPage": "देशों में दिखाएं", "chipActionGoToCountryPage": "देशों में दिखाएं",
"@chipActionGoToCountryPage": {}, "@chipActionGoToCountryPage": {},
"chipActionHide": "छिपाए", "chipActionHide": "छिपाए",
"@chipActionHide": {} "@chipActionHide": {},
"chipActionShowCollection": "कोलेक्शन में दिखाए",
"@chipActionShowCollection": {},
"chipActionFilterOut": "फिल्टर करें",
"@chipActionFilterOut": {},
"chipActionLock": "लॉक",
"@chipActionLock": {},
"chipActionPin": "शीर्ष पर पिन करें",
"@chipActionPin": {},
"chipActionGoToExplorerPage": "एक्सप्लोरर में दिखाए",
"@chipActionGoToExplorerPage": {},
"entryActionRotateCW": "दक्षिणावर्त घुमाएं",
"@entryActionRotateCW": {},
"entryActionViewSource": "सोर्स देखें",
"@entryActionViewSource": {},
"entryActionShowGeoTiffOnMap": "मैप ओवरले के रूप में देखे",
"@entryActionShowGeoTiffOnMap": {},
"entryActionViewMotionPhotoVideo": "वीडियो खोलें",
"@entryActionViewMotionPhotoVideo": {},
"entryActionOpen": "के साथ खोलें",
"@entryActionOpen": {},
"entryActionRemoveFavourite": "पसंदीदा से निकालें",
"@entryActionRemoveFavourite": {},
"entryInfoActionRemoveMetadata": "मेटाडाटा हटाएं",
"@entryInfoActionRemoveMetadata": {},
"entryInfoActionRemoveLocation": "लोकेशन हटाएं",
"@entryInfoActionRemoveLocation": {},
"entryActionFlip": "क्षैतिज फ्लिप करे",
"@entryActionFlip": {},
"entryActionShareVideoOnly": "केवल वीडियो शेयर करें",
"@entryActionShareVideoOnly": {},
"entryActionConvertMotionPhotoToStillImage": "स्थिर छवि में परिवर्तित करें",
"@entryActionConvertMotionPhotoToStillImage": {},
"entryActionOpenMap": "मैप एप में दिखाएं",
"@entryActionOpenMap": {},
"entryActionSetAs": "के रूप में सेट करें",
"@entryActionSetAs": {},
"entryActionRotateScreen": "स्क्रीन घुमाएँ",
"@entryActionRotateScreen": {},
"videoActionCaptureFrame": "फ्रेम कैप्चर करें",
"@videoActionCaptureFrame": {},
"chipActionCreateAlbum": "एल्बम बनाएं",
"@chipActionCreateAlbum": {},
"chipActionCreateVault": "वॉल्ट बनाएं",
"@chipActionCreateVault": {},
"videoActionPlay": "चलाएं",
"@videoActionPlay": {},
"videoActionReplay10": "10 सेकंड्स पीछे ले",
"@videoActionReplay10": {},
"videoActionSkip10": "10 सेकंड्स आगे लें",
"@videoActionSkip10": {},
"videoActionUnmute": "अनम्यूट करे",
"@videoActionUnmute": {},
"slideshowActionShowInCollection": "संग्रह में दिखाएं",
"@slideshowActionShowInCollection": {},
"slideshowActionResume": "रिज्यूम करें",
"@slideshowActionResume": {},
"filterTypeAnimatedLabel": "एनिमेटेड",
"@filterTypeAnimatedLabel": {},
"filterTypeMotionPhotoLabel": "मोशन फोटो",
"@filterTypeMotionPhotoLabel": {},
"filterTypePanoramaLabel": "पैनोरमा",
"@filterTypePanoramaLabel": {},
"sourceStateLocatingCountries": "देश खोज रहे हैं",
"@sourceStateLocatingCountries": {},
"sourceStateLocatingPlaces": "स्थान खोज रहें हैं",
"@sourceStateLocatingPlaces": {},
"chipActionFilterIn": "में फिल्टर करें",
"@chipActionFilterIn": {},
"chipActionRename": "नाम बदले",
"@chipActionRename": {},
"chipActionUnpin": "शीर्ष से अनपिन करें",
"@chipActionUnpin": {},
"entryActionEdit": "एडिट करें",
"@entryActionEdit": {},
"videoActionMute": "म्यूट करे",
"@videoActionMute": {},
"videoActionPause": "रोके",
"@videoActionPause": {},
"entryInfoActionEditTags": "टैग्स एडिट करे",
"@entryInfoActionEditTags": {},
"filterOnThisDayLabel": "इस दिन पर",
"@filterOnThisDayLabel": {},
"filterTypeSphericalVideoLabel": "360° वीडियो",
"@filterTypeSphericalVideoLabel": {},
"filterMimeVideoLabel": "वीडियो",
"@filterMimeVideoLabel": {},
"viewerActionSettings": "सैटिंग",
"@viewerActionSettings": {},
"entryActionCast": "कास्ट करें",
"@entryActionCast": {},
"entryInfoActionExportMetadata": "मेटाडाटा एक्सपोर्ट करें",
"@entryInfoActionExportMetadata": {},
"chipActionSetCover": "कवर सेट करें",
"@chipActionSetCover": {},
"entryActionCopyToClipboard": "क्लिपबोर्ड पर कॉपी करें",
"@entryActionCopyToClipboard": {},
"entryActionDelete": "मिटाएं",
"@entryActionDelete": {},
"entryActionExport": "एक्सपोर्ट करें",
"@entryActionExport": {},
"entryActionInfo": "जानकारी",
"@entryActionInfo": {},
"entryActionConvert": "बदले",
"@entryActionConvert": {},
"entryActionRename": "नाम बदलें",
"@entryActionRename": {},
"entryActionRestore": "रिस्टोर करे",
"@entryActionRestore": {},
"entryActionRotateCCW": "वामावर्त स्थिति में घुमाएं",
"@entryActionRotateCCW": {},
"entryActionPrint": "प्रिंट करे",
"@entryActionPrint": {},
"entryActionShare": "शेयर करे",
"@entryActionShare": {},
"entryActionShareImageOnly": "केवल इमेज शेयर करें",
"@entryActionShareImageOnly": {},
"entryActionAddFavourite": "पसंदीदा में जोड़े",
"@entryActionAddFavourite": {},
"videoActionSelectStreams": "ट्रैक्स को चुने",
"@videoActionSelectStreams": {},
"videoActionSetSpeed": "चलाने की गति",
"@videoActionSetSpeed": {},
"entryInfoActionEditDate": "दिनांक व समय एडिट करे",
"@entryInfoActionEditDate": {},
"entryInfoActionEditLocation": "लोकेशन एडिट करे",
"@entryInfoActionEditLocation": {},
"entryInfoActionEditRating": "रेटिंग एडिट करे",
"@entryInfoActionEditRating": {},
"editorTransformRotate": "घुमाएं",
"@editorTransformRotate": {},
"cropAspectRatioOriginal": "ओरिजनल",
"@cropAspectRatioOriginal": {},
"filterFavouriteLabel": "पसंदीदा",
"@filterFavouriteLabel": {},
"filterRecentlyAddedLabel": "हाल ही में शामिल की गई",
"@filterRecentlyAddedLabel": {},
"filterMimeImageLabel": "इमेज",
"@filterMimeImageLabel": {},
"keepScreenOnVideoPlayback": "वीडियो प्लेबैक के दौरान",
"@keepScreenOnVideoPlayback": {},
"displayRefreshRatePreferLowest": "न्यूनतम दर",
"@displayRefreshRatePreferLowest": {},
"nameConflictStrategyRename": "नाम बदलें",
"@nameConflictStrategyRename": {},
"unitSystemMetric": "Metric",
"@unitSystemMetric": {},
"viewerTransitionSlide": "स्लाइड",
"@viewerTransitionSlide": {},
"viewerTransitionFade": "फेड",
"@viewerTransitionFade": {},
"newAlbumDialogStorageLabel": "स्टोरेज:",
"@newAlbumDialogStorageLabel": {},
"newVaultDialogTitle": "नया वॉल्ट",
"@newVaultDialogTitle": {},
"vaultDialogLockModeWhenScreenOff": "लॉक करे,जब स्क्रीन बंद हो जाती है",
"@vaultDialogLockModeWhenScreenOff": {},
"filterTaggedLabel": "टैग किया गया",
"@filterTaggedLabel": {},
"mapStyleGoogleTerrain": "गूगल मैप्स (टेरेन)",
"@mapStyleGoogleTerrain": {},
"themeBrightnessDark": "Dark",
"@themeBrightnessDark": {},
"themeBrightnessBlack": "Black",
"@themeBrightnessBlack": {},
"videoControlsPlaySeek": "पिछड़े / आगे की तलाश करें",
"@videoControlsPlaySeek": {},
"mapStyleOsmHot": "Humanitarian OSM",
"@mapStyleOsmHot": {},
"filterAspectRatioPortraitLabel": "पोर्ट्रेट",
"@filterAspectRatioPortraitLabel": {},
"filterNoAddressLabel": "एड्रेस रहित",
"@filterNoAddressLabel": {},
"filterNoRatingLabel": "रेट नहीं किया गया",
"@filterNoRatingLabel": {},
"filterRatingRejectedLabel": "अस्वीकृत",
"@filterRatingRejectedLabel": {},
"mapStyleGoogleNormal": "गूगल मैप्स",
"@mapStyleGoogleNormal": {},
"mapStyleGoogleHybrid": "गूगल मैप्स (हाइब्रिड)",
"@mapStyleGoogleHybrid": {},
"mapStyleStamenWatercolor": "Stamen Watercolor",
"@mapStyleStamenWatercolor": {},
"unitSystemImperial": "Imperial",
"@unitSystemImperial": {},
"passwordDialogEnter": "पासवर्ड दर्ज करें",
"@passwordDialogEnter": {},
"filterBinLabel": "रीसाइकल बिन",
"@filterBinLabel": {},
"filterTypeRawLabel": "Raw",
"@filterTypeRawLabel": {},
"albumTierVaults": "संदूक",
"@albumTierVaults": {},
"albumTierRegular": "अन्य",
"@albumTierRegular": {},
"coordinateFormatDms": "DMS",
"@coordinateFormatDms": {},
"coordinateFormatDecimal": "Decimal degrees",
"@coordinateFormatDecimal": {},
"coordinateDms": "{coordinate} {direction}",
"@coordinateDms": {
"placeholders": {
"coordinate": {
"type": "String",
"example": "38° 41 47.72″"
},
"direction": {
"type": "String",
"example": "S"
}
}
},
"coordinateDmsSouth": "द",
"@coordinateDmsSouth": {},
"coordinateDmsWest": "प",
"@coordinateDmsWest": {},
"displayRefreshRatePreferHighest": "उच्चतम दर",
"@displayRefreshRatePreferHighest": {},
"lengthUnitPixel": "px",
"@lengthUnitPixel": {},
"subtitlePositionTop": "शीर्ष",
"@subtitlePositionTop": {},
"subtitlePositionBottom": "नीचे",
"@subtitlePositionBottom": {},
"videoLoopModeNever": "कभी नहीं",
"@videoLoopModeNever": {},
"videoPlaybackMuted": "बिना ध्वनि के चलाएं",
"@videoPlaybackMuted": {},
"viewerTransitionNone": "कोई नहीं",
"@viewerTransitionNone": {},
"nameConflictDialogSingleSourceMessage": "गंतव्य फ़ोल्डर में कुछ फ़ाइलों का नाम समान है।।",
"@nameConflictDialogSingleSourceMessage": {},
"noMatchingAppDialogMessage": "इसमें कोई ऐप नहीं है जो इसे संभाल सकता है।।",
"@noMatchingAppDialogMessage": {},
"binEntriesConfirmationDialogMessage": "{count, plural, =1{Move this item to the recycle bin?} other{Move these {count} items to the recycle bin?}}",
"@binEntriesConfirmationDialogMessage": {
"placeholders": {
"count": {
"format": "decimalPattern"
}
}
},
"deleteEntriesConfirmationDialogMessage": "{count, plural, =1{Delete this item?} other{Delete these {count} items?}}",
"@deleteEntriesConfirmationDialogMessage": {
"placeholders": {
"count": {
"format": "decimalPattern"
}
}
},
"newAlbumDialogTitle": "नया एल्बम",
"@newAlbumDialogTitle": {},
"newAlbumDialogNameLabel": "एल्बम का नाम",
"@newAlbumDialogNameLabel": {},
"pinDialogEnter": "पिन दर्ज करें",
"@pinDialogEnter": {},
"pinDialogConfirm": "पिन कन्फर्म करें",
"@pinDialogConfirm": {},
"passwordDialogConfirm": "पासवर्ड कन्फर्म करें",
"@passwordDialogConfirm": {},
"videoLoopModeAlways": "हमेशा",
"@videoLoopModeAlways": {},
"videoPlaybackSkip": "छोड़े",
"@videoPlaybackSkip": {},
"newAlbumDialogNameLabelAlreadyExistsHelper": "डायरेक्टरी पहले से मौजूद",
"@newAlbumDialogNameLabelAlreadyExistsHelper": {},
"coordinateDmsEast": "पू",
"@coordinateDmsEast": {},
"moveUndatedConfirmationDialogSetDate": "तारीख सहेजें",
"@moveUndatedConfirmationDialogSetDate": {},
"notEnoughSpaceDialogMessage": "इस ऑपरेशन को पूरा करने के लिए \"{volume}\" पर “{neededSize}” खाली जगह की आवश्यकता है, लेकिन केवल {freeSize} जगह है।।",
"@notEnoughSpaceDialogMessage": {
"placeholders": {
"neededSize": {
"type": "String",
"example": "314 MB"
},
"freeSize": {
"type": "String",
"example": "123 MB"
},
"volume": {
"type": "String",
"example": "SD card",
"description": "the name of a storage volume"
}
}
},
"renameAlbumDialogLabel": "नया नाम",
"@renameAlbumDialogLabel": {},
"wallpaperTargetLock": "लॉक स्क्रीन",
"@wallpaperTargetLock": {},
"unsupportedTypeDialogMessage": "{count, plural, =1{This operation is not supported for items of the following type: {types}.} other{This operation is not supported for items of the following types: {types}.}}",
"@unsupportedTypeDialogMessage": {
"placeholders": {
"count": {},
"types": {
"type": "String",
"example": "GIF, TIFF, MP4",
"description": "a list of unsupported types"
}
}
},
"restrictedAccessDialogMessage": "इस एप्लिकेशन को \"{volume}\" की {directory} में फ़ाइलों को संशोधित करने की अनुमति नहीं है।\n\nकृपया किसी अन्य directory में आइटम स्थानांतरित करने के लिए एक पूर्व-स्थापित फ़ाइल प्रबंधक या गैलरी ऐप का उपयोग करें।।",
"@restrictedAccessDialogMessage": {
"placeholders": {
"directory": {
"type": "String",
"description": "the name of a directory, using the output of `rootDirectoryDescription` or `otherDirectoryDescription`"
},
"volume": {
"type": "String",
"example": "SD card",
"description": "the name of a storage volume"
}
}
},
"videoResumeDialogMessage": "क्या आप {time} पर पुन: चलाना चाहते हैं?",
"@videoResumeDialogMessage": {
"placeholders": {
"time": {
"type": "String",
"example": "13:37"
}
}
},
"videoStartOverButtonLabel": "पुन: प्रारंभ करें",
"@videoStartOverButtonLabel": {},
"vaultDialogLockTypeLabel": "लॉक प्रकार",
"@vaultDialogLockTypeLabel": {},
"patternDialogConfirm": "पैटर्न कन्फर्म करे",
"@patternDialogConfirm": {},
"patternDialogEnter": "पैटर्न दर्ज करें",
"@patternDialogEnter": {},
"authenticateToUnlockVault": "वॉल्ट को अनलॉक करने के लिए प्रमाणीकरण करें",
"@authenticateToUnlockVault": {},
"settingsVideoEnablePip": "पिक्चर-इन-पिक्चर",
"@settingsVideoEnablePip": {},
"videoControlsPlay": "चलाएं",
"@videoControlsPlay": {},
"videoControlsPlayOutside": "अन्य प्लेयर के साथ खोलें",
"@videoControlsPlayOutside": {},
"videoControlsNone": "कोई नहीं",
"@videoControlsNone": {},
"videoLoopModeShortOnly": "केवल लघु वीडियो",
"@videoLoopModeShortOnly": {},
"videoPlaybackWithSound": "ध्वनि के साथ चलाए",
"@videoPlaybackWithSound": {},
"viewerTransitionParallax": "पैरालैक्स",
"@viewerTransitionParallax": {},
"viewerTransitionZoomIn": "ज़ूम इन",
"@viewerTransitionZoomIn": {},
"wallpaperTargetHome": "होम स्क्रीन",
"@wallpaperTargetHome": {},
"widgetDisplayedItemRandom": "यादृच्छिक",
"@widgetDisplayedItemRandom": {},
"videoResumeButtonLabel": "पुन: चलाएं",
"@videoResumeButtonLabel": {},
"filterNoTitleLabel": "शीर्षकहीन",
"@filterNoTitleLabel": {},
"stopTooltip": "रोके",
"@stopTooltip": {},
"vaultLockTypePin": "पिन",
"@vaultLockTypePin": {},
"wallpaperTargetHomeLock": "होम और लॉक स्क्रीन",
"@wallpaperTargetHomeLock": {},
"widgetDisplayedItemMostRecent": "हाल ही के",
"@widgetDisplayedItemMostRecent": {},
"filterAspectRatioLandscapeLabel": "लैंडस्केप",
"@filterAspectRatioLandscapeLabel": {},
"filterNoDateLabel": "अदिनांकित",
"@filterNoDateLabel": {},
"filterNoTagLabel": "टैग नहीं किया गया",
"@filterNoTagLabel": {},
"filterTypeGeotiffLabel": "GeoTIFF",
"@filterTypeGeotiffLabel": {},
"accessibilityAnimationsKeep": "स्क्रीन प्रभाव रखें",
"@accessibilityAnimationsKeep": {},
"vaultLockTypePattern": "पैटर्न",
"@vaultLockTypePattern": {},
"coordinateDmsNorth": "उ",
"@coordinateDmsNorth": {},
"albumTierNew": "नया",
"@albumTierNew": {},
"albumTierApps": "ऐप्स",
"@albumTierApps": {},
"lengthUnitPercent": "%",
"@lengthUnitPercent": {},
"nameConflictStrategyReplace": "बदलें",
"@nameConflictStrategyReplace": {},
"themeBrightnessLight": "Light",
"@themeBrightnessLight": {},
"vaultLockTypePassword": "पासवर्ड",
"@vaultLockTypePassword": {},
"storageVolumeDescriptionFallbackNonPrimary": "एसडी कार्ड",
"@storageVolumeDescriptionFallbackNonPrimary": {},
"storageAccessDialogMessage": "अगले स्क्रीन में \"{volume}\" के {directory} का चयन करें ताकि यह ऐप इसके लिए पहुंच सके।।",
"@storageAccessDialogMessage": {
"placeholders": {
"directory": {
"type": "String",
"description": "the name of a directory, using the output of `rootDirectoryDescription` or `otherDirectoryDescription`"
},
"volume": {
"type": "String",
"example": "SD card",
"description": "the name of a storage volume"
}
}
},
"rootDirectoryDescription": "root directory",
"@rootDirectoryDescription": {},
"otherDirectoryDescription": "“{name}” directory",
"@otherDirectoryDescription": {
"placeholders": {
"name": {
"type": "String",
"example": "Pictures",
"description": "the name of a specific directory"
}
}
},
"missingSystemFilePickerDialogMessage": "सिस्टम फ़ाइल पिकर लापता या अक्षम है। कृपया इसे सक्षम करें और फिर से प्रयास करें।।",
"@missingSystemFilePickerDialogMessage": {},
"nameConflictDialogMultipleSourceMessage": "कुछ फ़ाइलों का नाम समान है।।",
"@nameConflictDialogMultipleSourceMessage": {},
"addShortcutDialogLabel": "शॉर्टकट लेबल",
"@addShortcutDialogLabel": {},
"newVaultWarningDialogMessage": "वॉल्ट में आइटम केवल इस ऐप के लिए व अन्य के लिए नहीं उपलब्ध हैं।\n\nयदि आप इस ऐप को अनइंस्टॉल करते हैं, या इस ऐप डेटा को साफ़ करते हैं, तो आप इन सभी आइटम को खो देंगे।।",
"@newVaultWarningDialogMessage": {},
"configureVaultDialogTitle": "वॉल्ट को कॉन्फ़िगर करना",
"@configureVaultDialogTitle": {},
"moveUndatedConfirmationDialogMessage": "आगे बढ़ने से पहले आइटम की तारीख सेव करे?",
"@moveUndatedConfirmationDialogMessage": {},
"setCoverDialogLatest": "नवीनतम आइटम",
"@setCoverDialogLatest": {},
"hideFilterConfirmationDialogMessage": "मैचिंग तस्वीरें और वीडियो आपके कलेक्शन से छिपे होंगे। आप उन्हें फिर से \"गोपनीयता\" सेटिंग्स से दिखा सकते हैं।\n\nक्या आप उन्हें छिपाना चाहते हैं?",
"@hideFilterConfirmationDialogMessage": {},
"renameEntrySetPageTitle": "नाम बदलें",
"@renameEntrySetPageTitle": {},
"authenticateToConfigureVault": "वॉल्ट को कॉन्फ़िगर करने के लिए प्रमाणीकरण करें",
"@authenticateToConfigureVault": {},
"renameAlbumDialogLabelAlreadyExistsHelper": "डायरेक्टरी पहले से मौजूद",
"@renameAlbumDialogLabelAlreadyExistsHelper": {}
} }

View file

@ -1374,5 +1374,11 @@
"renameProcessorHash": "Hash", "renameProcessorHash": "Hash",
"@renameProcessorHash": {}, "@renameProcessorHash": {},
"chipActionShowCollection": "Tampilkan di Koleksi", "chipActionShowCollection": "Tampilkan di Koleksi",
"@chipActionShowCollection": {} "@chipActionShowCollection": {},
"explorerActionSelectStorageVolume": "Pilih penyimpanan",
"@explorerActionSelectStorageVolume": {},
"selectStorageVolumeDialogTitle": "Pilih Penyimpanan",
"@selectStorageVolumeDialogTitle": {},
"setHomeCustom": "Kustom",
"@setHomeCustom": {}
} }

View file

@ -1384,5 +1384,11 @@
"explorerActionSelectStorageVolume": "저장공간 선택", "explorerActionSelectStorageVolume": "저장공간 선택",
"@explorerActionSelectStorageVolume": {}, "@explorerActionSelectStorageVolume": {},
"selectStorageVolumeDialogTitle": "저장공간", "selectStorageVolumeDialogTitle": "저장공간",
"@selectStorageVolumeDialogTitle": {} "@selectStorageVolumeDialogTitle": {},
"sortByDuration": "길이",
"@sortByDuration": {},
"sortOrderShortestFirst": "짧은 순",
"@sortOrderShortestFirst": {},
"sortOrderLongestFirst": "긴 순",
"@sortOrderLongestFirst": {}
} }

View file

@ -65,11 +65,11 @@
"@sourceStateLocatingPlaces": {}, "@sourceStateLocatingPlaces": {},
"chipActionDelete": "Verwijderen", "chipActionDelete": "Verwijderen",
"@chipActionDelete": {}, "@chipActionDelete": {},
"chipActionGoToAlbumPage": "Tonen Albums", "chipActionGoToAlbumPage": "In Albums tonen",
"@chipActionGoToAlbumPage": {}, "@chipActionGoToAlbumPage": {},
"chipActionGoToCountryPage": "Tonen in Landen", "chipActionGoToCountryPage": "In Landen tonen",
"@chipActionGoToCountryPage": {}, "@chipActionGoToCountryPage": {},
"chipActionGoToTagPage": "Tonen in Labels", "chipActionGoToTagPage": "In Labels tonen",
"@chipActionGoToTagPage": {}, "@chipActionGoToTagPage": {},
"chipActionFilterOut": "Uitfilteren", "chipActionFilterOut": "Uitfilteren",
"@chipActionFilterOut": {}, "@chipActionFilterOut": {},
@ -113,7 +113,7 @@
"@entryActionShare": {}, "@entryActionShare": {},
"entryActionViewSource": "Bron bekijken", "entryActionViewSource": "Bron bekijken",
"@entryActionViewSource": {}, "@entryActionViewSource": {},
"entryActionShowGeoTiffOnMap": "Tonen als map overlay", "entryActionShowGeoTiffOnMap": "Als kaart-overlay tonen",
"@entryActionShowGeoTiffOnMap": {}, "@entryActionShowGeoTiffOnMap": {},
"entryActionConvertMotionPhotoToStillImage": "Converteren naar stilstaand beeld", "entryActionConvertMotionPhotoToStillImage": "Converteren naar stilstaand beeld",
"@entryActionConvertMotionPhotoToStillImage": {}, "@entryActionConvertMotionPhotoToStillImage": {},
@ -125,7 +125,7 @@
"@entryActionOpen": {}, "@entryActionOpen": {},
"entryActionSetAs": "Instellen als", "entryActionSetAs": "Instellen als",
"@entryActionSetAs": {}, "@entryActionSetAs": {},
"entryActionOpenMap": "Tonen in map app", "entryActionOpenMap": "In Kaarten-app tonen",
"@entryActionOpenMap": {}, "@entryActionOpenMap": {},
"entryActionRotateScreen": "Scherm roteren", "entryActionRotateScreen": "Scherm roteren",
"@entryActionRotateScreen": {}, "@entryActionRotateScreen": {},
@ -147,7 +147,7 @@
"@videoActionReplay10": {}, "@videoActionReplay10": {},
"videoActionSkip10": "10 seconden vooruit", "videoActionSkip10": "10 seconden vooruit",
"@videoActionSkip10": {}, "@videoActionSkip10": {},
"videoActionSelectStreams": "Tracks selecteren", "videoActionSelectStreams": "Sporen selecteren",
"@videoActionSelectStreams": {}, "@videoActionSelectStreams": {},
"videoActionSetSpeed": "Afspeelsnelheid", "videoActionSetSpeed": "Afspeelsnelheid",
"@videoActionSetSpeed": {}, "@videoActionSetSpeed": {},
@ -155,13 +155,13 @@
"@viewerActionSettings": {}, "@viewerActionSettings": {},
"slideshowActionResume": "Hervatten", "slideshowActionResume": "Hervatten",
"@slideshowActionResume": {}, "@slideshowActionResume": {},
"slideshowActionShowInCollection": "Tonen in Collectie", "slideshowActionShowInCollection": "In Collectie tonen",
"@slideshowActionShowInCollection": {}, "@slideshowActionShowInCollection": {},
"entryInfoActionEditDate": "Bewerk datum & tijd", "entryInfoActionEditDate": "Datum & tijd bewerken",
"@entryInfoActionEditDate": {}, "@entryInfoActionEditDate": {},
"entryInfoActionEditLocation": "Bewerk locatie", "entryInfoActionEditLocation": "Locatie bewerken",
"@entryInfoActionEditLocation": {}, "@entryInfoActionEditLocation": {},
"entryInfoActionEditTitleDescription": "Wijzig titel & omschrijving", "entryInfoActionEditTitleDescription": "Titel & beschrijving bewerken",
"@entryInfoActionEditTitleDescription": {}, "@entryInfoActionEditTitleDescription": {},
"entryInfoActionEditRating": "Waardering bewerken", "entryInfoActionEditRating": "Waardering bewerken",
"@entryInfoActionEditRating": {}, "@entryInfoActionEditRating": {},
@ -221,11 +221,11 @@
"@coordinateDmsWest": {}, "@coordinateDmsWest": {},
"unitSystemMetric": "Metrisch", "unitSystemMetric": "Metrisch",
"@unitSystemMetric": {}, "@unitSystemMetric": {},
"unitSystemImperial": "Imperiaal", "unitSystemImperial": "Brits-Amerikaans",
"@unitSystemImperial": {}, "@unitSystemImperial": {},
"videoLoopModeNever": "Nooit", "videoLoopModeNever": "Nooit",
"@videoLoopModeNever": {}, "@videoLoopModeNever": {},
"videoLoopModeShortOnly": "Enkel korte videos", "videoLoopModeShortOnly": "Alleen korte video's",
"@videoLoopModeShortOnly": {}, "@videoLoopModeShortOnly": {},
"videoLoopModeAlways": "Altijd", "videoLoopModeAlways": "Altijd",
"@videoLoopModeAlways": {}, "@videoLoopModeAlways": {},
@ -233,7 +233,7 @@
"@videoControlsPlay": {}, "@videoControlsPlay": {},
"videoControlsPlaySeek": "Speel & zoek terug/vooruit", "videoControlsPlaySeek": "Speel & zoek terug/vooruit",
"@videoControlsPlaySeek": {}, "@videoControlsPlaySeek": {},
"videoControlsPlayOutside": "Openen met andere speler", "videoControlsPlayOutside": "Met andere speler openen",
"@videoControlsPlayOutside": {}, "@videoControlsPlayOutside": {},
"videoControlsNone": "Geen", "videoControlsNone": "Geen",
"@videoControlsNone": {}, "@videoControlsNone": {},
@ -255,13 +255,13 @@
"@nameConflictStrategySkip": {}, "@nameConflictStrategySkip": {},
"keepScreenOnNever": "Nooit", "keepScreenOnNever": "Nooit",
"@keepScreenOnNever": {}, "@keepScreenOnNever": {},
"keepScreenOnViewerOnly": "Enkel Viewer pagina", "keepScreenOnViewerOnly": "Alleen Viewerpagina",
"@keepScreenOnViewerOnly": {}, "@keepScreenOnViewerOnly": {},
"keepScreenOnAlways": "Altijd", "keepScreenOnAlways": "Altijd",
"@keepScreenOnAlways": {}, "@keepScreenOnAlways": {},
"accessibilityAnimationsRemove": "Scherm effecten uitschakelen", "accessibilityAnimationsRemove": "Schermeffecten uitschakelen",
"@accessibilityAnimationsRemove": {}, "@accessibilityAnimationsRemove": {},
"accessibilityAnimationsKeep": "Scherm effecten houden", "accessibilityAnimationsKeep": "Schermeffecten behouden",
"@accessibilityAnimationsKeep": {}, "@accessibilityAnimationsKeep": {},
"displayRefreshRatePreferHighest": "Hoogste waardering", "displayRefreshRatePreferHighest": "Hoogste waardering",
"@displayRefreshRatePreferHighest": {}, "@displayRefreshRatePreferHighest": {},
@ -269,7 +269,7 @@
"@displayRefreshRatePreferLowest": {}, "@displayRefreshRatePreferLowest": {},
"videoPlaybackSkip": "Overslaan", "videoPlaybackSkip": "Overslaan",
"@videoPlaybackSkip": {}, "@videoPlaybackSkip": {},
"videoPlaybackMuted": "Gedempte afspelen", "videoPlaybackMuted": "Gedempt afspelen",
"@videoPlaybackMuted": {}, "@videoPlaybackMuted": {},
"videoPlaybackWithSound": "Met geluid afspelen", "videoPlaybackWithSound": "Met geluid afspelen",
"@videoPlaybackWithSound": {}, "@videoPlaybackWithSound": {},
@ -289,13 +289,13 @@
"@viewerTransitionZoomIn": {}, "@viewerTransitionZoomIn": {},
"viewerTransitionNone": "Geen", "viewerTransitionNone": "Geen",
"@viewerTransitionNone": {}, "@viewerTransitionNone": {},
"wallpaperTargetHome": "Home scherm", "wallpaperTargetHome": "Startscherm",
"@wallpaperTargetHome": {}, "@wallpaperTargetHome": {},
"wallpaperTargetLock": "Vergrendel scherm", "wallpaperTargetLock": "Vergrendelingsscherm",
"@wallpaperTargetLock": {}, "@wallpaperTargetLock": {},
"wallpaperTargetHomeLock": "Home and Vergrendel schermen", "wallpaperTargetHomeLock": "Start- en vergrendelingsschermen",
"@wallpaperTargetHomeLock": {}, "@wallpaperTargetHomeLock": {},
"widgetOpenPageHome": "Open startscherm", "widgetOpenPageHome": "Startscherm openen",
"@widgetOpenPageHome": {}, "@widgetOpenPageHome": {},
"albumTierNew": "Nieuw", "albumTierNew": "Nieuw",
"@albumTierNew": {}, "@albumTierNew": {},
@ -315,7 +315,7 @@
"@rootDirectoryDescription": {}, "@rootDirectoryDescription": {},
"otherDirectoryDescription": "“{name}” map", "otherDirectoryDescription": "“{name}” map",
"@otherDirectoryDescription": {}, "@otherDirectoryDescription": {},
"storageAccessDialogMessage": "Selecteer de {directory} van “{volume}”, in het volgende scherm om deze app er toegang toe te geven.", "storageAccessDialogMessage": "Selecteer in het volgende scherm de {directory} van “{volume}” om deze app er toegang toe te geven.",
"@storageAccessDialogMessage": {}, "@storageAccessDialogMessage": {},
"restrictedAccessDialogMessage": "Deze applicatie mag geen bestanden wijzigen in de {directory} van “{volume}”,.\n\n Gebruik een vooraf geïnstalleerde filemanager of galerij-app om de items naar een andere map te verplaatsen.", "restrictedAccessDialogMessage": "Deze applicatie mag geen bestanden wijzigen in de {directory} van “{volume}”,.\n\n Gebruik een vooraf geïnstalleerde filemanager of galerij-app om de items naar een andere map te verplaatsen.",
"@restrictedAccessDialogMessage": {}, "@restrictedAccessDialogMessage": {},
@ -335,17 +335,17 @@
"@addShortcutButtonLabel": {}, "@addShortcutButtonLabel": {},
"noMatchingAppDialogMessage": "Er zijn geen apps die dit ondersteunen.", "noMatchingAppDialogMessage": "Er zijn geen apps die dit ondersteunen.",
"@noMatchingAppDialogMessage": {}, "@noMatchingAppDialogMessage": {},
"binEntriesConfirmationDialogMessage": "{count, plural, =1{Dit item naar de prullenbak verplaatsen??} other{Verplaats deze {count} items naar de prullenbak?}}", "binEntriesConfirmationDialogMessage": "{count, plural, =1{Dit item naar de prullenbak verplaatsen??} other{Deze {count} items naar de prullenbak verplaatsen?}}",
"@binEntriesConfirmationDialogMessage": {}, "@binEntriesConfirmationDialogMessage": {},
"deleteEntriesConfirmationDialogMessage": "{count, plural, =1{Verwijder dit item?} other{Verwijder deze {count} items?}}", "deleteEntriesConfirmationDialogMessage": "{count, plural, =1{Dit item verwijderen?} other{Deze {count} items verwijderen?}}",
"@deleteEntriesConfirmationDialogMessage": {}, "@deleteEntriesConfirmationDialogMessage": {},
"moveUndatedConfirmationDialogMessage": "Datums opslaan voordat u doorgaat??", "moveUndatedConfirmationDialogMessage": "Datums opslaan alvorens door te gaan?",
"@moveUndatedConfirmationDialogMessage": {}, "@moveUndatedConfirmationDialogMessage": {},
"moveUndatedConfirmationDialogSetDate": "Datums opslaan", "moveUndatedConfirmationDialogSetDate": "Datums opslaan",
"@moveUndatedConfirmationDialogSetDate": {}, "@moveUndatedConfirmationDialogSetDate": {},
"videoResumeDialogMessage": "Wil je het afspelen hervatten op {time}?", "videoResumeDialogMessage": "Afspelen hervatten om {time}?",
"@videoResumeDialogMessage": {}, "@videoResumeDialogMessage": {},
"videoStartOverButtonLabel": "OPNIEUW BEGINNEN", "videoStartOverButtonLabel": "OPNIEUW AFSPELLEN",
"@videoStartOverButtonLabel": {}, "@videoStartOverButtonLabel": {},
"videoResumeButtonLabel": "HERVATTEN", "videoResumeButtonLabel": "HERVATTEN",
"@videoResumeButtonLabel": {}, "@videoResumeButtonLabel": {},
@ -355,7 +355,7 @@
"@setCoverDialogAuto": {}, "@setCoverDialogAuto": {},
"setCoverDialogCustom": "Aangepast", "setCoverDialogCustom": "Aangepast",
"@setCoverDialogCustom": {}, "@setCoverDialogCustom": {},
"hideFilterConfirmationDialogMessage": "Overeenkomende fotos en videos worden verborgen binnen jouw verzameling. Je kunt ze opnieuw weergeven via de “Privacy”-instellingen.\n\nWeet je zeker dat je ze wilt verbergen?", "hideFilterConfirmationDialogMessage": "Overeenkomstige fotos en videos worden verborgen binnen jouw verzameling. Je kunt ze opnieuw weergeven via de “Privacy”-instellingen.\n\nWeet je zeker dat je ze wilt verbergen?",
"@hideFilterConfirmationDialogMessage": {}, "@hideFilterConfirmationDialogMessage": {},
"newAlbumDialogTitle": "Nieuw Album", "newAlbumDialogTitle": "Nieuw Album",
"@newAlbumDialogTitle": {}, "@newAlbumDialogTitle": {},
@ -381,11 +381,11 @@
"@renameProcessorCounter": {}, "@renameProcessorCounter": {},
"renameProcessorName": "Naam", "renameProcessorName": "Naam",
"@renameProcessorName": {}, "@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": "{count, plural, =1{Dit album en het item erbinnen verwijderen?} other{Dit album en de {count} items erbinnen verwijderen?}}",
"@deleteSingleAlbumConfirmationDialogMessage": {}, "@deleteSingleAlbumConfirmationDialogMessage": {},
"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": "{count, plural, =1{Deze albums en de items erbinnen verwijderen?} other{Deze albums en de {count} items erbinnen verwijderen?}}",
"@deleteMultiAlbumConfirmationDialogMessage": {}, "@deleteMultiAlbumConfirmationDialogMessage": {},
"exportEntryDialogFormat": "Formaat:", "exportEntryDialogFormat": "Type:",
"@exportEntryDialogFormat": {}, "@exportEntryDialogFormat": {},
"exportEntryDialogWidth": "Breedte", "exportEntryDialogWidth": "Breedte",
"@exportEntryDialogWidth": {}, "@exportEntryDialogWidth": {},
@ -397,11 +397,11 @@
"@editEntryDialogTargetFieldsHeader": {}, "@editEntryDialogTargetFieldsHeader": {},
"editEntryDateDialogTitle": "Datum & Tijd", "editEntryDateDialogTitle": "Datum & Tijd",
"@editEntryDateDialogTitle": {}, "@editEntryDateDialogTitle": {},
"editEntryDateDialogSetCustom": "Stel een custom datum in", "editEntryDateDialogSetCustom": "Aangepaste datum instellen",
"@editEntryDateDialogSetCustom": {}, "@editEntryDateDialogSetCustom": {},
"editEntryDateDialogCopyField": "Kopiëren van andere datum", "editEntryDateDialogCopyField": "Van andere datum kopiëren",
"@editEntryDateDialogCopyField": {}, "@editEntryDateDialogCopyField": {},
"editEntryDialogCopyFromItem": "Kopiëren van ander item", "editEntryDialogCopyFromItem": "Van ander item kopiëren",
"@editEntryDialogCopyFromItem": {}, "@editEntryDialogCopyFromItem": {},
"editEntryDateDialogExtractFromTitle": "Uit titel halen", "editEntryDateDialogExtractFromTitle": "Uit titel halen",
"@editEntryDateDialogExtractFromTitle": {}, "@editEntryDateDialogExtractFromTitle": {},
@ -415,7 +415,7 @@
"@durationDialogMinutes": {}, "@durationDialogMinutes": {},
"editEntryLocationDialogTitle": "Locatie", "editEntryLocationDialogTitle": "Locatie",
"@editEntryLocationDialogTitle": {}, "@editEntryLocationDialogTitle": {},
"editEntryLocationDialogChooseOnMap": "Kies op kaart", "editEntryLocationDialogChooseOnMap": "Op kaart kiezen",
"@editEntryLocationDialogChooseOnMap": {}, "@editEntryLocationDialogChooseOnMap": {},
"editEntryLocationDialogLatitude": "Breedtegraad", "editEntryLocationDialogLatitude": "Breedtegraad",
"@editEntryLocationDialogLatitude": {}, "@editEntryLocationDialogLatitude": {},
@ -443,9 +443,9 @@
"@videoStreamSelectionDialogText": {}, "@videoStreamSelectionDialogText": {},
"videoStreamSelectionDialogOff": "Uit", "videoStreamSelectionDialogOff": "Uit",
"@videoStreamSelectionDialogOff": {}, "@videoStreamSelectionDialogOff": {},
"videoStreamSelectionDialogTrack": "Nummer", "videoStreamSelectionDialogTrack": "Spoor",
"@videoStreamSelectionDialogTrack": {}, "@videoStreamSelectionDialogTrack": {},
"videoStreamSelectionDialogNoSelection": "Er zijn geen andere nummers.", "videoStreamSelectionDialogNoSelection": "Er zijn geen andere sporen.",
"@videoStreamSelectionDialogNoSelection": {}, "@videoStreamSelectionDialogNoSelection": {},
"genericSuccessFeedback": "Klaar!", "genericSuccessFeedback": "Klaar!",
"@genericSuccessFeedback": {}, "@genericSuccessFeedback": {},
@ -453,9 +453,9 @@
"@genericFailureFeedback": {}, "@genericFailureFeedback": {},
"menuActionConfigureView": "Beeld", "menuActionConfigureView": "Beeld",
"@menuActionConfigureView": {}, "@menuActionConfigureView": {},
"menuActionSelect": "Selecteer", "menuActionSelect": "Selecteren",
"@menuActionSelect": {}, "@menuActionSelect": {},
"menuActionSelectAll": "Selecteer alles", "menuActionSelectAll": "Alles selecteren",
"@menuActionSelectAll": {}, "@menuActionSelectAll": {},
"menuActionSelectNone": "Selectie ongedaan maken", "menuActionSelectNone": "Selectie ongedaan maken",
"@menuActionSelectNone": {}, "@menuActionSelectNone": {},
@ -493,17 +493,17 @@
"@aboutPageTitle": {}, "@aboutPageTitle": {},
"aboutLinkLicense": "Licentie", "aboutLinkLicense": "Licentie",
"@aboutLinkLicense": {}, "@aboutLinkLicense": {},
"aboutBugSectionTitle": "Bug Reporteren", "aboutBugSectionTitle": "Foutmelding",
"@aboutBugSectionTitle": {}, "@aboutBugSectionTitle": {},
"aboutBugSaveLogInstruction": "Sla applicatielogs op in een bestand", "aboutBugSaveLogInstruction": "Sla applicatielogs op in een bestand",
"@aboutBugSaveLogInstruction": {}, "@aboutBugSaveLogInstruction": {},
"aboutBugCopyInfoInstruction": "Kopieer systeem informatie", "aboutBugCopyInfoInstruction": "Systeeminformatie kopiëren",
"@aboutBugCopyInfoInstruction": {}, "@aboutBugCopyInfoInstruction": {},
"aboutBugCopyInfoButton": "Kopieer", "aboutBugCopyInfoButton": "Kopieer",
"@aboutBugCopyInfoButton": {}, "@aboutBugCopyInfoButton": {},
"aboutBugReportInstruction": "Reporteer op GitHub met de logs en systeeminformatie", "aboutBugReportInstruction": "Melden op GitHub met de logs en systeeminformatie",
"@aboutBugReportInstruction": {}, "@aboutBugReportInstruction": {},
"aboutBugReportButton": "Reporteer", "aboutBugReportButton": "Melden",
"@aboutBugReportButton": {}, "@aboutBugReportButton": {},
"aboutCreditsSectionTitle": "Dankbetuiging", "aboutCreditsSectionTitle": "Dankbetuiging",
"@aboutCreditsSectionTitle": {}, "@aboutCreditsSectionTitle": {},
@ -531,9 +531,9 @@
"@collectionPageTitle": {}, "@collectionPageTitle": {},
"collectionPickPageTitle": "Kies", "collectionPickPageTitle": "Kies",
"@collectionPickPageTitle": {}, "@collectionPickPageTitle": {},
"collectionSelectPageTitle": "Selecteer items", "collectionSelectPageTitle": "Items selecteren",
"@collectionSelectPageTitle": {}, "@collectionSelectPageTitle": {},
"collectionActionShowTitleSearch": "Laat titel filter zien", "collectionActionShowTitleSearch": "Titelfilter weergeven",
"@collectionActionShowTitleSearch": {}, "@collectionActionShowTitleSearch": {},
"collectionActionHideTitleSearch": "Verberg titel filter", "collectionActionHideTitleSearch": "Verberg titel filter",
"@collectionActionHideTitleSearch": {}, "@collectionActionHideTitleSearch": {},
@ -547,7 +547,7 @@
"@collectionActionMove": {}, "@collectionActionMove": {},
"collectionActionRescan": "Opnieuw indexeren", "collectionActionRescan": "Opnieuw indexeren",
"@collectionActionRescan": {}, "@collectionActionRescan": {},
"collectionActionEdit": "Wijzigen", "collectionActionEdit": "Bewerken",
"@collectionActionEdit": {}, "@collectionActionEdit": {},
"collectionSearchTitlesHintText": "Zoek op titel", "collectionSearchTitlesHintText": "Zoek op titel",
"@collectionSearchTitlesHintText": {}, "@collectionSearchTitlesHintText": {},
@ -575,7 +575,7 @@
"@collectionMoveFailureFeedback": {}, "@collectionMoveFailureFeedback": {},
"collectionRenameFailureFeedback": "{count, plural, =1{Kan 1 item niet hernoemen} other{Kan {count} items niet hernoemen}}", "collectionRenameFailureFeedback": "{count, plural, =1{Kan 1 item niet hernoemen} other{Kan {count} items niet hernoemen}}",
"@collectionRenameFailureFeedback": {}, "@collectionRenameFailureFeedback": {},
"collectionEditFailureFeedback": "{count, plural, =1{Kan 1 item niet wijzigen} other{Kan {count} items niet wijzigen}}", "collectionEditFailureFeedback": "{count, plural, =1{Kan 1 item niet bewerken} other{Kan {count} items niet bewerken}}",
"@collectionEditFailureFeedback": {}, "@collectionEditFailureFeedback": {},
"collectionExportFailureFeedback": "{count, plural, =1{Kan 1 pagina niet exporteren} other{Kan {count} paginas niet exporteren}}", "collectionExportFailureFeedback": "{count, plural, =1{Kan 1 pagina niet exporteren} other{Kan {count} paginas niet exporteren}}",
"@collectionExportFailureFeedback": {}, "@collectionExportFailureFeedback": {},
@ -585,7 +585,7 @@
"@collectionMoveSuccessFeedback": {}, "@collectionMoveSuccessFeedback": {},
"collectionRenameSuccessFeedback": "{count, plural, =1{1 item hernoemd} other{{count} items hernoemd}}", "collectionRenameSuccessFeedback": "{count, plural, =1{1 item hernoemd} other{{count} items hernoemd}}",
"@collectionRenameSuccessFeedback": {}, "@collectionRenameSuccessFeedback": {},
"collectionEditSuccessFeedback": "{count, plural, =1{1 item gewijzigd} other{{count} items gewijzigd}}", "collectionEditSuccessFeedback": "{count, plural, =1{1 item bewerkt} other{{count} items bewerkt}}",
"@collectionEditSuccessFeedback": {}, "@collectionEditSuccessFeedback": {},
"collectionEmptyFavourites": "Geen favourieten", "collectionEmptyFavourites": "Geen favourieten",
"@collectionEmptyFavourites": {}, "@collectionEmptyFavourites": {},
@ -595,9 +595,9 @@
"@collectionEmptyImages": {}, "@collectionEmptyImages": {},
"collectionEmptyGrantAccessButtonLabel": "Toegang verlenen", "collectionEmptyGrantAccessButtonLabel": "Toegang verlenen",
"@collectionEmptyGrantAccessButtonLabel": {}, "@collectionEmptyGrantAccessButtonLabel": {},
"collectionSelectSectionTooltip": "Selecteer sectie", "collectionSelectSectionTooltip": "Sectie selecteren",
"@collectionSelectSectionTooltip": {}, "@collectionSelectSectionTooltip": {},
"collectionDeselectSectionTooltip": "Deselecteer sectie", "collectionDeselectSectionTooltip": "Sectie niet selecteren",
"@collectionDeselectSectionTooltip": {}, "@collectionDeselectSectionTooltip": {},
"drawerAboutButton": "Over", "drawerAboutButton": "Over",
"@drawerAboutButton": {}, "@drawerAboutButton": {},
@ -677,7 +677,7 @@
"@albumCamera": {}, "@albumCamera": {},
"albumDownload": "Opslaan", "albumDownload": "Opslaan",
"@albumDownload": {}, "@albumDownload": {},
"albumScreenshots": "Schermafbeeldingen", "albumScreenshots": "Schermopnames",
"@albumScreenshots": {}, "@albumScreenshots": {},
"albumScreenRecordings": "Schermopnames", "albumScreenRecordings": "Schermopnames",
"@albumScreenRecordings": {}, "@albumScreenRecordings": {},
@ -751,25 +751,25 @@
"@settingsHomeTile": {}, "@settingsHomeTile": {},
"settingsHomeDialogTitle": "Startscherm", "settingsHomeDialogTitle": "Startscherm",
"@settingsHomeDialogTitle": {}, "@settingsHomeDialogTitle": {},
"settingsShowBottomNavigationBar": "Laat onderste navigatiebalk zien", "settingsShowBottomNavigationBar": "Onderste navigatiebalk weergeven",
"@settingsShowBottomNavigationBar": {}, "@settingsShowBottomNavigationBar": {},
"settingsKeepScreenOnTile": "Houd het scherm aan", "settingsKeepScreenOnTile": "Scherm aan houden",
"@settingsKeepScreenOnTile": {}, "@settingsKeepScreenOnTile": {},
"settingsKeepScreenOnDialogTitle": "Houd het scherm aan", "settingsKeepScreenOnDialogTitle": "Scherm aan houden",
"@settingsKeepScreenOnDialogTitle": {}, "@settingsKeepScreenOnDialogTitle": {},
"settingsDoubleBackExit": "Tik twee keer op “terug” om af te sluiten", "settingsDoubleBackExit": "Twee keer op “terug” tikken om af te sluiten",
"@settingsDoubleBackExit": {}, "@settingsDoubleBackExit": {},
"settingsConfirmationTile": "Bevestigingsscherm", "settingsConfirmationTile": "Bevestigingsdialogen",
"@settingsConfirmationTile": {}, "@settingsConfirmationTile": {},
"settingsConfirmationDialogTitle": "Bevestigingsschermen", "settingsConfirmationDialogTitle": "Bevestigingsdialogen",
"@settingsConfirmationDialogTitle": {}, "@settingsConfirmationDialogTitle": {},
"settingsConfirmationBeforeDeleteItems": "Bevestig voordat je items voor altijd verwijdert", "settingsConfirmationBeforeDeleteItems": "Bevestiging vragen voordat items voor altijd worden verwijderd",
"@settingsConfirmationBeforeDeleteItems": {}, "@settingsConfirmationBeforeDeleteItems": {},
"settingsConfirmationBeforeMoveToBinItems": "Bevestig voordat u items naar de prullenbak verplaatst", "settingsConfirmationBeforeMoveToBinItems": "Bevestiging vragen voordat items naar de prullenbak worden verplaatst",
"@settingsConfirmationBeforeMoveToBinItems": {}, "@settingsConfirmationBeforeMoveToBinItems": {},
"settingsConfirmationBeforeMoveUndatedItems": "Bevestigvoordat u ongedateerde items verplaatst", "settingsConfirmationBeforeMoveUndatedItems": "Bevestiging vragen voordat ongedateerde items worden verplaatst",
"@settingsConfirmationBeforeMoveUndatedItems": {}, "@settingsConfirmationBeforeMoveUndatedItems": {},
"settingsConfirmationAfterMoveToBinItems": "Toon bevestigingsbericht na het verplaatsen van items naar de prullenbak", "settingsConfirmationAfterMoveToBinItems": "Bevestigingsbericht weergeven na het verplaatsen van items naar de prullenbak",
"@settingsConfirmationAfterMoveToBinItems": {}, "@settingsConfirmationAfterMoveToBinItems": {},
"settingsNavigationDrawerTile": "Navigatiemenu", "settingsNavigationDrawerTile": "Navigatiemenu",
"@settingsNavigationDrawerTile": {}, "@settingsNavigationDrawerTile": {},
@ -791,35 +791,35 @@
"@settingsThumbnailOverlayTile": {}, "@settingsThumbnailOverlayTile": {},
"settingsThumbnailOverlayPageTitle": "Overlay", "settingsThumbnailOverlayPageTitle": "Overlay",
"@settingsThumbnailOverlayPageTitle": {}, "@settingsThumbnailOverlayPageTitle": {},
"settingsThumbnailShowFavouriteIcon": "Favorieten icoon zichtbaar", "settingsThumbnailShowFavouriteIcon": "Favorieten-pictogram tonen",
"@settingsThumbnailShowFavouriteIcon": {}, "@settingsThumbnailShowFavouriteIcon": {},
"settingsThumbnailShowTagIcon": "Label-pictogram tonen", "settingsThumbnailShowTagIcon": "Label-pictogram tonen",
"@settingsThumbnailShowTagIcon": {}, "@settingsThumbnailShowTagIcon": {},
"settingsThumbnailShowLocationIcon": "Locatie icoon zichtbaar", "settingsThumbnailShowLocationIcon": "Locatie-pictogram tonen",
"@settingsThumbnailShowLocationIcon": {}, "@settingsThumbnailShowLocationIcon": {},
"settingsThumbnailShowMotionPhotoIcon": "Bewegende foto icoon zichtbaar", "settingsThumbnailShowMotionPhotoIcon": "Bewegende foto-pictogram tonen",
"@settingsThumbnailShowMotionPhotoIcon": {}, "@settingsThumbnailShowMotionPhotoIcon": {},
"settingsThumbnailShowRating": "Waardering tonen", "settingsThumbnailShowRating": "Waardering tonen",
"@settingsThumbnailShowRating": {}, "@settingsThumbnailShowRating": {},
"settingsThumbnailShowRawIcon": "RAW icoon zichtbaar", "settingsThumbnailShowRawIcon": "RAW-pictogram tonen",
"@settingsThumbnailShowRawIcon": {}, "@settingsThumbnailShowRawIcon": {},
"settingsThumbnailShowVideoDuration": "Videoduur zichtbaar", "settingsThumbnailShowVideoDuration": "Videoduur tonen",
"@settingsThumbnailShowVideoDuration": {}, "@settingsThumbnailShowVideoDuration": {},
"settingsCollectionQuickActionsTile": "Snelle bewerkingen", "settingsCollectionQuickActionsTile": "Snelle bewerkingen",
"@settingsCollectionQuickActionsTile": {}, "@settingsCollectionQuickActionsTile": {},
"settingsCollectionQuickActionEditorPageTitle": "Snelle bewerkingen", "settingsCollectionQuickActionEditorPageTitle": "Snelle acties",
"@settingsCollectionQuickActionEditorPageTitle": {}, "@settingsCollectionQuickActionEditorPageTitle": {},
"settingsCollectionQuickActionTabBrowsing": "Blader", "settingsCollectionQuickActionTabBrowsing": "Blader",
"@settingsCollectionQuickActionTabBrowsing": {}, "@settingsCollectionQuickActionTabBrowsing": {},
"settingsCollectionQuickActionTabSelecting": "Selecteren", "settingsCollectionQuickActionTabSelecting": "Selecteren",
"@settingsCollectionQuickActionTabSelecting": {}, "@settingsCollectionQuickActionTabSelecting": {},
"settingsCollectionBrowsingQuickActionEditorBanner": "Houd ingedrukt om knoppen te verplaatsen en te selecteren welke acties worden weergegeven bij het bladeren door items.", "settingsCollectionBrowsingQuickActionEditorBanner": "Houd knoppen ingedrukt om deze te verplaatsen en te selecteren welke acties worden weergegeven bij het bladeren door items.",
"@settingsCollectionBrowsingQuickActionEditorBanner": {}, "@settingsCollectionBrowsingQuickActionEditorBanner": {},
"settingsCollectionSelectionQuickActionEditorBanner": "Houd ingedrukt om knoppen te verplaatsen en te selecteren welke acties worden weergegeven bij het selecteren van items.", "settingsCollectionSelectionQuickActionEditorBanner": "Houd knoppen ingedrukt om deze te verplaatsen en te selecteren welke acties worden weergegeven bij het selecteren van items.",
"@settingsCollectionSelectionQuickActionEditorBanner": {}, "@settingsCollectionSelectionQuickActionEditorBanner": {},
"settingsViewerSectionTitle": "Voorbeeld", "settingsViewerSectionTitle": "Voorbeeld",
"@settingsViewerSectionTitle": {}, "@settingsViewerSectionTitle": {},
"settingsViewerGestureSideTapNext": "Druk op het scherm om het vorige/volgende item weer te geven", "settingsViewerGestureSideTapNext": "Tik op het scherm om het vorige/volgende item weer te geven",
"@settingsViewerGestureSideTapNext": {}, "@settingsViewerGestureSideTapNext": {},
"settingsViewerUseCutout": "Uitgesneden gebied gebruiken", "settingsViewerUseCutout": "Uitgesneden gebied gebruiken",
"@settingsViewerUseCutout": {}, "@settingsViewerUseCutout": {},
@ -831,9 +831,9 @@
"@settingsImageBackground": {}, "@settingsImageBackground": {},
"settingsViewerQuickActionsTile": "Snelle bewerkingen", "settingsViewerQuickActionsTile": "Snelle bewerkingen",
"@settingsViewerQuickActionsTile": {}, "@settingsViewerQuickActionsTile": {},
"settingsViewerQuickActionEditorPageTitle": "Snelle bewerkingen", "settingsViewerQuickActionEditorPageTitle": "Snelle acties",
"@settingsViewerQuickActionEditorPageTitle": {}, "@settingsViewerQuickActionEditorPageTitle": {},
"settingsViewerQuickActionEditorBanner": "Houd ingedrukt om knoppen te verplaatsen en te selecteren welke acties in de viewer worden weergegeven.", "settingsViewerQuickActionEditorBanner": "Houd knoppen ingedrukt om deze te verplaatsen en te selecteren welke acties in de viewer worden weergegeven.",
"@settingsViewerQuickActionEditorBanner": {}, "@settingsViewerQuickActionEditorBanner": {},
"settingsViewerQuickActionEditorDisplayedButtonsSectionTitle": "Zichtbare knoppen", "settingsViewerQuickActionEditorDisplayedButtonsSectionTitle": "Zichtbare knoppen",
"@settingsViewerQuickActionEditorDisplayedButtonsSectionTitle": {}, "@settingsViewerQuickActionEditorDisplayedButtonsSectionTitle": {},
@ -845,17 +845,17 @@
"@settingsViewerOverlayTile": {}, "@settingsViewerOverlayTile": {},
"settingsViewerOverlayPageTitle": "Overlay", "settingsViewerOverlayPageTitle": "Overlay",
"@settingsViewerOverlayPageTitle": {}, "@settingsViewerOverlayPageTitle": {},
"settingsViewerShowOverlayOnOpening": "Zichtbaar bij openen", "settingsViewerShowOverlayOnOpening": "Bij openen weergeven",
"@settingsViewerShowOverlayOnOpening": {}, "@settingsViewerShowOverlayOnOpening": {},
"settingsViewerShowMinimap": "Laat kleine kaart zien", "settingsViewerShowMinimap": "Kleine kaart tonen",
"@settingsViewerShowMinimap": {}, "@settingsViewerShowMinimap": {},
"settingsViewerShowInformation": "Laat informatie zien", "settingsViewerShowInformation": "Informatie tonen",
"@settingsViewerShowInformation": {}, "@settingsViewerShowInformation": {},
"settingsViewerShowInformationSubtitle": "Laat titel, datum, locatie, etc zien.", "settingsViewerShowInformationSubtitle": "Titel, datum, locatie, etc.",
"@settingsViewerShowInformationSubtitle": {}, "@settingsViewerShowInformationSubtitle": {},
"settingsViewerShowShootingDetails": "Laat opnamedetails zien", "settingsViewerShowShootingDetails": "Opnamedetails tonen",
"@settingsViewerShowShootingDetails": {}, "@settingsViewerShowShootingDetails": {},
"settingsViewerShowOverlayThumbnails": "Laat miniaturen zien", "settingsViewerShowOverlayThumbnails": "Miniaturen tonen",
"@settingsViewerShowOverlayThumbnails": {}, "@settingsViewerShowOverlayThumbnails": {},
"settingsViewerEnableOverlayBlurEffect": "Vervagingseffect", "settingsViewerEnableOverlayBlurEffect": "Vervagingseffect",
"@settingsViewerEnableOverlayBlurEffect": {}, "@settingsViewerEnableOverlayBlurEffect": {},
@ -883,9 +883,9 @@
"@settingsVideoPageTitle": {}, "@settingsVideoPageTitle": {},
"settingsVideoSectionTitle": "Video", "settingsVideoSectionTitle": "Video",
"@settingsVideoSectionTitle": {}, "@settingsVideoSectionTitle": {},
"settingsVideoShowVideos": "Videos", "settingsVideoShowVideos": "Video's weergeven",
"@settingsVideoShowVideos": {}, "@settingsVideoShowVideos": {},
"settingsVideoEnableHardwareAcceleration": "Hardware acceleratie", "settingsVideoEnableHardwareAcceleration": "Hardware-versnelling",
"@settingsVideoEnableHardwareAcceleration": {}, "@settingsVideoEnableHardwareAcceleration": {},
"settingsVideoAutoPlay": "Automatisch afspelen", "settingsVideoAutoPlay": "Automatisch afspelen",
"@settingsVideoAutoPlay": {}, "@settingsVideoAutoPlay": {},
@ -905,7 +905,7 @@
"@settingsSubtitleThemeTextAlignmentDialogTitle": {}, "@settingsSubtitleThemeTextAlignmentDialogTitle": {},
"settingsSubtitleThemeTextSize": "Tekstgroote", "settingsSubtitleThemeTextSize": "Tekstgroote",
"@settingsSubtitleThemeTextSize": {}, "@settingsSubtitleThemeTextSize": {},
"settingsSubtitleThemeShowOutline": "Laat omtrek en schaduw zien", "settingsSubtitleThemeShowOutline": "Omtrek en schaduw tonen",
"@settingsSubtitleThemeShowOutline": {}, "@settingsSubtitleThemeShowOutline": {},
"settingsSubtitleThemeTextColor": "Tekstkleur", "settingsSubtitleThemeTextColor": "Tekstkleur",
"@settingsSubtitleThemeTextColor": {}, "@settingsSubtitleThemeTextColor": {},
@ -937,9 +937,9 @@
"@settingsAllowInstalledAppAccess": {}, "@settingsAllowInstalledAppAccess": {},
"settingsAllowInstalledAppAccessSubtitle": "Gebruikt om de albumweergave te verbeteren", "settingsAllowInstalledAppAccessSubtitle": "Gebruikt om de albumweergave te verbeteren",
"@settingsAllowInstalledAppAccessSubtitle": {}, "@settingsAllowInstalledAppAccessSubtitle": {},
"settingsAllowErrorReporting": "Anonieme foutrapportage toestaan", "settingsAllowErrorReporting": "Anonieme foutmeldingen toestaan",
"@settingsAllowErrorReporting": {}, "@settingsAllowErrorReporting": {},
"settingsSaveSearchHistory": "Bewaar zoekgeschiedenis", "settingsSaveSearchHistory": "Zoekgeschiedenis opslaan",
"@settingsSaveSearchHistory": {}, "@settingsSaveSearchHistory": {},
"settingsEnableBin": "Prullenbak gebruiken", "settingsEnableBin": "Prullenbak gebruiken",
"@settingsEnableBin": {}, "@settingsEnableBin": {},
@ -957,7 +957,7 @@
"@settingsHiddenFiltersEmpty": {}, "@settingsHiddenFiltersEmpty": {},
"settingsHiddenItemsTabPaths": "Verborgen paden", "settingsHiddenItemsTabPaths": "Verborgen paden",
"@settingsHiddenItemsTabPaths": {}, "@settingsHiddenItemsTabPaths": {},
"settingsHiddenPathsBanner": "Fotos en videos in deze mappen, of een van hun submappen, verschijnen niet in je verzameling.", "settingsHiddenPathsBanner": "Fotos en videos in deze mappen en onderliggende mappen, verschijnen niet in je verzameling.",
"@settingsHiddenPathsBanner": {}, "@settingsHiddenPathsBanner": {},
"addPathTooltip": "Pad toevoegen", "addPathTooltip": "Pad toevoegen",
"@addPathTooltip": {}, "@addPathTooltip": {},
@ -977,7 +977,7 @@
"@settingsRemoveAnimationsTile": {}, "@settingsRemoveAnimationsTile": {},
"settingsRemoveAnimationsDialogTitle": "Animaties verwijderen", "settingsRemoveAnimationsDialogTitle": "Animaties verwijderen",
"@settingsRemoveAnimationsDialogTitle": {}, "@settingsRemoveAnimationsDialogTitle": {},
"settingsTimeToTakeActionTile": "Tijd om actie te ondernemen", "settingsTimeToTakeActionTile": "Reactietijd",
"@settingsTimeToTakeActionTile": {}, "@settingsTimeToTakeActionTile": {},
"settingsDisplaySectionTitle": "Scherm", "settingsDisplaySectionTitle": "Scherm",
"@settingsDisplaySectionTitle": {}, "@settingsDisplaySectionTitle": {},
@ -985,13 +985,13 @@
"@settingsThemeBrightnessTile": {}, "@settingsThemeBrightnessTile": {},
"settingsThemeBrightnessDialogTitle": "Thema", "settingsThemeBrightnessDialogTitle": "Thema",
"@settingsThemeBrightnessDialogTitle": {}, "@settingsThemeBrightnessDialogTitle": {},
"settingsThemeColorHighlights": "Kleur highlights", "settingsThemeColorHighlights": "Kleurmarkeringen",
"@settingsThemeColorHighlights": {}, "@settingsThemeColorHighlights": {},
"settingsThemeEnableDynamicColor": "Dynamische kleur", "settingsThemeEnableDynamicColor": "Dynamische kleuren",
"@settingsThemeEnableDynamicColor": {}, "@settingsThemeEnableDynamicColor": {},
"settingsDisplayRefreshRateModeTile": "Vernieuwingsfrequentie weergeven", "settingsDisplayRefreshRateModeTile": "Vernieuwingssnelheid weergeven",
"@settingsDisplayRefreshRateModeTile": {}, "@settingsDisplayRefreshRateModeTile": {},
"settingsDisplayRefreshRateModeDialogTitle": "Vernieuwingsfrequentie", "settingsDisplayRefreshRateModeDialogTitle": "Vernieuwingssnelheid",
"@settingsDisplayRefreshRateModeDialogTitle": {}, "@settingsDisplayRefreshRateModeDialogTitle": {},
"settingsLanguageSectionTitle": "Taal & landinstellingen", "settingsLanguageSectionTitle": "Taal & landinstellingen",
"@settingsLanguageSectionTitle": {}, "@settingsLanguageSectionTitle": {},
@ -999,9 +999,9 @@
"@settingsLanguageTile": {}, "@settingsLanguageTile": {},
"settingsLanguagePageTitle": "Taal", "settingsLanguagePageTitle": "Taal",
"@settingsLanguagePageTitle": {}, "@settingsLanguagePageTitle": {},
"settingsCoordinateFormatTile": "Coördinaten format", "settingsCoordinateFormatTile": "Coördinaten-weergave",
"@settingsCoordinateFormatTile": {}, "@settingsCoordinateFormatTile": {},
"settingsCoordinateFormatDialogTitle": "Coördinaten format", "settingsCoordinateFormatDialogTitle": "Coördinaten-weergave",
"@settingsCoordinateFormatDialogTitle": {}, "@settingsCoordinateFormatDialogTitle": {},
"settingsUnitSystemTile": "Eenheden", "settingsUnitSystemTile": "Eenheden",
"@settingsUnitSystemTile": {}, "@settingsUnitSystemTile": {},
@ -1013,7 +1013,7 @@
"@settingsWidgetPageTitle": {}, "@settingsWidgetPageTitle": {},
"settingsWidgetShowOutline": "Contour", "settingsWidgetShowOutline": "Contour",
"@settingsWidgetShowOutline": {}, "@settingsWidgetShowOutline": {},
"settingsWidgetOpenPage": "Wanneer u op de widget tikt", "settingsWidgetOpenPage": "Bij het tikken op de widget",
"@settingsWidgetOpenPage": {}, "@settingsWidgetOpenPage": {},
"settingsCollectionTile": "Verzameling", "settingsCollectionTile": "Verzameling",
"@settingsCollectionTile": {}, "@settingsCollectionTile": {},
@ -1067,7 +1067,7 @@
"@viewerInfoLabelAddress": {}, "@viewerInfoLabelAddress": {},
"mapStyleDialogTitle": "Kaartstijl", "mapStyleDialogTitle": "Kaartstijl",
"@mapStyleDialogTitle": {}, "@mapStyleDialogTitle": {},
"mapStyleTooltip": "Selecteer kaart stijl", "mapStyleTooltip": "Kaartstijl selecteren",
"@mapStyleTooltip": {}, "@mapStyleTooltip": {},
"mapZoomInTooltip": "Inzoomen", "mapZoomInTooltip": "Inzoomen",
"@mapZoomInTooltip": {}, "@mapZoomInTooltip": {},
@ -1079,13 +1079,13 @@
"@mapAttributionOsmHot": {}, "@mapAttributionOsmHot": {},
"mapAttributionStamen": "Kaartgegevens © [OpenStreetMap](https://www.openstreetmap.org/copyright) bijdragers • Tegels door [Stamen Design](https://stamen.com), [CC BY 3.0](https://creativecommons.org/licenses/by/3.0)", "mapAttributionStamen": "Kaartgegevens © [OpenStreetMap](https://www.openstreetmap.org/copyright) bijdragers • Tegels door [Stamen Design](https://stamen.com), [CC BY 3.0](https://creativecommons.org/licenses/by/3.0)",
"@mapAttributionStamen": {}, "@mapAttributionStamen": {},
"openMapPageTooltip": "Bekijk op kaartpagina", "openMapPageTooltip": "Op kaartpagina tonen",
"@openMapPageTooltip": {}, "@openMapPageTooltip": {},
"mapEmptyRegion": "Geen afbeeldingen in de geselecteerde regio", "mapEmptyRegion": "Geen afbeeldingen in dit gebied",
"@mapEmptyRegion": {}, "@mapEmptyRegion": {},
"viewerInfoOpenEmbeddedFailureFeedback": "Kan ingesloten gegevens niet extraheren", "viewerInfoOpenEmbeddedFailureFeedback": "Kan ingesloten gegevens niet extraheren",
"@viewerInfoOpenEmbeddedFailureFeedback": {}, "@viewerInfoOpenEmbeddedFailureFeedback": {},
"viewerInfoOpenLinkText": "Open", "viewerInfoOpenLinkText": "Openen",
"@viewerInfoOpenLinkText": {}, "@viewerInfoOpenLinkText": {},
"viewerInfoViewXmlLinkText": "Bekijk XML", "viewerInfoViewXmlLinkText": "Bekijk XML",
"@viewerInfoViewXmlLinkText": {}, "@viewerInfoViewXmlLinkText": {},
@ -1119,19 +1119,19 @@
"@panoramaDisableSensorControl": {}, "@panoramaDisableSensorControl": {},
"sourceViewerPageTitle": "Source", "sourceViewerPageTitle": "Source",
"@sourceViewerPageTitle": {}, "@sourceViewerPageTitle": {},
"filePickerShowHiddenFiles": "Verborgen bestanden laten zien", "filePickerShowHiddenFiles": "Verborgen bestanden weergeven",
"@filePickerShowHiddenFiles": {}, "@filePickerShowHiddenFiles": {},
"filePickerDoNotShowHiddenFiles": "Verborgen bestanden niet laten zien", "filePickerDoNotShowHiddenFiles": "Verborgen bestanden niet tonen",
"@filePickerDoNotShowHiddenFiles": {}, "@filePickerDoNotShowHiddenFiles": {},
"filePickerOpenFrom": "Openen met", "filePickerOpenFrom": "Openen van",
"@filePickerOpenFrom": {}, "@filePickerOpenFrom": {},
"filePickerNoItems": "Geen items", "filePickerNoItems": "Geen items",
"@filePickerNoItems": {}, "@filePickerNoItems": {},
"filePickerUseThisFolder": "Deze map gebruiken", "filePickerUseThisFolder": "Deze map gebruiken",
"@filePickerUseThisFolder": {}, "@filePickerUseThisFolder": {},
"widgetOpenPageCollection": "Open verzameling", "widgetOpenPageCollection": "Verzameling openen",
"@widgetOpenPageCollection": {}, "@widgetOpenPageCollection": {},
"widgetOpenPageViewer": "Open voorbeeld", "widgetOpenPageViewer": "Voorbeeld openen",
"@widgetOpenPageViewer": {}, "@widgetOpenPageViewer": {},
"durationDialogSeconds": "Seconden", "durationDialogSeconds": "Seconden",
"@durationDialogSeconds": {}, "@durationDialogSeconds": {},
@ -1167,9 +1167,9 @@
"@filterNoAddressLabel": {}, "@filterNoAddressLabel": {},
"filterAspectRatioPortraitLabel": "Staand", "filterAspectRatioPortraitLabel": "Staand",
"@filterAspectRatioPortraitLabel": {}, "@filterAspectRatioPortraitLabel": {},
"widgetDisplayedItemRandom": "Willekeurige", "widgetDisplayedItemRandom": "Willekeurig",
"@widgetDisplayedItemRandom": {}, "@widgetDisplayedItemRandom": {},
"widgetDisplayedItemMostRecent": "Meest recente", "widgetDisplayedItemMostRecent": "Laatst gebruikt",
"@widgetDisplayedItemMostRecent": {}, "@widgetDisplayedItemMostRecent": {},
"keepScreenOnVideoPlayback": "Tijdens het afspelen van video", "keepScreenOnVideoPlayback": "Tijdens het afspelen van video",
"@keepScreenOnVideoPlayback": {}, "@keepScreenOnVideoPlayback": {},
@ -1191,7 +1191,7 @@
"@stopTooltip": {}, "@stopTooltip": {},
"chipActionLock": "Vergrendel", "chipActionLock": "Vergrendel",
"@chipActionLock": {}, "@chipActionLock": {},
"chipActionShowCountryStates": "Status tonen", "chipActionShowCountryStates": "Staten tonen",
"@chipActionShowCountryStates": {}, "@chipActionShowCountryStates": {},
"chipActionGoToPlacePage": "In Plaatsen tonen", "chipActionGoToPlacePage": "In Plaatsen tonen",
"@chipActionGoToPlacePage": {}, "@chipActionGoToPlacePage": {},
@ -1199,15 +1199,15 @@
"@subtitlePositionTop": {}, "@subtitlePositionTop": {},
"subtitlePositionBottom": "Onder", "subtitlePositionBottom": "Onder",
"@subtitlePositionBottom": {}, "@subtitlePositionBottom": {},
"settingsThumbnailShowHdrIcon": "HDR icoon zichtbaar", "settingsThumbnailShowHdrIcon": "HDR-pictogram tonen",
"@settingsThumbnailShowHdrIcon": {}, "@settingsThumbnailShowHdrIcon": {},
"editorTransformCrop": "Bijsnijden", "editorTransformCrop": "Bijsnijden",
"@editorTransformCrop": {}, "@editorTransformCrop": {},
"patternDialogConfirm": "Bevestig patroon", "patternDialogConfirm": "Patroon bevestigen",
"@patternDialogConfirm": {}, "@patternDialogConfirm": {},
"pinDialogEnter": "Voer PIN in", "pinDialogEnter": "Voer PIN in",
"@pinDialogEnter": {}, "@pinDialogEnter": {},
"settingsAskEverytime": "Vraag elke keer", "settingsAskEverytime": "Elke keer vragen",
"@settingsAskEverytime": {}, "@settingsAskEverytime": {},
"aboutDataUsageExternal": "Extern", "aboutDataUsageExternal": "Extern",
"@aboutDataUsageExternal": {}, "@aboutDataUsageExternal": {},
@ -1217,7 +1217,7 @@
"@maxBrightnessAlways": {}, "@maxBrightnessAlways": {},
"patternDialogEnter": "Voer patroon in", "patternDialogEnter": "Voer patroon in",
"@patternDialogEnter": {}, "@patternDialogEnter": {},
"settingsViewerShowDescription": "Laat beschrijving zien", "settingsViewerShowDescription": "Beschrijving tonen",
"@settingsViewerShowDescription": {}, "@settingsViewerShowDescription": {},
"exportEntryDialogQuality": "Kwaliteit", "exportEntryDialogQuality": "Kwaliteit",
"@exportEntryDialogQuality": {}, "@exportEntryDialogQuality": {},
@ -1241,13 +1241,13 @@
"@overlayHistogramLuminance": {}, "@overlayHistogramLuminance": {},
"videoResumptionModeNever": "Nooit", "videoResumptionModeNever": "Nooit",
"@videoResumptionModeNever": {}, "@videoResumptionModeNever": {},
"pinDialogConfirm": "Bevestig PIN", "pinDialogConfirm": "PIN bevestigen",
"@pinDialogConfirm": {}, "@pinDialogConfirm": {},
"passwordDialogEnter": "Voer wachtwoord in", "passwordDialogEnter": "Voer wachtwoord in",
"@passwordDialogEnter": {}, "@passwordDialogEnter": {},
"passwordDialogConfirm": "Bevestig wachtwoord", "passwordDialogConfirm": "Wachtwoord bevestigen",
"@passwordDialogConfirm": {}, "@passwordDialogConfirm": {},
"settingsViewerShowHistogram": "Laat histogram zien", "settingsViewerShowHistogram": "Histogram weergeven",
"@settingsViewerShowHistogram": {}, "@settingsViewerShowHistogram": {},
"settingsVideoGestureVerticalDragBrightnessVolume": "Veeg omhoog of naar beneden om helderheid/volume aan te passen", "settingsVideoGestureVerticalDragBrightnessVolume": "Veeg omhoog of naar beneden om helderheid/volume aan te passen",
"@settingsVideoGestureVerticalDragBrightnessVolume": {}, "@settingsVideoGestureVerticalDragBrightnessVolume": {},
@ -1271,7 +1271,7 @@
"@videoResumptionModeAlways": {}, "@videoResumptionModeAlways": {},
"exportEntryDialogWriteMetadata": "Metadata schrijven", "exportEntryDialogWriteMetadata": "Metadata schrijven",
"@exportEntryDialogWriteMetadata": {}, "@exportEntryDialogWriteMetadata": {},
"chipActionShowCollection": "Tonen in Collectie", "chipActionShowCollection": "In Collectie tonen",
"@chipActionShowCollection": {}, "@chipActionShowCollection": {},
"entryActionCast": "Casten", "entryActionCast": "Casten",
"@entryActionCast": {}, "@entryActionCast": {},
@ -1329,7 +1329,7 @@
"@settingsVideoBackgroundMode": {}, "@settingsVideoBackgroundMode": {},
"configureVaultDialogTitle": "Kluis configureren", "configureVaultDialogTitle": "Kluis configureren",
"@configureVaultDialogTitle": {}, "@configureVaultDialogTitle": {},
"settingsWidgetDisplayedItem": "Getoond item", "settingsWidgetDisplayedItem": "Zichtbaar item",
"@settingsWidgetDisplayedItem": {}, "@settingsWidgetDisplayedItem": {},
"albumTierVaults": "Kluizen", "albumTierVaults": "Kluizen",
"@albumTierVaults": {}, "@albumTierVaults": {},
@ -1367,7 +1367,7 @@
"@widgetTapUpdateWidget": {}, "@widgetTapUpdateWidget": {},
"authenticateToConfigureVault": "Verifieer om de kluis te configureren", "authenticateToConfigureVault": "Verifieer om de kluis te configureren",
"@authenticateToConfigureVault": {}, "@authenticateToConfigureVault": {},
"settingsConfirmationVaultDataLoss": "Waarschuwing voor verlies van kluisgegevens weergeven", "settingsConfirmationVaultDataLoss": "Waarschuwing weergeven voor verlies van kluisgegevens",
"@settingsConfirmationVaultDataLoss": {}, "@settingsConfirmationVaultDataLoss": {},
"newVaultDialogTitle": "Nieuwe kluis", "newVaultDialogTitle": "Nieuwe kluis",
"@newVaultDialogTitle": {}, "@newVaultDialogTitle": {},
@ -1383,8 +1383,14 @@
"@collectionActionSetHome": {}, "@collectionActionSetHome": {},
"setHomeCustom": "Aangepast", "setHomeCustom": "Aangepast",
"@setHomeCustom": {}, "@setHomeCustom": {},
"explorerActionSelectStorageVolume": "Selecteer opslag", "explorerActionSelectStorageVolume": "Opslag selecteren",
"@explorerActionSelectStorageVolume": {}, "@explorerActionSelectStorageVolume": {},
"selectStorageVolumeDialogTitle": "Selecteer opslag", "selectStorageVolumeDialogTitle": "Opslag selecteren",
"@selectStorageVolumeDialogTitle": {} "@selectStorageVolumeDialogTitle": {},
"sortByDuration": "Op lengte",
"@sortByDuration": {},
"sortOrderShortestFirst": "Kortste eerst",
"@sortOrderShortestFirst": {},
"sortOrderLongestFirst": "Langste eerst",
"@sortOrderLongestFirst": {}
} }

View file

@ -1536,5 +1536,17 @@
"chipActionGoToExplorerPage": "Pokaż w przeglądarce", "chipActionGoToExplorerPage": "Pokaż w przeglądarce",
"@chipActionGoToExplorerPage": {}, "@chipActionGoToExplorerPage": {},
"explorerPageTitle": "Przeglądarka", "explorerPageTitle": "Przeglądarka",
"@explorerPageTitle": {} "@explorerPageTitle": {},
"selectStorageVolumeDialogTitle": "Wybierz pamięć",
"@selectStorageVolumeDialogTitle": {},
"explorerActionSelectStorageVolume": "Wybierz pamięć",
"@explorerActionSelectStorageVolume": {},
"setHomeCustom": "Własny",
"@setHomeCustom": {},
"sortByDuration": "Według czasu trwania",
"@sortByDuration": {},
"sortOrderShortestFirst": "Najkrótsze najpierw",
"@sortOrderShortestFirst": {},
"sortOrderLongestFirst": "Najdłuższe najpierw",
"@sortOrderLongestFirst": {}
} }

View file

@ -1380,5 +1380,15 @@
"explorerPageTitle": "Проводник", "explorerPageTitle": "Проводник",
"@explorerPageTitle": {}, "@explorerPageTitle": {},
"explorerActionSelectStorageVolume": "Выбрать хранилище", "explorerActionSelectStorageVolume": "Выбрать хранилище",
"@explorerActionSelectStorageVolume": {} "@explorerActionSelectStorageVolume": {},
"selectStorageVolumeDialogTitle": "Выбрать хранилище",
"@selectStorageVolumeDialogTitle": {},
"sortByDuration": "По продолжительности",
"@sortByDuration": {},
"sortOrderLongestFirst": "Сначала самый длинный",
"@sortOrderLongestFirst": {},
"setHomeCustom": "По своему",
"@setHomeCustom": {},
"sortOrderShortestFirst": "Сначала самый короткий",
"@sortOrderShortestFirst": {}
} }

View file

@ -1,5 +1,5 @@
{ {
"viewerActionLock": "Lås", "viewerActionLock": "Lås visaren",
"@viewerActionLock": {}, "@viewerActionLock": {},
"entryInfoActionEditTags": "Redigera taggar", "entryInfoActionEditTags": "Redigera taggar",
"@entryInfoActionEditTags": {}, "@entryInfoActionEditTags": {},
@ -7,19 +7,19 @@
"@videoActionPlay": {}, "@videoActionPlay": {},
"viewerActionSettings": "Inställningar", "viewerActionSettings": "Inställningar",
"@viewerActionSettings": {}, "@viewerActionSettings": {},
"albumTierSpecial": "Vanlig", "albumTierSpecial": "Vanligt förekommande",
"@albumTierSpecial": {}, "@albumTierSpecial": {},
"displayRefreshRatePreferLowest": "Lägsta intervall", "displayRefreshRatePreferLowest": "Lägsta intervall",
"@displayRefreshRatePreferLowest": {}, "@displayRefreshRatePreferLowest": {},
"keepScreenOnViewerOnly": "Visningssidan bara", "keepScreenOnViewerOnly": "Endast visningssidan",
"@keepScreenOnViewerOnly": {}, "@keepScreenOnViewerOnly": {},
"mapStyleOsmHot": "Humanitarian OSM", "mapStyleOsmHot": "Humanitär OSM",
"@mapStyleOsmHot": {}, "@mapStyleOsmHot": {},
"videoResumptionModeAlways": "Alltid", "videoResumptionModeAlways": "Alltid",
"@videoResumptionModeAlways": {}, "@videoResumptionModeAlways": {},
"storageVolumeDescriptionFallbackPrimary": "Intern lagring", "storageVolumeDescriptionFallbackPrimary": "Intern lagring",
"@storageVolumeDescriptionFallbackPrimary": {}, "@storageVolumeDescriptionFallbackPrimary": {},
"widgetOpenPageCollection": "Öppen insamling", "widgetOpenPageCollection": "Öppna samling",
"@widgetOpenPageCollection": {}, "@widgetOpenPageCollection": {},
"widgetTapUpdateWidget": "Uppdatera widgeten", "widgetTapUpdateWidget": "Uppdatera widgeten",
"@widgetTapUpdateWidget": {}, "@widgetTapUpdateWidget": {},
@ -206,7 +206,7 @@
"@entryActionDelete": {}, "@entryActionDelete": {},
"entryActionCopyToClipboard": "Spara till urklipp", "entryActionCopyToClipboard": "Spara till urklipp",
"@entryActionCopyToClipboard": {}, "@entryActionCopyToClipboard": {},
"viewerActionUnlock": "Öppna", "viewerActionUnlock": "Lås upp visaren",
"@viewerActionUnlock": {}, "@viewerActionUnlock": {},
"slideshowActionResume": "Återuppta", "slideshowActionResume": "Återuppta",
"@slideshowActionResume": {}, "@slideshowActionResume": {},
@ -238,9 +238,9 @@
"@cropAspectRatioOriginal": {}, "@cropAspectRatioOriginal": {},
"cropAspectRatioSquare": "Fyrkant", "cropAspectRatioSquare": "Fyrkant",
"@cropAspectRatioSquare": {}, "@cropAspectRatioSquare": {},
"filterAspectRatioLandscapeLabel": "Liggande", "filterAspectRatioLandscapeLabel": "Liggande bilder",
"@filterAspectRatioLandscapeLabel": {}, "@filterAspectRatioLandscapeLabel": {},
"filterAspectRatioPortraitLabel": "Porträtt", "filterAspectRatioPortraitLabel": "Stående bilder",
"@filterAspectRatioPortraitLabel": {}, "@filterAspectRatioPortraitLabel": {},
"filterBinLabel": "Papperskorg", "filterBinLabel": "Papperskorg",
"@filterBinLabel": {}, "@filterBinLabel": {},
@ -333,9 +333,9 @@
"@mapStyleGoogleNormal": {}, "@mapStyleGoogleNormal": {},
"mapStyleGoogleHybrid": "Google Maps (Hybrid)", "mapStyleGoogleHybrid": "Google Maps (Hybrid)",
"@mapStyleGoogleHybrid": {}, "@mapStyleGoogleHybrid": {},
"mapStyleGoogleTerrain": "Google Maps (Terrain)", "mapStyleGoogleTerrain": "Google Maps (Terräng)",
"@mapStyleGoogleTerrain": {}, "@mapStyleGoogleTerrain": {},
"mapStyleStamenWatercolor": "Stamen Watercolor", "mapStyleStamenWatercolor": "Stamen Watercolor (Akvarell)",
"@mapStyleStamenWatercolor": {}, "@mapStyleStamenWatercolor": {},
"maxBrightnessNever": "Alldrig", "maxBrightnessNever": "Alldrig",
"@maxBrightnessNever": {}, "@maxBrightnessNever": {},
@ -377,11 +377,11 @@
"@settingsVideoEnablePip": {}, "@settingsVideoEnablePip": {},
"videoControlsPlay": "Spela", "videoControlsPlay": "Spela",
"@videoControlsPlay": {}, "@videoControlsPlay": {},
"videoControlsPlaySeek": "Spela & sök bakåt/framåt", "videoControlsPlaySeek": "Spela & spola bakåt/framåt",
"@videoControlsPlaySeek": {}, "@videoControlsPlaySeek": {},
"videoControlsPlayOutside": "Öppna med annan spelare", "videoControlsPlayOutside": "Öppna med annan spelare",
"@videoControlsPlayOutside": {}, "@videoControlsPlayOutside": {},
"videoControlsNone": "Ingen", "videoControlsNone": "Inga",
"@videoControlsNone": {}, "@videoControlsNone": {},
"videoLoopModeNever": "Aldrig", "videoLoopModeNever": "Aldrig",
"@videoLoopModeNever": {}, "@videoLoopModeNever": {},
@ -409,9 +409,9 @@
"@widgetDisplayedItemRandom": {}, "@widgetDisplayedItemRandom": {},
"widgetDisplayedItemMostRecent": "Alldra senast", "widgetDisplayedItemMostRecent": "Alldra senast",
"@widgetDisplayedItemMostRecent": {}, "@widgetDisplayedItemMostRecent": {},
"widgetOpenPageHome": "Öppna hem", "widgetOpenPageHome": "Öppna startsida",
"@widgetOpenPageHome": {}, "@widgetOpenPageHome": {},
"otherDirectoryDescription": "“{name}” map", "otherDirectoryDescription": "“{name}”-katalogen",
"@otherDirectoryDescription": { "@otherDirectoryDescription": {
"placeholders": { "placeholders": {
"name": { "name": {
@ -421,7 +421,7 @@
} }
} }
}, },
"storageAccessDialogMessage": "Var snäll och välj {directory} av“{volume}” på nästa skärm för att ge appen åtkomst till den.", "storageAccessDialogMessage": "Vänligen välj {directory} i “{volume}” på nästa skärm för att ge appen åtkomst till den.",
"@storageAccessDialogMessage": { "@storageAccessDialogMessage": {
"placeholders": { "placeholders": {
"directory": { "directory": {
@ -523,7 +523,7 @@
"@nameConflictDialogSingleSourceMessage": {}, "@nameConflictDialogSingleSourceMessage": {},
"nameConflictDialogMultipleSourceMessage": "Vissa filer har samma namn.", "nameConflictDialogMultipleSourceMessage": "Vissa filer har samma namn.",
"@nameConflictDialogMultipleSourceMessage": {}, "@nameConflictDialogMultipleSourceMessage": {},
"noMatchingAppDialogMessage": "Det finns inga appar som kan hantera detta.", "noMatchingAppDialogMessage": "Det finns inga applikationer som kan hantera detta.",
"@noMatchingAppDialogMessage": {}, "@noMatchingAppDialogMessage": {},
"moveUndatedConfirmationDialogSetDate": "Spara datum", "moveUndatedConfirmationDialogSetDate": "Spara datum",
"@moveUndatedConfirmationDialogSetDate": {}, "@moveUndatedConfirmationDialogSetDate": {},
@ -752,7 +752,7 @@
"@viewerTransitionFade": {}, "@viewerTransitionFade": {},
"wallpaperTargetHomeLock": "Hem och låsskärmar", "wallpaperTargetHomeLock": "Hem och låsskärmar",
"@wallpaperTargetHomeLock": {}, "@wallpaperTargetHomeLock": {},
"missingSystemFilePickerDialogMessage": "systemets filväljare är borta eller avstängd. Var snäll och sätt på den och försök igen.", "missingSystemFilePickerDialogMessage": "Systemets filväljare saknas eller har inaktiverats. Vänligen aktivera denna och försök igen.",
"@missingSystemFilePickerDialogMessage": {}, "@missingSystemFilePickerDialogMessage": {},
"renameProcessorCounter": "Räknare", "renameProcessorCounter": "Räknare",
"@renameProcessorCounter": {}, "@renameProcessorCounter": {},
@ -833,5 +833,66 @@
"chipActionUnpin": "Släpp från fästet", "chipActionUnpin": "Släpp från fästet",
"@chipActionUnpin": {}, "@chipActionUnpin": {},
"chipActionShowCollection": "Visa i samling", "chipActionShowCollection": "Visa i samling",
"@chipActionShowCollection": {} "@chipActionShowCollection": {},
"videoActionABRepeat": "A-B återupprepa",
"@videoActionABRepeat": {},
"videoRepeatActionSetStart": "Ange start",
"@videoRepeatActionSetStart": {},
"restrictedAccessDialogMessage": "Denna applikation har ej tillåtelse att modifiera filer i {directory} i \"{volume}\".\n\nVänligen använd en förinstallerad filhanterare eller galleriapplikation för att flytta filerna till en annan katalog.",
"@restrictedAccessDialogMessage": {
"placeholders": {
"directory": {
"type": "String",
"description": "the name of a directory, using the output of `rootDirectoryDescription` or `otherDirectoryDescription`"
},
"volume": {
"type": "String",
"example": "SD card",
"description": "the name of a storage volume"
}
}
},
"widgetOpenPageViewer": "Öppna bildspel",
"@widgetOpenPageViewer": {},
"rootDirectoryDescription": "grundkatalog",
"@rootDirectoryDescription": {},
"notEnoughSpaceDialogMessage": "Denna åtgärd behöver {neededSize} ledigt utrymme på \"{volume}\" för att kunna slutföras, men det är enbart {freeSize} kvar.",
"@notEnoughSpaceDialogMessage": {
"placeholders": {
"neededSize": {
"type": "String",
"example": "314 MB"
},
"freeSize": {
"type": "String",
"example": "123 MB"
},
"volume": {
"type": "String",
"example": "SD card",
"description": "the name of a storage volume"
}
}
},
"addShortcutButtonLabel": "LÄGG TILL",
"@addShortcutButtonLabel": {},
"addShortcutDialogLabel": "Rubrik för genväg",
"@addShortcutDialogLabel": {},
"unsupportedTypeDialogMessage": "{count, plural, =1{Denna åtgärd stöds ej för filer av denna typ: {types}.} other{Denna åtgärd stöds ej för följande typer av filer: {types}.}}",
"@unsupportedTypeDialogMessage": {
"placeholders": {
"count": {},
"types": {
"type": "String",
"example": "GIF, TIFF, MP4",
"description": "a list of unsupported types"
}
}
},
"stopTooltip": "Stopp",
"@stopTooltip": {},
"chipActionGoToExplorerPage": "Visa i utforskaren",
"@chipActionGoToExplorerPage": {},
"videoRepeatActionSetEnd": "Ange slut",
"@videoRepeatActionSetEnd": {}
} }

View file

@ -7,7 +7,7 @@
"@welcomeOptional": {}, "@welcomeOptional": {},
"welcomeTermsToggle": "Hüküm ve koşulları kabul ediyorum", "welcomeTermsToggle": "Hüküm ve koşulları kabul ediyorum",
"@welcomeTermsToggle": {}, "@welcomeTermsToggle": {},
"itemCount": "{count, plural, other{{count} öğe}}", "itemCount": "{count, plural, other{{count} öge}}",
"@itemCount": {}, "@itemCount": {},
"timeSeconds": "{count, plural, other{{count} saniye}}", "timeSeconds": "{count, plural, other{{count} saniye}}",
"@timeSeconds": {}, "@timeSeconds": {},
@ -273,7 +273,7 @@
"@rootDirectoryDescription": {}, "@rootDirectoryDescription": {},
"otherDirectoryDescription": "“{name}” dizin", "otherDirectoryDescription": "“{name}” dizin",
"@otherDirectoryDescription": {}, "@otherDirectoryDescription": {},
"storageAccessDialogMessage": "Bu uygulamaya erişim sağlamak için lütfen bir sonraki ekranda “{volume}” öğesinin {directory} dizinini seçin.", "storageAccessDialogMessage": "Bir sonraki ekranda \"{volume}\" içindeki {directory} dizinini seçerek bu uygulamaya erişim izni verin.",
"@storageAccessDialogMessage": {}, "@storageAccessDialogMessage": {},
"restrictedAccessDialogMessage": "Bu uygulamanın “{volume}” içindeki {directory} dosyaları değiştirmesine izin verilmiyor.\n\nÖğeleri başka bir dizine taşımak için lütfen önceden yüklenmiş bir dosya yöneticisi veya galeri uygulaması kullanın.", "restrictedAccessDialogMessage": "Bu uygulamanın “{volume}” içindeki {directory} dosyaları değiştirmesine izin verilmiyor.\n\nÖğeleri başka bir dizine taşımak için lütfen önceden yüklenmiş bir dosya yöneticisi veya galeri uygulaması kullanın.",
"@restrictedAccessDialogMessage": {}, "@restrictedAccessDialogMessage": {},
@ -281,7 +281,7 @@
"@notEnoughSpaceDialogMessage": {}, "@notEnoughSpaceDialogMessage": {},
"missingSystemFilePickerDialogMessage": "Sistem dosya seçicisi eksik veya devre dışı. Lütfen etkinleştirin ve tekrar deneyin.", "missingSystemFilePickerDialogMessage": "Sistem dosya seçicisi eksik veya devre dışı. Lütfen etkinleştirin ve tekrar deneyin.",
"@missingSystemFilePickerDialogMessage": {}, "@missingSystemFilePickerDialogMessage": {},
"unsupportedTypeDialogMessage": "{count, plural, =1{Bu işlem aşağıdaki türdeki öğeler için desteklenmez: {types}.} other{Bu işlem aşağıdaki türlerdeki öğeler için desteklenmez: {types}.}}", "unsupportedTypeDialogMessage": "{count, plural, =1{Bu işlem şu türdeki ögeler için desteklenmez: {types}.} other{Bu işlem şu türlerdeki ögeler için desteklenmez: {types}.}}",
"@unsupportedTypeDialogMessage": {}, "@unsupportedTypeDialogMessage": {},
"nameConflictDialogSingleSourceMessage": "Hedef klasördeki bazı dosyalar aynı ada sahip.", "nameConflictDialogSingleSourceMessage": "Hedef klasördeki bazı dosyalar aynı ada sahip.",
"@nameConflictDialogSingleSourceMessage": {}, "@nameConflictDialogSingleSourceMessage": {},
@ -293,11 +293,11 @@
"@addShortcutButtonLabel": {}, "@addShortcutButtonLabel": {},
"noMatchingAppDialogMessage": "Bununla ilgilenebilecek bir uygulama yok.", "noMatchingAppDialogMessage": "Bununla ilgilenebilecek bir uygulama yok.",
"@noMatchingAppDialogMessage": {}, "@noMatchingAppDialogMessage": {},
"binEntriesConfirmationDialogMessage": "{count, plural, =1{Bu öğe geri dönüşüm kutusuna taşınsın mı?} other{Bu {count} madde geri dönüşüm kutusuna atılsın mı?}}", "binEntriesConfirmationDialogMessage": "{count, plural, =1{Bu öge geri dönüşüm kutusuna taşınsın mı?} other{Bu {count} öge geri dönüşüm kutusuna atılsın mı?}}",
"@binEntriesConfirmationDialogMessage": {}, "@binEntriesConfirmationDialogMessage": {},
"deleteEntriesConfirmationDialogMessage": "{count, plural, =1{Bu öğe silinsin mi?} other{Bu {count} öğe silinsin mi?}}", "deleteEntriesConfirmationDialogMessage": "{count, plural, =1{Bu öge silinsin mi?} other{Bu {count} öge silinsin mi?}}",
"@deleteEntriesConfirmationDialogMessage": {}, "@deleteEntriesConfirmationDialogMessage": {},
"moveUndatedConfirmationDialogMessage": "Devam etmeden önce öğe tarihleri kaydedilsin mi?", "moveUndatedConfirmationDialogMessage": "Devam etmeden önce öge tarihleri kaydedilsin mi?",
"@moveUndatedConfirmationDialogMessage": {}, "@moveUndatedConfirmationDialogMessage": {},
"moveUndatedConfirmationDialogSetDate": "Tarihleri kaydet", "moveUndatedConfirmationDialogSetDate": "Tarihleri kaydet",
"@moveUndatedConfirmationDialogSetDate": {}, "@moveUndatedConfirmationDialogSetDate": {},
@ -307,7 +307,7 @@
"@videoStartOverButtonLabel": {}, "@videoStartOverButtonLabel": {},
"videoResumeButtonLabel": "SÜRDÜR", "videoResumeButtonLabel": "SÜRDÜR",
"@videoResumeButtonLabel": {}, "@videoResumeButtonLabel": {},
"setCoverDialogLatest": "Son öğe", "setCoverDialogLatest": "Son öge",
"@setCoverDialogLatest": {}, "@setCoverDialogLatest": {},
"setCoverDialogAuto": "Otomatik", "setCoverDialogAuto": "Otomatik",
"@setCoverDialogAuto": {}, "@setCoverDialogAuto": {},
@ -359,7 +359,7 @@
"@editEntryDateDialogSetCustom": {}, "@editEntryDateDialogSetCustom": {},
"editEntryDateDialogCopyField": "Başka bir tarihten kopyala", "editEntryDateDialogCopyField": "Başka bir tarihten kopyala",
"@editEntryDateDialogCopyField": {}, "@editEntryDateDialogCopyField": {},
"editEntryDialogCopyFromItem": "Başka bir öğeden kopyala", "editEntryDialogCopyFromItem": "Başka bir ögeden kopyala",
"@editEntryDialogCopyFromItem": {}, "@editEntryDialogCopyFromItem": {},
"editEntryDateDialogExtractFromTitle": "Başlıktan ayıkla", "editEntryDateDialogExtractFromTitle": "Başlıktan ayıkla",
"@editEntryDateDialogExtractFromTitle": {}, "@editEntryDateDialogExtractFromTitle": {},
@ -523,25 +523,25 @@
"@dateYesterday": {}, "@dateYesterday": {},
"dateThisMonth": "Bu ay", "dateThisMonth": "Bu ay",
"@dateThisMonth": {}, "@dateThisMonth": {},
"collectionDeleteFailureFeedback": "{count, plural, =1{1 öğe silinemedi} other{{count} öğe silinemedi}}", "collectionDeleteFailureFeedback": "{count, plural, =1{1 öge silinemedi} other{{count} öge silinemedi}}",
"@collectionDeleteFailureFeedback": {}, "@collectionDeleteFailureFeedback": {},
"collectionCopyFailureFeedback": "{count, plural, =1{1 öğe kopyalanamadı} other{{count} öğe kopyalanamadı}}", "collectionCopyFailureFeedback": "{count, plural, =1{1 öge kopyalanamadı} other{{count} öge kopyalanamadı}}",
"@collectionCopyFailureFeedback": {}, "@collectionCopyFailureFeedback": {},
"collectionMoveFailureFeedback": "{count, plural, =1{1 öğe taşınamadı} other{{count} öğe taşınamadı}}", "collectionMoveFailureFeedback": "{count, plural, =1{1 öge taşınamadı} other{{count} öge taşınamadı}}",
"@collectionMoveFailureFeedback": {}, "@collectionMoveFailureFeedback": {},
"collectionRenameFailureFeedback": "{count, plural, =1{1 öğenin adı değiştirilemedi} other{{count} öğenin adı değiştirilemedi}}", "collectionRenameFailureFeedback": "{count, plural, =1{1 ögenin adı değiştirilemedi} other{{count} ögenin adı değiştirilemedi}}",
"@collectionRenameFailureFeedback": {}, "@collectionRenameFailureFeedback": {},
"collectionEditFailureFeedback": "{count, plural, =1{1 öğe düzenlenemedi} other{{count} öğe düzenlenemedi}}", "collectionEditFailureFeedback": "{count, plural, =1{1 öge düzenlenemedi} other{{count} öge düzenlenemedi}}",
"@collectionEditFailureFeedback": {}, "@collectionEditFailureFeedback": {},
"collectionExportFailureFeedback": "{count, plural, =1{1 sayfa dışa aktarılamadı} other{{count} sayfa dışa aktarılamadı}}", "collectionExportFailureFeedback": "{count, plural, =1{1 sayfa dışa aktarılamadı} other{{count} sayfa dışa aktarılamadı}}",
"@collectionExportFailureFeedback": {}, "@collectionExportFailureFeedback": {},
"collectionCopySuccessFeedback": "{count, plural, =1{1 öğe kopyalandı} other{{count} öğe kopyalandı}}", "collectionCopySuccessFeedback": "{count, plural, =1{1 öge kopyalandı} other{{count} öge kopyalandı}}",
"@collectionCopySuccessFeedback": {}, "@collectionCopySuccessFeedback": {},
"collectionMoveSuccessFeedback": "{count, plural, =1{1 öğe taşındı} other{{count} öğe taşındı}}", "collectionMoveSuccessFeedback": "{count, plural, =1{1 öge taşındı} other{{count} öge taşındı}}",
"@collectionMoveSuccessFeedback": {}, "@collectionMoveSuccessFeedback": {},
"collectionRenameSuccessFeedback": "{count, plural, =1{1 öğenin adı değiştirildi} other{{count} öğenin adı değiştirildi}}", "collectionRenameSuccessFeedback": "{count, plural, =1{1 ögenin adı değiştirildi} other{{count} ögenin adı değiştirildi}}",
"@collectionRenameSuccessFeedback": {}, "@collectionRenameSuccessFeedback": {},
"collectionEditSuccessFeedback": "{count, plural, =1{1 öğe düzenlendi} other{{count} öğe düzenlendi}}", "collectionEditSuccessFeedback": "{count, plural, =1{1 öge düzenlendi} other{{count} öge düzenlendi}}",
"@collectionEditSuccessFeedback": {}, "@collectionEditSuccessFeedback": {},
"collectionEmptyFavourites": "Favori yok", "collectionEmptyFavourites": "Favori yok",
"@collectionEmptyFavourites": {}, "@collectionEmptyFavourites": {},
@ -705,7 +705,7 @@
"@settingsNavigationDrawerTile": {}, "@settingsNavigationDrawerTile": {},
"settingsNavigationDrawerEditorPageTitle": "Gezinti Menüsü", "settingsNavigationDrawerEditorPageTitle": "Gezinti Menüsü",
"@settingsNavigationDrawerEditorPageTitle": {}, "@settingsNavigationDrawerEditorPageTitle": {},
"settingsNavigationDrawerBanner": "Menü öğelerini taşımak ve yeniden sıralamak için dokunun ve basılı tutun.", "settingsNavigationDrawerBanner": "Menü ögelerini taşımak ve yeniden sıralamak için dokunun ve basılı tutun.",
"@settingsNavigationDrawerBanner": {}, "@settingsNavigationDrawerBanner": {},
"settingsNavigationDrawerTabTypes": "Türler", "settingsNavigationDrawerTabTypes": "Türler",
"@settingsNavigationDrawerTabTypes": {}, "@settingsNavigationDrawerTabTypes": {},
@ -743,9 +743,9 @@
"@settingsCollectionQuickActionTabBrowsing": {}, "@settingsCollectionQuickActionTabBrowsing": {},
"settingsCollectionQuickActionTabSelecting": "Seçme", "settingsCollectionQuickActionTabSelecting": "Seçme",
"@settingsCollectionQuickActionTabSelecting": {}, "@settingsCollectionQuickActionTabSelecting": {},
"settingsCollectionBrowsingQuickActionEditorBanner": "Düğmeleri hareket ettirmek ve öğelere göz atarken hangi eylemlerin görüntüleneceğini seçmek için dokunun ve basılı tutun.", "settingsCollectionBrowsingQuickActionEditorBanner": "Düğmeleri hareket ettirmek ve ögelere göz atarken hangi eylemlerin görüntüleneceğini seçmek için dokunun ve basılı tutun.",
"@settingsCollectionBrowsingQuickActionEditorBanner": {}, "@settingsCollectionBrowsingQuickActionEditorBanner": {},
"settingsCollectionSelectionQuickActionEditorBanner": "Düğmeleri hareket ettirmek ve öğeleri seçerken hangi eylemlerin görüntüleneceğini seçmek için dokunun ve basılı tutun.", "settingsCollectionSelectionQuickActionEditorBanner": "Düğmeleri hareket ettirmek ve ögeleri seçerken hangi eylemlerin görüntüleneceğini seçmek için dokunun ve basılı tutun.",
"@settingsCollectionSelectionQuickActionEditorBanner": {}, "@settingsCollectionSelectionQuickActionEditorBanner": {},
"settingsViewerSectionTitle": "Görüntüleyici", "settingsViewerSectionTitle": "Görüntüleyici",
"@settingsViewerSectionTitle": {}, "@settingsViewerSectionTitle": {},
@ -851,9 +851,9 @@
"@settingsSaveSearchHistory": {}, "@settingsSaveSearchHistory": {},
"settingsEnableBin": "Geri dönüşüm kutusunu kullan", "settingsEnableBin": "Geri dönüşüm kutusunu kullan",
"@settingsEnableBin": {}, "@settingsEnableBin": {},
"settingsEnableBinSubtitle": "Silinen öğeleri 30 gün boyunca saklar", "settingsEnableBinSubtitle": "Silinen ögeleri 30 gün boyunca saklar",
"@settingsEnableBinSubtitle": {}, "@settingsEnableBinSubtitle": {},
"settingsHiddenItemsTile": "Gizli öğeler", "settingsHiddenItemsTile": "Gizli ögeler",
"@settingsHiddenItemsTile": {}, "@settingsHiddenItemsTile": {},
"settingsHiddenItemsPageTitle": "Gizli Öğeler", "settingsHiddenItemsPageTitle": "Gizli Öğeler",
"@settingsHiddenItemsPageTitle": {}, "@settingsHiddenItemsPageTitle": {},
@ -921,7 +921,7 @@
"@settingsCollectionTile": {}, "@settingsCollectionTile": {},
"statsPageTitle": "İstatistikler", "statsPageTitle": "İstatistikler",
"@statsPageTitle": {}, "@statsPageTitle": {},
"statsWithGps": "{count, plural, =1{1 konuma sahip öğe} other{{count} konuma sahip öğe}}", "statsWithGps": "{count, plural, =1{1 öge konuma sahip} other{{count} öge konuma sahip}}",
"@statsWithGps": {}, "@statsWithGps": {},
"statsTopCountriesSectionTitle": "Öne Çıkan Ülkeler", "statsTopCountriesSectionTitle": "Öne Çıkan Ülkeler",
"@statsTopCountriesSectionTitle": {}, "@statsTopCountriesSectionTitle": {},
@ -1019,7 +1019,7 @@
"@filePickerDoNotShowHiddenFiles": {}, "@filePickerDoNotShowHiddenFiles": {},
"filePickerOpenFrom": "Şuradan aç", "filePickerOpenFrom": "Şuradan aç",
"@filePickerOpenFrom": {}, "@filePickerOpenFrom": {},
"filePickerNoItems": "Öğe yok", "filePickerNoItems": "Öge yok",
"@filePickerNoItems": {}, "@filePickerNoItems": {},
"filePickerUseThisFolder": "Bu klasörü kullan", "filePickerUseThisFolder": "Bu klasörü kullan",
"@filePickerUseThisFolder": {}, "@filePickerUseThisFolder": {},
@ -1049,7 +1049,7 @@
"@sortOrderLargestFirst": {}, "@sortOrderLargestFirst": {},
"settingsConfirmationAfterMoveToBinItems": "Öğeleri geri dönüşüm kutusuna taşıdıktan sonra mesaj göster", "settingsConfirmationAfterMoveToBinItems": "Öğeleri geri dönüşüm kutusuna taşıdıktan sonra mesaj göster",
"@settingsConfirmationAfterMoveToBinItems": {}, "@settingsConfirmationAfterMoveToBinItems": {},
"settingsViewerGestureSideTapNext": "Önceki/sonraki öğeyi göstermek için ekran kenarlarına dokunun", "settingsViewerGestureSideTapNext": "Önceki/sonraki ögeyi göstermek için ekran kenarlarına dokunun",
"@settingsViewerGestureSideTapNext": {}, "@settingsViewerGestureSideTapNext": {},
"settingsSlideshowVideoPlaybackTile": "Video oynatma", "settingsSlideshowVideoPlaybackTile": "Video oynatma",
"@settingsSlideshowVideoPlaybackTile": {}, "@settingsSlideshowVideoPlaybackTile": {},
@ -1161,7 +1161,7 @@
"@widgetDisplayedItemMostRecent": {}, "@widgetDisplayedItemMostRecent": {},
"settingsSubtitleThemeTextPositionTile": "Metin konumu", "settingsSubtitleThemeTextPositionTile": "Metin konumu",
"@settingsSubtitleThemeTextPositionTile": {}, "@settingsSubtitleThemeTextPositionTile": {},
"settingsWidgetDisplayedItem": "Görüntülenen öğe", "settingsWidgetDisplayedItem": "Görüntülenen öge",
"@settingsWidgetDisplayedItem": {}, "@settingsWidgetDisplayedItem": {},
"settingsSubtitleThemeTextPositionDialogTitle": "Metin Konumu", "settingsSubtitleThemeTextPositionDialogTitle": "Metin Konumu",
"@settingsSubtitleThemeTextPositionDialogTitle": {}, "@settingsSubtitleThemeTextPositionDialogTitle": {},
@ -1378,5 +1378,17 @@
"chipActionGoToExplorerPage": "Gezginde göster", "chipActionGoToExplorerPage": "Gezginde göster",
"@chipActionGoToExplorerPage": {}, "@chipActionGoToExplorerPage": {},
"explorerPageTitle": "Gezgin", "explorerPageTitle": "Gezgin",
"@explorerPageTitle": {} "@explorerPageTitle": {},
"explorerActionSelectStorageVolume": "Depolama alanı seç",
"@explorerActionSelectStorageVolume": {},
"selectStorageVolumeDialogTitle": "Depolama Alanı Seç",
"@selectStorageVolumeDialogTitle": {},
"setHomeCustom": "Özel",
"@setHomeCustom": {},
"sortByDuration": "Süreye göre",
"@sortByDuration": {},
"sortOrderShortestFirst": "Önce en kısa",
"@sortOrderShortestFirst": {},
"sortOrderLongestFirst": "Önce en uzun",
"@sortOrderLongestFirst": {}
} }

View file

@ -1542,5 +1542,11 @@
"explorerActionSelectStorageVolume": "Обрати сховище", "explorerActionSelectStorageVolume": "Обрати сховище",
"@explorerActionSelectStorageVolume": {}, "@explorerActionSelectStorageVolume": {},
"selectStorageVolumeDialogTitle": "Оберіть сховище", "selectStorageVolumeDialogTitle": "Оберіть сховище",
"@selectStorageVolumeDialogTitle": {} "@selectStorageVolumeDialogTitle": {},
"sortByDuration": "За тривалістю",
"@sortByDuration": {},
"sortOrderShortestFirst": "Спершу найкоротше",
"@sortOrderShortestFirst": {},
"sortOrderLongestFirst": "Спершу найдовше",
"@sortOrderLongestFirst": {}
} }

View file

@ -1378,5 +1378,17 @@
"explorerPageTitle": "资源管理器", "explorerPageTitle": "资源管理器",
"@explorerPageTitle": {}, "@explorerPageTitle": {},
"chipActionGoToExplorerPage": "在资源管理器中显示", "chipActionGoToExplorerPage": "在资源管理器中显示",
"@chipActionGoToExplorerPage": {} "@chipActionGoToExplorerPage": {},
"setHomeCustom": "自定义",
"@setHomeCustom": {},
"explorerActionSelectStorageVolume": "选择存储器",
"@explorerActionSelectStorageVolume": {},
"selectStorageVolumeDialogTitle": "选择存储器",
"@selectStorageVolumeDialogTitle": {},
"sortByDuration": "按时长",
"@sortByDuration": {},
"sortOrderLongestFirst": "先长后短",
"@sortOrderLongestFirst": {},
"sortOrderShortestFirst": "先短后长",
"@sortOrderShortestFirst": {}
} }

View file

@ -1,6 +1,6 @@
import 'package:aves/app_flavor.dart'; import 'package:aves/app_flavor.dart';
import 'package:aves/main_common.dart'; import 'package:aves/main_common.dart';
import 'package:aves/widgets/intent.dart'; import 'package:aves/model/app/intent.dart';
// https://developer.android.com/studio/command-line/adb.html#IntentSpec // https://developer.android.com/studio/command-line/adb.html#IntentSpec
// adb shell am start -n deckers.thibault.aves.debug/deckers.thibault.aves.MainActivity -a android.intent.action.EDIT -d content://media/external/images/media/183128 -t image/* // adb shell am start -n deckers.thibault.aves.debug/deckers.thibault.aves.MainActivity -a android.intent.action.EDIT -d content://media/external/images/media/183128 -t image/*

View file

@ -95,6 +95,7 @@ class Contributors {
Contributor('elfriob', 'elfriob@ya.ru'), Contributor('elfriob', 'elfriob@ya.ru'),
Contributor('Stephan Paternotte', 'stephan@paternottes.net'), Contributor('Stephan Paternotte', 'stephan@paternottes.net'),
Contributor('Tung Anh', 'buihuutunganh2007@gmail.com'), Contributor('Tung Anh', 'buihuutunganh2007@gmail.com'),
Contributor('Adrien N', 'adriennathaniel1999@gmail.com'),
// Contributor('Alvi Khan', 'aveenalvi@gmail.com'), // Bengali // Contributor('Alvi Khan', 'aveenalvi@gmail.com'), // Bengali
// Contributor('Htet Oo Hlaing', 'htetoh2006@outlook.com'), // Burmese // Contributor('Htet Oo Hlaing', 'htetoh2006@outlook.com'), // Burmese
// Contributor('Khant', 'khant@users.noreply.hosted.weblate.org'), // Burmese // Contributor('Khant', 'khant@users.noreply.hosted.weblate.org'), // Burmese
@ -113,6 +114,7 @@ class Contributors {
// Contributor('Prasanta-Hembram', 'Prasantahembram720@gmail.com'), // Santali // Contributor('Prasanta-Hembram', 'Prasantahembram720@gmail.com'), // Santali
// Contributor('mytja', 'mamnju21@gmail.com'), // Slovenian // Contributor('mytja', 'mamnju21@gmail.com'), // Slovenian
// Contributor('Shift18', 'bribable.lawyer@posteo.net'), // Swedish // Contributor('Shift18', 'bribable.lawyer@posteo.net'), // Swedish
// Contributor('Andreas Håll', 'ante_skalman@hotmail.com'), // Swedish
// Contributor('Nattapong K', 'mixer5056@gmail.com'), // Thai // Contributor('Nattapong K', 'mixer5056@gmail.com'), // Thai
}; };
} }

View file

@ -338,12 +338,6 @@ class Dependencies {
license: apache2, license: apache2,
sourceUrl: 'https://github.com/jifalops/dart-latlong', sourceUrl: 'https://github.com/jifalops/dart-latlong',
), ),
Dependency(
name: 'Material Color Utilities',
license: apache2,
licenseUrl: 'https://github.com/material-foundation/material-color-utilities/tree/main/dart/LICENSE',
sourceUrl: 'https://github.com/material-foundation/material-color-utilities/tree/main/dart',
),
Dependency( Dependency(
name: 'Memory Leak Tracker', name: 'Memory Leak Tracker',
license: bsd3, license: bsd3,

View file

@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:aves/model/apps.dart'; import 'package:aves/model/app_inventory.dart';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/album.dart';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';

View file

@ -6,7 +6,7 @@ import 'package:aves/model/metadata/address.dart';
import 'package:aves/model/metadata/catalog.dart'; import 'package:aves/model/metadata/catalog.dart';
import 'package:aves/model/metadata/trash.dart'; import 'package:aves/model/metadata/trash.dart';
import 'package:aves/model/vaults/details.dart'; import 'package:aves/model/vaults/details.dart';
import 'package:aves/model/video_playback.dart'; import 'package:aves/model/viewer/video_playback.dart';
abstract class MetadataDb { abstract class MetadataDb {
int get nextId; int get nextId;

View file

@ -10,7 +10,7 @@ import 'package:aves/model/metadata/address.dart';
import 'package:aves/model/metadata/catalog.dart'; import 'package:aves/model/metadata/catalog.dart';
import 'package:aves/model/metadata/trash.dart'; import 'package:aves/model/metadata/trash.dart';
import 'package:aves/model/vaults/details.dart'; import 'package:aves/model/vaults/details.dart';
import 'package:aves/model/video_playback.dart'; import 'package:aves/model/viewer/video_playback.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';

View file

@ -2,9 +2,9 @@ import 'dart:async';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/geotiff.dart'; import 'package:aves/model/media/geotiff.dart';
import 'package:aves/model/metadata/catalog.dart'; import 'package:aves/model/metadata/catalog.dart';
import 'package:aves/model/video/metadata.dart'; import 'package:aves/model/media/video/metadata.dart';
import 'package:aves/ref/mime_types.dart'; import 'package:aves/ref/mime_types.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/services/metadata/svg_metadata_service.dart'; import 'package:aves/services/metadata/svg_metadata_service.dart';

View file

@ -4,7 +4,7 @@ import 'dart:collection';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/model/entry/extensions/multipage.dart';
import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/video/metadata.dart'; import 'package:aves/model/media/video/metadata.dart';
import 'package:aves/ref/mime_types.dart'; import 'package:aves/ref/mime_types.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/services/metadata/svg_metadata_service.dart'; import 'package:aves/services/metadata/svg_metadata_service.dart';

View file

@ -35,4 +35,12 @@ class AvesEntrySort {
final c = (b.sizeBytes ?? 0).compareTo(a.sizeBytes ?? 0); final c = (b.sizeBytes ?? 0).compareTo(a.sizeBytes ?? 0);
return c != 0 ? c : compareByDate(a, b); return c != 0 ? c : compareByDate(a, b);
} }
// compare by:
// 1) duration descending
// 2) date descending
static int compareByDuration(AvesEntry a, AvesEntry b) {
final c = (b.durationMillis ?? 0).compareTo(a.durationMillis ?? 0);
return c != 0 ? c : compareByDate(a, b);
}
} }

View file

@ -2,11 +2,11 @@ import 'dart:async';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/metadata/catalog.dart'; import 'package:aves/model/metadata/catalog.dart';
import 'package:aves/model/video/channel_layouts.dart'; import 'package:aves/model/media/video/channel_layouts.dart';
import 'package:aves/model/video/codecs.dart'; import 'package:aves/model/media/video/codecs.dart';
import 'package:aves/model/video/profiles/aac.dart'; import 'package:aves/model/media/video/profiles/aac.dart';
import 'package:aves/model/video/profiles/h264.dart'; import 'package:aves/model/media/video/profiles/h264.dart';
import 'package:aves/model/video/profiles/hevc.dart'; import 'package:aves/model/media/video/profiles/hevc.dart';
import 'package:aves/ref/languages.dart'; import 'package:aves/ref/languages.dart';
import 'package:aves/ref/locales.dart'; import 'package:aves/ref/locales.dart';
import 'package:aves/ref/mime_types.dart'; import 'package:aves/ref/mime_types.dart';

View file

@ -1,4 +1,4 @@
import 'package:aves/convert/metadata/fields.dart'; import 'package:aves/convert/convert.dart';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';

View file

@ -6,7 +6,7 @@ import 'package:collection/collection.dart';
import 'package:flutter/widgets.dart'; import 'package:flutter/widgets.dart';
mixin AppSettings on SettingsAccess { mixin AppSettings on SettingsAccess {
static const int _recentFilterHistoryMax = 10; static const int recentFilterHistoryMax = 20;
bool get hasAcceptedTerms => getBool(SettingKeys.hasAcceptedTermsKey) ?? SettingsDefaults.hasAcceptedTerms; bool get hasAcceptedTerms => getBool(SettingKeys.hasAcceptedTermsKey) ?? SettingsDefaults.hasAcceptedTerms;
@ -105,9 +105,9 @@ mixin AppSettings on SettingsAccess {
List<String> get recentDestinationAlbums => getStringList(SettingKeys.recentDestinationAlbumsKey) ?? []; List<String> get recentDestinationAlbums => getStringList(SettingKeys.recentDestinationAlbumsKey) ?? [];
set recentDestinationAlbums(List<String> newValue) => set(SettingKeys.recentDestinationAlbumsKey, newValue.take(_recentFilterHistoryMax).toList()); set recentDestinationAlbums(List<String> newValue) => set(SettingKeys.recentDestinationAlbumsKey, newValue.take(recentFilterHistoryMax).toList());
List<CollectionFilter> get recentTags => (getStringList(SettingKeys.recentTagsKey) ?? []).map(CollectionFilter.fromJson).whereNotNull().toList(); List<CollectionFilter> get recentTags => (getStringList(SettingKeys.recentTagsKey) ?? []).map(CollectionFilter.fromJson).whereNotNull().toList();
set recentTags(List<CollectionFilter> newValue) => set(SettingKeys.recentTagsKey, newValue.take(_recentFilterHistoryMax).map((filter) => filter.toJson()).toList()); set recentTags(List<CollectionFilter> newValue) => set(SettingKeys.recentTagsKey, newValue.take(recentFilterHistoryMax).map((filter) => filter.toJson()).toList());
} }

View file

@ -9,17 +9,18 @@ mixin PrivacySettings on SettingsAccess, SearchSettings {
set hiddenFilters(Set<CollectionFilter> newValue) => set(SettingKeys.hiddenFiltersKey, newValue.map((filter) => filter.toJson()).toList()); set hiddenFilters(Set<CollectionFilter> newValue) => set(SettingKeys.hiddenFiltersKey, newValue.map((filter) => filter.toJson()).toList());
void changeFilterVisibility(Set<CollectionFilter> filters, bool visible) { void changeFilterVisibility(Set<CollectionFilter> filters, bool visible) {
final _deactivatedHiddenFilters = deactivatedHiddenFilters;
final _hiddenFilters = hiddenFilters; final _hiddenFilters = hiddenFilters;
_deactivatedHiddenFilters.removeAll(filters);
if (visible) { if (visible) {
_hiddenFilters.removeAll(filters); _hiddenFilters.removeAll(filters);
} else { } else {
_hiddenFilters.addAll(filters); _hiddenFilters.addAll(filters);
searchHistory = searchHistory..removeWhere(filters.contains); searchHistory = searchHistory..removeWhere(filters.contains);
final _deactivatedHiddenFilters = deactivatedHiddenFilters;
_deactivatedHiddenFilters.removeAll(filters);
deactivatedHiddenFilters = _deactivatedHiddenFilters;
} }
deactivatedHiddenFilters = _deactivatedHiddenFilters;
hiddenFilters = _hiddenFilters; hiddenFilters = _hiddenFilters;
} }
@ -29,14 +30,18 @@ mixin PrivacySettings on SettingsAccess, SearchSettings {
void activateHiddenFilter(CollectionFilter filter, bool active) { void activateHiddenFilter(CollectionFilter filter, bool active) {
final _deactivatedHiddenFilters = deactivatedHiddenFilters; final _deactivatedHiddenFilters = deactivatedHiddenFilters;
final _hiddenFilters = hiddenFilters;
if (active) { if (active) {
_deactivatedHiddenFilters.remove(filter); _deactivatedHiddenFilters.remove(filter);
_hiddenFilters.add(filter);
searchHistory = searchHistory..remove(filter);
} else { } else {
_deactivatedHiddenFilters.add(filter); _deactivatedHiddenFilters.add(filter);
_hiddenFilters.remove(filter);
} }
deactivatedHiddenFilters = _deactivatedHiddenFilters;
final visible = !active; deactivatedHiddenFilters = _deactivatedHiddenFilters;
changeFilterVisibility({filter}, visible); hiddenFilters = _hiddenFilters;
} }
} }

View file

@ -161,6 +161,7 @@ class CollectionLens with ChangeNotifier {
case EntrySortFactor.rating: case EntrySortFactor.rating:
return !filters.any((f) => f is RatingFilter); return !filters.any((f) => f is RatingFilter);
case EntrySortFactor.size: case EntrySortFactor.size:
case EntrySortFactor.duration:
return false; return false;
} }
} }
@ -261,6 +262,8 @@ class CollectionLens with ChangeNotifier {
_filteredSortedEntries.sort(AvesEntrySort.compareByRating); _filteredSortedEntries.sort(AvesEntrySort.compareByRating);
case EntrySortFactor.size: case EntrySortFactor.size:
_filteredSortedEntries.sort(AvesEntrySort.compareBySize); _filteredSortedEntries.sort(AvesEntrySort.compareBySize);
case EntrySortFactor.duration:
_filteredSortedEntries.sort(AvesEntrySort.compareByDuration);
} }
if (sortReverse) { if (sortReverse) {
_filteredSortedEntries = _filteredSortedEntries.reversed.toList(); _filteredSortedEntries = _filteredSortedEntries.reversed.toList();
@ -294,6 +297,7 @@ class CollectionLens with ChangeNotifier {
case EntrySortFactor.rating: case EntrySortFactor.rating:
sections = groupBy<AvesEntry, EntryRatingSectionKey>(_filteredSortedEntries, (entry) => EntryRatingSectionKey(entry.rating)); sections = groupBy<AvesEntry, EntryRatingSectionKey>(_filteredSortedEntries, (entry) => EntryRatingSectionKey(entry.rating));
case EntrySortFactor.size: case EntrySortFactor.size:
case EntrySortFactor.duration:
sections = Map.fromEntries([ sections = Map.fromEntries([
MapEntry(const SectionKey(), _filteredSortedEntries), MapEntry(const SectionKey(), _filteredSortedEntries),
]); ]);

View file

@ -1,6 +1,6 @@
import 'dart:async'; import 'dart:async';
import 'package:aves/model/apps.dart'; import 'package:aves/model/app_inventory.dart';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';

View file

@ -118,11 +118,6 @@ class PlatformMediaEditService implements MediaEditService {
required String destinationAlbum, required String destinationAlbum,
required NameConflictStrategy nameConflictStrategy, required NameConflictStrategy nameConflictStrategy,
}) { }) {
// TODO TLAD remove log when OOMs are inspected
entries.where((v) => (v.sizeBytes ?? 0) > 20000000).forEach((entry) {
reportService.log('convert large entry=$entry size=${entry.sizeBytes}');
});
try { try {
return _opStream return _opStream
.receiveBroadcastStream(<String, dynamic>{ .receiveBroadcastStream(<String, dynamic>{

View file

@ -80,11 +80,6 @@ class PlatformMetadataEditService implements MetadataEditService {
Map<MetadataType, dynamic> metadata, { Map<MetadataType, dynamic> metadata, {
bool autoCorrectTrailerOffset = true, bool autoCorrectTrailerOffset = true,
}) async { }) async {
// TODO TLAD remove log when OOMs are inspected
if ((entry.sizeBytes ?? 0) > 20000000) {
await reportService.log('edit metadata of large entry=$entry size=${entry.sizeBytes}');
}
try { try {
final result = await _platform.invokeMethod('editMetadata', <String, dynamic>{ final result = await _platform.invokeMethod('editMetadata', <String, dynamic>{
'entry': entry.toPlatformEntryMap(), 'entry': entry.toPlatformEntryMap(),

View file

@ -2,12 +2,11 @@ import 'package:aves/convert/convert.dart';
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/model/entry/extensions/multipage.dart';
import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/entry/extensions/props.dart';
import 'package:aves/model/geotiff.dart'; import 'package:aves/model/media/geotiff.dart';
import 'package:aves/model/metadata/catalog.dart'; import 'package:aves/model/metadata/catalog.dart';
import 'package:aves/model/metadata/overlay.dart'; import 'package:aves/model/metadata/overlay.dart';
import 'package:aves/model/multipage.dart'; import 'package:aves/model/multipage.dart';
import 'package:aves/model/panorama.dart'; import 'package:aves/model/media/panorama.dart';
import 'package:aves/ref/mime_types.dart';
import 'package:aves/services/common/service_policy.dart'; import 'package:aves/services/common/service_policy.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves/services/metadata/xmp.dart'; import 'package:aves/services/metadata/xmp.dart';
@ -69,11 +68,6 @@ class PlatformMetadataFetchService implements MetadataFetchService {
Future<CatalogMetadata?> getCatalogMetadata(AvesEntry entry, {bool background = false}) async { Future<CatalogMetadata?> getCatalogMetadata(AvesEntry entry, {bool background = false}) async {
if (entry.isSvg) return null; if (entry.isSvg) return null;
// TODO TLAD remove log when MP4/TIFF-related OOMs are fixed
if ({MimeTypes.mp4, MimeTypes.tiff}.contains(entry.mimeType) && (entry.sizeBytes ?? 0) > 20000000) {
await reportService.log('catalog large entry=$entry size=${entry.sizeBytes}');
}
Future<CatalogMetadata?> call() async { Future<CatalogMetadata?> call() async {
try { try {
// returns map with: // returns map with:

View file

@ -29,11 +29,10 @@ class ADurations {
// collection animations // collection animations
static const filterBarRemovalAnimation = Duration(milliseconds: 400); static const filterBarRemovalAnimation = Duration(milliseconds: 400);
static const collectionOpOverlayAnimation = Duration(milliseconds: 300); static const collectionOpOverlayAnimation = Duration(milliseconds: 300);
static const sectionHeaderAnimation = Duration(milliseconds: 200);
static const thumbnailOverlayAnimation = Duration(milliseconds: 200);
// search animations // search animations
static const filterRowExpandAnimation = Duration(milliseconds: 300); static const filterRowExpandAnimation = Duration(milliseconds: 300);
static const searchBodyTransition = Duration(milliseconds: 300);
// viewer animations // viewer animations
static const thumbnailScrollerScrollAnimation = Duration(milliseconds: 200); static const thumbnailScrollerScrollAnimation = Duration(milliseconds: 200);

View file

@ -28,6 +28,7 @@ class AIcons {
static const descriptionUntitled = Icons.comments_disabled_outlined; static const descriptionUntitled = Icons.comments_disabled_outlined;
static const disc = Icons.fiber_manual_record; static const disc = Icons.fiber_manual_record;
static const display = Icons.light_mode_outlined; static const display = Icons.light_mode_outlined;
static const duration = Icons.timelapse_outlined;
static const error = Icons.error_outline; static const error = Icons.error_outline;
static const explorer = Icons.account_tree_outlined; static const explorer = Icons.account_tree_outlined;
static const folder = Icons.folder_outlined; static const folder = Icons.folder_outlined;
@ -162,6 +163,8 @@ class AIcons {
static const zoomOut = Icons.remove_outlined; static const zoomOut = Icons.remove_outlined;
static const collapse = Icons.expand_less_outlined; static const collapse = Icons.expand_less_outlined;
static const expand = Icons.expand_more_outlined; static const expand = Icons.expand_more_outlined;
static const up = Icons.keyboard_arrow_up_outlined;
static const down = Icons.keyboard_arrow_down_outlined;
static const previous = Icons.chevron_left_outlined; static const previous = Icons.chevron_left_outlined;
static const next = Icons.chevron_right_outlined; static const next = Icons.chevron_right_outlined;

View file

@ -1,4 +1,4 @@
import 'package:aves/model/apps.dart'; import 'package:aves/model/app_inventory.dart';
import 'package:aves/model/vaults/vaults.dart'; import 'package:aves/model/vaults/vaults.dart';
import 'package:aves/services/common/services.dart'; import 'package:aves/services/common/services.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
@ -7,6 +7,8 @@ import 'package:flutter/foundation.dart';
final AndroidFileUtils androidFileUtils = AndroidFileUtils._private(); final AndroidFileUtils androidFileUtils = AndroidFileUtils._private();
enum _State { uninitialized, initializing, initialized }
class AndroidFileUtils { class AndroidFileUtils {
// cf https://developer.android.com/reference/android/content/ContentResolver#SCHEME_CONTENT // cf https://developer.android.com/reference/android/content/ContentResolver#SCHEME_CONTENT
static const contentScheme = 'content'; static const contentScheme = 'content';
@ -27,13 +29,19 @@ class AndroidFileUtils {
late final String dcimPath, downloadPath, moviesPath, picturesPath, avesVideoCapturesPath; late final String dcimPath, downloadPath, moviesPath, picturesPath, avesVideoCapturesPath;
late final Set<String> videoCapturesPaths; late final Set<String> videoCapturesPaths;
Set<StorageVolume> storageVolumes = {}; Set<StorageVolume> storageVolumes = {};
bool _initialized = false; _State _initialized = _State.uninitialized;
AndroidFileUtils._private(); AndroidFileUtils._private();
Future<void> init() async { Future<void> init() async {
if (_initialized) return; if (_initialized == _State.uninitialized) {
_initialized = _State.initializing;
await _doInit();
_initialized = _State.initialized;
}
}
Future<void> _doInit() async {
separator = pContext.separator; separator = pContext.separator;
await _initStorageVolumes(); await _initStorageVolumes();
vaultRoot = await storageService.getVaultRoot(); vaultRoot = await storageService.getVaultRoot();
@ -50,8 +58,6 @@ class AndroidFileUtils {
// from Aves // from Aves
avesVideoCapturesPath, avesVideoCapturesPath,
}; };
_initialized = true;
} }
Future<void> _initStorageVolumes() async { Future<void> _initStorageVolumes() async {

View file

@ -9,6 +9,7 @@ extension ExtraExplorerActionView on ExplorerAction {
return switch (this) { return switch (this) {
ExplorerAction.addShortcut => l10n.collectionActionAddShortcut, ExplorerAction.addShortcut => l10n.collectionActionAddShortcut,
ExplorerAction.setHome => l10n.collectionActionSetHome, ExplorerAction.setHome => l10n.collectionActionSetHome,
ExplorerAction.stats => l10n.menuActionStats,
}; };
} }
@ -18,6 +19,7 @@ extension ExtraExplorerActionView on ExplorerAction {
return switch (this) { return switch (this) {
ExplorerAction.addShortcut => AIcons.addShortcut, ExplorerAction.addShortcut => AIcons.addShortcut,
ExplorerAction.setHome => AIcons.home, ExplorerAction.setHome => AIcons.home,
ExplorerAction.stats => AIcons.stats,
}; };
} }
} }

View file

@ -8,6 +8,7 @@ extension ExtraMapActionView on MapAction {
final l10n = context.l10n; final l10n = context.l10n;
return switch (this) { return switch (this) {
MapAction.selectStyle => l10n.mapStyleTooltip, MapAction.selectStyle => l10n.mapStyleTooltip,
MapAction.openMapApp => l10n.entryActionOpenMap,
MapAction.zoomIn => l10n.mapZoomInTooltip, MapAction.zoomIn => l10n.mapZoomInTooltip,
MapAction.zoomOut => l10n.mapZoomOutTooltip, MapAction.zoomOut => l10n.mapZoomOutTooltip,
}; };
@ -18,6 +19,7 @@ extension ExtraMapActionView on MapAction {
IconData _getIconData() { IconData _getIconData() {
return switch (this) { return switch (this) {
MapAction.selectStyle => AIcons.layers, MapAction.selectStyle => AIcons.layers,
MapAction.openMapApp => AIcons.openOutside,
MapAction.zoomIn => AIcons.zoomIn, MapAction.zoomIn => AIcons.zoomIn,
MapAction.zoomOut => AIcons.zoomOut, MapAction.zoomOut => AIcons.zoomOut,
}; };

View file

@ -11,6 +11,7 @@ extension ExtraEntrySortFactorView on EntrySortFactor {
EntrySortFactor.name => l10n.sortByAlbumFileName, EntrySortFactor.name => l10n.sortByAlbumFileName,
EntrySortFactor.rating => l10n.sortByRating, EntrySortFactor.rating => l10n.sortByRating,
EntrySortFactor.size => l10n.sortBySize, EntrySortFactor.size => l10n.sortBySize,
EntrySortFactor.duration => l10n.sortByDuration,
}; };
} }
@ -20,6 +21,7 @@ extension ExtraEntrySortFactorView on EntrySortFactor {
EntrySortFactor.name => AIcons.name, EntrySortFactor.name => AIcons.name,
EntrySortFactor.rating => AIcons.rating, EntrySortFactor.rating => AIcons.rating,
EntrySortFactor.size => AIcons.size, EntrySortFactor.size => AIcons.size,
EntrySortFactor.duration => AIcons.duration,
}; };
} }
@ -30,6 +32,7 @@ extension ExtraEntrySortFactorView on EntrySortFactor {
EntrySortFactor.name => reverse ? l10n.sortOrderZtoA : l10n.sortOrderAtoZ, EntrySortFactor.name => reverse ? l10n.sortOrderZtoA : l10n.sortOrderAtoZ,
EntrySortFactor.rating => reverse ? l10n.sortOrderLowestFirst : l10n.sortOrderHighestFirst, EntrySortFactor.rating => reverse ? l10n.sortOrderLowestFirst : l10n.sortOrderHighestFirst,
EntrySortFactor.size => reverse ? l10n.sortOrderSmallestFirst : l10n.sortOrderLargestFirst, EntrySortFactor.size => reverse ? l10n.sortOrderSmallestFirst : l10n.sortOrderLargestFirst,
EntrySortFactor.duration => reverse ? l10n.sortOrderShortestFirst : l10n.sortOrderLongestFirst,
}; };
} }
} }

View file

@ -2,6 +2,7 @@ export 'src/actions/chip.dart';
export 'src/actions/chip_set.dart'; export 'src/actions/chip_set.dart';
export 'src/actions/entry.dart'; export 'src/actions/entry.dart';
export 'src/actions/entry_set.dart'; export 'src/actions/entry_set.dart';
export 'src/actions/explorer.dart';
export 'src/actions/map.dart'; export 'src/actions/map.dart';
export 'src/actions/map_cluster.dart'; export 'src/actions/map_cluster.dart';
export 'src/actions/share.dart'; export 'src/actions/share.dart';

View file

@ -18,11 +18,13 @@ import 'package:flutter/services.dart';
const _widgetDrawChannel = MethodChannel('deckers.thibault/aves/widget_draw'); const _widgetDrawChannel = MethodChannel('deckers.thibault/aves/widget_draw');
void widgetMainCommon(AppFlavor flavor) async { void widgetMainCommon(AppFlavor flavor) async {
debugPrint('Widget main start');
WidgetsFlutterBinding.ensureInitialized(); WidgetsFlutterBinding.ensureInitialized();
initPlatformServices(); initPlatformServices();
await settings.init(monitorPlatformSettings: false); await settings.init(monitorPlatformSettings: false);
await reportService.init(); await reportService.init();
debugPrint('Widget channel method handling setup');
_widgetDrawChannel.setMethodCallHandler((call) async { _widgetDrawChannel.setMethodCallHandler((call) async {
// widget settings may be modified in a different process after channel setup // widget settings may be modified in a different process after channel setup
await settings.reload(); await settings.reload();

View file

@ -4,7 +4,7 @@ import 'dart:math';
import 'package:aves/app_flavor.dart'; import 'package:aves/app_flavor.dart';
import 'package:aves/app_mode.dart'; import 'package:aves/app_mode.dart';
import 'package:aves/l10n/l10n.dart'; import 'package:aves/l10n/l10n.dart';
import 'package:aves/model/apps.dart'; import 'package:aves/model/app_inventory.dart';
import 'package:aves/model/device.dart'; import 'package:aves/model/device.dart';
import 'package:aves/model/filters/recent.dart'; import 'package:aves/model/filters/recent.dart';
import 'package:aves/model/settings/defaults.dart'; import 'package:aves/model/settings/defaults.dart';
@ -345,12 +345,14 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
child: ValueListenableBuilder<PageTransitionsBuilder>( child: ValueListenableBuilder<PageTransitionsBuilder>(
valueListenable: _pageTransitionsBuilderNotifier, valueListenable: _pageTransitionsBuilderNotifier,
builder: (context, pageTransitionsBuilder, child) { builder: (context, pageTransitionsBuilder, child) {
final theme = Theme.of(context);
return Theme( return Theme(
data: Theme.of(context).copyWith( data: theme.copyWith(
pageTransitionsTheme: areAnimationsEnabled pageTransitionsTheme: areAnimationsEnabled
? PageTransitionsTheme(builders: {TargetPlatform.android: pageTransitionsBuilder}) ? PageTransitionsTheme(builders: {TargetPlatform.android: pageTransitionsBuilder})
// strip page transitions used by `MaterialPageRoute` // strip page transitions used by `MaterialPageRoute`
: const DirectPageTransitionsTheme(), : const DirectPageTransitionsTheme(),
splashFactory: areAnimationsEnabled ? theme.splashFactory : NoSplash.splashFactory,
), ),
child: MediaQueryDataProvider(child: child!), child: MediaQueryDataProvider(child: child!),
); );
@ -411,6 +413,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
void didHaveMemoryPressure() { void didHaveMemoryPressure() {
super.didHaveMemoryPressure(); super.didHaveMemoryPressure();
reportService.log('App memory pressure'); reportService.log('App memory pressure');
imageCache.clear();
} }
@override @override
@ -631,7 +634,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
final shouldReset = _exitedMainByPop; final shouldReset = _exitedMainByPop;
_exitedMainByPop = false; _exitedMainByPop = false;
if (!shouldReset && (intentData ?? {}).isEmpty) { if (!shouldReset && (intentData ?? {}).values.whereNotNull().isEmpty) {
reportService.log('Relaunch'); reportService.log('Relaunch');
return; return;
} }
@ -665,6 +668,9 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
} }
} }
// Flutter has various overscroll indicator implementations for Android:
// - `StretchingOverscrollIndicator`, default when using Material 3
// - `GlowingOverscrollIndicator`, default when not using Material 3
class AvesScrollBehavior extends MaterialScrollBehavior { class AvesScrollBehavior extends MaterialScrollBehavior {
@override @override
Widget buildOverscrollIndicator(BuildContext context, Widget child, ScrollableDetails details) { Widget buildOverscrollIndicator(BuildContext context, Widget child, ScrollableDetails details) {
@ -674,11 +680,7 @@ class AvesScrollBehavior extends MaterialScrollBehavior {
axisDirection: details.direction, axisDirection: details.direction,
child: child, child: child,
) )
: GlowingOverscrollIndicator( : child;
axisDirection: details.direction,
color: Colors.white,
child: child,
);
} }
} }

View file

@ -79,6 +79,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
EntrySortFactor.size, EntrySortFactor.size,
EntrySortFactor.name, EntrySortFactor.name,
EntrySortFactor.rating, EntrySortFactor.rating,
EntrySortFactor.duration,
]; ];
static const _groupOptions = [ static const _groupOptions = [
@ -94,6 +95,11 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
TileLayout.list, TileLayout.list,
]; ];
static const _trashSelectionQuickActions = [
EntrySetAction.delete,
EntrySetAction.restore,
];
@override @override
void initState() { void initState() {
super.initState(); super.initState();
@ -388,7 +394,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
final hasSelection = selectedItemCount > 0; final hasSelection = selectedItemCount > 0;
final browsingQuickActions = settings.collectionBrowsingQuickActions; final browsingQuickActions = settings.collectionBrowsingQuickActions;
final selectionQuickActions = isTrash ? [EntrySetAction.delete, EntrySetAction.restore] : settings.collectionSelectionQuickActions; final selectionQuickActions = isTrash ? _trashSelectionQuickActions : settings.collectionSelectionQuickActions;
final quickActions = (isSelecting ? selectionQuickActions : browsingQuickActions).take(max(0, availableCount - 1)).toList(); final quickActions = (isSelecting ? selectionQuickActions : browsingQuickActions).take(max(0, availableCount - 1)).toList();
final quickActionButtons = quickActions.where(isVisible).map( final quickActionButtons = quickActions.where(isVisible).map(
(action) => _buildButtonIcon(context, action, enabled: canApply(action), selection: selection), (action) => _buildButtonIcon(context, action, enabled: canApply(action), selection: selection),
@ -430,7 +436,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
title: context.l10n.collectionActionEdit, title: context.l10n.collectionActionEdit,
items: [ items: [
_buildRotateAndFlipMenuItems(context, canApply: canApply), _buildRotateAndFlipMenuItems(context, canApply: canApply),
...EntrySetActions.edit.where((v) => isVisible(v) && !selectionQuickActions.contains(v)).map((action) => _toMenuItem(action, enabled: canApply(action), selection: selection)), ...EntrySetActions.edit.where((v) => isVisible(v) && !quickActions.contains(v)).map((action) => _toMenuItem(action, enabled: canApply(action), selection: selection)),
], ],
), ),
]; ];

View file

@ -697,6 +697,7 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> with Widge
addAlbums(collection, sectionLayouts, crumbs); addAlbums(collection, sectionLayouts, crumbs);
case EntrySortFactor.rating: case EntrySortFactor.rating:
case EntrySortFactor.size: case EntrySortFactor.size:
case EntrySortFactor.duration:
break; break;
} }
return crumbs; return crumbs;

View file

@ -57,6 +57,10 @@ class CollectionDraggableThumbLabel extends StatelessWidget {
return [ return [
if (entry.sizeBytes != null) formatFileSize(context.locale, entry.sizeBytes!, round: 0), if (entry.sizeBytes != null) formatFileSize(context.locale, entry.sizeBytes!, round: 0),
]; ];
case EntrySortFactor.duration:
return [
if (entry.durationMillis != null) entry.durationText,
];
} }
}, },
); );

View file

@ -1,8 +1,10 @@
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/widgets/common/identity/aves_app_bar.dart'; import 'package:aves/widgets/common/identity/aves_app_bar.dart';
import 'package:aves/widgets/common/identity/aves_filter_chip.dart'; import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
class FilterBar extends StatefulWidget { class FilterBar extends StatefulWidget {
static const EdgeInsets chipPadding = EdgeInsets.symmetric(horizontal: 4); static const EdgeInsets chipPadding = EdgeInsets.symmetric(horizontal: 4);
@ -12,7 +14,7 @@ class FilterBar extends StatefulWidget {
final List<CollectionFilter> filters; final List<CollectionFilter> filters;
final bool interactive; final bool interactive;
final FilterCallback? onTap, onRemove; final AFilterCallback? onTap, onRemove;
FilterBar({ FilterBar({
super.key, super.key,
@ -45,7 +47,7 @@ class _FilterBarState extends State<FilterBar> {
existing.removeAt(index); existing.removeAt(index);
// only animate item removal when triggered by a user interaction with the chip, // only animate item removal when triggered by a user interaction with the chip,
// not from automatic chip replacement following chip selection // not from automatic chip replacement following chip selection
final animate = _userTappedFilter == filter; final animate = context.read<Settings>().animate && _userTappedFilter == filter;
listState!.removeItem( listState!.removeItem(
index, index,
animate animate
@ -123,7 +125,7 @@ class _FilterBarState extends State<FilterBar> {
class _Chip extends StatelessWidget { class _Chip extends StatelessWidget {
final CollectionFilter filter; final CollectionFilter filter;
final bool single, interactive; final bool single, interactive;
final FilterCallback? onTap, onRemove; final AFilterCallback? onTap, onRemove;
const _Chip({ const _Chip({
required this.filter, required this.filter,
@ -142,7 +144,7 @@ class _Chip extends StatelessWidget {
key: ValueKey(filter), key: ValueKey(filter),
filter: filter, filter: filter,
maxWidth: single maxWidth: single
? AvesFilterChip.computeMaxWidth( ? AvesFilterChip.computeMaxWidthForRow(
context, context,
minChipPerRow: 1, minChipPerRow: 1,
chipPadding: FilterBar.chipPadding.horizontal, chipPadding: FilterBar.chipPadding.horizontal,

View file

@ -66,6 +66,7 @@ class CollectionSectionHeader extends StatelessWidget {
selectable: selectable, selectable: selectable,
); );
case EntrySortFactor.size: case EntrySortFactor.size:
case EntrySortFactor.duration:
break; break;
} }
return null; return null;

View file

@ -1,14 +1,17 @@
import 'dart:async'; import 'dart:async';
import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/album.dart';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart';
import 'package:aves/widgets/common/identity/aves_filter_chip.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/filter_quick_chooser_mixin.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
class AlbumQuickChooser extends StatelessWidget { class AlbumQuickChooser extends StatelessWidget with FilterQuickChooserMixin<String> {
final ValueNotifier<String?> valueNotifier; final ValueNotifier<String?> valueNotifier;
@override
final List<String> options; final List<String> options;
final bool blurred; final bool blurred;
final PopupMenuPosition chooserPosition; final PopupMenuPosition chooserPosition;
@ -25,7 +28,6 @@ class AlbumQuickChooser extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final source = context.read<CollectionSource>();
return MenuQuickChooser<String>( return MenuQuickChooser<String>(
valueNotifier: valueNotifier, valueNotifier: valueNotifier,
options: options, options: options,
@ -33,10 +35,17 @@ class AlbumQuickChooser extends StatelessWidget {
blurred: blurred, blurred: blurred,
chooserPosition: chooserPosition, chooserPosition: chooserPosition,
pointerGlobalPosition: pointerGlobalPosition, pointerGlobalPosition: pointerGlobalPosition,
itemBuilder: (context, album) => AvesFilterChip( maxTotalOptionCount: FilterQuickChooserMixin.maxTotalOptionCount,
filter: AlbumFilter(album, source.getAlbumDisplayName(context, album)), itemHeight: computeItemHeight(context),
allowGenericIcon: false, contentWidth: computeLargestItemWidth,
), itemBuilder: itemBuilder,
emptyBuilder: (context) => Text(context.l10n.albumEmpty),
); );
} }
@override
CollectionFilter buildFilter(BuildContext context, String option) {
final source = context.read<CollectionSource>();
return AlbumFilter(option, source.getAlbumDisplayName(context, option));
}
} }

View file

@ -1,9 +1,10 @@
import 'dart:async'; import 'dart:async';
import 'dart:math';
import 'package:aves/theme/durations.dart'; import 'package:aves/theme/durations.dart';
import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/common/quick_chooser.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/quick_chooser.dart';
import 'package:aves_ui/aves_ui.dart'; import 'package:aves_ui/aves_ui.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart'; import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
@ -16,9 +17,13 @@ class MenuQuickChooser<T> extends StatefulWidget {
final bool blurred; final bool blurred;
final PopupMenuPosition chooserPosition; final PopupMenuPosition chooserPosition;
final Stream<Offset> pointerGlobalPosition; final Stream<Offset> pointerGlobalPosition;
final int maxTotalOptionCount;
final double itemHeight;
final double? Function(BuildContext context)? contentWidth;
final Widget Function(BuildContext context, T menuItem) itemBuilder; final Widget Function(BuildContext context, T menuItem) itemBuilder;
final WidgetBuilder? emptyBuilder;
static const int maxOptionCount = 5; static const int maxVisibleOptionCount = 5;
MenuQuickChooser({ MenuQuickChooser({
super.key, super.key,
@ -28,8 +33,12 @@ class MenuQuickChooser<T> extends StatefulWidget {
required this.blurred, required this.blurred,
required this.chooserPosition, required this.chooserPosition,
required this.pointerGlobalPosition, required this.pointerGlobalPosition,
this.maxTotalOptionCount = maxVisibleOptionCount,
this.itemHeight = kMinInteractiveDimension,
this.contentWidth,
required this.itemBuilder, required this.itemBuilder,
}) : options = options.take(maxOptionCount).toList(); this.emptyBuilder,
}) : options = options.take(maxTotalOptionCount).toList();
@override @override
State<MenuQuickChooser<T>> createState() => _MenuQuickChooserState<T>(); State<MenuQuickChooser<T>> createState() => _MenuQuickChooserState<T>();
@ -38,6 +47,10 @@ class MenuQuickChooser<T> extends StatefulWidget {
class _MenuQuickChooserState<T> extends State<MenuQuickChooser<T>> { class _MenuQuickChooserState<T> extends State<MenuQuickChooser<T>> {
final List<StreamSubscription> _subscriptions = []; final List<StreamSubscription> _subscriptions = [];
final ValueNotifier<Rect> _selectedRowRect = ValueNotifier(Rect.zero); final ValueNotifier<Rect> _selectedRowRect = ValueNotifier(Rect.zero);
final ScrollController _scrollController = ScrollController();
int _scrollDirection = 0;
Timer? _scrollUpdateTimer;
Offset _globalPosition = Offset.zero;
ValueNotifier<T?> get valueNotifier => widget.valueNotifier; ValueNotifier<T?> get valueNotifier => widget.valueNotifier;
@ -45,12 +58,26 @@ class _MenuQuickChooserState<T> extends State<MenuQuickChooser<T>> {
bool get reversed => widget.autoReverse && widget.chooserPosition == PopupMenuPosition.over; bool get reversed => widget.autoReverse && widget.chooserPosition == PopupMenuPosition.over;
static const double intraPadding = 8; bool get scrollable => options.length > MenuQuickChooser.maxVisibleOptionCount;
int get visibleOptionCount => min(MenuQuickChooser.maxVisibleOptionCount, options.length);
double get itemHeight => widget.itemHeight;
double get contentHeight => max(0, itemHeight * visibleOptionCount + _intraPadding * (visibleOptionCount - 1));
static const double _selectorMargin = 24;
static const double _intraPadding = 8;
static const double _nonScrollablePaddingHeight = _intraPadding;
static const double _scrollerAreaHeight = kMinInteractiveDimension;
static const double scrollMaxPixelPerSecond = 600.0;
static const Duration scrollUpdateInterval = Duration(milliseconds: 100);
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_registerWidget(widget); _registerWidget(widget);
WidgetsBinding.instance.addPostFrameCallback((_) => setState(() {}));
} }
@override @override
@ -69,6 +96,9 @@ class _MenuQuickChooserState<T> extends State<MenuQuickChooser<T>> {
@override @override
void dispose() { void dispose() {
_unregisterWidget(widget); _unregisterWidget(widget);
_selectedRowRect.dispose();
_scrollController.dispose();
_scrollUpdateTimer?.cancel();
super.dispose(); super.dispose();
} }
@ -91,28 +121,11 @@ class _MenuQuickChooserState<T> extends State<MenuQuickChooser<T>> {
builder: (context, selectedValue, child) { builder: (context, selectedValue, child) {
final durations = context.watch<DurationsData>(); final durations = context.watch<DurationsData>();
List<Widget> optionChildren = options.mapIndexed((index, value) { if (options.isEmpty) {
final isFirst = index == (reversed ? options.length - 1 : 0);
return Padding( return Padding(
padding: EdgeInsets.only(top: isFirst ? intraPadding : 0, bottom: intraPadding), padding: const EdgeInsets.all(16),
child: widget.itemBuilder(context, value), child: widget.emptyBuilder?.call(context) ?? const SizedBox(),
); );
}).toList();
optionChildren = AnimationConfiguration.toStaggeredList(
duration: durations.staggeredAnimation * .5,
delay: durations.staggeredAnimationDelay * .5 * timeDilation,
childAnimationBuilder: (child) => SlideAnimation(
verticalOffset: 50.0 * (widget.chooserPosition == PopupMenuPosition.over ? 1 : -1),
child: FadeInAnimation(
child: child,
),
),
children: optionChildren,
);
if (reversed) {
optionChildren = optionChildren.reversed.toList();
} }
return Stack( return Stack(
@ -137,12 +150,67 @@ class _MenuQuickChooserState<T> extends State<MenuQuickChooser<T>> {
return child; return child;
}, },
), ),
Padding( Container(
padding: const EdgeInsetsDirectional.only(start: 24), width: widget.contentWidth?.call(context),
margin: const EdgeInsetsDirectional.only(start: _selectorMargin),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, children: [
children: optionChildren, scrollable
? ListenableBuilder(
listenable: _scrollController,
builder: (context, child) => Opacity(
opacity: canGoUp ? 1 : .5,
child: child,
),
child: _buildScrollerArea(AIcons.up),
)
: const SizedBox(height: _nonScrollablePaddingHeight),
ConstrainedBox(
constraints: BoxConstraints.tightFor(height: contentHeight),
child: ListView.separated(
reverse: reversed,
controller: _scrollController,
shrinkWrap: true,
padding: EdgeInsets.zero,
itemBuilder: (context, index) {
final child = Container(
alignment: AlignmentDirectional.centerStart,
constraints: BoxConstraints.tightFor(height: itemHeight),
child: widget.itemBuilder(context, options[index]),
);
if (index < MenuQuickChooser.maxVisibleOptionCount) {
// only animate items visible on first render
return AnimationConfiguration.staggeredList(
position: index,
duration: durations.staggeredAnimation * .5,
delay: durations.staggeredAnimationDelay * .5 * timeDilation,
child: SlideAnimation(
verticalOffset: 50.0 * (widget.chooserPosition == PopupMenuPosition.over ? 1 : -1),
child: FadeInAnimation(
child: child,
),
),
);
} else {
return child;
}
},
separatorBuilder: (context, index) => const SizedBox(height: _intraPadding),
itemCount: options.length,
),
),
scrollable
? ListenableBuilder(
listenable: _scrollController,
builder: (context, child) => Opacity(
opacity: canGoDown ? 1 : .5,
child: child,
),
child: _buildScrollerArea(AIcons.down),
)
: const SizedBox(height: _nonScrollablePaddingHeight),
],
), ),
), ),
], ],
@ -152,30 +220,97 @@ class _MenuQuickChooserState<T> extends State<MenuQuickChooser<T>> {
); );
} }
void _onPointerMove(Offset globalPosition) { bool get canGoUp {
final padding = QuickChooser.margin.vertical + QuickChooser.padding.vertical; if (!_scrollController.hasClients) return false;
final position = _scrollController.position;
return reversed ? position.pixels < position.maxScrollExtent : 0 < position.pixels;
}
bool get canGoDown {
if (!_scrollController.hasClients) return false;
final position = _scrollController.position;
return reversed ? 0 < position.pixels : position.pixels < position.maxScrollExtent;
}
Widget _buildScrollerArea(IconData icon) {
return Container(
alignment: Alignment.center,
height: _scrollerAreaHeight,
margin: const EdgeInsetsDirectional.only(end: _selectorMargin),
child: Icon(icon),
);
}
void _onPointerMove(Offset globalPosition) {
_globalPosition = globalPosition;
final chooserBox = context.findRenderObject() as RenderBox?;
if (chooserBox == null) return;
final chooserBox = context.findRenderObject() as RenderBox;
final chooserSize = chooserBox.size; final chooserSize = chooserBox.size;
final contentWidth = chooserSize.width; final contentWidth = chooserSize.width;
final contentHeight = chooserSize.height - padding; final chooserBoxEdgeHeight = (QuickChooser.margin.vertical + QuickChooser.padding.vertical) / 2;
final optionCount = options.length; final localPosition = chooserBox.globalToLocal(globalPosition);
final itemHeight = (contentHeight - (optionCount + 1) * intraPadding) / optionCount; final dx = localPosition.dx;
if (!(0 < dx && dx < contentWidth)) {
valueNotifier.value = null;
return;
}
final local = chooserBox.globalToLocal(globalPosition); double dy = localPosition.dy - chooserBoxEdgeHeight;
final dx = local.dx; int scrollDirection = 0;
final dy = local.dy - padding / 2; if (scrollable) {
dy -= _scrollerAreaHeight;
if (-_scrollerAreaHeight < dy && dy < 0) {
scrollDirection = reversed ? 1 : -1;
} else if (contentHeight < dy && dy < contentHeight + _scrollerAreaHeight) {
scrollDirection = reversed ? -1 : 1;
}
_scroll(scrollDirection);
} else {
dy -= _nonScrollablePaddingHeight;
}
T? selectedValue; T? selectedValue;
if (0 < dx && dx < contentWidth && 0 < dy && dy < contentHeight) { if (scrollDirection == 0 && 0 < dy && dy < contentHeight) {
final index = (optionCount * dy / contentHeight).floor(); final visibleOffset = reversed ? contentHeight - dy : dy;
if (0 <= index && index < optionCount) { final fullItemHeight = itemHeight + _intraPadding;
selectedValue = options[reversed ? optionCount - 1 - index : index]; final scrollOffset = _scrollController.offset;
final top = index * (itemHeight + intraPadding) + intraPadding; final index = (visibleOffset + _intraPadding + scrollOffset) ~/ (fullItemHeight);
if (0 <= index && index < options.length) {
selectedValue = options[index];
double fromEdge = fullItemHeight * index;
fromEdge += (scrollable ? _scrollerAreaHeight - scrollOffset : _nonScrollablePaddingHeight);
final top = reversed ? chooserSize.height - chooserBoxEdgeHeight - fromEdge - fullItemHeight : fromEdge;
_selectedRowRect.value = Rect.fromLTWH(0, top, contentWidth, itemHeight); _selectedRowRect.value = Rect.fromLTWH(0, top, contentWidth, itemHeight);
} }
} }
valueNotifier.value = selectedValue; valueNotifier.value = selectedValue;
} }
void _scroll(int scrollDirection) {
if (scrollDirection == _scrollDirection) return;
_scrollDirection = scrollDirection;
_scrollUpdateTimer?.cancel();
final current = _scrollController.offset;
if (scrollDirection == 0) {
_scrollController.jumpTo(current);
return;
}
final target = scrollDirection > 0 ? _scrollController.position.maxScrollExtent : .0;
if (target != current) {
final distance = target - current;
final millis = distance * 1000 / scrollMaxPixelPerSecond / scrollDirection;
_scrollController.animateTo(
target,
duration: Duration(milliseconds: millis.round()),
curve: Curves.linear,
);
// use a timer to update the selection, because `_onPointerMove`
// is not called when the pointer stays still while the view is scrolling
_scrollUpdateTimer = Timer.periodic(scrollUpdateInterval, (_) => _onPointerMove(_globalPosition));
}
}
} }

View file

@ -0,0 +1,56 @@
import 'dart:math';
import 'package:aves/model/filters/filters.dart';
import 'package:aves/model/settings/modules/app.dart';
import 'package:aves/widgets/common/identity/aves_filter_chip.dart';
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
mixin FilterQuickChooserMixin<T> {
List<T> get options;
static const int maxTotalOptionCount = AppSettings.recentFilterHistoryMax;
static const double _chipPadding = AvesFilterChip.defaultPadding;
static const bool _chipAllowGenericIcon = false;
CollectionFilter buildFilter(BuildContext context, T option);
Widget itemBuilder(BuildContext context, T option) {
return AvesFilterChip(
filter: buildFilter(context, option),
allowGenericIcon: _chipAllowGenericIcon,
padding: _chipPadding,
maxWidth: double.infinity,
);
}
double computeItemHeight(BuildContext context) => AvesFilterChip.minChipHeight;
double? computeLargestItemWidth(BuildContext context) {
if (options.isEmpty) return null;
final textStyle = DefaultTextStyle.of(context).style.copyWith(
fontSize: AvesFilterChip.fontSize,
);
final textDirection = Directionality.of(context);
final textScaler = MediaQuery.textScalerOf(context);
final iconSize = textScaler.scale(AvesFilterChip.iconSize);
return options.map((option) {
final filter = buildFilter(context, option);
final icon = filter.iconBuilder(context, iconSize, allowGenericIcon: _chipAllowGenericIcon);
final label = filter.getLabel(context);
final paragraph = RenderParagraph(
TextSpan(text: label, style: textStyle),
textDirection: textDirection,
textScaler: textScaler,
)..layout(const BoxConstraints(), parentUsesSize: true);
final labelWidth = paragraph.getMaxIntrinsicWidth(double.infinity);
double chipWidth = labelWidth + _chipPadding * 4;
if (icon != null) {
chipWidth += iconSize + _chipPadding;
}
return max(AvesFilterChip.minChipWidth, chipWidth);
}).reduce(max);
}
}

View file

@ -5,7 +5,7 @@ import 'package:aves/model/source/collection_source.dart';
import 'package:aves/view/view.dart'; import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/album_chooser.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/album_chooser.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/common/button.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/button.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/filter_quick_chooser_mixin.dart';
import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
import 'package:aves/widgets/filter_grids/common/filter_nav_page.dart'; import 'package:aves/widgets/filter_grids/common/filter_nav_page.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
@ -42,7 +42,7 @@ class _MoveButtonState extends ChooserQuickButtonState<MoveButton, String> {
final source = context.read<CollectionSource>(); final source = context.read<CollectionSource>();
final rawAlbums = source.rawAlbums; final rawAlbums = source.rawAlbums;
final options = settings.recentDestinationAlbums.where(rawAlbums.contains).toList(); final options = settings.recentDestinationAlbums.where(rawAlbums.contains).toList();
final takeCount = MenuQuickChooser.maxOptionCount - options.length; final takeCount = FilterQuickChooserMixin.maxTotalOptionCount - options.length;
if (takeCount > 0) { if (takeCount > 0) {
final filters = rawAlbums.whereNot(options.contains).map((album) => AlbumFilter(album, null)).toSet(); final filters = rawAlbums.whereNot(options.contains).map((album) => AlbumFilter(album, null)).toSet();
final allMapEntries = filters.map((filter) => FilterGridItem(filter, source.recentEntry(filter))).toList(); final allMapEntries = filters.map((filter) => FilterGridItem(filter, source.recentEntry(filter))).toList();

View file

@ -51,7 +51,6 @@ class _ShareButtonState extends ChooserQuickButtonState<ShareButton, ShareAction
child: ShareQuickChooser( child: ShareQuickChooser(
valueNotifier: chooserValueNotifier, valueNotifier: chooserValueNotifier,
options: options, options: options,
autoReverse: false,
blurred: widget.blurred, blurred: widget.blurred,
chooserPosition: chooserPosition, chooserPosition: chooserPosition,
pointerGlobalPosition: pointerGlobalPosition, pointerGlobalPosition: pointerGlobalPosition,

View file

@ -1,24 +1,26 @@
import 'dart:async'; import 'dart:async';
import 'dart:math';
import 'package:aves/view/view.dart'; import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart';
import 'package:aves/widgets/common/basic/popup/menu_row.dart'; import 'package:aves/widgets/common/basic/popup/menu_row.dart';
import 'package:aves_model/aves_model.dart'; import 'package:aves_model/aves_model.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class ShareQuickChooser extends StatelessWidget { class ShareQuickChooser extends StatelessWidget {
final ValueNotifier<ShareAction?> valueNotifier; final ValueNotifier<ShareAction?> valueNotifier;
final List<ShareAction> options; final List<ShareAction> options;
final bool autoReverse;
final bool blurred; final bool blurred;
final PopupMenuPosition chooserPosition; final PopupMenuPosition chooserPosition;
final Stream<Offset> pointerGlobalPosition; final Stream<Offset> pointerGlobalPosition;
static const _itemPadding = EdgeInsetsDirectional.only(end: 8);
const ShareQuickChooser({ const ShareQuickChooser({
super.key, super.key,
required this.valueNotifier, required this.valueNotifier,
required this.options, required this.options,
required this.autoReverse,
required this.blurred, required this.blurred,
required this.chooserPosition, required this.chooserPosition,
required this.pointerGlobalPosition, required this.pointerGlobalPosition,
@ -29,20 +31,39 @@ class ShareQuickChooser extends StatelessWidget {
return MenuQuickChooser<ShareAction>( return MenuQuickChooser<ShareAction>(
valueNotifier: valueNotifier, valueNotifier: valueNotifier,
options: options, options: options,
autoReverse: autoReverse, autoReverse: false,
blurred: blurred, blurred: blurred,
chooserPosition: chooserPosition, chooserPosition: chooserPosition,
pointerGlobalPosition: pointerGlobalPosition, pointerGlobalPosition: pointerGlobalPosition,
itemBuilder: (context, action) => ConstrainedBox( itemHeight: kMinInteractiveDimension,
constraints: const BoxConstraints(minHeight: kMinInteractiveDimension), contentWidth: _computeLargestItemWidth,
child: Padding( itemBuilder: (context, action) => Padding(
padding: const EdgeInsetsDirectional.only(end: 8), padding: _itemPadding,
child: MenuRow( child: MenuRow(
text: action.getText(context), text: action.getText(context),
icon: action.getIcon(), icon: action.getIcon(),
),
), ),
), ),
); );
} }
double? _computeLargestItemWidth(BuildContext context) {
if (options.isEmpty) return null;
final textStyle = DefaultTextStyle.of(context).style;
final textDirection = Directionality.of(context);
final textScaler = MediaQuery.textScalerOf(context);
final iconSize = IconTheme.of(context).size ?? 24;
return options.map((action) {
final text = action.getText(context);
final paragraph = RenderParagraph(
TextSpan(text: text, style: textStyle),
textDirection: textDirection,
textScaler: textScaler,
)..layout(const BoxConstraints(), parentUsesSize: true);
final labelWidth = paragraph.getMaxIntrinsicWidth(double.infinity);
return iconSize + MenuRow.leadingPadding.horizontal + labelWidth + _itemPadding.horizontal;
}).reduce(max);
}
} }

View file

@ -4,7 +4,7 @@ import 'package:aves/model/settings/settings.dart';
import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/collection_source.dart';
import 'package:aves/view/view.dart'; import 'package:aves/view/view.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/common/button.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/button.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/filter_quick_chooser_mixin.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/tag_chooser.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/tag_chooser.dart';
import 'package:aves/widgets/common/providers/media_query_data_provider.dart'; import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
import 'package:aves/widgets/filter_grids/common/filter_nav_page.dart'; import 'package:aves/widgets/filter_grids/common/filter_nav_page.dart';
@ -38,7 +38,7 @@ class _TagButtonState extends ChooserQuickButtonState<TagButton, CollectionFilte
@override @override
Widget buildChooser(Animation<double> animation, PopupMenuPosition chooserPosition) { Widget buildChooser(Animation<double> animation, PopupMenuPosition chooserPosition) {
final options = settings.recentTags; final options = settings.recentTags;
final takeCount = MenuQuickChooser.maxOptionCount - options.length; final takeCount = FilterQuickChooserMixin.maxTotalOptionCount - options.length;
if (takeCount > 0) { if (takeCount > 0) {
final source = context.read<CollectionSource>(); final source = context.read<CollectionSource>();
final filters = source.sortedTags.map(TagFilter.new).whereNot(options.contains).toSet(); final filters = source.sortedTags.map(TagFilter.new).whereNot(options.contains).toSet();

View file

@ -2,11 +2,13 @@ import 'dart:async';
import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/filters.dart';
import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart';
import 'package:aves/widgets/common/identity/aves_filter_chip.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/filter_quick_chooser_mixin.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
class TagQuickChooser extends StatelessWidget { class TagQuickChooser extends StatelessWidget with FilterQuickChooserMixin<CollectionFilter> {
final ValueNotifier<CollectionFilter?> valueNotifier; final ValueNotifier<CollectionFilter?> valueNotifier;
@override
final List<CollectionFilter> options; final List<CollectionFilter> options;
final bool blurred; final bool blurred;
final PopupMenuPosition chooserPosition; final PopupMenuPosition chooserPosition;
@ -30,10 +32,14 @@ class TagQuickChooser extends StatelessWidget {
blurred: blurred, blurred: blurred,
chooserPosition: chooserPosition, chooserPosition: chooserPosition,
pointerGlobalPosition: pointerGlobalPosition, pointerGlobalPosition: pointerGlobalPosition,
itemBuilder: (context, filter) => AvesFilterChip( maxTotalOptionCount: FilterQuickChooserMixin.maxTotalOptionCount,
filter: filter, itemHeight: computeItemHeight(context),
allowGenericIcon: false, contentWidth: computeLargestItemWidth,
), itemBuilder: itemBuilder,
emptyBuilder: (context) => Text(context.l10n.tagEmpty),
); );
} }
@override
CollectionFilter buildFilter(BuildContext context, CollectionFilter option) => option;
} }

View file

@ -1,6 +1,7 @@
import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/entry.dart';
import 'package:aves/model/entry/extensions/favourites.dart'; import 'package:aves/model/entry/extensions/favourites.dart';
import 'package:aves/model/favourites.dart'; import 'package:aves/model/favourites.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/colors.dart'; import 'package:aves/theme/colors.dart';
import 'package:aves/theme/icons.dart'; import 'package:aves/theme/icons.dart';
import 'package:aves/widgets/common/basic/popup/menu_row.dart'; import 'package:aves/widgets/common/basic/popup/menu_row.dart';
@ -73,6 +74,7 @@ class _FavouriteTogglerState extends State<FavouriteToggler> {
icon: const Icon(isNotFavouriteIcon), icon: const Icon(isNotFavouriteIcon),
); );
} }
final animate = context.select<Settings, bool>((v) => v.animate);
return Stack( return Stack(
alignment: Alignment.center, alignment: Alignment.center,
children: [ children: [
@ -82,14 +84,15 @@ class _FavouriteTogglerState extends State<FavouriteToggler> {
focusNode: widget.focusNode, focusNode: widget.focusNode,
tooltip: isFavourite ? context.l10n.entryActionRemoveFavourite : context.l10n.entryActionAddFavourite, tooltip: isFavourite ? context.l10n.entryActionRemoveFavourite : context.l10n.entryActionAddFavourite,
), ),
Sweeper( if (animate)
key: ValueKey(entries.length == 1 ? entries.first : entries.length), Sweeper(
builder: (context) => Icon( key: ValueKey(entries.length == 1 ? entries.first : entries.length),
favouriteSweeperIcon, builder: (context) => Icon(
color: context.select<AvesColorsData, Color>((v) => v.favourite), favouriteSweeperIcon,
color: context.select<AvesColorsData, Color>((v) => v.favourite),
),
toggledNotifier: _isFavouriteNotifier,
), ),
toggledNotifier: _isFavouriteNotifier,
),
], ],
); );
}, },

View file

@ -95,9 +95,9 @@ class _PlayTogglerState extends State<PlayToggler> with SingleTickerProviderStat
void _onStatusChanged(VideoStatus status) { void _onStatusChanged(VideoStatus status) {
final status = _playPauseAnimation.status; final status = _playPauseAnimation.status;
if (isPlaying && status != AnimationStatus.forward && status != AnimationStatus.completed) { if (isPlaying && !status.isForwardOrCompleted) {
_playPauseAnimation.forward(); _playPauseAnimation.forward();
} else if (!isPlaying && status != AnimationStatus.reverse && status != AnimationStatus.dismissed) { } else if (!isPlaying && status.isForwardOrCompleted) {
_playPauseAnimation.reverse(); _playPauseAnimation.reverse();
} }
} }

View file

@ -24,6 +24,7 @@ typedef MarginComputer = EdgeInsets Function(BuildContext context);
mixin FeedbackMixin { mixin FeedbackMixin {
static final ValueNotifier<MarginComputer?> snackBarMarginOverrideNotifier = ValueNotifier(null); static final ValueNotifier<MarginComputer?> snackBarMarginOverrideNotifier = ValueNotifier(null);
static OverlaySupportEntry? _overlayNotificationEntry;
static EdgeInsets snackBarMarginDefault(BuildContext context) { static EdgeInsets snackBarMarginDefault(BuildContext context) {
return EdgeInsets.only( return EdgeInsets.only(
@ -31,7 +32,11 @@ mixin FeedbackMixin {
); );
} }
void dismissFeedback(BuildContext context) => ScaffoldMessenger.of(context).hideCurrentSnackBar(); void dismissFeedback(BuildContext context) {
ScaffoldMessenger.of(context).hideCurrentSnackBar();
_overlayNotificationEntry?.dismiss();
_overlayNotificationEntry = null;
}
void showFeedback(BuildContext context, FeedbackType type, String message, [SnackBarAction? action]) { void showFeedback(BuildContext context, FeedbackType type, String message, [SnackBarAction? action]) {
ScaffoldMessengerState? scaffoldMessenger; ScaffoldMessengerState? scaffoldMessenger;
@ -67,8 +72,7 @@ mixin FeedbackMixin {
// and space under the snack bar `margin` does not receive gestures // and space under the snack bar `margin` does not receive gestures
// (because it is used by the `Dismissible` wrapping the snack bar) // (because it is used by the `Dismissible` wrapping the snack bar)
// so we use `showOverlayNotification` instead // so we use `showOverlayNotification` instead
OverlaySupportEntry? notificationOverlayEntry; _overlayNotificationEntry = showOverlayNotification(
notificationOverlayEntry = showOverlayNotification(
(context) => SafeArea( (context) => SafeArea(
bottom: false, bottom: false,
child: ValueListenableBuilder<MarginComputer?>( child: ValueListenableBuilder<MarginComputer?>(
@ -89,7 +93,7 @@ mixin FeedbackMixin {
foregroundColor: WidgetStateProperty.all(snackBarTheme.actionTextColor), foregroundColor: WidgetStateProperty.all(snackBarTheme.actionTextColor),
), ),
onPressed: () { onPressed: () {
notificationOverlayEntry?.dismiss(); dismissFeedback(context);
action.onPressed(); action.onPressed();
}, },
child: Text(action.label), child: Text(action.label),
@ -97,7 +101,7 @@ mixin FeedbackMixin {
: null, : null,
animation: kAlwaysCompleteAnimation, animation: kAlwaysCompleteAnimation,
dismissDirection: DismissDirection.horizontal, dismissDirection: DismissDirection.horizontal,
onDismiss: () => notificationOverlayEntry?.dismiss(), onDismiss: () => dismissFeedback(context),
), ),
), ),
), ),

View file

@ -135,8 +135,8 @@ class _OverlaySnackBarState extends State<OverlaySnackBar> {
super.dispose(); super.dispose();
} }
void _onAnimationStatusChanged(AnimationStatus animationStatus) { void _onAnimationStatusChanged(AnimationStatus status) {
if (animationStatus == AnimationStatus.completed) { if (status.isCompleted) {
if (widget.onVisible != null && !_wasVisible) { if (widget.onVisible != null && !_wasVisible) {
widget.onVisible!(); widget.onVisible!();
} }

View file

@ -1,37 +0,0 @@
import 'package:flutter/material.dart';
class AvesPopupMenuButton<T> extends PopupMenuButton<T> {
final VoidCallback? onMenuOpened;
const AvesPopupMenuButton({
super.key,
required super.itemBuilder,
super.initialValue,
super.onSelected,
super.onCanceled,
super.tooltip,
super.elevation,
super.padding = const EdgeInsets.all(8),
super.child,
super.icon,
super.offset = Offset.zero,
super.enabled = true,
super.shape,
super.color,
super.enableFeedback,
super.iconSize,
this.onMenuOpened,
super.popUpAnimationStyle,
});
@override
PopupMenuButtonState<T> createState() => _AvesPopupMenuButtonState<T>();
}
class _AvesPopupMenuButtonState<T> extends PopupMenuButtonState<T> {
@override
void showButtonMenu() {
(widget as AvesPopupMenuButton).onMenuOpened?.call();
super.showButtonMenu();
}
}

View file

@ -10,6 +10,8 @@ class MenuRow extends StatelessWidget {
this.icon, this.icon,
}); });
static const leadingPadding = EdgeInsetsDirectional.only(end: 12);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Row( return Row(
@ -17,7 +19,7 @@ class MenuRow extends StatelessWidget {
children: [ children: [
if (icon != null) if (icon != null)
Padding( Padding(
padding: const EdgeInsetsDirectional.only(end: 12), padding: leadingPadding,
child: IconTheme.merge( child: IconTheme.merge(
data: IconThemeData( data: IconThemeData(
color: ListTileTheme.of(context).iconColor, color: ListTileTheme.of(context).iconColor,

View file

@ -17,7 +17,7 @@ class AvesPopScope extends StatelessWidget {
final blocker = handlers.firstWhereOrNull((v) => !v.canPop(context)); final blocker = handlers.firstWhereOrNull((v) => !v.canPop(context));
return PopScope( return PopScope(
canPop: blocker == null, canPop: blocker == null,
onPopInvoked: (didPop) { onPopInvokedWithResult: (didPop, result) {
if (!didPop) { if (!didPop) {
blocker?.onPopBlocked(context); blocker?.onPopBlocked(context);
} }

View file

@ -12,7 +12,7 @@ class TitledExpandableFilterRow extends StatelessWidget {
final ValueNotifier<String?> expandedNotifier; final ValueNotifier<String?> expandedNotifier;
final bool showGenericIcon; final bool showGenericIcon;
final HeroType Function(CollectionFilter filter)? heroTypeBuilder; final HeroType Function(CollectionFilter filter)? heroTypeBuilder;
final FilterCallback onTap; final AFilterCallback onTap;
final OffsetFilterCallback? onLongPress; final OffsetFilterCallback? onLongPress;
const TitledExpandableFilterRow({ const TitledExpandableFilterRow({
@ -96,8 +96,8 @@ class ExpandableFilterRow extends StatelessWidget {
final bool showGenericIcon; final bool showGenericIcon;
final Widget? Function(CollectionFilter)? leadingBuilder; final Widget? Function(CollectionFilter)? leadingBuilder;
final HeroType Function(CollectionFilter filter)? heroTypeBuilder; final HeroType Function(CollectionFilter filter)? heroTypeBuilder;
final FilterCallback onTap; final AFilterCallback onTap;
final FilterCallback? onRemove; final AFilterCallback? onRemove;
final OffsetFilterCallback? onLongPress; final OffsetFilterCallback? onLongPress;
static const double horizontalPadding = 8; static const double horizontalPadding = 8;

View file

@ -105,7 +105,7 @@ class _SweeperState extends State<Sweeper> with SingleTickerProviderStateMixin {
void _onAnimationStatusChanged(AnimationStatus status) { void _onAnimationStatusChanged(AnimationStatus status) {
setState(() {}); setState(() {});
if (status == AnimationStatus.completed) { if (status.isCompleted) {
widget.onSweepEnd?.call(); widget.onSweepEnd?.call();
} }
} }

View file

@ -189,6 +189,7 @@ class _SectionSelectableLeading<T> extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
if (!selectable) return _buildBrowsing(context); if (!selectable) return _buildBrowsing(context);
final duration = context.select<DurationsData, Duration>((v) => v.formTransition);
final isSelecting = context.select<Selection<T>, bool>((selection) => selection.isSelecting); final isSelecting = context.select<Selection<T>, bool>((selection) => selection.isSelecting);
final Widget child = isSelecting final Widget child = isSelecting
? _SectionSelectingLeading<T>( ? _SectionSelectingLeading<T>(
@ -201,7 +202,7 @@ class _SectionSelectableLeading<T> extends StatelessWidget {
descendantsAreFocusable: false, descendantsAreFocusable: false,
descendantsAreTraversable: false, descendantsAreTraversable: false,
child: AnimatedSwitcher( child: AnimatedSwitcher(
duration: ADurations.sectionHeaderAnimation, duration: duration,
switchInCurve: Curves.easeInOut, switchInCurve: Curves.easeInOut,
switchOutCurve: Curves.easeInOut, switchOutCurve: Curves.easeInOut,
transitionBuilder: (child, animation) { transitionBuilder: (child, animation) {
@ -240,11 +241,12 @@ class _SectionSelectingLeading<T> extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final duration = context.select<DurationsData, Duration>((v) => v.formTransition);
final sectionEntries = context.watch<SectionedListLayout<T>>().sections[sectionKey] ?? []; final sectionEntries = context.watch<SectionedListLayout<T>>().sections[sectionKey] ?? [];
final selection = context.watch<Selection<T>>(); final selection = context.watch<Selection<T>>();
final isSelected = selection.isSelected(sectionEntries); final isSelected = selection.isSelected(sectionEntries);
return AnimatedSwitcher( return AnimatedSwitcher(
duration: ADurations.sectionHeaderAnimation, duration: duration,
switchInCurve: Curves.easeOutBack, switchInCurve: Curves.easeOutBack,
switchOutCurve: Curves.easeOutBack, switchOutCurve: Curves.easeOutBack,
transitionBuilder: (child, animation) => ScaleTransition( transitionBuilder: (child, animation) => ScaleTransition(

View file

@ -10,8 +10,6 @@ class GridItemSelectionOverlay<T> extends StatelessWidget {
final BorderRadius? borderRadius; final BorderRadius? borderRadius;
final EdgeInsets? padding; final EdgeInsets? padding;
static const duration = ADurations.thumbnailOverlayAnimation;
const GridItemSelectionOverlay({ const GridItemSelectionOverlay({
super.key, super.key,
required this.item, required this.item,
@ -21,6 +19,7 @@ class GridItemSelectionOverlay<T> extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final duration = context.select<DurationsData, Duration>((v) => v.formTransition);
final isSelecting = context.select<Selection<T>, bool>((selection) => selection.isSelecting); final isSelecting = context.select<Selection<T>, bool>((selection) => selection.isSelecting);
return AnimatedSwitcher( return AnimatedSwitcher(
duration: duration, duration: duration,

View file

@ -55,9 +55,9 @@ class _GridSelectionGestureDetectorState<T> extends State<GridSelectionGestureDe
return scrollableBox.size.width; return scrollableBox.size.width;
} }
static const double scrollEdgeRatio = .15; static const double _scrollEdgeRatio = .15;
static const double scrollMaxPixelPerSecond = 600.0; static const double _scrollMaxPixelPerSecond = 600.0;
static const Duration scrollUpdateInterval = Duration(milliseconds: 100); static const Duration _scrollUpdateInterval = Duration(milliseconds: 100);
@override @override
void initState() { void initState() {
@ -158,7 +158,7 @@ class _GridSelectionGestureDetectorState<T> extends State<GridSelectionGestureDe
final top = dy < height / 2; final top = dy < height / 2;
final distanceToEdge = max(0, top ? dy - _scrollableInsets.top : height - dy - _scrollableInsets.bottom); final distanceToEdge = max(0, top ? dy - _scrollableInsets.top : height - dy - _scrollableInsets.bottom);
final threshold = height * scrollEdgeRatio; final threshold = height * _scrollEdgeRatio;
if (distanceToEdge < threshold) { if (distanceToEdge < threshold) {
_setScrollSpeed((top ? -1 : 1) * roundToPrecision((threshold - distanceToEdge) / threshold, decimals: 1)); _setScrollSpeed((top ? -1 : 1) * roundToPrecision((threshold - distanceToEdge) / threshold, decimals: 1));
} else { } else {
@ -185,7 +185,7 @@ class _GridSelectionGestureDetectorState<T> extends State<GridSelectionGestureDe
final target = speedFactor > 0 ? scrollController.position.maxScrollExtent : .0; final target = speedFactor > 0 ? scrollController.position.maxScrollExtent : .0;
if (target != current) { if (target != current) {
final distance = target - current; final distance = target - current;
final millis = distance * 1000 / scrollMaxPixelPerSecond / speedFactor; final millis = distance * 1000 / _scrollMaxPixelPerSecond / speedFactor;
scrollController.animateTo( scrollController.animateTo(
target, target,
duration: Duration(milliseconds: millis.round()), duration: Duration(milliseconds: millis.round()),
@ -193,7 +193,7 @@ class _GridSelectionGestureDetectorState<T> extends State<GridSelectionGestureDe
); );
// use a timer to update the selection, because `onLongPressMoveUpdate` // use a timer to update the selection, because `onLongPressMoveUpdate`
// is not called when the pointer stays still while the view is scrolling // is not called when the pointer stays still while the view is scrolling
_selectionUpdateTimer = Timer.periodic(scrollUpdateInterval, (_) => _onLongPressUpdate()); _selectionUpdateTimer = Timer.periodic(_scrollUpdateInterval, (_) => _onLongPressUpdate());
} }
} }

View file

@ -40,6 +40,50 @@ class AvesAppBar extends StatelessWidget {
final colorScheme = theme.colorScheme; final colorScheme = theme.colorScheme;
final textScaler = MediaQuery.textScalerOf(context); final textScaler = MediaQuery.textScalerOf(context);
final useTvLayout = settings.useTvLayout; final useTvLayout = settings.useTvLayout;
Widget? _leading = leading;
if (_leading != null) {
_leading = FontSizeIconTheme(
child: _leading,
);
}
Widget _title = FontSizeIconTheme(
child: LayoutBuilder(
builder: (context, constraints) {
return Row(
key: ValueKey(transitionKey),
children: [
Expanded(child: title),
...(actions(context, max(0, constraints.maxWidth - _titleMinWidth))),
],
);
},
),
);
final animate = context.select<Settings, bool>((v) => v.animate);
if (animate) {
_title = Hero(
tag: titleHeroTag,
flightShuttleBuilder: _flightShuttleBuilder,
transitionOnUserGestures: true,
child: AnimatedSwitcher(
duration: context.read<DurationsData>().iconAnimation,
child: _title,
),
);
if (_leading != null) {
_leading = Hero(
tag: leadingHeroTag,
flightShuttleBuilder: _flightShuttleBuilder,
transitionOnUserGestures: true,
child: _leading,
);
}
}
return SliverPersistentHeader( return SliverPersistentHeader(
floating: !useTvLayout, floating: !useTvLayout,
pinned: pinned, pinned: pinned,
@ -70,43 +114,16 @@ class AvesAppBar extends StatelessWidget {
height: textScaler.scale(kToolbarHeight), height: textScaler.scale(kToolbarHeight),
child: Row( child: Row(
children: [ children: [
leading != null _leading != null
? Padding( ? Padding(
padding: const EdgeInsets.symmetric(horizontal: 4), padding: const EdgeInsets.symmetric(horizontal: 4),
child: Hero( child: _leading,
tag: leadingHeroTag,
flightShuttleBuilder: _flightShuttleBuilder,
transitionOnUserGestures: true,
child: FontSizeIconTheme(
child: leading!,
),
),
) )
: const SizedBox(width: 16), : const SizedBox(width: 16),
Expanded( Expanded(
child: DefaultTextStyle( child: DefaultTextStyle(
style: theme.appBarTheme.titleTextStyle!, style: theme.appBarTheme.titleTextStyle!,
child: Hero( child: _title,
tag: titleHeroTag,
flightShuttleBuilder: _flightShuttleBuilder,
transitionOnUserGestures: true,
child: AnimatedSwitcher(
duration: context.read<DurationsData>().iconAnimation,
child: FontSizeIconTheme(
child: LayoutBuilder(
builder: (context, constraints) {
return Row(
key: ValueKey(transitionKey),
children: [
Expanded(child: title),
...(actions(context, max(0, constraints.maxWidth - _titleMinWidth))),
],
);
},
),
),
),
),
), ),
), ),
], ],

View file

@ -26,7 +26,7 @@ import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart'; import 'package:flutter/scheduler.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
typedef FilterCallback = void Function(CollectionFilter filter); typedef AFilterCallback = void Function(CollectionFilter filter);
typedef OffsetFilterCallback = void Function(BuildContext context, CollectionFilter filter, Offset tapPosition); typedef OffsetFilterCallback = void Function(BuildContext context, CollectionFilter filter, Offset tapPosition);
enum HeroType { always, onTap, never } enum HeroType { always, onTap, never }
@ -56,9 +56,10 @@ class AvesFilterChip extends StatefulWidget {
final double padding; final double padding;
final double? maxWidth; final double? maxWidth;
final HeroType heroType; final HeroType heroType;
final FilterCallback? onTap, onRemove; final AFilterCallback? onTap, onRemove;
final OffsetFilterCallback? onLongPress; final OffsetFilterCallback? onLongPress;
static const double defaultPadding = 6.0;
static const double defaultRadius = 32; static const double defaultRadius = 32;
static const double outlineWidth = 2; static const double outlineWidth = 2;
static const double minChipHeight = kMinInteractiveDimension; static const double minChipHeight = kMinInteractiveDimension;
@ -79,7 +80,7 @@ class AvesFilterChip extends StatefulWidget {
this.banner, this.banner,
this.leadingOverride, this.leadingOverride,
this.details, this.details,
this.padding = 6.0, this.padding = defaultPadding,
this.maxWidth, this.maxWidth,
this.heroType = HeroType.onTap, this.heroType = HeroType.onTap,
this.onTap, this.onTap,
@ -87,7 +88,7 @@ class AvesFilterChip extends StatefulWidget {
this.onLongPress = showDefaultLongPressMenu, this.onLongPress = showDefaultLongPressMenu,
}); });
static double computeMaxWidth( static double computeMaxWidthForRow(
BuildContext context, { BuildContext context, {
required int minChipPerRow, required int minChipPerRow,
required double chipPadding, required double chipPadding,
@ -347,7 +348,7 @@ class _AvesFilterChipState extends State<AvesFilterChip> {
maxWidth: max( maxWidth: max(
AvesFilterChip.minChipWidth, AvesFilterChip.minChipWidth,
widget.maxWidth ?? widget.maxWidth ??
AvesFilterChip.computeMaxWidth( AvesFilterChip.computeMaxWidthForRow(
context, context,
minChipPerRow: 2, minChipPerRow: 2,
chipPadding: FilterBar.chipPadding.horizontal, chipPadding: FilterBar.chipPadding.horizontal,

View file

@ -124,7 +124,13 @@ class MapButtonPanel extends StatelessWidget {
Padding( Padding(
padding: EdgeInsets.only(top: padding), padding: EdgeInsets.only(top: padding),
// key is expected by test driver // key is expected by test driver
child: _buildButton(context, MapAction.selectStyle, buttonKey: const Key('map-menu-layers')), child: Column(
children: [
_buildButton(context, MapAction.selectStyle, buttonKey: const Key('map-menu-layers')),
SizedBox(height: padding),
_buildButton(context, MapAction.openMapApp),
],
),
), ),
], ],
), ),

Some files were not shown because too many files have changed in this diff Show more