diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
index a7501e099..a4fd88b54 100644
--- a/.github/workflows/check.yml
+++ b/.github/workflows/check.yml
@@ -17,7 +17,7 @@ jobs:
# Available versions may lag behind https://github.com/flutter/flutter.git
- uses: subosito/flutter-action@v2
with:
- flutter-version: '3.3.2'
+ flutter-version: '3.3.4'
channel: 'stable'
- name: Clone the repository.
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 659f91280..92122dc20 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -19,7 +19,7 @@ jobs:
# Available versions may lag behind https://github.com/flutter/flutter.git
- uses: subosito/flutter-action@v2
with:
- flutter-version: '3.3.2'
+ flutter-version: '3.3.4'
channel: 'stable'
# Workaround for this Android Gradle Plugin issue (supposedly fixed in AGP 4.1):
@@ -56,15 +56,15 @@ jobs:
rm release.keystore.asc
mkdir outputs
(cd scripts/; ./apply_flavor_play.sh)
- flutter build appbundle -t lib/main_play.dart --flavor play --bundle-sksl-path shaders_3.3.2.sksl.json
+ flutter build appbundle -t lib/main_play.dart --flavor play --bundle-sksl-path shaders_3.3.4.sksl.json
cp build/app/outputs/bundle/playRelease/*.aab outputs
- flutter build apk -t lib/main_play.dart --flavor play --bundle-sksl-path shaders_3.3.2.sksl.json
+ flutter build apk -t lib/main_play.dart --flavor play --bundle-sksl-path shaders_3.3.4.sksl.json
cp build/app/outputs/apk/play/release/*.apk outputs
(cd scripts/; ./apply_flavor_huawei.sh)
- flutter build apk -t lib/main_huawei.dart --flavor huawei --bundle-sksl-path shaders_3.3.2.sksl.json
+ flutter build apk -t lib/main_huawei.dart --flavor huawei --bundle-sksl-path shaders_3.3.4.sksl.json
cp build/app/outputs/apk/huawei/release/*.apk outputs
(cd scripts/; ./apply_flavor_izzy.sh)
- flutter build apk -t lib/main_izzy.dart --flavor izzy --split-per-abi --bundle-sksl-path shaders_3.3.2.sksl.json
+ flutter build apk -t lib/main_izzy.dart --flavor izzy --split-per-abi --bundle-sksl-path shaders_3.3.4.sksl.json
cp build/app/outputs/apk/izzy/release/*.apk outputs
rm $AVES_STORE_FILE
env:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4a258bf65..0dea5d4bc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,32 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
+## [v1.7.1] - 2022-10-09
+
+### Added
+
+- mosaic layout
+- reverse filters to filter out/in
+- Collection: selection edit actions available as quick actions
+- Albums: group by content type
+- Info: improved display for XMP
+- Stats: top albums
+- Stats: open full top listings
+- Video: option for muted auto play
+- Slideshow / Screen saver: option for no transition
+- Slideshow / Screen saver: animated zoom effect
+- Widget: tap action setting
+- Wallpaper: scroll effect option
+
+### Changed
+
+- upgraded Flutter to stable v3.3.4
+
+### Fixed
+
+- restoring to missing Download subdir
+- crash when cataloguing PNG with large chunks
+
## [v1.7.0] - 2022-09-19
### Added
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 7347da8c8..a26f23c51 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -155,22 +155,22 @@ repositories {
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
implementation 'androidx.core:core-ktx:1.9.0'
- implementation 'androidx.exifinterface:exifinterface:1.3.3'
+ implementation 'androidx.exifinterface:exifinterface:1.3.4'
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'com.caverock:androidsvg-aar:1.4'
- implementation 'com.commonsware.cwac:document:0.4.1'
+ implementation 'com.commonsware.cwac:document:0.5.0'
implementation 'com.drewnoakes:metadata-extractor:2.18.0'
// forked, built by JitPack, cf https://jitpack.io/p/deckerst/Android-TiffBitmapFactory
implementation 'com.github.deckerst:Android-TiffBitmapFactory:876e53870a'
// forked, built by JitPack, cf https://jitpack.io/p/deckerst/pixymeta-android
implementation 'com.github.deckerst:pixymeta-android:706bd73d6e'
- implementation 'com.github.bumptech.glide:glide:4.13.2'
+ implementation 'com.github.bumptech.glide:glide:4.14.2'
// huawei flavor only
- huaweiImplementation 'com.huawei.agconnect:agconnect-core:1.5.2.300'
+ huaweiImplementation 'com.huawei.agconnect:agconnect-core:1.7.2.300'
- kapt 'androidx.annotation:annotation:1.4.0'
- kapt 'com.github.bumptech.glide:compiler:4.13.0'
+ kapt 'androidx.annotation:annotation:1.5.0'
+ kapt 'com.github.bumptech.glide:compiler:4.14.2'
compileOnly rootProject.findProject(':streams_channel')
}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 28fa877f6..5e4b6da99 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -1,10 +1,10 @@
platform -> dart
- // - need Context
- MethodChannel(messenger, DeviceHandler.CHANNEL).setMethodCallHandler(DeviceHandler(this))
- MethodChannel(messenger, GeocodingHandler.CHANNEL).setMethodCallHandler(GeocodingHandler(this))
- MethodChannel(messenger, MediaStoreHandler.CHANNEL).setMethodCallHandler(MediaStoreHandler(this))
- MethodChannel(messenger, MetadataFetchHandler.CHANNEL).setMethodCallHandler(MetadataFetchHandler(this))
- MethodChannel(messenger, StorageHandler.CHANNEL).setMethodCallHandler(StorageHandler(this))
-
- // result streaming: dart -> platform ->->-> dart
- // - need Context
- StreamsChannel(messenger, ImageByteStreamHandler.CHANNEL).setStreamHandlerFactory { args -> ImageByteStreamHandler(this, args) }
- StreamsChannel(messenger, MediaStoreStreamHandler.CHANNEL).setStreamHandlerFactory { args -> MediaStoreStreamHandler(this, args) }
-
- // channel for service management
- backgroundChannel = MethodChannel(messenger, BACKGROUND_CHANNEL).apply {
- setMethodCallHandler(context)
- }
+ initChannels(this)
HandlerThread("Analysis service handler", Process.THREAD_PRIORITY_BACKGROUND).apply {
start()
@@ -94,7 +73,36 @@ class AnalysisService : MethodChannel.MethodCallHandler, Service() {
return START_NOT_STICKY
}
- override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
+ private fun detachAndStop() {
+ analysisServiceBinder.detach()
+ stopSelf()
+ }
+
+ private fun initChannels(context: Context) {
+ val messenger = flutterEngine!!.dartExecutor
+
+ // channels for analysis
+
+ // dart -> platform -> dart
+ // - need Context
+ MethodChannel(messenger, DeviceHandler.CHANNEL).setMethodCallHandler(DeviceHandler(context))
+ MethodChannel(messenger, GeocodingHandler.CHANNEL).setMethodCallHandler(GeocodingHandler(context))
+ MethodChannel(messenger, MediaStoreHandler.CHANNEL).setMethodCallHandler(MediaStoreHandler(context))
+ MethodChannel(messenger, MetadataFetchHandler.CHANNEL).setMethodCallHandler(MetadataFetchHandler(context))
+ MethodChannel(messenger, StorageHandler.CHANNEL).setMethodCallHandler(StorageHandler(context))
+
+ // result streaming: dart -> platform ->->-> dart
+ // - need Context
+ StreamsChannel(messenger, ImageByteStreamHandler.CHANNEL).setStreamHandlerFactory { args -> ImageByteStreamHandler(context, args) }
+ StreamsChannel(messenger, MediaStoreStreamHandler.CHANNEL).setStreamHandlerFactory { args -> MediaStoreStreamHandler(context, args) }
+
+ // channel for service management
+ backgroundChannel = MethodChannel(messenger, BACKGROUND_CHANNEL).apply {
+ setMethodCallHandler { call, result -> onMethodCall(call, result) }
+ }
+ }
+
+ private fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
when (call.method) {
"initialized" -> {
Log.d(LOG_TAG, "background channel is ready")
@@ -119,11 +127,6 @@ class AnalysisService : MethodChannel.MethodCallHandler, Service() {
}
}
- private fun detachAndStop() {
- analysisServiceBinder.detach()
- stopSelf()
- }
-
private fun buildNotification(title: String? = null, message: String? = null): Notification {
val pendingIntentFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/HomeWidgetConfigureActivity.kt b/android/app/src/main/kotlin/deckers/thibault/aves/HomeWidgetConfigureActivity.kt
index 70c55dbaf..a7026b9e1 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/HomeWidgetConfigureActivity.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/HomeWidgetConfigureActivity.kt
@@ -3,7 +3,6 @@ package deckers.thibault.aves
import android.appwidget.AppWidgetManager
import android.content.Intent
import android.os.Bundle
-import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
class HomeWidgetSettingsActivity : MainActivity() {
@@ -26,7 +25,7 @@ class HomeWidgetSettingsActivity : MainActivity() {
}
val messenger = flutterEngine!!.dartExecutor
- MethodChannel(messenger, CHANNEL).setMethodCallHandler { call: MethodCall, result: MethodChannel.Result ->
+ MethodChannel(messenger, CHANNEL).setMethodCallHandler { call, result ->
when (call.method) {
"configure" -> {
result.success(null)
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/HomeWidgetProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/HomeWidgetProvider.kt
index 4da0d671c..307eadc55 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/HomeWidgetProvider.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/HomeWidgetProvider.kt
@@ -5,6 +5,8 @@ import android.appwidget.AppWidgetManager
import android.appwidget.AppWidgetProvider
import android.content.Context
import android.content.Intent
+import android.content.res.Configuration
+import android.content.res.Resources
import android.graphics.Bitmap
import android.net.Uri
import android.os.Build
@@ -62,6 +64,18 @@ class HomeWidgetProvider : AppWidgetProvider() {
}
}
+ private fun getDevicePixelRatio(): Float = Resources.getSystem().displayMetrics.density
+
+ private fun getWidgetSizePx(context: Context, widgetInfo: Bundle): Pair {
+ val devicePixelRatio = getDevicePixelRatio()
+ val isPortrait = context.resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT
+ val widthKey = if (isPortrait) AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH else AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH
+ val heightKey = if (isPortrait) AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT else AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT
+ val widthPx = (widgetInfo.getInt(widthKey) * devicePixelRatio).roundToInt()
+ val heightPx = (widgetInfo.getInt(heightKey) * devicePixelRatio).roundToInt()
+ return Pair(widthPx, heightPx)
+ }
+
private suspend fun getBytes(
context: Context,
widgetId: Int,
@@ -69,9 +83,7 @@ class HomeWidgetProvider : AppWidgetProvider() {
drawEntryImage: Boolean,
reuseEntry: Boolean = false,
): ByteArray? {
- val devicePixelRatio = context.resources.displayMetrics.density
- val widthPx = (widgetInfo.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH) * devicePixelRatio).roundToInt()
- val heightPx = (widgetInfo.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT) * devicePixelRatio).roundToInt()
+ val (widthPx, heightPx) = getWidgetSizePx(context, widgetInfo)
if (widthPx == 0 || heightPx == 0) return null
initFlutterEngine(context)
@@ -85,7 +97,7 @@ class HomeWidgetProvider : AppWidgetProvider() {
"widgetId" to widgetId,
"widthPx" to widthPx,
"heightPx" to heightPx,
- "devicePixelRatio" to devicePixelRatio,
+ "devicePixelRatio" to getDevicePixelRatio(),
"drawEntryImage" to drawEntryImage,
"reuseEntry" to reuseEntry,
), object : MethodChannel.Result {
@@ -120,9 +132,8 @@ class HomeWidgetProvider : AppWidgetProvider() {
) {
bytes ?: return
- val devicePixelRatio = context.resources.displayMetrics.density
- val widthPx = (widgetInfo.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH) * devicePixelRatio).roundToInt()
- val heightPx = (widgetInfo.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT) * devicePixelRatio).roundToInt()
+ val (widthPx, heightPx) = getWidgetSizePx(context, widgetInfo)
+ if (widthPx == 0 || heightPx == 0) return
try {
val bitmap = Bitmap.createBitmap(widthPx, heightPx, Bitmap.Config.ARGB_8888)
@@ -198,6 +209,5 @@ class HomeWidgetProvider : AppWidgetProvider() {
StreamsChannel(messenger, ImageByteStreamHandler.CHANNEL).setStreamHandlerFactory { args -> ImageByteStreamHandler(context, args) }
StreamsChannel(messenger, MediaStoreStreamHandler.CHANNEL).setStreamHandlerFactory { args -> MediaStoreStreamHandler(context, args) }
}
-
}
}
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/SearchSuggestionsProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/SearchSuggestionsProvider.kt
index 59920a934..4627eeb52 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/SearchSuggestionsProvider.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/SearchSuggestionsProvider.kt
@@ -15,7 +15,6 @@ import deckers.thibault.aves.utils.ContextUtils.resourceUri
import deckers.thibault.aves.utils.FlutterUtils
import deckers.thibault.aves.utils.LogUtils
import io.flutter.embedding.engine.FlutterEngine
-import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import kotlinx.coroutines.*
import java.util.*
@@ -74,14 +73,15 @@ class SearchSuggestionsProvider : ContentProvider() {
}
val messenger = flutterEngine!!.dartExecutor
- val backgroundChannel = MethodChannel(messenger, BACKGROUND_CHANNEL)
- backgroundChannel.setMethodCallHandler { call: MethodCall, result: MethodChannel.Result ->
- when (call.method) {
- "initialized" -> {
- Log.d(LOG_TAG, "background channel is ready")
- result.success(null)
+ val backgroundChannel = MethodChannel(messenger, BACKGROUND_CHANNEL).apply {
+ setMethodCallHandler { call, result ->
+ when (call.method) {
+ "initialized" -> {
+ Log.d(LOG_TAG, "background channel is ready")
+ result.success(null)
+ }
+ else -> result.notImplemented()
}
- else -> result.notImplemented()
}
}
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/WallpaperActivity.kt b/android/app/src/main/kotlin/deckers/thibault/aves/WallpaperActivity.kt
index 60c37d9ec..8192d1ce6 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/WallpaperActivity.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/WallpaperActivity.kt
@@ -1,5 +1,6 @@
package deckers.thibault.aves
+import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Build
@@ -16,50 +17,22 @@ import deckers.thibault.aves.channel.streams.ImageByteStreamHandler
import deckers.thibault.aves.utils.LogUtils
import deckers.thibault.aves.utils.getParcelableExtraCompat
import io.flutter.embedding.android.FlutterActivity
+import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
class WallpaperActivity : FlutterActivity() {
private lateinit var intentDataMap: MutableMap
override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
Log.i(LOG_TAG, "onCreate intent=$intent")
intent.extras?.takeUnless { it.isEmpty }?.let {
Log.i(LOG_TAG, "onCreate intent extras=$it")
}
-
- super.onCreate(savedInstanceState)
-
- val messenger = flutterEngine!!.dartExecutor
-
- // dart -> platform -> dart
- // - need Context
- MethodChannel(messenger, DeviceHandler.CHANNEL).setMethodCallHandler(DeviceHandler(this))
- MethodChannel(messenger, EmbeddedDataHandler.CHANNEL).setMethodCallHandler(EmbeddedDataHandler(this))
- MethodChannel(messenger, MediaFetchBytesHandler.CHANNEL, AvesByteSendingMethodCodec.INSTANCE).setMethodCallHandler(MediaFetchBytesHandler(context))
- MethodChannel(messenger, MediaFetchObjectHandler.CHANNEL).setMethodCallHandler(MediaFetchObjectHandler(this))
- MethodChannel(messenger, MetadataFetchHandler.CHANNEL).setMethodCallHandler(MetadataFetchHandler(this))
- MethodChannel(messenger, StorageHandler.CHANNEL).setMethodCallHandler(StorageHandler(this))
- // - need ContextWrapper
- MethodChannel(messenger, AccessibilityHandler.CHANNEL).setMethodCallHandler(AccessibilityHandler(this))
- MethodChannel(messenger, WallpaperHandler.CHANNEL).setMethodCallHandler(WallpaperHandler(this))
- // - need Activity
- MethodChannel(messenger, WindowHandler.CHANNEL).setMethodCallHandler(ActivityWindowHandler(this))
-
- // result streaming: dart -> platform ->->-> dart
- // - need Context
- StreamsChannel(messenger, ImageByteStreamHandler.CHANNEL).setStreamHandlerFactory { args -> ImageByteStreamHandler(this, args) }
-
- // intent handling
- // detail fetch: dart -> platform
intentDataMap = extractIntentData(intent)
- MethodChannel(messenger, MainActivity.INTENT_CHANNEL).setMethodCallHandler { call, result ->
- when (call.method) {
- "getIntentData" -> {
- result.success(intentDataMap)
- intentDataMap.clear()
- }
- }
- }
+
+ initChannels(this)
}
override fun onStart() {
@@ -76,6 +49,41 @@ class WallpaperActivity : FlutterActivity() {
}
}
+ private fun initChannels(activity: Activity) {
+ val messenger = flutterEngine!!.dartExecutor
+
+ // dart -> platform -> dart
+ // - need Context
+ MethodChannel(messenger, DeviceHandler.CHANNEL).setMethodCallHandler(DeviceHandler(activity))
+ MethodChannel(messenger, EmbeddedDataHandler.CHANNEL).setMethodCallHandler(EmbeddedDataHandler(activity))
+ MethodChannel(messenger, MediaFetchBytesHandler.CHANNEL, AvesByteSendingMethodCodec.INSTANCE).setMethodCallHandler(MediaFetchBytesHandler(activity))
+ MethodChannel(messenger, MediaFetchObjectHandler.CHANNEL).setMethodCallHandler(MediaFetchObjectHandler(activity))
+ MethodChannel(messenger, MetadataFetchHandler.CHANNEL).setMethodCallHandler(MetadataFetchHandler(activity))
+ MethodChannel(messenger, StorageHandler.CHANNEL).setMethodCallHandler(StorageHandler(activity))
+ // - need ContextWrapper
+ MethodChannel(messenger, AccessibilityHandler.CHANNEL).setMethodCallHandler(AccessibilityHandler(activity))
+ MethodChannel(messenger, WallpaperHandler.CHANNEL).setMethodCallHandler(WallpaperHandler(activity))
+ // - need Activity
+ MethodChannel(messenger, WindowHandler.CHANNEL).setMethodCallHandler(ActivityWindowHandler(activity))
+
+ // result streaming: dart -> platform ->->-> dart
+ // - need Context
+ StreamsChannel(messenger, ImageByteStreamHandler.CHANNEL).setStreamHandlerFactory { args -> ImageByteStreamHandler(activity, args) }
+
+ // intent handling
+ // detail fetch: dart -> platform
+ MethodChannel(messenger, MainActivity.INTENT_CHANNEL).setMethodCallHandler { call, result -> onMethodCall(call, result) }
+ }
+
+ private fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
+ when (call.method) {
+ "getIntentData" -> {
+ result.success(intentDataMap)
+ intentDataMap.clear()
+ }
+ }
+ }
+
private fun extractIntentData(intent: Intent?): MutableMap {
when (intent?.action) {
Intent.ACTION_ATTACH_DATA, Intent.ACTION_SET_WALLPAPER -> {
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/decoder/AvesAppGlideModule.kt b/android/app/src/main/kotlin/deckers/thibault/aves/decoder/AvesAppGlideModule.kt
index 2b6f43d18..0b699c80a 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/decoder/AvesAppGlideModule.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/decoder/AvesAppGlideModule.kt
@@ -21,7 +21,7 @@ class AvesAppGlideModule : AppGlideModule() {
override fun registerComponents(context: Context, glide: Glide, registry: Registry) {
// prevent ExifInterface error logs
// cf https://github.com/bumptech/glide/issues/3383
- glide.registry.imageHeaderParsers.compatRemoveIf { parser: ImageHeaderParser? -> parser is ExifInterfaceImageHeaderParser }
+ registry.imageHeaderParsers.compatRemoveIf { parser: ImageHeaderParser? -> parser is ExifInterfaceImageHeaderParser }
}
override fun isManifestParsingEnabled(): Boolean = false
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/XMP.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/XMP.kt
index 540f37fa7..24cb4ec40 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/XMP.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/XMP.kt
@@ -98,7 +98,7 @@ object XMP {
if (MimeTypes.isHeic(mimeType) && !foundXmp && StorageUtils.isMediaStoreContentUri(uri) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
try {
val xmpBytes = context.queryContentResolverProp(uri, mimeType, MediaStore.MediaColumns.XMP)
- if (xmpBytes is ByteArray) {
+ if (xmpBytes is ByteArray && xmpBytes.size > 0) {
val xmpMeta = XMPMetaFactory.parseFromBuffer(xmpBytes, SafeXmpReader.PARSE_OPTIONS)
processXmp(xmpMeta)
}
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/Helper.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/Helper.kt
index 06add40ae..8f6672ce9 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/Helper.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/Helper.kt
@@ -67,6 +67,7 @@ object Helper {
val metadata = when (fileType) {
FileType.Jpeg -> safeReadJpeg(inputStream)
+ FileType.Png -> safeReadPng(inputStream)
FileType.Tiff,
FileType.Arw,
FileType.Cr2,
@@ -95,6 +96,10 @@ object Helper {
return metadata
}
+ private fun safeReadPng(input: InputStream): com.drew.metadata.Metadata {
+ return SafePngMetadataReader.readMetadata(input)
+ }
+
@Throws(IOException::class, TiffProcessingException::class)
fun safeReadTiff(input: InputStream): com.drew.metadata.Metadata {
val reader = RandomAccessStreamReader(input, RandomAccessStreamReader.DEFAULT_CHUNK_LENGTH, safeReadStreamLength)
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafePngMetadataReader.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafePngMetadataReader.kt
new file mode 100644
index 000000000..b0baa7973
--- /dev/null
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafePngMetadataReader.kt
@@ -0,0 +1,302 @@
+package deckers.thibault.aves.metadata.metadataextractor
+
+import android.util.Log
+import com.drew.imaging.png.*
+import com.drew.imaging.tiff.TiffProcessingException
+import com.drew.imaging.tiff.TiffReader
+import com.drew.lang.*
+import com.drew.lang.annotations.NotNull
+import com.drew.metadata.ErrorDirectory
+import com.drew.metadata.Metadata
+import com.drew.metadata.StringValue
+import com.drew.metadata.exif.ExifTiffHandler
+import com.drew.metadata.icc.IccReader
+import com.drew.metadata.png.PngChromaticitiesDirectory
+import com.drew.metadata.png.PngDirectory
+import com.drew.metadata.xmp.XmpReader
+import deckers.thibault.aves.utils.LogUtils
+import java.io.ByteArrayInputStream
+import java.io.IOException
+import java.io.InputStream
+import java.util.zip.InflaterInputStream
+import java.util.zip.ZipException
+
+// adapted from `PngMetadataReader` to prevent reading OOM from large chunks
+// as of `metadata-extractor` v2.18.0, there is no way to customize the reader
+// without copying `desiredChunkTypes` and the whole `processChunk` function
+object SafePngMetadataReader {
+ private val LOG_TAG = LogUtils.createTag()
+
+ // arbitrary size to detect chunks that may yield an OOM
+ private const val chunkSizeDangerThreshold = SafeXmpReader.segmentTypeSizeDangerThreshold
+
+ private val latin1Encoding = Charsets.ISO_8859_1
+ private val desiredChunkTypes: Set = hashSetOf(
+ PngChunkType.IHDR,
+ PngChunkType.PLTE,
+ PngChunkType.tRNS,
+ PngChunkType.cHRM,
+ PngChunkType.sRGB,
+ PngChunkType.gAMA,
+ PngChunkType.iCCP,
+ PngChunkType.bKGD,
+ PngChunkType.tEXt,
+ PngChunkType.zTXt,
+ PngChunkType.iTXt,
+ PngChunkType.tIME,
+ PngChunkType.pHYs,
+ PngChunkType.sBIT,
+ PngChunkType.eXIf,
+ )
+
+ @Throws(IOException::class, PngProcessingException::class)
+ fun readMetadata(inputStream: InputStream): Metadata {
+ val chunks = PngChunkReader().extract(StreamReader(inputStream), desiredChunkTypes)
+ val metadata = Metadata()
+ for (chunk in chunks) {
+ try {
+ processChunk(metadata, chunk)
+ } catch (e: Exception) {
+ metadata.addDirectory(ErrorDirectory("Exception reading PNG chunk: " + e.message))
+ }
+ }
+ return metadata
+ }
+
+ @Throws(PngProcessingException::class, IOException::class)
+ private fun processChunk(@NotNull metadata: Metadata, @NotNull chunk: PngChunk) {
+ val chunkType = chunk.type
+ val bytes = chunk.bytes
+
+ // TLAD insert start
+ if (bytes.size > chunkSizeDangerThreshold) {
+ Log.w(LOG_TAG, "PNG chunk $chunkType is too large, with a size of ${bytes.size} B")
+ return
+ }
+ // TLAD insert end
+
+ if (chunkType == PngChunkType.IHDR) {
+ val header = PngHeader(bytes)
+ val directory = PngDirectory(PngChunkType.IHDR)
+ directory.setInt(PngDirectory.TAG_IMAGE_WIDTH, header.imageWidth)
+ directory.setInt(PngDirectory.TAG_IMAGE_HEIGHT, header.imageHeight)
+ directory.setInt(PngDirectory.TAG_BITS_PER_SAMPLE, header.bitsPerSample.toInt())
+ directory.setInt(PngDirectory.TAG_COLOR_TYPE, header.colorType.numericValue)
+ directory.setInt(PngDirectory.TAG_COMPRESSION_TYPE, header.compressionType.toInt() and 0xFF) // make sure it's unsigned
+ directory.setInt(PngDirectory.TAG_FILTER_METHOD, header.filterMethod.toInt())
+ directory.setInt(PngDirectory.TAG_INTERLACE_METHOD, header.interlaceMethod.toInt())
+ metadata.addDirectory(directory)
+ } else if (chunkType == PngChunkType.PLTE) {
+ val directory = PngDirectory(PngChunkType.PLTE)
+ directory.setInt(PngDirectory.TAG_PALETTE_SIZE, bytes.size / 3)
+ metadata.addDirectory(directory)
+ } else if (chunkType == PngChunkType.tRNS) {
+ val directory = PngDirectory(PngChunkType.tRNS)
+ directory.setInt(PngDirectory.TAG_PALETTE_HAS_TRANSPARENCY, 1)
+ metadata.addDirectory(directory)
+ } else if (chunkType == PngChunkType.sRGB) {
+ val srgbRenderingIntent = bytes[0].toInt()
+ val directory = PngDirectory(PngChunkType.sRGB)
+ directory.setInt(PngDirectory.TAG_SRGB_RENDERING_INTENT, srgbRenderingIntent)
+ metadata.addDirectory(directory)
+ } else if (chunkType == PngChunkType.cHRM) {
+ val chromaticities = PngChromaticities(bytes)
+ val directory = PngChromaticitiesDirectory()
+ directory.setInt(PngChromaticitiesDirectory.TAG_WHITE_POINT_X, chromaticities.whitePointX)
+ directory.setInt(PngChromaticitiesDirectory.TAG_WHITE_POINT_Y, chromaticities.whitePointY)
+ directory.setInt(PngChromaticitiesDirectory.TAG_RED_X, chromaticities.redX)
+ directory.setInt(PngChromaticitiesDirectory.TAG_RED_Y, chromaticities.redY)
+ directory.setInt(PngChromaticitiesDirectory.TAG_GREEN_X, chromaticities.greenX)
+ directory.setInt(PngChromaticitiesDirectory.TAG_GREEN_Y, chromaticities.greenY)
+ directory.setInt(PngChromaticitiesDirectory.TAG_BLUE_X, chromaticities.blueX)
+ directory.setInt(PngChromaticitiesDirectory.TAG_BLUE_Y, chromaticities.blueY)
+ metadata.addDirectory(directory)
+ } else if (chunkType == PngChunkType.gAMA) {
+ val gammaInt = ByteConvert.toInt32BigEndian(bytes)
+ SequentialByteArrayReader(bytes).int32
+ val directory = PngDirectory(PngChunkType.gAMA)
+ directory.setDouble(PngDirectory.TAG_GAMMA, gammaInt / 100000.0)
+ metadata.addDirectory(directory)
+ } else if (chunkType == PngChunkType.iCCP) {
+ val reader: SequentialReader = SequentialByteArrayReader(bytes)
+
+ // Profile Name is 1-79 bytes, followed by the 1 byte null character
+ val profileNameBytes = reader.getNullTerminatedBytes(79 + 1)
+ val directory = PngDirectory(PngChunkType.iCCP)
+ directory.setStringValue(PngDirectory.TAG_ICC_PROFILE_NAME, StringValue(profileNameBytes, latin1Encoding))
+ val compressionMethod = reader.int8
+ // Only compression method allowed by the spec is zero: deflate
+ if (compressionMethod.toInt() == 0) {
+ // bytes left for compressed text is:
+ // total bytes length - (profilenamebytes length + null byte + compression method byte)
+ val bytesLeft = bytes.size - (profileNameBytes.size + 1 + 1)
+ val compressedProfile = reader.getBytes(bytesLeft)
+ try {
+ val inflateStream = InflaterInputStream(ByteArrayInputStream(compressedProfile))
+ IccReader().extract(RandomAccessStreamReader(inflateStream), metadata, directory)
+ inflateStream.close()
+ } catch (zex: ZipException) {
+ directory.addError(String.format("Exception decompressing PNG iCCP chunk : %s", zex.message))
+ metadata.addDirectory(directory)
+ }
+ } else {
+ directory.addError("Invalid compression method value")
+ }
+ metadata.addDirectory(directory)
+ } else if (chunkType == PngChunkType.bKGD) {
+ val directory = PngDirectory(PngChunkType.bKGD)
+ directory.setByteArray(PngDirectory.TAG_BACKGROUND_COLOR, bytes)
+ metadata.addDirectory(directory)
+ } else if (chunkType == PngChunkType.tEXt) {
+ val reader: SequentialReader = SequentialByteArrayReader(bytes)
+
+ // Keyword is 1-79 bytes, followed by the 1 byte null character
+ val keywordsv = reader.getNullTerminatedStringValue(79 + 1, latin1Encoding)
+ val keyword = keywordsv.toString()
+
+ // bytes left for text is:
+ // total bytes length - (Keyword length + null byte)
+ val bytesLeft = bytes.size - (keywordsv.bytes.size + 1)
+ val value = reader.getNullTerminatedStringValue(bytesLeft, latin1Encoding)
+ val textPairs: MutableList = ArrayList()
+ textPairs.add(KeyValuePair(keyword, value))
+ val directory = PngDirectory(PngChunkType.tEXt)
+ directory.setObject(PngDirectory.TAG_TEXTUAL_DATA, textPairs)
+ metadata.addDirectory(directory)
+ } else if (chunkType == PngChunkType.zTXt) {
+ val reader: SequentialReader = SequentialByteArrayReader(bytes)
+
+ // Keyword is 1-79 bytes, followed by the 1 byte null character
+ val keywordsv = reader.getNullTerminatedStringValue(79 + 1, latin1Encoding)
+ val keyword = keywordsv.toString()
+ val compressionMethod = reader.int8
+
+ // bytes left for compressed text is:
+ // total bytes length - (Keyword length + null byte + compression method byte)
+ val bytesLeft = bytes.size - (keywordsv.bytes.size + 1 + 1)
+ var textBytes: ByteArray? = null
+ if (compressionMethod.toInt() == 0) {
+ try {
+ textBytes = StreamUtil.readAllBytes(InflaterInputStream(ByteArrayInputStream(bytes, bytes.size - bytesLeft, bytesLeft)))
+ } catch (zex: ZipException) {
+ val directory = PngDirectory(PngChunkType.zTXt)
+ directory.addError(String.format("Exception decompressing PNG zTXt chunk with keyword \"%s\": %s", keyword, zex.message))
+ metadata.addDirectory(directory)
+ }
+ } else {
+ val directory = PngDirectory(PngChunkType.zTXt)
+ directory.addError("Invalid compression method value")
+ metadata.addDirectory(directory)
+ }
+ if (textBytes != null) {
+ if (keyword == "XML:com.adobe.xmp") {
+ // NOTE in testing images, the XMP has parsed successfully, but we are not extracting tags from it as necessary
+ XmpReader().extract(textBytes, metadata)
+ } else {
+ val textPairs: MutableList = ArrayList()
+ textPairs.add(KeyValuePair(keyword, StringValue(textBytes, latin1Encoding)))
+ val directory = PngDirectory(PngChunkType.zTXt)
+ directory.setObject(PngDirectory.TAG_TEXTUAL_DATA, textPairs)
+ metadata.addDirectory(directory)
+ }
+ }
+ } else if (chunkType == PngChunkType.iTXt) {
+ val reader: SequentialReader = SequentialByteArrayReader(bytes)
+
+ // Keyword is 1-79 bytes, followed by the 1 byte null character
+ val keywordsv = reader.getNullTerminatedStringValue(79 + 1, latin1Encoding)
+ val keyword = keywordsv.toString()
+ val compressionFlag = reader.int8
+ val compressionMethod = reader.int8
+ // TODO we currently ignore languageTagBytes and translatedKeywordBytes
+ val languageTagBytes = reader.getNullTerminatedBytes(bytes.size)
+ val translatedKeywordBytes = reader.getNullTerminatedBytes(bytes.size)
+
+ // bytes left for compressed text is:
+ // total bytes length - (Keyword length + null byte + comp flag byte + comp method byte + lang length + null byte + translated length + null byte)
+ val bytesLeft = bytes.size - (keywordsv.bytes.size + 1 + 1 + 1 + languageTagBytes.size + 1 + translatedKeywordBytes.size + 1)
+ var textBytes: ByteArray? = null
+ if (compressionFlag.toInt() == 0) {
+ textBytes = reader.getNullTerminatedBytes(bytesLeft)
+ } else if (compressionFlag.toInt() == 1) {
+ if (compressionMethod.toInt() == 0) {
+ try {
+ textBytes = StreamUtil.readAllBytes(InflaterInputStream(ByteArrayInputStream(bytes, bytes.size - bytesLeft, bytesLeft)))
+ } catch (zex: ZipException) {
+ val directory = PngDirectory(PngChunkType.iTXt)
+ directory.addError(String.format("Exception decompressing PNG iTXt chunk with keyword \"%s\": %s", keyword, zex.message))
+ metadata.addDirectory(directory)
+ }
+ } else {
+ val directory = PngDirectory(PngChunkType.iTXt)
+ directory.addError("Invalid compression method value")
+ metadata.addDirectory(directory)
+ }
+ } else {
+ val directory = PngDirectory(PngChunkType.iTXt)
+ directory.addError("Invalid compression flag value")
+ metadata.addDirectory(directory)
+ }
+ if (textBytes != null) {
+ if (keyword == "XML:com.adobe.xmp") {
+ // NOTE in testing images, the XMP has parsed successfully, but we are not extracting tags from it as necessary
+ XmpReader().extract(textBytes, metadata)
+ } else {
+ val textPairs: MutableList = ArrayList()
+ textPairs.add(KeyValuePair(keyword, StringValue(textBytes, latin1Encoding)))
+ val directory = PngDirectory(PngChunkType.iTXt)
+ directory.setObject(PngDirectory.TAG_TEXTUAL_DATA, textPairs)
+ metadata.addDirectory(directory)
+ }
+ }
+ } else if (chunkType == PngChunkType.tIME) {
+ val reader = SequentialByteArrayReader(bytes)
+ val year = reader.uInt16
+ val month = reader.uInt8.toInt()
+ val day = reader.uInt8.toInt()
+ val hour = reader.uInt8.toInt()
+ val minute = reader.uInt8.toInt()
+ val second = reader.uInt8.toInt()
+ val directory = PngDirectory(PngChunkType.tIME)
+ if (DateUtil.isValidDate(year, month - 1, day) && DateUtil.isValidTime(hour, minute, second)) {
+ val dateString = String.format("%04d:%02d:%02d %02d:%02d:%02d", year, month, day, hour, minute, second)
+ directory.setString(PngDirectory.TAG_LAST_MODIFICATION_TIME, dateString)
+ } else {
+ directory.addError(
+ String.format(
+ "PNG tIME data describes an invalid date/time: year=%d month=%d day=%d hour=%d minute=%d second=%d",
+ year, month, day, hour, minute, second
+ )
+ )
+ }
+ metadata.addDirectory(directory)
+ } else if (chunkType == PngChunkType.pHYs) {
+ val reader = SequentialByteArrayReader(bytes)
+ val pixelsPerUnitX = reader.int32
+ val pixelsPerUnitY = reader.int32
+ val unitSpecifier = reader.int8
+ val directory = PngDirectory(PngChunkType.pHYs)
+ directory.setInt(PngDirectory.TAG_PIXELS_PER_UNIT_X, pixelsPerUnitX)
+ directory.setInt(PngDirectory.TAG_PIXELS_PER_UNIT_Y, pixelsPerUnitY)
+ directory.setInt(PngDirectory.TAG_UNIT_SPECIFIER, unitSpecifier.toInt())
+ metadata.addDirectory(directory)
+ } else if (chunkType == PngChunkType.sBIT) {
+ val directory = PngDirectory(PngChunkType.sBIT)
+ directory.setByteArray(PngDirectory.TAG_SIGNIFICANT_BITS, bytes)
+ metadata.addDirectory(directory)
+ } else if (chunkType == PngChunkType.eXIf) {
+ try {
+ val handler = ExifTiffHandler(metadata, null)
+ TiffReader().processTiff(ByteArrayReader(bytes), handler, 0)
+ } catch (ex: TiffProcessingException) {
+ val directory = PngDirectory(PngChunkType.eXIf)
+ directory.addError(ex.message)
+ metadata.addDirectory(directory)
+ } catch (ex: IOException) {
+ val directory = PngDirectory(PngChunkType.eXIf)
+ directory.addError(ex.message)
+ metadata.addDirectory(directory)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafeXmpReader.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafeXmpReader.kt
index c70c09ae2..db58da6d6 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafeXmpReader.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/metadataextractor/SafeXmpReader.kt
@@ -135,7 +135,7 @@ class SafeXmpReader : XmpReader() {
private val LOG_TAG = LogUtils.createTag()
// arbitrary size to detect extended XMP that may yield an OOM
- private const val segmentTypeSizeDangerThreshold = 3 * (1 shl 20) // MB
+ const val segmentTypeSizeDangerThreshold = 3 * (1 shl 20) // MB
// tighter node limits for faster loading
val PARSE_OPTIONS: ParseOptions = ParseOptions().setXMPNodesToLimit(
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt
index 4e7ff6ca3..9d5749561 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt
@@ -41,6 +41,7 @@ class SourceEntry {
var height: Int? = null
private var sourceRotationDegrees: Int? = null
private var sizeBytes: Long? = null
+ private var dateAddedSecs: Long? = null
private var dateModifiedSecs: Long? = null
private var sourceDateTakenMillis: Long? = null
private var durationMillis: Long? = null
@@ -61,6 +62,7 @@ class SourceEntry {
sourceRotationDegrees = map["sourceRotationDegrees"] as Int?
sizeBytes = toLong(map["sizeBytes"])
title = map["title"] as String?
+ dateAddedSecs = toLong(map["dateAddedSecs"])
dateModifiedSecs = toLong(map["dateModifiedSecs"])
sourceDateTakenMillis = toLong(map["sourceDateTakenMillis"])
durationMillis = toLong(map["durationMillis"])
@@ -83,6 +85,7 @@ class SourceEntry {
"sourceRotationDegrees" to (sourceRotationDegrees ?: 0),
"sizeBytes" to sizeBytes,
"title" to title,
+ "dateAddedSecs" to dateAddedSecs,
"dateModifiedSecs" to dateModifiedSecs,
"sourceDateTakenMillis" to sourceDateTakenMillis,
"durationMillis" to durationMillis,
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/FileImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/FileImageProvider.kt
index 66d6f1035..3c3a624d5 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/FileImageProvider.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/FileImageProvider.kt
@@ -22,7 +22,12 @@ internal class FileImageProvider : ImageProvider() {
try {
val file = File(path)
if (file.exists()) {
- entry.initFromFile(path, file.name, file.length(), file.lastModified() / 1000)
+ entry.initFromFile(
+ path = path,
+ title = file.name,
+ sizeBytes = file.length(),
+ dateModifiedSecs = file.lastModified() / 1000,
+ )
}
} catch (e: SecurityException) {
callback.onFailure(e)
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt
index 442d35190..d13143269 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt
@@ -7,7 +7,6 @@ import android.content.*
import android.media.MediaScannerConnection
import android.net.Uri
import android.os.Build
-import android.os.Environment
import android.provider.MediaStore
import android.util.Log
import com.commonsware.cwac.document.DocumentFileCompat
@@ -87,7 +86,7 @@ class MediaStoreImageProvider : ImageProvider() {
val alwaysValid: NewEntryChecker = fun(_: Int, _: Int): Boolean = true
val onSuccess: NewEntryHandler = fun(entry: FieldMap) { fetched.add(entry) }
if (id != null) {
- if (!found && (sourceMimeType == null || isImage(sourceMimeType))) {
+ if (sourceMimeType == null || isImage(sourceMimeType)) {
val contentUri = ContentUris.withAppendedId(IMAGE_CONTENT_URI, id)
found = fetchFrom(context, alwaysValid, onSuccess, contentUri, IMAGE_PROJECTION)
}
@@ -190,6 +189,7 @@ class MediaStoreImageProvider : ImageProvider() {
val sizeColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE)
val widthColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.WIDTH)
val heightColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.HEIGHT)
+ val dateAddedColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_ADDED)
val dateModifiedColumn = cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.DATE_MODIFIED)
val dateTakenColumn = cursor.getColumnIndex(MediaColumns.DATE_TAKEN)
@@ -225,6 +225,7 @@ class MediaStoreImageProvider : ImageProvider() {
"height" to height,
"sourceRotationDegrees" to if (orientationColumn != -1) cursor.getInt(orientationColumn) else 0,
"sizeBytes" to cursor.getLong(sizeColumn),
+ "dateAddedSecs" to cursor.getInt(dateAddedColumn),
"dateModifiedSecs" to dateModifiedSecs,
"sourceDateTakenMillis" to if (dateTakenColumn != -1) cursor.getLong(dateTakenColumn) else null,
"durationMillis" to durationMillis,
@@ -391,8 +392,13 @@ class MediaStoreImageProvider : ImageProvider() {
effectiveTargetDir = targetDir
targetDirDocFile = StorageUtils.createDirectoryDocIfAbsent(activity, targetDir)
if (!File(targetDir).exists()) {
- callback.onFailure(Exception("failed to create directory at path=$targetDir"))
- return
+ val downloadDirPath = StorageUtils.getDownloadDirPath(activity, targetDir)
+ val isDownloadSubdir = downloadDirPath != null && targetDir.startsWith(downloadDirPath)
+ // download subdirectories can be created later by Media Store insertion
+ if (!isDownloadSubdir) {
+ callback.onFailure(Exception("failed to create directory at path=$targetDir"))
+ return
+ }
}
}
@@ -535,54 +541,57 @@ class MediaStoreImageProvider : ImageProvider() {
targetNameWithoutExtension: String,
write: (OutputStream) -> Unit,
): String {
- return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && isDownloadDir(activity, targetDir)) {
- val targetFileName = "$targetNameWithoutExtension${extensionFor(mimeType)}"
- val values = ContentValues().apply {
- put(MediaStore.MediaColumns.DISPLAY_NAME, targetFileName)
- put(MediaStore.MediaColumns.IS_PENDING, 1)
- }
- val resolver = activity.contentResolver
- val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ val downloadDirPath = StorageUtils.getDownloadDirPath(activity, targetDir)
+ val isDownloadSubdir = downloadDirPath != null && targetDir.startsWith(downloadDirPath)
+ if (isDownloadSubdir) {
+ val volumePath = StorageUtils.getVolumePath(activity, targetDir)
+ val relativePath = targetDir.substring(volumePath?.length ?: 0)
- uri?.let {
- resolver.openOutputStream(uri)?.use(write)
- values.clear()
- values.put(MediaStore.MediaColumns.IS_PENDING, 0)
- resolver.update(uri, values, null, null)
- } ?: throw Exception("MediaStore failed for some reason")
-
- File(targetDir, targetFileName).path
- } else {
- targetDirDocFile ?: throw Exception("failed to get tree doc for directory at path=$targetDir")
-
- // the file created from a `TreeDocumentFile` is also a `TreeDocumentFile`
- // but in order to open an output stream to it, we need to use a `SingleDocumentFile`
- // through a document URI, not a tree URI
- // note that `DocumentFile.getParentFile()` returns null if we did not pick a tree first
- val targetTreeFile = targetDirDocFile.createFile(mimeType, targetNameWithoutExtension)
- val targetDocFile = DocumentFileCompat.fromSingleUri(activity, targetTreeFile.uri)
-
- try {
- targetDocFile.openOutputStream().use(write)
- } catch (e: Exception) {
- // remove empty file
- if (targetDocFile.exists()) {
- targetDocFile.delete()
+ val targetFileName = "$targetNameWithoutExtension${extensionFor(mimeType)}"
+ val values = ContentValues().apply {
+ put(MediaStore.MediaColumns.DISPLAY_NAME, targetFileName)
+ put(MediaStore.MediaColumns.RELATIVE_PATH, relativePath)
+ put(MediaStore.MediaColumns.IS_PENDING, 1)
}
- throw e
+ val resolver = activity.contentResolver
+ val uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values)
+
+ uri?.let {
+ resolver.openOutputStream(uri)?.use(write)
+ values.clear()
+ values.put(MediaStore.MediaColumns.IS_PENDING, 0)
+ resolver.update(uri, values, null, null)
+ } ?: throw Exception("MediaStore failed for some reason")
+
+ return File(targetDir, targetFileName).path
}
-
- // the source file name and the created document file name can be different when:
- // - a file with the same name already exists, some implementations give a suffix like ` (1)`, some *do not*
- // - the original extension does not match the extension added by the underlying provider
- val fileName = targetDocFile.name
- targetDir + fileName
}
- }
- private fun isDownloadDir(context: Context, dirPath: String): Boolean {
- val relativeDir = removeTrailingSeparator(PathSegments(context, dirPath).relativeDir ?: "")
- return relativeDir == Environment.DIRECTORY_DOWNLOADS
+ targetDirDocFile ?: throw Exception("failed to get tree doc for directory at path=$targetDir")
+
+ // the file created from a `TreeDocumentFile` is also a `TreeDocumentFile`
+ // but in order to open an output stream to it, we need to use a `SingleDocumentFile`
+ // through a document URI, not a tree URI
+ // note that `DocumentFile.getParentFile()` returns null if we did not pick a tree first
+ val targetTreeFile = targetDirDocFile.createFile(mimeType, targetNameWithoutExtension)
+ val targetDocFile = DocumentFileCompat.fromSingleUri(activity, targetTreeFile.uri)
+
+ try {
+ targetDocFile.openOutputStream().use(write)
+ } catch (e: Exception) {
+ // remove empty file
+ if (targetDocFile.exists()) {
+ targetDocFile.delete()
+ }
+ throw e
+ }
+
+ // the source file name and the created document file name can be different when:
+ // - a file with the same name already exists, some implementations give a suffix like ` (1)`, some *do not*
+ // - the original extension does not match the extension added by the underlying provider
+ val fileName = targetDocFile.name
+ return targetDir + fileName
}
override suspend fun renameMultiple(
@@ -782,6 +791,7 @@ class MediaStoreImageProvider : ImageProvider() {
// we retrieve updated fields as the renamed/moved file became a new entry in the Media Store
val projection = arrayOf(
+ MediaStore.MediaColumns.DATE_ADDED,
MediaStore.MediaColumns.DATE_MODIFIED,
)
try {
@@ -791,6 +801,7 @@ class MediaStoreImageProvider : ImageProvider() {
newFields["uri"] = uri.toString()
newFields["contentId"] = uri.tryParseId()
newFields["path"] = path
+ cursor.getColumnIndex(MediaStore.MediaColumns.DATE_ADDED).let { if (it != -1) newFields["dateAddedSecs"] = cursor.getInt(it) }
cursor.getColumnIndex(MediaStore.MediaColumns.DATE_MODIFIED).let { if (it != -1) newFields["dateModifiedSecs"] = cursor.getInt(it) }
cursor.close()
return newFields
@@ -864,6 +875,7 @@ class MediaStoreImageProvider : ImageProvider() {
MediaStore.MediaColumns.SIZE,
MediaStore.MediaColumns.WIDTH,
MediaStore.MediaColumns.HEIGHT,
+ MediaStore.MediaColumns.DATE_ADDED,
MediaStore.MediaColumns.DATE_MODIFIED,
MediaColumns.DATE_TAKEN,
)
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt
index 55a134192..6f7cf280e 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt
@@ -104,9 +104,8 @@ object MimeTypes {
else -> false
}
- // as of androidx.exifinterface:exifinterface:1.3.3
+ // as of androidx.exifinterface:exifinterface:1.3.4
fun canEditExif(mimeType: String) = when (mimeType) {
- DNG,
JPEG,
PNG,
WEBP -> true
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/PermissionManager.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/PermissionManager.kt
index 2d78f9a52..08355a5de 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/PermissionManager.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/PermissionManager.kt
@@ -105,7 +105,11 @@ object PermissionManager {
val primaryDir = dirSegments.firstOrNull()
if (getRestrictedPrimaryDirectories().contains(primaryDir) && dirSegments.size > 1) {
// request secondary directory (if any) for restricted primary directory
- dirSet.add(dirSegments.take(2).joinToString(File.separator))
+ val dir = dirSegments.take(2).joinToString(File.separator)
+ // only register directories that exist on storage, so they can be selected for access grant
+ if (File(volumePath, dir).exists()) {
+ dirSet.add(dir)
+ }
} else {
primaryDir?.let { dirSet.add(it) }
}
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt
index fef6748b5..db1bd8e80 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt
@@ -9,6 +9,7 @@ import android.content.pm.PackageManager
import android.media.MediaMetadataRetriever
import android.net.Uri
import android.os.Build
+import android.os.Environment
import android.os.storage.StorageManager
import android.provider.DocumentsContract
import android.provider.MediaStore
@@ -93,6 +94,10 @@ object StorageUtils {
return getVolumePaths(context).firstOrNull { anyPath.startsWith(it) }
}
+ fun getDownloadDirPath(context: Context, anyPath: String): String? {
+ return getVolumePath(context, anyPath)?.let { volumePath -> ensureTrailingSeparator(File(volumePath, Environment.DIRECTORY_DOWNLOADS).path) }
+ }
+
private fun getPathStepIterator(context: Context, anyPath: String, root: String?): Iterator? {
val rootLength = (root ?: getVolumePath(context, anyPath))?.length ?: return null
diff --git a/android/build.gradle b/android/build.gradle
index f35c09e44..42c5dc7f7 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -7,13 +7,13 @@ buildscript {
maven { url 'https://developer.huawei.com/repo/' }
}
dependencies {
- classpath 'com.android.tools.build:gradle:7.2.2'
+ classpath 'com.android.tools.build:gradle:7.3.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
// GMS & Firebase Crashlytics (used by some flavors only)
classpath 'com.google.gms:google-services:4.3.14'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.2'
// HMS (used by some flavors only)
- classpath 'com.huawei.agconnect:agcp:1.5.2.300'
+ classpath 'com.huawei.agconnect:agcp:1.7.2.300'
}
}
@@ -21,7 +21,7 @@ allprojects {
repositories {
google()
mavenCentral()
- maven {url 'https://developer.huawei.com/repo/'}
+ maven { url 'https://developer.huawei.com/repo/' }
}
// gradle.projectsEvaluated {
// tasks.withType(JavaCompile) {
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index 080650cd5..e0e447a3d 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip
diff --git a/fastlane/metadata/android/de/images/phoneScreenshots/1.png b/fastlane/metadata/android/de/images/phoneScreenshots/1.png
index ad9cb124a..ff49b0a6e 100644
Binary files a/fastlane/metadata/android/de/images/phoneScreenshots/1.png and b/fastlane/metadata/android/de/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/el/images/phoneScreenshots/1.png b/fastlane/metadata/android/el/images/phoneScreenshots/1.png
index 54c24fa81..eefd8f1bb 100644
Binary files a/fastlane/metadata/android/el/images/phoneScreenshots/1.png and b/fastlane/metadata/android/el/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/en-US/changelogs/1081.txt b/fastlane/metadata/android/en-US/changelogs/1081.txt
new file mode 100644
index 000000000..e5b6ac839
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/1081.txt
@@ -0,0 +1,5 @@
+In v1.7.1:
+- view your photos with the mosaic layout
+- reverse filters to filter out/in
+- set wallpapers with scroll effect
+Full changelog available on GitHub
\ No newline at end of file
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png
index eac80ee54..da2db556d 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/es-MX/images/phoneScreenshots/1.png b/fastlane/metadata/android/es-MX/images/phoneScreenshots/1.png
index 2615a94a2..7c0e52710 100644
Binary files a/fastlane/metadata/android/es-MX/images/phoneScreenshots/1.png and b/fastlane/metadata/android/es-MX/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/fr/images/phoneScreenshots/1.png b/fastlane/metadata/android/fr/images/phoneScreenshots/1.png
index 985209ece..40ad40622 100644
Binary files a/fastlane/metadata/android/fr/images/phoneScreenshots/1.png and b/fastlane/metadata/android/fr/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/id/images/phoneScreenshots/1.png b/fastlane/metadata/android/id/images/phoneScreenshots/1.png
index 7b5bb8c8d..fc94cf155 100644
Binary files a/fastlane/metadata/android/id/images/phoneScreenshots/1.png and b/fastlane/metadata/android/id/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/it/images/phoneScreenshots/1.png b/fastlane/metadata/android/it/images/phoneScreenshots/1.png
index 9489221ca..d6b796478 100644
Binary files a/fastlane/metadata/android/it/images/phoneScreenshots/1.png and b/fastlane/metadata/android/it/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/ja/images/phoneScreenshots/1.png b/fastlane/metadata/android/ja/images/phoneScreenshots/1.png
index 7113ce3c1..197d4d7c9 100644
Binary files a/fastlane/metadata/android/ja/images/phoneScreenshots/1.png and b/fastlane/metadata/android/ja/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/ko/images/phoneScreenshots/1.png b/fastlane/metadata/android/ko/images/phoneScreenshots/1.png
index af02b0701..dfafa5766 100644
Binary files a/fastlane/metadata/android/ko/images/phoneScreenshots/1.png and b/fastlane/metadata/android/ko/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/nl/images/phoneScreenshots/1.png b/fastlane/metadata/android/nl/images/phoneScreenshots/1.png
index d10712612..5a9beeef1 100644
Binary files a/fastlane/metadata/android/nl/images/phoneScreenshots/1.png and b/fastlane/metadata/android/nl/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/1.png b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/1.png
index 44b10be73..e09364a89 100644
Binary files a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/1.png and b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/ru/images/phoneScreenshots/1.png b/fastlane/metadata/android/ru/images/phoneScreenshots/1.png
index a9aa6c997..59abfcced 100644
Binary files a/fastlane/metadata/android/ru/images/phoneScreenshots/1.png and b/fastlane/metadata/android/ru/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/tr/images/phoneScreenshots/1.png b/fastlane/metadata/android/tr/images/phoneScreenshots/1.png
index b1fe36389..dd9987e27 100644
Binary files a/fastlane/metadata/android/tr/images/phoneScreenshots/1.png and b/fastlane/metadata/android/tr/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/1.png b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/1.png
index 7b493db17..d9e7b1573 100644
Binary files a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/1.png and b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/1.png differ
diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb
index 595392152..4d7abfcab 100644
--- a/lib/l10n/app_de.arb
+++ b/lib/l10n/app_de.arb
@@ -41,6 +41,8 @@
"chipActionGoToAlbumPage": "Anzeigen in Alben",
"chipActionGoToCountryPage": "Anzeigen in Ländern",
"chipActionGoToTagPage": "Zeige in Tags",
+ "chipActionFilterOut": "Filtern ohne",
+ "chipActionFilterIn": "Filtern mit",
"chipActionHide": "Ausblenden",
"chipActionPin": "Oben Anpinnen",
"chipActionUnpin": "Nicht mehr Anpinen",
@@ -88,15 +90,18 @@
"entryInfoActionEditDate": "Datum & Uhrzeit bearbeiten",
"entryInfoActionEditLocation": "Standort bearbeiten",
+ "entryInfoActionEditTitleDescription": "Titel und Beschreibung bearbeiten",
"entryInfoActionEditRating": "Bewertung bearbeiten",
"entryInfoActionEditTags": "Tags bearbeiten",
"entryInfoActionRemoveMetadata": "Metadaten entfernen",
"filterBinLabel": "Papierkorb",
"filterFavouriteLabel": "Favorit",
+ "filterNoDateLabel": "Undatiert",
"filterNoLocationLabel": "Ungeortet",
"filterNoRatingLabel": "Nicht bewertet",
"filterNoTagLabel": "Unmarkiert",
+ "filterNoTitleLabel": "Unbenannt",
"filterOnThisDayLabel": "Am heutigen Tag",
"filterRecentlyAddedLabel": "Kürzlich hinzugefügt",
"filterRatingRejectedLabel": "Verworfen",
@@ -152,9 +157,9 @@
"displayRefreshRatePreferHighest": "Höchste Rate",
"displayRefreshRatePreferLowest": "Niedrigste Rate",
- "slideshowVideoPlaybackSkip": "Überspringen",
- "slideshowVideoPlaybackMuted": "Stumm abspielen",
- "slideshowVideoPlaybackWithSound": "Mit Ton abspielen",
+ "videoPlaybackSkip": "Überspringen",
+ "videoPlaybackMuted": "Stumm abspielen",
+ "videoPlaybackWithSound": "Mit Ton abspielen",
"themeBrightnessLight": "Hell",
"themeBrightnessDark": "Dunkel",
@@ -164,11 +169,15 @@
"viewerTransitionParallax": "Parallaxe",
"viewerTransitionFade": "Ausblenden",
"viewerTransitionZoomIn": "Heranzoomen",
+ "viewerTransitionNone": "Keine",
"wallpaperTargetHome": "Startbildschirm",
"wallpaperTargetLock": "Sperrbildschirm",
"wallpaperTargetHomeLock": "Start- und Sperrbildschirm",
+ "widgetOpenPageHome": "Startseite öffnen",
+ "widgetOpenPageViewer": "Viewer öffnen",
+
"albumTierNew": "Neu",
"albumTierPinned": "Angeheftet",
"albumTierSpecial": "Häufig verwendet",
@@ -284,7 +293,9 @@
"viewDialogSortSectionTitle": "Sortieren",
"viewDialogGroupSectionTitle": "Gruppe",
"viewDialogLayoutSectionTitle": "Layout",
+ "viewDialogReverseSortOrder": "Umgekehrte Sortierung",
+ "tileLayoutMosaic": "Mosaik",
"tileLayoutGrid": "Kacheln",
"tileLayoutList": "Liste",
@@ -387,10 +398,22 @@
"sortByAlbumFileName": "Nach Album & Dateiname",
"sortByRating": "Nach Bewertung",
+ "sortOrderNewestFirst": "Neueste zuerst",
+ "sortOrderOldestFirst": "Älteste zuerst",
+ "sortOrderAtoZ": "A zu Z",
+ "sortOrderZtoA": "Z zu A",
+ "sortOrderHighestFirst": "Höchste zuerst",
+ "sortOrderLowestFirst": "Niedrigste zuerst",
+ "sortOrderLargestFirst": "Größtes zuerst",
+ "sortOrderSmallestFirst": "Kleinste zuerst",
+
"albumGroupTier": "Nach Ebene",
+ "albumGroupType": "Nach Typ",
"albumGroupVolume": "Nach Speichervolumen",
"albumGroupNone": "Nicht gruppieren",
+ "albumMimeTypeMixed": "Gemischt",
+
"albumPickPageTitleCopy": "In Album kopieren",
"albumPickPageTitleExport": "In Album exportieren",
"albumPickPageTitleMove": "Zum Album verschieben",
@@ -424,10 +447,12 @@
"searchPlacesSectionTitle": "Orte",
"searchTagsSectionTitle": "Tags",
"searchRatingSectionTitle": "Bewertungen",
+ "searchMetadataSectionTitle": "Metadaten",
"settingsPageTitle": "Einstellungen",
"settingsSystemDefault": "System",
"settingsDefault": "Standard",
+ "settingsDisabled": "Deaktiviert",
"settingsSearchFieldLabel": "Einstellungen durchsuchen",
"settingsSearchEmpty": "Keine passende Einstellung",
@@ -510,10 +535,9 @@
"settingsSlideshowRepeat": "Wiederholung",
"settingsSlideshowShuffle": "Mischen",
"settingsSlideshowFillScreen": "Bildschirm ausfüllen",
+ "settingsSlideshowAnimatedZoomEffect": "Animierter Zoomeffekt",
"settingsSlideshowTransitionTile": "Übergang",
- "settingsSlideshowTransitionDialogTitle": "Übergang",
"settingsSlideshowIntervalTile": "Intervall",
- "settingsSlideshowIntervalDialogTitle": "Intervall",
"settingsSlideshowVideoPlaybackTile": "Videowiedergabe",
"settingsSlideshowVideoPlaybackDialogTitle": "Videowiedergabe",
@@ -521,7 +545,7 @@
"settingsVideoSectionTitle": "Video",
"settingsVideoShowVideos": "Videos anzeigen",
"settingsVideoEnableHardwareAcceleration": "Hardware-Beschleunigung",
- "settingsVideoEnableAutoPlay": "Automatische Wiedergabe",
+ "settingsVideoAutoPlay": "Automatische Wiedergabe",
"settingsVideoLoopModeTile": "Schleifen-Modus",
"settingsVideoLoopModeDialogTitle": "Schleifen-Modus",
@@ -543,7 +567,6 @@
"settingsVideoControlsTile": "Steuerung",
"settingsVideoControlsPageTitle": "Steuerung",
"settingsVideoButtonsTile": "Schaltflächen",
- "settingsVideoButtonsDialogTitle": "Schaltflächen",
"settingsVideoGestureDoubleTapTogglePlay": "Doppeltippen zum Abspielen/Pausieren",
"settingsVideoGestureSideDoubleTapSeek": "Doppeltippen auf die Bildschirmränder zum Rückwärts-/Vorwärtsspringen",
@@ -576,7 +599,6 @@
"settingsRemoveAnimationsTile": "Animationen entfernen",
"settingsRemoveAnimationsDialogTitle": "Animationen entfernen",
"settingsTimeToTakeActionTile": "Zeit zum Reagieren",
- "settingsTimeToTakeActionDialogTitle": "Zeit zum Reagieren",
"settingsDisplaySectionTitle": "Anzeige",
"settingsThemeBrightnessTile": "Thema",
@@ -598,6 +620,7 @@
"settingsWidgetPageTitle": "Bilderrahmen",
"settingsWidgetShowOutline": "Gliederung",
+ "settingsWidgetOpenPage": "Beim Tippen auf das Widget",
"settingsCollectionTile": "Sammlung",
@@ -606,6 +629,7 @@
"statsTopCountriesSectionTitle": "Top-Länder",
"statsTopPlacesSectionTitle": "Top-Plätze",
"statsTopTagsSectionTitle": "Top-Tags",
+ "statsTopAlbumsSectionTitle": "Top-Alben",
"viewerOpenPanoramaButtonLabel": "ÖFFNE PANORAMA",
"viewerSetWallpaperButtonLabel": "HINTERGRUNDBILD EINSTELLEN",
@@ -650,6 +674,8 @@
"viewerInfoSearchSuggestionResolution": "Auflösung",
"viewerInfoSearchSuggestionRights": "Rechte",
+ "wallpaperUseScrollEffect": "Scroll-Effekt auf dem Startbildschirm verwenden",
+
"tagEditorPageTitle": "Tags bearbeiten",
"tagEditorPageNewTagFieldLabel": "Neuer Tag",
"tagEditorPageAddTagTooltip": "Tag hinzufügen",
diff --git a/lib/l10n/app_el.arb b/lib/l10n/app_el.arb
index f00605985..a9d77008e 100644
--- a/lib/l10n/app_el.arb
+++ b/lib/l10n/app_el.arb
@@ -41,6 +41,8 @@
"chipActionGoToAlbumPage": "Εμφάνιση στα Άλμπουμ",
"chipActionGoToCountryPage": "Εμφάνιση στις χώρες",
"chipActionGoToTagPage": "Εμφάνιση στις ετικέτες",
+ "chipActionFilterOut": "Χωρίς φιλτράρισμα",
+ "chipActionFilterIn": "Με φιλτράρισμα",
"chipActionHide": "Απόκρυψη",
"chipActionPin": "Καρφίτσωμα στην κορυφή",
"chipActionUnpin": "Ξέκαρφίτσωμα από την κορυφή",
@@ -155,9 +157,9 @@
"displayRefreshRatePreferHighest": "Υψηλότερος ρυθμός",
"displayRefreshRatePreferLowest": "Χαμηλότερος ρυθμός",
- "slideshowVideoPlaybackSkip": "Παράλειψη",
- "slideshowVideoPlaybackMuted": "Αναπαραγωγή σε σίγαση",
- "slideshowVideoPlaybackWithSound": "Αναπαραγωγή με ήχο",
+ "videoPlaybackSkip": "Παράλειψη",
+ "videoPlaybackMuted": "Αναπαραγωγή σε σίγαση",
+ "videoPlaybackWithSound": "Αναπαραγωγή με ήχο",
"themeBrightnessLight": "Φωτεινό",
"themeBrightnessDark": "Σκούρο",
@@ -167,11 +169,15 @@
"viewerTransitionParallax": "Παράλλαξη",
"viewerTransitionFade": "Ξεθώριασμα",
"viewerTransitionZoomIn": "Μεγέθυνση",
+ "viewerTransitionNone": "Καμία",
"wallpaperTargetHome": "Αρχική οθόνη",
"wallpaperTargetLock": "Οθόνη κλειδώματος",
"wallpaperTargetHomeLock": "Αρχική οθόνη και οθόνη κλειδώματος",
+ "widgetOpenPageHome": "Άνοιγμα αρχικής σελίδας",
+ "widgetOpenPageViewer": "Άνοιγμα προβολέα αρχείων",
+
"albumTierNew": "Νέα",
"albumTierPinned": "Καρφιτσωμένα",
"albumTierSpecial": "Συστήματος",
@@ -289,6 +295,7 @@
"viewDialogLayoutSectionTitle": "Διαταξη",
"viewDialogReverseSortOrder": "Αντίστροφη σειρά ταξινόμησης",
+ "tileLayoutMosaic": "Ψηφιδωτό",
"tileLayoutGrid": "Πλέγμα",
"tileLayoutList": "Λίστα",
@@ -401,9 +408,12 @@
"sortOrderSmallestFirst": "Τα μικρότερα πρώτα",
"albumGroupTier": "Ανά βαθμίδα",
+ "albumGroupType": "Ανά τύπο",
"albumGroupVolume": "Ανά αποθηκευτική μονάδα",
"albumGroupNone": "Να μην γίνει ομαδοποίηση",
+ "albumMimeTypeMixed": "Μικτα",
+
"albumPickPageTitleCopy": "Αντιγραφή στο άλμπουμ",
"albumPickPageTitleExport": "Εξαγωγή στο άλμπουμ",
"albumPickPageTitleMove": "Μετακίνηση στο άλμπουμ",
@@ -442,6 +452,7 @@
"settingsPageTitle": "Ρυθμισεις",
"settingsSystemDefault": "Σύστημα",
"settingsDefault": "Προεπιλογή",
+ "settingsDisabled": "Απενεργοποιημένο",
"settingsSearchFieldLabel": "Αναζήτηση ρυθμίσεων",
"settingsSearchEmpty": "Δεν υπάρχει αντίστοιχη ρύθμιση",
@@ -524,10 +535,9 @@
"settingsSlideshowRepeat": "Επανάληψη",
"settingsSlideshowShuffle": "Τυχαία σειρά",
"settingsSlideshowFillScreen": "Χρησιμοποίηση πλήρης οθόνης",
+ "settingsSlideshowAnimatedZoomEffect": "Εφέ κινούμενου ζουμ",
"settingsSlideshowTransitionTile": "Μετάβαση",
- "settingsSlideshowTransitionDialogTitle": "Μεταβαση",
"settingsSlideshowIntervalTile": "Διάρκεια",
- "settingsSlideshowIntervalDialogTitle": "Διαρκεια",
"settingsSlideshowVideoPlaybackTile": "Αναπαραγωγή βίντεο",
"settingsSlideshowVideoPlaybackDialogTitle": "Αναπαραγωγη Βιντεο",
@@ -535,7 +545,7 @@
"settingsVideoSectionTitle": "Βιντεο",
"settingsVideoShowVideos": "Εμφάνιση των βίντεο στη συλλογή",
"settingsVideoEnableHardwareAcceleration": "Επιτάχυνση υλισμικού",
- "settingsVideoEnableAutoPlay": "Αυτόματη αναπαραγωγή κατά το άνοιγμα",
+ "settingsVideoAutoPlay": "Αυτόματη αναπαραγωγή κατά το άνοιγμα",
"settingsVideoLoopModeTile": "Επανάληψη αυτόματα στο τέλος κάθε βίντεο",
"settingsVideoLoopModeDialogTitle": "Επαναληψη Αυτοματα στο Τελος Καθε Βιντεο",
@@ -557,7 +567,6 @@
"settingsVideoControlsTile": "Έλεγχος",
"settingsVideoControlsPageTitle": "Ελεγχος",
"settingsVideoButtonsTile": "Κουμπιά",
- "settingsVideoButtonsDialogTitle": "Κουμπια",
"settingsVideoGestureDoubleTapTogglePlay": "Αγγίξτε την οθόνη δύο φορές για αναπαραγωγή/παύση",
"settingsVideoGestureSideDoubleTapSeek": "Αγγίξτε δύο φορές τις άκρες της οθόνης για να πάτε πίσω/εμπρός",
@@ -590,7 +599,6 @@
"settingsRemoveAnimationsTile": "Κατάργηση κινούμενων εικόνων",
"settingsRemoveAnimationsDialogTitle": "Καταργηση Κινουμενων Εικονων",
"settingsTimeToTakeActionTile": "Χρόνος λήψης ενεργειών",
- "settingsTimeToTakeActionDialogTitle": "Χρονος Ληψης Ενεργειων",
"settingsDisplaySectionTitle": "Οθονη",
"settingsThemeBrightnessTile": "Θέμα",
@@ -612,6 +620,7 @@
"settingsWidgetPageTitle": "Κορνιζα",
"settingsWidgetShowOutline": "Περίγραμμα",
+ "settingsWidgetOpenPage": "Όταν πατάτε στο γραφικό στοιχείο",
"settingsCollectionTile": "Συλλογή",
@@ -620,6 +629,7 @@
"statsTopCountriesSectionTitle": "Κορυφαιες Χωρες",
"statsTopPlacesSectionTitle": "Κορυφαια Μερη",
"statsTopTagsSectionTitle": "Κορυφαιες Ετικετες",
+ "statsTopAlbumsSectionTitle": "Κορυφαια Αλμπουμ",
"viewerOpenPanoramaButtonLabel": "Άνοιγμα πανοραμικών",
"viewerSetWallpaperButtonLabel": "ΟΡΙΣΜΟΣ ΤΑΠΕΤΣΑΡΙΑΣ",
@@ -664,6 +674,8 @@
"viewerInfoSearchSuggestionResolution": "Ανάλυση",
"viewerInfoSearchSuggestionRights": "Δικαιώματα",
+ "wallpaperUseScrollEffect": "Εφέ κύλισης στην αρχική οθόνη",
+
"tagEditorPageTitle": "Επεξεργασια Ετικετων",
"tagEditorPageNewTagFieldLabel": "Νέα ετικέτα",
"tagEditorPageAddTagTooltip": "Προσθήκη ετικέτας",
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb
index b563fe5f1..13901991d 100644
--- a/lib/l10n/app_en.arb
+++ b/lib/l10n/app_en.arb
@@ -69,6 +69,8 @@
"chipActionGoToAlbumPage": "Show in Albums",
"chipActionGoToCountryPage": "Show in Countries",
"chipActionGoToTagPage": "Show in Tags",
+ "chipActionFilterOut": "Filter out",
+ "chipActionFilterIn": "Filter in",
"chipActionHide": "Hide",
"chipActionPin": "Pin to top",
"chipActionUnpin": "Unpin from top",
@@ -195,9 +197,9 @@
"displayRefreshRatePreferHighest": "Highest rate",
"displayRefreshRatePreferLowest": "Lowest rate",
- "slideshowVideoPlaybackSkip": "Skip",
- "slideshowVideoPlaybackMuted": "Play muted",
- "slideshowVideoPlaybackWithSound": "Play with sound",
+ "videoPlaybackSkip": "Skip",
+ "videoPlaybackMuted": "Play muted",
+ "videoPlaybackWithSound": "Play with sound",
"themeBrightnessLight": "Light",
"themeBrightnessDark": "Dark",
@@ -207,11 +209,15 @@
"viewerTransitionParallax": "Parallax",
"viewerTransitionFade": "Fade",
"viewerTransitionZoomIn": "Zoom in",
+ "viewerTransitionNone": "None",
"wallpaperTargetHome": "Home screen",
"wallpaperTargetLock": "Lock screen",
"wallpaperTargetHomeLock": "Home and lock screens",
+ "widgetOpenPageHome": "Open home",
+ "widgetOpenPageViewer": "Open viewer",
+
"albumTierNew": "New",
"albumTierPinned": "Pinned",
"albumTierSpecial": "Common",
@@ -419,6 +425,7 @@
"viewDialogLayoutSectionTitle": "Layout",
"viewDialogReverseSortOrder": "Reverse sort order",
+ "tileLayoutMosaic": "Mosaic",
"tileLayoutGrid": "Grid",
"tileLayoutList": "List",
@@ -581,9 +588,12 @@
"sortOrderSmallestFirst": "Smallest first",
"albumGroupTier": "By tier",
+ "albumGroupType": "By type",
"albumGroupVolume": "By storage volume",
"albumGroupNone": "Do not group",
+ "albumMimeTypeMixed": "Mixed",
+
"albumPickPageTitleCopy": "Copy to Album",
"albumPickPageTitleExport": "Export to Album",
"albumPickPageTitleMove": "Move to Album",
@@ -620,8 +630,9 @@
"searchMetadataSectionTitle": "Metadata",
"settingsPageTitle": "Settings",
- "settingsSystemDefault": "System",
+ "settingsSystemDefault": "System default",
"settingsDefault": "Default",
+ "settingsDisabled": "Disabled",
"settingsSearchFieldLabel": "Search settings",
"settingsSearchEmpty": "No matching setting",
@@ -704,10 +715,9 @@
"settingsSlideshowRepeat": "Repeat",
"settingsSlideshowShuffle": "Shuffle",
"settingsSlideshowFillScreen": "Fill screen",
+ "settingsSlideshowAnimatedZoomEffect": "Animated zoom effect",
"settingsSlideshowTransitionTile": "Transition",
- "settingsSlideshowTransitionDialogTitle": "Transition",
"settingsSlideshowIntervalTile": "Interval",
- "settingsSlideshowIntervalDialogTitle": "Interval",
"settingsSlideshowVideoPlaybackTile": "Video playback",
"settingsSlideshowVideoPlaybackDialogTitle": "Video Playback",
@@ -715,7 +725,7 @@
"settingsVideoSectionTitle": "Video",
"settingsVideoShowVideos": "Show videos",
"settingsVideoEnableHardwareAcceleration": "Hardware acceleration",
- "settingsVideoEnableAutoPlay": "Auto play",
+ "settingsVideoAutoPlay": "Auto play",
"settingsVideoLoopModeTile": "Loop mode",
"settingsVideoLoopModeDialogTitle": "Loop Mode",
@@ -737,7 +747,6 @@
"settingsVideoControlsTile": "Controls",
"settingsVideoControlsPageTitle": "Controls",
"settingsVideoButtonsTile": "Buttons",
- "settingsVideoButtonsDialogTitle": "Buttons",
"settingsVideoGestureDoubleTapTogglePlay": "Double tap to play/pause",
"settingsVideoGestureSideDoubleTapSeek": "Double tap on screen edges to seek backward/forward",
@@ -770,7 +779,6 @@
"settingsRemoveAnimationsTile": "Remove animations",
"settingsRemoveAnimationsDialogTitle": "Remove Animations",
"settingsTimeToTakeActionTile": "Time to take action",
- "settingsTimeToTakeActionDialogTitle": "Time to Take Action",
"settingsDisplaySectionTitle": "Display",
"settingsThemeBrightnessTile": "Theme",
@@ -792,6 +800,7 @@
"settingsWidgetPageTitle": "Photo Frame",
"settingsWidgetShowOutline": "Outline",
+ "settingsWidgetOpenPage": "When tapping on the widget",
"settingsCollectionTile": "Collection",
@@ -805,6 +814,7 @@
"statsTopCountriesSectionTitle": "Top Countries",
"statsTopPlacesSectionTitle": "Top Places",
"statsTopTagsSectionTitle": "Top Tags",
+ "statsTopAlbumsSectionTitle": "Top Albums",
"viewerOpenPanoramaButtonLabel": "OPEN PANORAMA",
"viewerSetWallpaperButtonLabel": "SET WALLPAPER",
@@ -849,6 +859,8 @@
"viewerInfoSearchSuggestionResolution": "Resolution",
"viewerInfoSearchSuggestionRights": "Rights",
+ "wallpaperUseScrollEffect": "Use scroll effect on home screen",
+
"tagEditorPageTitle": "Edit Tags",
"tagEditorPageNewTagFieldLabel": "New tag",
"tagEditorPageAddTagTooltip": "Add tag",
diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb
index e84adbb27..936e600f3 100644
--- a/lib/l10n/app_es.arb
+++ b/lib/l10n/app_es.arb
@@ -151,9 +151,9 @@
"displayRefreshRatePreferHighest": "Alta tasa",
"displayRefreshRatePreferLowest": "Baja tasa",
- "slideshowVideoPlaybackSkip": "Saltear",
- "slideshowVideoPlaybackMuted": "Reproducir sin sonido",
- "slideshowVideoPlaybackWithSound": "Reproducir con sonido",
+ "videoPlaybackSkip": "Saltear",
+ "videoPlaybackMuted": "Reproducir sin sonido",
+ "videoPlaybackWithSound": "Reproducir con sonido",
"themeBrightnessLight": "Claro",
"themeBrightnessDark": "Obscuro",
@@ -509,9 +509,7 @@
"settingsSlideshowShuffle": "Mezclar",
"settingsSlideshowFillScreen": "Llenar pantalla",
"settingsSlideshowTransitionTile": "Transición",
- "settingsSlideshowTransitionDialogTitle": "Transición",
"settingsSlideshowIntervalTile": "Intervalo",
- "settingsSlideshowIntervalDialogTitle": "Intervalo",
"settingsSlideshowVideoPlaybackTile": "Reproducción de video",
"settingsSlideshowVideoPlaybackDialogTitle": "Reproducción de video",
@@ -519,7 +517,7 @@
"settingsVideoSectionTitle": "Video",
"settingsVideoShowVideos": "Mostrar videos",
"settingsVideoEnableHardwareAcceleration": "Aceleración por hardware",
- "settingsVideoEnableAutoPlay": "Reproducción automática",
+ "settingsVideoAutoPlay": "Reproducción automática",
"settingsVideoLoopModeTile": "Modo bucle",
"settingsVideoLoopModeDialogTitle": "Modo bucle",
@@ -541,7 +539,6 @@
"settingsVideoControlsTile": "Controles",
"settingsVideoControlsPageTitle": "Controles",
"settingsVideoButtonsTile": "Botones",
- "settingsVideoButtonsDialogTitle": "Botones",
"settingsVideoGestureDoubleTapTogglePlay": "Doble toque para reproducir/pausar",
"settingsVideoGestureSideDoubleTapSeek": "Doble toque en los bordes de la pantalla para buscar atrás/adelante",
@@ -574,7 +571,6 @@
"settingsRemoveAnimationsTile": "Remover animaciones",
"settingsRemoveAnimationsDialogTitle": "Remover animaciones",
"settingsTimeToTakeActionTile": "Retraso para ejecutar una acción",
- "settingsTimeToTakeActionDialogTitle": "Retraso para ejecutar una acción",
"settingsDisplaySectionTitle": "Pantalla",
"settingsThemeBrightnessTile": "Tema",
diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb
index 67fff4117..4e397da56 100644
--- a/lib/l10n/app_fr.arb
+++ b/lib/l10n/app_fr.arb
@@ -41,6 +41,8 @@
"chipActionGoToAlbumPage": "Afficher dans Albums",
"chipActionGoToCountryPage": "Afficher dans Pays",
"chipActionGoToTagPage": "Afficher dans Libellés",
+ "chipActionFilterOut": "Exclure",
+ "chipActionFilterIn": "Inclure",
"chipActionHide": "Masquer",
"chipActionPin": "Épingler",
"chipActionUnpin": "Retirer",
@@ -155,9 +157,9 @@
"displayRefreshRatePreferHighest": "Fréquence maximale",
"displayRefreshRatePreferLowest": "Fréquence minimale",
- "slideshowVideoPlaybackSkip": "Passer",
- "slideshowVideoPlaybackMuted": "Jouer sans son",
- "slideshowVideoPlaybackWithSound": "Jouer avec son",
+ "videoPlaybackSkip": "Passer",
+ "videoPlaybackMuted": "Jouer sans son",
+ "videoPlaybackWithSound": "Jouer avec son",
"themeBrightnessLight": "Clair",
"themeBrightnessDark": "Sombre",
@@ -167,11 +169,15 @@
"viewerTransitionParallax": "Parallaxe",
"viewerTransitionFade": "Fondu",
"viewerTransitionZoomIn": "Zoom",
+ "viewerTransitionNone": "Aucune",
"wallpaperTargetHome": "Écran d’accueil",
"wallpaperTargetLock": "Écran de verrouillage",
"wallpaperTargetHomeLock": "Écrans accueil et verrouillage",
+ "widgetOpenPageHome": "Ouvrir la page d’accueil",
+ "widgetOpenPageViewer": "Ouvrir la visionneuse",
+
"albumTierNew": "Nouveaux",
"albumTierPinned": "Épinglés",
"albumTierSpecial": "Standards",
@@ -289,6 +295,7 @@
"viewDialogLayoutSectionTitle": "Vue",
"viewDialogReverseSortOrder": "Inverser l’ordre",
+ "tileLayoutMosaic": "Mosaïque",
"tileLayoutGrid": "Grille",
"tileLayoutList": "Liste",
@@ -401,9 +408,12 @@
"sortOrderSmallestFirst": "Moins larges d’abord",
"albumGroupTier": "par importance",
+ "albumGroupType": "par type",
"albumGroupVolume": "par volume de stockage",
"albumGroupNone": "ne pas grouper",
+ "albumMimeTypeMixed": "Mixte",
+
"albumPickPageTitleCopy": "Copie",
"albumPickPageTitleExport": "Export",
"albumPickPageTitleMove": "Déplacement",
@@ -442,6 +452,7 @@
"settingsPageTitle": "Réglages",
"settingsSystemDefault": "Système",
"settingsDefault": "Par défaut",
+ "settingsDisabled": "Désactivé",
"settingsSearchFieldLabel": "Recherche de réglages",
"settingsSearchEmpty": "Aucun réglage correspondant",
@@ -524,10 +535,9 @@
"settingsSlideshowRepeat": "Répéter",
"settingsSlideshowShuffle": "Aléatoire",
"settingsSlideshowFillScreen": "Remplir l’écran",
+ "settingsSlideshowAnimatedZoomEffect": "Effet de zoom animé",
"settingsSlideshowTransitionTile": "Transition",
- "settingsSlideshowTransitionDialogTitle": "Transition",
"settingsSlideshowIntervalTile": "Intervalle",
- "settingsSlideshowIntervalDialogTitle": "Intervalle",
"settingsSlideshowVideoPlaybackTile": "Lecture de vidéos",
"settingsSlideshowVideoPlaybackDialogTitle": "Lecture de vidéos",
@@ -535,7 +545,7 @@
"settingsVideoSectionTitle": "Vidéo",
"settingsVideoShowVideos": "Afficher les vidéos",
"settingsVideoEnableHardwareAcceleration": "Accélération matérielle",
- "settingsVideoEnableAutoPlay": "Lecture automatique",
+ "settingsVideoAutoPlay": "Lecture automatique",
"settingsVideoLoopModeTile": "Lecture répétée",
"settingsVideoLoopModeDialogTitle": "Lecture répétée",
@@ -557,7 +567,6 @@
"settingsVideoControlsTile": "Contrôles",
"settingsVideoControlsPageTitle": "Contrôles",
"settingsVideoButtonsTile": "Boutons",
- "settingsVideoButtonsDialogTitle": "Boutons",
"settingsVideoGestureDoubleTapTogglePlay": "Appuyer deux fois pour lire ou mettre en pause",
"settingsVideoGestureSideDoubleTapSeek": "Appuyer deux fois sur les bords de l’écran pour reculer ou avancer",
@@ -590,7 +599,6 @@
"settingsRemoveAnimationsTile": "Suppression des animations",
"settingsRemoveAnimationsDialogTitle": "Suppression des animations",
"settingsTimeToTakeActionTile": "Délai pour effectuer une action",
- "settingsTimeToTakeActionDialogTitle": "Délai pour effectuer une action",
"settingsDisplaySectionTitle": "Affichage",
"settingsThemeBrightnessTile": "Thème",
@@ -612,6 +620,7 @@
"settingsWidgetPageTitle": "Cadre photo",
"settingsWidgetShowOutline": "Contours",
+ "settingsWidgetOpenPage": "Quand vous appuyez sur le widget",
"settingsCollectionTile": "Collection",
@@ -620,6 +629,7 @@
"statsTopCountriesSectionTitle": "Top pays",
"statsTopPlacesSectionTitle": "Top lieux",
"statsTopTagsSectionTitle": "Top libellés",
+ "statsTopAlbumsSectionTitle": "Top albums",
"viewerOpenPanoramaButtonLabel": "OUVRIR LE PANORAMA",
"viewerSetWallpaperButtonLabel": "APPLIQUER",
@@ -664,6 +674,8 @@
"viewerInfoSearchSuggestionResolution": "Résolution",
"viewerInfoSearchSuggestionRights": "Droits",
+ "wallpaperUseScrollEffect": "Utiliser l’effet de défilement sur l’écran d’accueil",
+
"tagEditorPageTitle": "Modifier les libellés",
"tagEditorPageNewTagFieldLabel": "Nouveau libellé",
"tagEditorPageAddTagTooltip": "Ajouter le libellé",
diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb
index 50b866d50..9f8c92407 100644
--- a/lib/l10n/app_id.arb
+++ b/lib/l10n/app_id.arb
@@ -155,9 +155,9 @@
"displayRefreshRatePreferHighest": "Penyegaran tertinggi",
"displayRefreshRatePreferLowest": "Penyegaran terendah",
- "slideshowVideoPlaybackSkip": "Lewati",
- "slideshowVideoPlaybackMuted": "Mainkan bisu",
- "slideshowVideoPlaybackWithSound": "Mainkan dengan suara",
+ "videoPlaybackSkip": "Lewati",
+ "videoPlaybackMuted": "Mainkan bisu",
+ "videoPlaybackWithSound": "Mainkan dengan suara",
"themeBrightnessLight": "Terang",
"themeBrightnessDark": "Gelap",
@@ -525,9 +525,7 @@
"settingsSlideshowShuffle": "Acak",
"settingsSlideshowFillScreen": "Isi layar",
"settingsSlideshowTransitionTile": "Transisi",
- "settingsSlideshowTransitionDialogTitle": "Transisi",
"settingsSlideshowIntervalTile": "Interval",
- "settingsSlideshowIntervalDialogTitle": "Interval",
"settingsSlideshowVideoPlaybackTile": "Putaran ulang video",
"settingsSlideshowVideoPlaybackDialogTitle": "Putaran Ulang Video",
@@ -535,7 +533,7 @@
"settingsVideoSectionTitle": "Video",
"settingsVideoShowVideos": "Tampilkan video",
"settingsVideoEnableHardwareAcceleration": "Akselerasi perangkat keras",
- "settingsVideoEnableAutoPlay": "Putar otomatis",
+ "settingsVideoAutoPlay": "Putar otomatis",
"settingsVideoLoopModeTile": "Putar ulang",
"settingsVideoLoopModeDialogTitle": "Putar Ulang",
@@ -557,7 +555,6 @@
"settingsVideoControlsTile": "Kontrol",
"settingsVideoControlsPageTitle": "Kontrol",
"settingsVideoButtonsTile": "Tombol",
- "settingsVideoButtonsDialogTitle": "Tombol",
"settingsVideoGestureDoubleTapTogglePlay": "Ketuk dua kali untuk mainkan/hentikan",
"settingsVideoGestureSideDoubleTapSeek": "Ketuk dua kali di tepi layar untuk mencari kebelakang/kedepan",
@@ -590,7 +587,6 @@
"settingsRemoveAnimationsTile": "Hapus animasi",
"settingsRemoveAnimationsDialogTitle": "Hapus Animasi",
"settingsTimeToTakeActionTile": "Waktu untuk mengambil tindakan",
- "settingsTimeToTakeActionDialogTitle": "Saatnya Bertindak",
"settingsDisplaySectionTitle": "Tampilan",
"settingsThemeBrightnessTile": "Tema",
diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb
index c2fe6374a..1e68520d5 100644
--- a/lib/l10n/app_it.arb
+++ b/lib/l10n/app_it.arb
@@ -40,7 +40,9 @@
"chipActionDelete": "Elimina",
"chipActionGoToAlbumPage": "Mostra negli album",
"chipActionGoToCountryPage": "Mostra nei Paesi",
- "chipActionGoToTagPage": "Mostra nelle etichette",
+ "chipActionGoToTagPage": "Mostra nelle Etichette",
+ "chipActionFilterOut": "Escludi",
+ "chipActionFilterIn": "Includi",
"chipActionHide": "Nascondi",
"chipActionPin": "Fissa in alto",
"chipActionUnpin": "Rimuovi dall’alto",
@@ -155,9 +157,9 @@
"displayRefreshRatePreferHighest": "Frequenza massima",
"displayRefreshRatePreferLowest": "Frequenza minima",
- "slideshowVideoPlaybackSkip": "Salta",
- "slideshowVideoPlaybackMuted": "Riproduci senza audio",
- "slideshowVideoPlaybackWithSound": "Riproduci con audio",
+ "videoPlaybackSkip": "Salta",
+ "videoPlaybackMuted": "Riproduci senza audio",
+ "videoPlaybackWithSound": "Riproduci con audio",
"themeBrightnessLight": "Chiaro",
"themeBrightnessDark": "Scuro",
@@ -167,11 +169,15 @@
"viewerTransitionParallax": "Parallasse",
"viewerTransitionFade": "Dissolvenza",
"viewerTransitionZoomIn": "Ingrandisci",
+ "viewerTransitionNone": "Nessuna",
"wallpaperTargetHome": "Schermata iniziale",
"wallpaperTargetLock": "Schermata di blocco",
"wallpaperTargetHomeLock": "Schermata iniziale e di blocco",
+ "widgetOpenPageHome": "Apri pagina iniziale",
+ "widgetOpenPageViewer": "Apri visualizzazione",
+
"albumTierNew": "Nuovi",
"albumTierPinned": "Fissati",
"albumTierSpecial": "Frequenti",
@@ -289,6 +295,7 @@
"viewDialogLayoutSectionTitle": "Layout",
"viewDialogReverseSortOrder": "Inverti ordinamento",
+ "tileLayoutMosaic": "Mosaico",
"tileLayoutGrid": "Griglia",
"tileLayoutList": "Lista",
@@ -390,6 +397,7 @@
"sortBySize": "Per dimensione",
"sortByAlbumFileName": "Per album e nome del file",
"sortByRating": "Per valutazione",
+
"sortOrderNewestFirst": "Prima i più nuovi",
"sortOrderOldestFirst": "Prima i più vecchi",
"sortOrderAtoZ": "Dalla A alla Z",
@@ -400,9 +408,12 @@
"sortOrderSmallestFirst": "Prima i più piccoli",
"albumGroupTier": "Per importanza",
+ "albumGroupType": "Per tipo",
"albumGroupVolume": "Per volume di archiviazione",
"albumGroupNone": "Non raggruppare",
+ "albumMimeTypeMixed": "Misto",
+
"albumPickPageTitleCopy": "Copia",
"albumPickPageTitleExport": "Esporta",
"albumPickPageTitleMove": "Sposta",
@@ -441,6 +452,7 @@
"settingsPageTitle": "Impostazioni",
"settingsSystemDefault": "Sistema",
"settingsDefault": "Predefinite",
+ "settingsDisabled": "Disabilitato",
"settingsSearchFieldLabel": "Ricerca impostazioni",
"settingsSearchEmpty": "Nessuna impostazione corrispondente",
@@ -523,10 +535,9 @@
"settingsSlideshowRepeat": "Ripeti",
"settingsSlideshowShuffle": "Ordine casuale",
"settingsSlideshowFillScreen": "Riempi schermo",
+ "settingsSlideshowAnimatedZoomEffect": "Effetto ingrandimento animato",
"settingsSlideshowTransitionTile": "Transizione",
- "settingsSlideshowTransitionDialogTitle": "Transizione",
"settingsSlideshowIntervalTile": "Intervallo",
- "settingsSlideshowIntervalDialogTitle": "Intervallo",
"settingsSlideshowVideoPlaybackTile": "Riproduzione video",
"settingsSlideshowVideoPlaybackDialogTitle": "Riproduzione video",
@@ -534,7 +545,7 @@
"settingsVideoSectionTitle": "Video",
"settingsVideoShowVideos": "Mostra video",
"settingsVideoEnableHardwareAcceleration": "Accelerazione hardware",
- "settingsVideoEnableAutoPlay": "Riproduzione automatica",
+ "settingsVideoAutoPlay": "Riproduzione automatica",
"settingsVideoLoopModeTile": "Modalità loop",
"settingsVideoLoopModeDialogTitle": "Modalità loop",
@@ -556,7 +567,6 @@
"settingsVideoControlsTile": "Controlli",
"settingsVideoControlsPageTitle": "Controlli",
"settingsVideoButtonsTile": "Pulsanti",
- "settingsVideoButtonsDialogTitle": "Pulsanti",
"settingsVideoGestureDoubleTapTogglePlay": "Doppio tocco per play/pausa",
"settingsVideoGestureSideDoubleTapSeek": "Doppio tocco sui bordi dello schermo per cercare avanti/indietro",
@@ -589,7 +599,6 @@
"settingsRemoveAnimationsTile": "Rimuovi animazioni",
"settingsRemoveAnimationsDialogTitle": "Rimuovi animazioni",
"settingsTimeToTakeActionTile": "Tempo di reazione",
- "settingsTimeToTakeActionDialogTitle": "Tempo di reazione",
"settingsDisplaySectionTitle": "Schermo",
"settingsThemeBrightnessTile": "Tema",
@@ -611,6 +620,7 @@
"settingsWidgetPageTitle": "Cornice foto",
"settingsWidgetShowOutline": "Contorno",
+ "settingsWidgetOpenPage": "Se tocchi il widget",
"settingsCollectionTile": "Collezione",
@@ -619,6 +629,7 @@
"statsTopCountriesSectionTitle": "Paesi più frequenti",
"statsTopPlacesSectionTitle": "Luoghi più frequenti",
"statsTopTagsSectionTitle": "Etichette più frequenti",
+ "statsTopAlbumsSectionTitle": "Album più frequenti",
"viewerOpenPanoramaButtonLabel": "APRI PANORAMA",
"viewerSetWallpaperButtonLabel": "IMPOSTA SFONDO",
@@ -663,6 +674,8 @@
"viewerInfoSearchSuggestionResolution": "Risoluzione",
"viewerInfoSearchSuggestionRights": "Diritti",
+ "wallpaperUseScrollEffect": "Usa effetto di scorrimento nella schermata iniziale",
+
"tagEditorPageTitle": "Modifica etichette",
"tagEditorPageNewTagFieldLabel": "Nuova etichetta",
"tagEditorPageAddTagTooltip": "Aggiungi etichetta",
diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb
index a65a58789..5304060f4 100644
--- a/lib/l10n/app_ja.arb
+++ b/lib/l10n/app_ja.arb
@@ -151,9 +151,9 @@
"displayRefreshRatePreferHighest": "高レート",
"displayRefreshRatePreferLowest": "低レート",
- "slideshowVideoPlaybackSkip": "スキップ",
- "slideshowVideoPlaybackMuted": "ミュート再生",
- "slideshowVideoPlaybackWithSound": "音声あり再生",
+ "videoPlaybackSkip": "スキップ",
+ "videoPlaybackMuted": "ミュート再生",
+ "videoPlaybackWithSound": "音声あり再生",
"themeBrightnessLight": "ライト",
"themeBrightnessDark": "ダーク",
@@ -508,9 +508,7 @@
"settingsSlideshowShuffle": "シャッフル",
"settingsSlideshowFillScreen": "画面いっぱいに表示",
"settingsSlideshowTransitionTile": "トランジション",
- "settingsSlideshowTransitionDialogTitle": "トランジション",
"settingsSlideshowIntervalTile": "間隔",
- "settingsSlideshowIntervalDialogTitle": "間隔",
"settingsSlideshowVideoPlaybackTile": "動画を再生",
"settingsSlideshowVideoPlaybackDialogTitle": "動画再生",
@@ -518,7 +516,7 @@
"settingsVideoSectionTitle": "動画",
"settingsVideoShowVideos": "動画を表示",
"settingsVideoEnableHardwareAcceleration": "ハードウェア アクセラレーション",
- "settingsVideoEnableAutoPlay": "自動再生",
+ "settingsVideoAutoPlay": "自動再生",
"settingsVideoLoopModeTile": "ループ モード",
"settingsVideoLoopModeDialogTitle": "ループ モード",
@@ -540,7 +538,6 @@
"settingsVideoControlsTile": "操作",
"settingsVideoControlsPageTitle": "操作",
"settingsVideoButtonsTile": "ボタン",
- "settingsVideoButtonsDialogTitle": "ボタン",
"settingsVideoGestureDoubleTapTogglePlay": "2回タップして再生/一時停止",
"settingsVideoGestureSideDoubleTapSeek": "画面の角を2回タップして早送り/早戻し",
@@ -573,7 +570,6 @@
"settingsRemoveAnimationsTile": "アニメーションの削除",
"settingsRemoveAnimationsDialogTitle": "アニメーションの削除",
"settingsTimeToTakeActionTile": "操作までの時間",
- "settingsTimeToTakeActionDialogTitle": "操作までの時間",
"settingsDisplaySectionTitle": "ディスプレイ",
"settingsThemeBrightnessTile": "テーマ",
diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb
index ea9379b64..64dc52ed8 100644
--- a/lib/l10n/app_ko.arb
+++ b/lib/l10n/app_ko.arb
@@ -41,6 +41,8 @@
"chipActionGoToAlbumPage": "앨범 페이지에서 보기",
"chipActionGoToCountryPage": "국가 페이지에서 보기",
"chipActionGoToTagPage": "태그 페이지에서 보기",
+ "chipActionFilterOut": "제외하기",
+ "chipActionFilterIn": "포함시키기",
"chipActionHide": "숨기기",
"chipActionPin": "고정",
"chipActionUnpin": "고정 해제",
@@ -155,9 +157,9 @@
"displayRefreshRatePreferHighest": "가장 높은 재생률",
"displayRefreshRatePreferLowest": "가장 낮은 재생률",
- "slideshowVideoPlaybackSkip": "생략",
- "slideshowVideoPlaybackMuted": "음소거 재생",
- "slideshowVideoPlaybackWithSound": "일반 재생",
+ "videoPlaybackSkip": "생략",
+ "videoPlaybackMuted": "음소거 재생",
+ "videoPlaybackWithSound": "일반 재생",
"themeBrightnessLight": "라이트",
"themeBrightnessDark": "다크",
@@ -167,11 +169,15 @@
"viewerTransitionParallax": "시차",
"viewerTransitionFade": "페이드",
"viewerTransitionZoomIn": "확대",
+ "viewerTransitionNone": "없음",
"wallpaperTargetHome": "홈 화면",
"wallpaperTargetLock": "잠금화면",
"wallpaperTargetHomeLock": "홈 및 잠금화면",
+ "widgetOpenPageHome": "홈 열기",
+ "widgetOpenPageViewer": "뷰어 열기",
+
"albumTierNew": "신규",
"albumTierPinned": "고정",
"albumTierSpecial": "기본",
@@ -289,6 +295,7 @@
"viewDialogLayoutSectionTitle": "배치",
"viewDialogReverseSortOrder": "순서를 뒤바꾸기",
+ "tileLayoutMosaic": "모자이크",
"tileLayoutGrid": "바둑판",
"tileLayoutList": "목록",
@@ -401,9 +408,12 @@
"sortOrderSmallestFirst": "작은 파일순",
"albumGroupTier": "단계별로",
+ "albumGroupType": "유형별로",
"albumGroupVolume": "저장공간별로",
"albumGroupNone": "묶음 없음",
+ "albumMimeTypeMixed": "혼합",
+
"albumPickPageTitleCopy": "앨범으로 복사",
"albumPickPageTitleExport": "앨범으로 내보내기",
"albumPickPageTitleMove": "앨범으로 이동",
@@ -440,8 +450,9 @@
"searchMetadataSectionTitle": "메타데이터",
"settingsPageTitle": "설정",
- "settingsSystemDefault": "시스템",
+ "settingsSystemDefault": "시스템 기본값",
"settingsDefault": "기본",
+ "settingsDisabled": "사용 안함",
"settingsSearchFieldLabel": "설정 검색",
"settingsSearchEmpty": "결과가 없습니다",
@@ -524,10 +535,9 @@
"settingsSlideshowRepeat": "반복",
"settingsSlideshowShuffle": "순서섞기",
"settingsSlideshowFillScreen": "화면 채우기",
+ "settingsSlideshowAnimatedZoomEffect": "애니메이션 확대/축소 효과",
"settingsSlideshowTransitionTile": "전환 효과",
- "settingsSlideshowTransitionDialogTitle": "전환 효과",
"settingsSlideshowIntervalTile": "교체 주기",
- "settingsSlideshowIntervalDialogTitle": "교체 주기",
"settingsSlideshowVideoPlaybackTile": "동영상 재생",
"settingsSlideshowVideoPlaybackDialogTitle": "동영상 재생",
@@ -535,7 +545,7 @@
"settingsVideoSectionTitle": "동영상",
"settingsVideoShowVideos": "미디어에 동영상 표시",
"settingsVideoEnableHardwareAcceleration": "하드웨어 가속",
- "settingsVideoEnableAutoPlay": "자동 재생",
+ "settingsVideoAutoPlay": "자동 재생",
"settingsVideoLoopModeTile": "반복 모드",
"settingsVideoLoopModeDialogTitle": "반복 모드",
@@ -557,7 +567,6 @@
"settingsVideoControlsTile": "제어",
"settingsVideoControlsPageTitle": "제어",
"settingsVideoButtonsTile": "버튼",
- "settingsVideoButtonsDialogTitle": "버튼",
"settingsVideoGestureDoubleTapTogglePlay": "두 번 탭해서 재생이나 일시정지하기",
"settingsVideoGestureSideDoubleTapSeek": "화면 측면에서 두 번 탭해서 앞뒤로 가기",
@@ -590,7 +599,6 @@
"settingsRemoveAnimationsTile": "애니메이션 삭제",
"settingsRemoveAnimationsDialogTitle": "애니메이션 삭제",
"settingsTimeToTakeActionTile": "액션 취하기 전 대기 시간",
- "settingsTimeToTakeActionDialogTitle": "액션 취하기 전 대기 시간",
"settingsDisplaySectionTitle": "디스플레이",
"settingsThemeBrightnessTile": "테마",
@@ -612,6 +620,7 @@
"settingsWidgetPageTitle": "사진 액자",
"settingsWidgetShowOutline": "윤곽",
+ "settingsWidgetOpenPage": "위젯을 탭하면",
"settingsCollectionTile": "미디어",
@@ -620,6 +629,7 @@
"statsTopCountriesSectionTitle": "국가 랭킹",
"statsTopPlacesSectionTitle": "장소 랭킹",
"statsTopTagsSectionTitle": "태그 랭킹",
+ "statsTopAlbumsSectionTitle": "앨범 랭킹",
"viewerOpenPanoramaButtonLabel": "파노라마 열기",
"viewerSetWallpaperButtonLabel": "설정",
@@ -664,6 +674,8 @@
"viewerInfoSearchSuggestionResolution": "해상도",
"viewerInfoSearchSuggestionRights": "권리",
+ "wallpaperUseScrollEffect": "홈 화면에 스크롤 효과 사용",
+
"tagEditorPageTitle": "태그 수정",
"tagEditorPageNewTagFieldLabel": "새 태그",
"tagEditorPageAddTagTooltip": "태그 추가",
diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb
index 4105b5218..2433598cd 100644
--- a/lib/l10n/app_nl.arb
+++ b/lib/l10n/app_nl.arb
@@ -41,6 +41,8 @@
"chipActionGoToAlbumPage": "Tonen Albums",
"chipActionGoToCountryPage": "Tonen in Landen",
"chipActionGoToTagPage": "Tonen in Labels",
+ "chipActionFilterOut": "Uitfilteren",
+ "chipActionFilterIn": "Infilteren",
"chipActionHide": "Verbergen",
"chipActionPin": "Bovenaan pinnen",
"chipActionUnpin": "Unpinnen",
@@ -155,9 +157,9 @@
"displayRefreshRatePreferHighest": "Hoogste waardering",
"displayRefreshRatePreferLowest": "Laagste waardering",
- "slideshowVideoPlaybackSkip": "Overslaan",
- "slideshowVideoPlaybackMuted": "Gedempte afspelen",
- "slideshowVideoPlaybackWithSound": "Met geluid afspelen",
+ "videoPlaybackSkip": "Overslaan",
+ "videoPlaybackMuted": "Gedempte afspelen",
+ "videoPlaybackWithSound": "Met geluid afspelen",
"themeBrightnessLight": "Licht",
"themeBrightnessDark": "Donker",
@@ -167,11 +169,15 @@
"viewerTransitionParallax": "Parallax",
"viewerTransitionFade": "Vervagen",
"viewerTransitionZoomIn": "Inzoomen",
+ "viewerTransitionNone": "Geen",
"wallpaperTargetHome": "Home scherm",
"wallpaperTargetLock": "Vergrendel scherm",
"wallpaperTargetHomeLock": "Home and Vergrendel schermen",
+ "widgetOpenPageHome": "Open startscherm",
+ "widgetOpenPageViewer": "Open viewer",
+
"albumTierNew": "Nieuw",
"albumTierPinned": "Gepint",
"albumTierSpecial": "Veelgebruikt",
@@ -289,6 +295,7 @@
"viewDialogLayoutSectionTitle": "Layout",
"viewDialogReverseSortOrder": "Draai sorteerrichting om",
+ "tileLayoutMosaic": "Mozaïek",
"tileLayoutGrid": "Raster",
"tileLayoutList": "Lijst",
@@ -401,9 +408,12 @@
"sortOrderSmallestFirst": "Kleinste eerst",
"albumGroupTier": "Op rang",
+ "albumGroupType": "Op type",
"albumGroupVolume": "Op opslagvolume",
"albumGroupNone": "Niet groeperen",
+ "albumMimeTypeMixed": "Gemengd",
+
"albumPickPageTitleCopy": "Kopieer naar Album",
"albumPickPageTitleExport": "Exporteer naar Album",
"albumPickPageTitleMove": "Verplaats naar Album",
@@ -442,6 +452,7 @@
"settingsPageTitle": "Instellingen",
"settingsSystemDefault": "Systeem",
"settingsDefault": "Standaard",
+ "settingsDisabled": "Uitgeschakeld",
"settingsSearchFieldLabel": "Instellingen doorzoeken",
"settingsSearchEmpty": "Geen instellingen gevonden",
@@ -524,10 +535,9 @@
"settingsSlideshowRepeat": "Herhalen",
"settingsSlideshowShuffle": "Shuffle",
"settingsSlideshowFillScreen": "Volledig scherm",
+ "settingsSlideshowAnimatedZoomEffect": "Geanimeerd zoomeffect",
"settingsSlideshowTransitionTile": "Overgang",
- "settingsSlideshowTransitionDialogTitle": "Overgang",
"settingsSlideshowIntervalTile": "Interval",
- "settingsSlideshowIntervalDialogTitle": "Interval",
"settingsSlideshowVideoPlaybackTile": "Video afspelen",
"settingsSlideshowVideoPlaybackDialogTitle": "Video afspelen",
@@ -535,7 +545,7 @@
"settingsVideoSectionTitle": "Video",
"settingsVideoShowVideos": "Videos",
"settingsVideoEnableHardwareAcceleration": "Hardware acceleratie",
- "settingsVideoEnableAutoPlay": "Automatisch afspelen",
+ "settingsVideoAutoPlay": "Automatisch afspelen",
"settingsVideoLoopModeTile": "Herhaald afspelen",
"settingsVideoLoopModeDialogTitle": "Herhaald afspelen",
@@ -557,7 +567,6 @@
"settingsVideoControlsTile": "Bediening",
"settingsVideoControlsPageTitle": "Bediening",
"settingsVideoButtonsTile": "Knoppen",
- "settingsVideoButtonsDialogTitle": "Knoppen",
"settingsVideoGestureDoubleTapTogglePlay": "Dubbeltik om te spelen/pauzeren",
"settingsVideoGestureSideDoubleTapSeek": "Dubbeltik op schermranden om achteruit/vooruit te zoeken",
@@ -590,7 +599,6 @@
"settingsRemoveAnimationsTile": "Animaties verwijderen",
"settingsRemoveAnimationsDialogTitle": "Animaties verwijderen",
"settingsTimeToTakeActionTile": "Tijd om actie te ondernemen",
- "settingsTimeToTakeActionDialogTitle": "Tijd om actie te ondernemen",
"settingsDisplaySectionTitle": "Scherm",
"settingsThemeBrightnessTile": "Thema",
@@ -612,6 +620,7 @@
"settingsWidgetPageTitle": "Foto Lijstje",
"settingsWidgetShowOutline": "Contour",
+ "settingsWidgetOpenPage": "Wanneer u op de widget tikt",
"settingsCollectionTile": "Verzameling",
@@ -620,6 +629,7 @@
"statsTopCountriesSectionTitle": "Top Landen",
"statsTopPlacesSectionTitle": "Top Plaatsen",
"statsTopTagsSectionTitle": "Top Labels",
+ "statsTopAlbumsSectionTitle": "Top Albums",
"viewerOpenPanoramaButtonLabel": "OPEN PANORAMA",
"viewerSetWallpaperButtonLabel": "ALS ACHTERGROND INSTELLEN",
@@ -664,6 +674,8 @@
"viewerInfoSearchSuggestionResolution": "Resolutie",
"viewerInfoSearchSuggestionRights": "Rechten",
+ "wallpaperUseScrollEffect": "Scroll-effect gebruiken op startscherm",
+
"tagEditorPageTitle": "Wijzig Labels",
"tagEditorPageNewTagFieldLabel": "Nieuw label",
"tagEditorPageAddTagTooltip": "Label toevoegen",
diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb
index f70c7674c..a13b8e424 100644
--- a/lib/l10n/app_pt.arb
+++ b/lib/l10n/app_pt.arb
@@ -41,6 +41,8 @@
"chipActionGoToAlbumPage": "Mostrar nos Álbuns",
"chipActionGoToCountryPage": "Mostrar em Países",
"chipActionGoToTagPage": "Mostrar em Etiquetas",
+ "chipActionFilterOut": "Filtrar dentro",
+ "chipActionFilterIn": "Filtrar fora",
"chipActionHide": "Ocultar",
"chipActionPin": "Fixar no topo",
"chipActionUnpin": "Desafixar do topo",
@@ -88,15 +90,18 @@
"entryInfoActionEditDate": "Editar data e hora",
"entryInfoActionEditLocation": "Editar localização",
+ "entryInfoActionEditTitleDescription": "Editar título e descrição",
"entryInfoActionEditRating": "Editar classificação",
"entryInfoActionEditTags": "Editar etiquetas",
"entryInfoActionRemoveMetadata": "Remover metadados",
"filterBinLabel": "Lixeira",
"filterFavouriteLabel": "Favorito",
+ "filterNoDateLabel": "Sem data",
"filterNoLocationLabel": "Não localizado",
"filterNoRatingLabel": "Sem classificação",
"filterNoTagLabel": "Sem etiqueta",
+ "filterNoTitleLabel": "Sem título",
"filterOnThisDayLabel": "Neste dia",
"filterRecentlyAddedLabel": "Adicionado recentemente",
"filterRatingRejectedLabel": "Rejeitado",
@@ -152,9 +157,9 @@
"displayRefreshRatePreferHighest": "Taxa mais alta",
"displayRefreshRatePreferLowest": "Taxa mais baixa",
- "slideshowVideoPlaybackSkip": "Pular",
- "slideshowVideoPlaybackMuted": "Reproduzir sem som",
- "slideshowVideoPlaybackWithSound": "Reproduzir com som",
+ "videoPlaybackSkip": "Pular",
+ "videoPlaybackMuted": "Reproduzir sem som",
+ "videoPlaybackWithSound": "Reproduzir com som",
"themeBrightnessLight": "Claro",
"themeBrightnessDark": "Escuro",
@@ -164,11 +169,15 @@
"viewerTransitionParallax": "Parallax",
"viewerTransitionFade": "Desvaneça",
"viewerTransitionZoomIn": "Mais zoom",
+ "viewerTransitionNone": "Nenhum",
"wallpaperTargetHome": "Tela inicial",
"wallpaperTargetLock": "Tela de bloqueio",
"wallpaperTargetHomeLock": "Telas iniciais e de bloqueio",
+ "widgetOpenPageHome": "Abrir inicial",
+ "widgetOpenPageViewer": "Abrir visualizador",
+
"albumTierNew": "Novo",
"albumTierPinned": "Fixada",
"albumTierSpecial": "Comum",
@@ -284,7 +293,9 @@
"viewDialogSortSectionTitle": "Organizar",
"viewDialogGroupSectionTitle": "Grupo",
"viewDialogLayoutSectionTitle": "Layout",
+ "viewDialogReverseSortOrder": "Ordem de classificação inversa",
+ "tileLayoutMosaic": "Mosaico",
"tileLayoutGrid": "Grid",
"tileLayoutList": "Lista",
@@ -387,10 +398,22 @@
"sortByAlbumFileName": "Por álbum e nome de arquivo",
"sortByRating": "Por classificação",
+ "sortOrderNewestFirst": "Os mais novos primeiro",
+ "sortOrderOldestFirst": "Mais velhos primeiro",
+ "sortOrderAtoZ": "A a Z",
+ "sortOrderZtoA": "Z a A",
+ "sortOrderHighestFirst": "Mais alto primeiro",
+ "sortOrderLowestFirst": "Mais baixo primeiro",
+ "sortOrderLargestFirst": "Maior primeiro",
+ "sortOrderSmallestFirst": "Menor primeiro",
+
+ "albumGroupType": "Por tipo",
"albumGroupTier": "Por nível",
"albumGroupVolume": "Por volume de armazenamento",
"albumGroupNone": "Não agrupe",
+ "albumMimeTypeMixed": "Misturado",
+
"albumPickPageTitleCopy": "Copiar para o álbum",
"albumPickPageTitleExport": "Exportar para o álbum",
"albumPickPageTitleMove": "Mover para o álbum",
@@ -424,10 +447,12 @@
"searchPlacesSectionTitle": "Locais",
"searchTagsSectionTitle": "Etiquetas",
"searchRatingSectionTitle": "Classificações",
+ "searchMetadataSectionTitle": "Metadados",
"settingsPageTitle": "Configurações",
"settingsSystemDefault": "Sistema",
"settingsDefault": "Padrão",
+ "settingsDisabled": "Desativado",
"settingsSearchFieldLabel": "Pesquisar configuração",
"settingsSearchEmpty": "Nenhuma configuração correspondente",
@@ -510,10 +535,9 @@
"settingsSlideshowRepeat": "Repetir",
"settingsSlideshowShuffle": "Embaralhar",
"settingsSlideshowFillScreen": "Preencher tela",
+ "settingsSlideshowAnimatedZoomEffect": "Efeito de zoom animado",
"settingsSlideshowTransitionTile": "Transição",
- "settingsSlideshowTransitionDialogTitle": "Transição",
"settingsSlideshowIntervalTile": "Intervalo",
- "settingsSlideshowIntervalDialogTitle": "Intervalo",
"settingsSlideshowVideoPlaybackTile": "Reprodução de vídeo",
"settingsSlideshowVideoPlaybackDialogTitle": "Reprodução de vídeo",
@@ -521,7 +545,7 @@
"settingsVideoSectionTitle": "Vídeo",
"settingsVideoShowVideos": "Mostrar vídeos",
"settingsVideoEnableHardwareAcceleration": "Aceleraçao do hardware",
- "settingsVideoEnableAutoPlay": "Reprodução automática",
+ "settingsVideoAutoPlay": "Reprodução automática",
"settingsVideoLoopModeTile": "Modo de loop",
"settingsVideoLoopModeDialogTitle": "Modo de loop",
@@ -543,7 +567,6 @@
"settingsVideoControlsTile": "Controles",
"settingsVideoControlsPageTitle": "Controles",
"settingsVideoButtonsTile": "Botões",
- "settingsVideoButtonsDialogTitle": "Botões",
"settingsVideoGestureDoubleTapTogglePlay": "Toque duas vezes para reproduzir/pausar",
"settingsVideoGestureSideDoubleTapSeek": "Toque duas vezes nas bordas da tela buscar para trás/para frente",
@@ -576,7 +599,6 @@
"settingsRemoveAnimationsTile": "Remover animações",
"settingsRemoveAnimationsDialogTitle": "Remover Animações",
"settingsTimeToTakeActionTile": "Tempo para executar uma ação",
- "settingsTimeToTakeActionDialogTitle": "Tempo para executar uma ação",
"settingsDisplaySectionTitle": "Tela",
"settingsThemeBrightnessTile": "Tema",
@@ -598,6 +620,7 @@
"settingsWidgetPageTitle": "Porta-retratos",
"settingsWidgetShowOutline": "Contorno",
+ "settingsWidgetOpenPage": "Ao tocar no widget",
"settingsCollectionTile": "Coleção",
@@ -606,6 +629,7 @@
"statsTopCountriesSectionTitle": "Principais Países",
"statsTopPlacesSectionTitle": "Principais Lugares",
"statsTopTagsSectionTitle": "Principais Etiquetas",
+ "statsTopAlbumsSectionTitle": "Principais Álbuns",
"viewerOpenPanoramaButtonLabel": "ABRIR PANORAMA",
"viewerSetWallpaperButtonLabel": "DEFINIR PAPEL DE PAREDE",
@@ -650,6 +674,8 @@
"viewerInfoSearchSuggestionResolution": "Resolução",
"viewerInfoSearchSuggestionRights": "Direitos",
+ "wallpaperUseScrollEffect": "Use o efeito de rolagem na tela inicial",
+
"tagEditorPageTitle": "Editar etiquetas",
"tagEditorPageNewTagFieldLabel": "Nova etiqueta",
"tagEditorPageAddTagTooltip": "Adicionar etiqueta",
diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb
index d6c29cb27..d82fbe696 100644
--- a/lib/l10n/app_ru.arb
+++ b/lib/l10n/app_ru.arb
@@ -41,6 +41,8 @@
"chipActionGoToAlbumPage": "Показать в Альбомах",
"chipActionGoToCountryPage": "Показать в Странах",
"chipActionGoToTagPage": "Показать в тегах",
+ "chipActionFilterOut": "Исключить",
+ "chipActionFilterIn": "Включить",
"chipActionHide": "Скрыть",
"chipActionPin": "Закрепить",
"chipActionUnpin": "Открепить",
@@ -155,9 +157,9 @@
"displayRefreshRatePreferHighest": "Наивысшая частота",
"displayRefreshRatePreferLowest": "Наименьшая частота",
- "slideshowVideoPlaybackSkip": "Пропустить",
- "slideshowVideoPlaybackMuted": "Играть без звука",
- "slideshowVideoPlaybackWithSound": "Играть со звуком",
+ "videoPlaybackSkip": "Пропустить",
+ "videoPlaybackMuted": "Играть без звука",
+ "videoPlaybackWithSound": "Играть со звуком",
"themeBrightnessLight": "Светлая",
"themeBrightnessDark": "Тёмная",
@@ -167,11 +169,15 @@
"viewerTransitionParallax": "Параллакс",
"viewerTransitionFade": "Затухание",
"viewerTransitionZoomIn": "Приближение",
+ "viewerTransitionNone": "Нет",
"wallpaperTargetHome": "Домашний экран",
"wallpaperTargetLock": "Экран блокировки",
"wallpaperTargetHomeLock": "Домашний экран и экран блокировки",
+ "widgetOpenPageHome": "Открыть главную страницу",
+ "widgetOpenPageViewer": "Просмотр текущего",
+
"albumTierNew": "Новые",
"albumTierPinned": "Закрепленные",
"albumTierSpecial": "Стандартные",
@@ -289,6 +295,7 @@
"viewDialogLayoutSectionTitle": "Макет",
"viewDialogReverseSortOrder": "Обратный порядок сортировки",
+ "tileLayoutMosaic": "Мозайка",
"tileLayoutGrid": "Сетка",
"tileLayoutList": "Список",
@@ -401,9 +408,12 @@
"sortOrderSmallestFirst": "Сначала маленькие",
"albumGroupTier": "По уровню",
+ "albumGroupType": "По типу",
"albumGroupVolume": "По накопителю",
"albumGroupNone": "Не группировать",
+ "albumMimeTypeMixed": "Разное",
+
"albumPickPageTitleCopy": "Копировать в альбом",
"albumPickPageTitleExport": "Экспорт в альбом",
"albumPickPageTitleMove": "Переместить в альбом",
@@ -442,6 +452,7 @@
"settingsPageTitle": "Настройки",
"settingsSystemDefault": "Система",
"settingsDefault": "По умолчанию",
+ "settingsDisabled": "Выключено",
"settingsSearchFieldLabel": "Поиск настроек",
"settingsSearchEmpty": "Нет соответствующих настроек",
@@ -524,10 +535,9 @@
"settingsSlideshowRepeat": "Повтор",
"settingsSlideshowShuffle": "Вперемешку",
"settingsSlideshowFillScreen": "Полный экран",
+ "settingsSlideshowAnimatedZoomEffect": "Анимация зум эффекта",
"settingsSlideshowTransitionTile": "Эффект перехода",
- "settingsSlideshowTransitionDialogTitle": "Эффект Перехода",
"settingsSlideshowIntervalTile": "Интервал",
- "settingsSlideshowIntervalDialogTitle": "Интервал",
"settingsSlideshowVideoPlaybackTile": "Проигрывание видео",
"settingsSlideshowVideoPlaybackDialogTitle": "Проигрывание Видео",
@@ -535,7 +545,7 @@
"settingsVideoSectionTitle": "Видео",
"settingsVideoShowVideos": "Показать видео",
"settingsVideoEnableHardwareAcceleration": "Аппаратное ускорение",
- "settingsVideoEnableAutoPlay": "Автозапуск воспроизведения",
+ "settingsVideoAutoPlay": "Автозапуск воспроизведения",
"settingsVideoLoopModeTile": "Циклический режим",
"settingsVideoLoopModeDialogTitle": "Цикличный режим",
@@ -557,7 +567,6 @@
"settingsVideoControlsTile": "Элементы управления",
"settingsVideoControlsPageTitle": "Элементы управления",
"settingsVideoButtonsTile": "Кнопки",
- "settingsVideoButtonsDialogTitle": "Кнопки",
"settingsVideoGestureDoubleTapTogglePlay": "Двойное нажатие для воспроизведения/паузы",
"settingsVideoGestureSideDoubleTapSeek": "Двойное нажатие на края экрана для перехода назад/вперёд",
@@ -590,7 +599,6 @@
"settingsRemoveAnimationsTile": "Удалить анимацию",
"settingsRemoveAnimationsDialogTitle": "Удалить анимацию",
"settingsTimeToTakeActionTile": "Время на выполнение действия",
- "settingsTimeToTakeActionDialogTitle": "Время на выполнение действия",
"settingsDisplaySectionTitle": "Отображение",
"settingsThemeBrightnessTile": "Тема",
@@ -612,6 +620,7 @@
"settingsWidgetPageTitle": "Фоторамка",
"settingsWidgetShowOutline": "Выделение",
+ "settingsWidgetOpenPage": "При нажатии на виджет",
"settingsCollectionTile": "Коллекция",
@@ -620,6 +629,7 @@
"statsTopCountriesSectionTitle": "Топ стран",
"statsTopPlacesSectionTitle": "Топ локаций",
"statsTopTagsSectionTitle": "Топ тегов",
+ "statsTopAlbumsSectionTitle": "Топ альбомов",
"viewerOpenPanoramaButtonLabel": "ОТКРЫТЬ ПАНОРАМУ",
"viewerSetWallpaperButtonLabel": "УСТАНОВИТЬ КАК ОБОИ",
@@ -664,6 +674,8 @@
"viewerInfoSearchSuggestionResolution": "Разрешение",
"viewerInfoSearchSuggestionRights": "Права",
+ "wallpaperUseScrollEffect": "Эффект прокрутки на домашнем экране",
+
"tagEditorPageTitle": "Изменить теги",
"tagEditorPageNewTagFieldLabel": "Новый тег",
"tagEditorPageAddTagTooltip": "Добавить тег",
diff --git a/lib/l10n/app_tr.arb b/lib/l10n/app_tr.arb
index 1dc670844..51da7c382 100644
--- a/lib/l10n/app_tr.arb
+++ b/lib/l10n/app_tr.arb
@@ -502,7 +502,7 @@
"settingsVideoSectionTitle": "Video",
"settingsVideoShowVideos": "Videoları göster",
"settingsVideoEnableHardwareAcceleration": "Donanım hızlandırma",
- "settingsVideoEnableAutoPlay": "Otomatik oynat",
+ "settingsVideoAutoPlay": "Otomatik oynat",
"settingsVideoLoopModeTile": "Döngü modu",
"settingsVideoLoopModeDialogTitle": "Döngü Modu",
@@ -524,7 +524,6 @@
"settingsVideoControlsTile": "Kontroller",
"settingsVideoControlsPageTitle": "Kontroller",
"settingsVideoButtonsTile": "Düğmeler",
- "settingsVideoButtonsDialogTitle": "Düğmeler",
"settingsVideoGestureDoubleTapTogglePlay": "Oynatmak/duraklatmak için çift dokunun",
"settingsVideoGestureSideDoubleTapSeek": "Geri/ileri aramak için ekran kenarlarına çift dokunun",
@@ -557,7 +556,6 @@
"settingsRemoveAnimationsTile": "Animasyonları kaldır",
"settingsRemoveAnimationsDialogTitle": "Animasyonları Kaldır",
"settingsTimeToTakeActionTile": "Harekete geçme zamanı",
- "settingsTimeToTakeActionDialogTitle": "Harekete Geçme Zamanı",
"settingsDisplaySectionTitle": "Ekran",
"settingsThemeBrightnessTile": "Tema",
diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb
index 539f42cd0..234b46dfc 100644
--- a/lib/l10n/app_zh.arb
+++ b/lib/l10n/app_zh.arb
@@ -41,6 +41,8 @@
"chipActionGoToAlbumPage": "在相册中显示",
"chipActionGoToCountryPage": "在国家中显示",
"chipActionGoToTagPage": "在标签中显示",
+ "chipActionFilterOut": "滤除",
+ "chipActionFilterIn": "筛选",
"chipActionHide": "隐藏",
"chipActionPin": "置顶",
"chipActionUnpin": "取消置顶",
@@ -155,9 +157,9 @@
"displayRefreshRatePreferHighest": "最高刷新率",
"displayRefreshRatePreferLowest": "最低刷新率",
- "slideshowVideoPlaybackSkip": "跳过",
- "slideshowVideoPlaybackMuted": "静音播放",
- "slideshowVideoPlaybackWithSound": "带音播放",
+ "videoPlaybackSkip": "跳过",
+ "videoPlaybackMuted": "静音播放",
+ "videoPlaybackWithSound": "带音播放",
"themeBrightnessLight": "浅色",
"themeBrightnessDark": "深色",
@@ -167,11 +169,15 @@
"viewerTransitionParallax": "视差滚动",
"viewerTransitionFade": "淡入淡出",
"viewerTransitionZoomIn": "放大",
+ "viewerTransitionNone": "无",
"wallpaperTargetHome": "主屏幕",
"wallpaperTargetLock": "锁屏界面",
"wallpaperTargetHomeLock": "主屏幕 + 锁屏界面",
+ "widgetOpenPageHome": "打开主页",
+ "widgetOpenPageViewer": "打开查看器",
+
"albumTierNew": "新的",
"albumTierPinned": "钉选",
"albumTierSpecial": "普通",
@@ -289,6 +295,7 @@
"viewDialogLayoutSectionTitle": "布局",
"viewDialogReverseSortOrder": "反向排序",
+ "tileLayoutMosaic": "马赛克",
"tileLayoutGrid": "网格",
"tileLayoutList": "列表",
@@ -401,9 +408,12 @@
"sortOrderSmallestFirst": "由小到大",
"albumGroupTier": "按层级",
+ "albumGroupType": "按类型",
"albumGroupVolume": "按存储卷",
"albumGroupNone": "不分组",
+ "albumMimeTypeMixed": "混合",
+
"albumPickPageTitleCopy": "复制到相册",
"albumPickPageTitleExport": "导出到相册",
"albumPickPageTitleMove": "移至相册",
@@ -442,6 +452,7 @@
"settingsPageTitle": "设置",
"settingsSystemDefault": "系统",
"settingsDefault": "默认",
+ "settingsDisabled": "禁用",
"settingsSearchFieldLabel": "搜索设置",
"settingsSearchEmpty": "无匹配设置项",
@@ -524,10 +535,9 @@
"settingsSlideshowRepeat": "重复",
"settingsSlideshowShuffle": "随机播放",
"settingsSlideshowFillScreen": "填充屏幕",
+ "settingsSlideshowAnimatedZoomEffect": "动画缩放效果",
"settingsSlideshowTransitionTile": "过渡动画",
- "settingsSlideshowTransitionDialogTitle": "过渡动画",
"settingsSlideshowIntervalTile": "时间间隔",
- "settingsSlideshowIntervalDialogTitle": "时间间隔",
"settingsSlideshowVideoPlaybackTile": "视频回放",
"settingsSlideshowVideoPlaybackDialogTitle": "视频回放",
@@ -535,7 +545,7 @@
"settingsVideoSectionTitle": "视频",
"settingsVideoShowVideos": "显示视频",
"settingsVideoEnableHardwareAcceleration": "硬件加速",
- "settingsVideoEnableAutoPlay": "自动播放",
+ "settingsVideoAutoPlay": "自动播放",
"settingsVideoLoopModeTile": "循环模式",
"settingsVideoLoopModeDialogTitle": "循环模式",
@@ -557,7 +567,6 @@
"settingsVideoControlsTile": "控件",
"settingsVideoControlsPageTitle": "控件",
"settingsVideoButtonsTile": "按钮",
- "settingsVideoButtonsDialogTitle": "按钮",
"settingsVideoGestureDoubleTapTogglePlay": "双击播放/暂停",
"settingsVideoGestureSideDoubleTapSeek": "双击屏幕边缘步进/步退",
@@ -590,7 +599,6 @@
"settingsRemoveAnimationsTile": "移除动画",
"settingsRemoveAnimationsDialogTitle": "移除动画",
"settingsTimeToTakeActionTile": "生效时间",
- "settingsTimeToTakeActionDialogTitle": "生效时间",
"settingsDisplaySectionTitle": "显示",
"settingsThemeBrightnessTile": "主题",
@@ -612,6 +620,7 @@
"settingsWidgetPageTitle": "相框",
"settingsWidgetShowOutline": "轮廓",
+ "settingsWidgetOpenPage": "轻触小部件时",
"settingsCollectionTile": "媒体集",
@@ -620,6 +629,7 @@
"statsTopCountriesSectionTitle": "热门国家",
"statsTopPlacesSectionTitle": "热门地点",
"statsTopTagsSectionTitle": "热门标签",
+ "statsTopAlbumsSectionTitle": "热门相册",
"viewerOpenPanoramaButtonLabel": "打开全景",
"viewerSetWallpaperButtonLabel": "设置壁纸",
@@ -664,6 +674,8 @@
"viewerInfoSearchSuggestionResolution": "分辨率",
"viewerInfoSearchSuggestionRights": "所有权",
+ "wallpaperUseScrollEffect": "在主屏幕上使用滚动效果",
+
"tagEditorPageTitle": "编辑标签",
"tagEditorPageNewTagFieldLabel": "新标签",
"tagEditorPageAddTagTooltip": "添加标签",
diff --git a/lib/main_common.dart b/lib/main_common.dart
index 775607d8b..a02f671be 100644
--- a/lib/main_common.dart
+++ b/lib/main_common.dart
@@ -33,6 +33,7 @@ void mainCommon(AppFlavor flavor) {
// - in profile/release mode: plain grey background
// This can be modified via `ErrorWidget.builder`
// ErrorWidget.builder = (details) => ErrorWidget(details.exception);
+ // cf https://docs.flutter.dev/testing/errors
runApp(AvesApp(flavor: flavor));
}
diff --git a/lib/model/actions/chip_actions.dart b/lib/model/actions/chip_actions.dart
index efae273d7..28b596579 100644
--- a/lib/model/actions/chip_actions.dart
+++ b/lib/model/actions/chip_actions.dart
@@ -6,6 +6,7 @@ enum ChipAction {
goToAlbumPage,
goToCountryPage,
goToTagPage,
+ reverse,
hide,
}
@@ -18,6 +19,9 @@ extension ExtraChipAction on ChipAction {
return context.l10n.chipActionGoToCountryPage;
case ChipAction.goToTagPage:
return context.l10n.chipActionGoToTagPage;
+ case ChipAction.reverse:
+ // different data depending on state
+ return context.l10n.chipActionFilterOut;
case ChipAction.hide:
return context.l10n.chipActionHide;
}
@@ -33,6 +37,8 @@ extension ExtraChipAction on ChipAction {
return AIcons.location;
case ChipAction.goToTagPage:
return AIcons.tag;
+ case ChipAction.reverse:
+ return AIcons.reverse;
case ChipAction.hide:
return AIcons.hide;
}
diff --git a/lib/model/actions/entry_set_actions.dart b/lib/model/actions/entry_set_actions.dart
index 0efb48962..92af82d5c 100644
--- a/lib/model/actions/entry_set_actions.dart
+++ b/lib/model/actions/entry_set_actions.dart
@@ -83,7 +83,7 @@ class EntrySetActions {
];
// exclude bin related actions
- static const collectionEditorSelection = [
+ static const collectionEditorSelectionRegular = [
EntrySetAction.share,
EntrySetAction.delete,
EntrySetAction.copy,
@@ -97,6 +97,18 @@ class EntrySetActions {
// editing actions are in their subsection
];
+ static const collectionEditorSelectionEdit = [
+ EntrySetAction.rotateCCW,
+ EntrySetAction.rotateCW,
+ EntrySetAction.flip,
+ EntrySetAction.editDate,
+ EntrySetAction.editLocation,
+ EntrySetAction.editTitleDescription,
+ EntrySetAction.editRating,
+ EntrySetAction.editTags,
+ EntrySetAction.removeMetadata,
+ ];
+
static const edit = [
EntrySetAction.editDate,
EntrySetAction.editLocation,
diff --git a/lib/model/db/db_metadata.dart b/lib/model/db/db_metadata.dart
index 7f5d70ea4..cc23707bf 100644
--- a/lib/model/db/db_metadata.dart
+++ b/lib/model/db/db_metadata.dart
@@ -10,8 +10,6 @@ import 'package:aves/model/video_playback.dart';
abstract class MetadataDb {
int get nextId;
- int get timestampSecs;
-
Future init();
Future dbFileSize();
diff --git a/lib/model/db/db_metadata_sqflite.dart b/lib/model/db/db_metadata_sqflite.dart
index 8ad1aa3fe..3e12a018a 100644
--- a/lib/model/db/db_metadata_sqflite.dart
+++ b/lib/model/db/db_metadata_sqflite.dart
@@ -34,9 +34,6 @@ class SqfliteMetadataDb implements MetadataDb {
@override
int get nextId => ++_lastId;
- @override
- int get timestampSecs => DateTime.now().millisecondsSinceEpoch ~/ 1000;
-
@override
Future init() async {
_db = await openDatabase(
diff --git a/lib/model/entry.dart b/lib/model/entry.dart
index 65443958b..a057462b0 100644
--- a/lib/model/entry.dart
+++ b/lib/model/entry.dart
@@ -26,7 +26,7 @@ import 'package:country_code/country_code.dart';
import 'package:flutter/foundation.dart';
import 'package:latlong2/latlong.dart';
-enum EntryDataType { basic, catalog, address, references }
+enum EntryDataType { basic, aspectRatio, catalog, address, references }
class AvesEntry {
// `sizeBytes`, `dateModifiedSecs` can be missing in viewer mode
@@ -150,6 +150,7 @@ class AvesEntry {
'sourceRotationDegrees': sourceRotationDegrees,
'sizeBytes': sizeBytes,
'title': sourceTitle,
+ 'dateAddedSecs': dateAddedSecs,
'dateModifiedSecs': dateModifiedSecs,
'sourceDateTakenMillis': sourceDateTakenMillis,
'durationMillis': durationMillis,
@@ -277,6 +278,8 @@ class AvesEntry {
bool get isMediaStoreContent => uri.startsWith('content://media/');
+ bool get isMediaStoreMediaContent => isMediaStoreContent && {'/external/images/', '/external/video/'}.any(uri.contains);
+
bool get canEdit => path != null && !trashed && isMediaStoreContent;
bool get canEditDate => canEdit && (canEditExif || canEditXmp);
@@ -291,9 +294,9 @@ class AvesEntry {
bool get canRotateAndFlip => canEdit && canEditExif;
- // as of androidx.exifinterface:exifinterface:1.3.3
- // `exifinterface` declares support for DNG, but `exifinterface` strips non-standard Exif tags when saving attributes,
- // and DNG requires DNG-specific tags saved along standard Exif. So `exifinterface` actually breaks DNG files.
+ // `exifinterface` v1.3.3 declared support for DNG, but it strips non-standard Exif tags when saving attributes,
+ // and DNG requires DNG-specific tags saved along standard Exif. So it was actually breaking DNG files.
+ // as of androidx.exifinterface:exifinterface:1.3.4
bool get canEditExif {
switch (mimeType.toLowerCase()) {
case MimeTypes.jpeg:
diff --git a/lib/model/entry_metadata_edition.dart b/lib/model/entry_metadata_edition.dart
index 1659b20bb..7692ef761 100644
--- a/lib/model/entry_metadata_edition.dart
+++ b/lib/model/entry_metadata_edition.dart
@@ -126,6 +126,7 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
if (newFields.isNotEmpty) {
dataTypes.addAll({
EntryDataType.basic,
+ EntryDataType.aspectRatio,
EntryDataType.catalog,
});
}
@@ -309,14 +310,18 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
}
Future> removeMetadata(Set types) async {
+ final Set dataTypes = {};
+
final newFields = await metadataEditService.removeTypes(this, types);
- return newFields.isEmpty
- ? {}
- : {
- EntryDataType.basic,
- EntryDataType.catalog,
- EntryDataType.address,
- };
+ if (newFields.isNotEmpty) {
+ dataTypes.addAll({
+ EntryDataType.basic,
+ EntryDataType.aspectRatio,
+ EntryDataType.catalog,
+ EntryDataType.address,
+ });
+ }
+ return dataTypes;
}
static void editIptcValues(List