Merge branch 'develop'
This commit is contained in:
commit
fb8a97c5c6
339 changed files with 9765 additions and 4573 deletions
2
.flutter
2
.flutter
|
@ -1 +1 @@
|
|||
Subproject commit f92f44110e87bad5ff168335c36da6f6053036e6
|
||||
Subproject commit efbf63d9c66b9f6ec30e9ad4611189aa80003d31
|
21
CHANGELOG.md
21
CHANGELOG.md
|
@ -4,6 +4,27 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
## <a id="unreleased"></a>[Unreleased]
|
||||
|
||||
## <a id="v1.9.0"></a>[v1.9.0] - 2023-08-21
|
||||
|
||||
### Added
|
||||
|
||||
- Video: improved seek accuracy, HDR support, AV1 support, playback speed from x0.25 to x4
|
||||
- support for animated AVIF (requires rescan)
|
||||
- Collection: filtering by rating range
|
||||
- Viewer: optionally show histogram on overlay
|
||||
- Viewer: external export actions available as quick actions
|
||||
- About: data usage
|
||||
|
||||
### Changed
|
||||
|
||||
- Accessibility: removing animations also removes the overscroll stretch effect
|
||||
- target Android 14 (API 34)
|
||||
- upgraded Flutter to stable v3.13.0
|
||||
|
||||
### Fixed
|
||||
|
||||
- flickering when starting videos
|
||||
|
||||
## <a id="v1.8.9"></a>[v1.8.9] - 2023-06-04
|
||||
|
||||
### Changed
|
||||
|
|
|
@ -2,6 +2,7 @@ import org.jetbrains.kotlin.gradle.tasks.KotlinCompile
|
|||
|
||||
plugins {
|
||||
id 'com.android.application'
|
||||
id 'com.google.devtools.ksp' version "$ksp_version"
|
||||
id 'kotlin-android'
|
||||
id 'kotlin-kapt'
|
||||
}
|
||||
|
@ -48,7 +49,7 @@ if (keystorePropertiesFile.exists()) {
|
|||
|
||||
android {
|
||||
namespace 'deckers.thibault.aves'
|
||||
compileSdk 33
|
||||
compileSdk 34
|
||||
ndkVersion flutter.ndkVersion
|
||||
|
||||
compileOptions {
|
||||
|
@ -75,7 +76,7 @@ android {
|
|||
// which implementation `DocumentBuilderImpl` is provided by the OS and is not customizable on Android,
|
||||
// but the implementation on API <19 is not robust enough and fails to build XMP documents
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 33
|
||||
targetSdkVersion 34
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
manifestPlaceholders = [googleApiKey: keystoreProperties["googleApiKey"] ?: "<NONE>",
|
||||
|
@ -175,10 +176,10 @@ android {
|
|||
tasks.withType(KotlinCompile).configureEach {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = '1.8'
|
||||
}
|
||||
kotlin {
|
||||
jvmToolchain(8)
|
||||
}
|
||||
|
||||
flutter {
|
||||
|
@ -202,7 +203,7 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1'
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'
|
||||
|
||||
implementation "androidx.appcompat:appcompat:1.6.1"
|
||||
implementation 'androidx.core:core-ktx:1.10.1'
|
||||
|
@ -224,7 +225,7 @@ dependencies {
|
|||
// - https://jitpack.io/p/deckerst/Android-TiffBitmapFactory
|
||||
// - https://jitpack.io/p/deckerst/mp4parser
|
||||
// - https://jitpack.io/p/deckerst/pixymeta-android
|
||||
implementation 'com.github.deckerst:Android-TiffBitmapFactory:876e53870a'
|
||||
implementation 'com.github.deckerst:Android-TiffBitmapFactory:90c06eebf4'
|
||||
implementation 'com.github.deckerst.mp4parser:isoparser:4cc0c5d06c'
|
||||
implementation 'com.github.deckerst.mp4parser:muxer:4cc0c5d06c'
|
||||
implementation 'com.github.deckerst:pixymeta-android:706bd73d6e'
|
||||
|
@ -232,10 +233,10 @@ dependencies {
|
|||
// huawei flavor only
|
||||
huaweiImplementation "com.huawei.agconnect:agconnect-core:$huawei_agconnect_version"
|
||||
|
||||
testImplementation "org.junit.jupiter:junit-jupiter-engine:5.9.2"
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.0'
|
||||
|
||||
kapt 'androidx.annotation:annotation:1.6.0'
|
||||
kapt "com.github.bumptech.glide:compiler:$glide_version"
|
||||
ksp "com.github.bumptech.glide:ksp:$glide_version"
|
||||
|
||||
compileOnly rootProject.findProject(':streams_channel')
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
TODO TLAD [Android 14 (API 34)] request/handle READ_MEDIA_VISUAL_USER_SELECTED permission
|
||||
cf https://developer.android.com/about/versions/14/changes/partial-photo-video-access
|
||||
-->
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||
<uses-permission
|
||||
|
@ -56,8 +57,9 @@
|
|||
allow install on API 19, despite the `minSdkVersion` declared in dependencies:
|
||||
- Google Maps is from API 20
|
||||
- the Security library is from API 21
|
||||
- FFmpegKit for Flutter is from API 24
|
||||
-->
|
||||
<uses-sdk tools:overrideLibrary="io.flutter.plugins.googlemaps, androidx.security:security-crypto" />
|
||||
<uses-sdk tools:overrideLibrary="io.flutter.plugins.googlemaps, androidx.security:security-crypto, com.arthenica.ffmpegkit.flutter" />
|
||||
|
||||
<!-- from Android 11, we should define <queries> to make other apps visible to this app -->
|
||||
<queries>
|
||||
|
@ -295,7 +297,8 @@
|
|||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
<!-- as of Flutter v3.10.1, Impeller badly renders text, fails to render videos, and crashes with Google Maps -->
|
||||
<!-- as of Flutter v3.10.1 (stable) / v3.12.0-15.0.pre.105 (master),
|
||||
Impeller badly renders text, fails to render videos, and crashes with Google Maps -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.EnableImpeller"
|
||||
android:value="false" />
|
||||
|
|
|
@ -9,11 +9,13 @@ import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
|
|||
import deckers.thibault.aves.model.FieldMap
|
||||
import deckers.thibault.aves.utils.PermissionManager
|
||||
import deckers.thibault.aves.utils.StorageUtils
|
||||
import deckers.thibault.aves.utils.StorageUtils.getFolderSize
|
||||
import deckers.thibault.aves.utils.StorageUtils.getPrimaryVolumePath
|
||||
import deckers.thibault.aves.utils.StorageUtils.getVolumePaths
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||
import io.flutter.util.PathUtils
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
|
@ -25,6 +27,7 @@ class StorageHandler(private val context: Context) : MethodCallHandler {
|
|||
|
||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||
when (call.method) {
|
||||
"getDataUsage" -> ioScope.launch { safe(call, result, ::getDataUsage) }
|
||||
"getStorageVolumes" -> ioScope.launch { safe(call, result, ::getStorageVolumes) }
|
||||
"getVaultRoot" -> ioScope.launch { safe(call, result, ::getVaultRoot) }
|
||||
"getFreeSpace" -> ioScope.launch { safe(call, result, ::getFreeSpace) }
|
||||
|
@ -39,6 +42,37 @@ class StorageHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private fun getDataUsage(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||
var internalCache = getFolderSize(context.cacheDir)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
internalCache += getFolderSize(context.codeCacheDir)
|
||||
}
|
||||
val externalCache = context.externalCacheDirs.map(::getFolderSize).sum()
|
||||
|
||||
val dataDir = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) context.dataDir else File(context.applicationInfo.dataDir)
|
||||
|
||||
val database = getFolderSize(File(dataDir, "databases"))
|
||||
val flutter = getFolderSize(File(PathUtils.getDataDirectory(context)))
|
||||
val vaults = getFolderSize(File(StorageUtils.getVaultRoot(context)))
|
||||
val trash = context.getExternalFilesDirs(null).mapNotNull { StorageUtils.trashDirFor(context, it.path) }.map(::getFolderSize).sum()
|
||||
|
||||
val internalData = getFolderSize(dataDir) - internalCache
|
||||
val externalData = context.getExternalFilesDirs(null).map(::getFolderSize).sum()
|
||||
val miscData = internalData + externalData - (database + flutter + vaults + trash)
|
||||
|
||||
result.success(
|
||||
hashMapOf(
|
||||
"database" to database,
|
||||
"flutter" to flutter,
|
||||
"vaults" to vaults,
|
||||
"trash" to trash,
|
||||
"miscData" to miscData,
|
||||
"internalCache" to internalCache,
|
||||
"externalCache" to externalCache,
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun getStorageVolumes(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||
val volumes = ArrayList<Map<String, Any>>()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
|
|
|
@ -96,12 +96,7 @@ class SvgRegionFetcher internal constructor(
|
|||
svg.renderToCanvas(canvas, renderOptions)
|
||||
|
||||
bitmap = Bitmap.createBitmap(bitmap, bleedX, bleedY, targetBitmapWidth, targetBitmapHeight)
|
||||
|
||||
if (bitmap != null) {
|
||||
result.success(bitmap.getBytes(canHaveAlpha = true, recycle = true))
|
||||
} else {
|
||||
result.error("fetch-null", "failed to decode region for uri=$uri regionRect=$regionRect", null)
|
||||
}
|
||||
result.success(bitmap.getBytes(canHaveAlpha = true, recycle = true))
|
||||
} catch (e: Exception) {
|
||||
result.error("fetch-read-exception", "failed to initialize region decoder for uri=$uri regionRect=$regionRect", e.message)
|
||||
}
|
||||
|
|
|
@ -205,7 +205,12 @@ class ImageByteStreamHandler(private val context: Context, private val arguments
|
|||
var len: Int
|
||||
while (inputStream.read(buffer).also { len = it } != -1) {
|
||||
// cannot decode image on Flutter side when using `buffer` directly
|
||||
success(buffer.copyOf(len))
|
||||
if (MemoryUtils.canAllocate(len)) {
|
||||
success(buffer.copyOf(len))
|
||||
} else {
|
||||
error("streamBytes-memory", "not enough memory to allocate $len bytes", null)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -80,11 +80,15 @@ internal class TiffFetcher(val model: TiffImage, val width: Int, val height: Int
|
|||
inDirectoryNumber = page
|
||||
inSampleSize = sampleSize
|
||||
}
|
||||
val bitmap = TiffBitmapFactory.decodeFileDescriptor(fd, options)
|
||||
if (bitmap == null) {
|
||||
callback.onLoadFailed(Exception("null bitmap"))
|
||||
} else {
|
||||
callback.onDataReady(bitmap)
|
||||
try {
|
||||
val bitmap = TiffBitmapFactory.decodeFileDescriptor(fd, options)
|
||||
if (bitmap == null) {
|
||||
callback.onLoadFailed(Exception("Decoding full TIFF yielded null bitmap"))
|
||||
} else {
|
||||
callback.onDataReady(bitmap)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
callback.onLoadFailed(e)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,9 @@ object Mp4ParserHelper {
|
|||
}
|
||||
// creating `IsoFile` with a `File` or a `File.inputStream()` yields `No such device`
|
||||
IsoFile(channel, boxParser).use { isoFile ->
|
||||
val fragmented = isoFile.boxes.any { box -> box is MovieFragmentBox || box is SegmentIndexBox }
|
||||
if (fragmented) throw Exception("editing fragmented movies is not supported")
|
||||
|
||||
val lastContentBox = isoFile.boxes.reversed().firstOrNull { box ->
|
||||
when {
|
||||
box == isoFile.movieBox -> false
|
||||
|
@ -60,7 +63,7 @@ object Mp4ParserHelper {
|
|||
else -> true
|
||||
}
|
||||
}
|
||||
lastContentBox ?: throw Exception("failed to find last context box")
|
||||
lastContentBox ?: throw Exception("failed to find last content box")
|
||||
val oldFileSize = isoFile.size
|
||||
var appendOffset = (isoFile.getBoxOffset { box -> box == lastContentBox })!! + lastContentBox.size
|
||||
|
||||
|
@ -97,7 +100,6 @@ object Mp4ParserHelper {
|
|||
if (trailing > 0) {
|
||||
addFreeBoxEdit(appendOffset, trailing)
|
||||
}
|
||||
|
||||
return edits
|
||||
}
|
||||
}
|
||||
|
@ -277,7 +279,9 @@ object Mp4ParserHelper {
|
|||
// creating `IsoFile` with a `File` or a `File.inputStream()` yields `No such device`
|
||||
IsoFile(channel, metadataBoxParser()).use { isoFile ->
|
||||
val userDataBox = Path.getPath<UserDataBox>(isoFile.movieBox, UserDataBox.TYPE)
|
||||
fields.putAll(extractBoxFields(userDataBox))
|
||||
if (userDataBox != null) {
|
||||
fields.putAll(extractBoxFields(userDataBox))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,6 @@ fun PackageManager.getApplicationInfoCompat(packageName: String, flags: Int): Ap
|
|||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
getApplicationInfo(packageName, PackageManager.ApplicationInfoFlags.of(flags.toLong()))
|
||||
} else {
|
||||
@Suppress("deprecation")
|
||||
getApplicationInfo(packageName, flags)
|
||||
}
|
||||
}
|
||||
|
@ -45,7 +44,6 @@ fun PackageManager.queryIntentActivitiesCompat(intent: Intent, flags: Int): List
|
|||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(flags.toLong()))
|
||||
} else {
|
||||
@Suppress("deprecation")
|
||||
queryIntentActivities(intent, flags)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,10 @@
|
|||
package deckers.thibault.aves.utils
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.app.Service
|
||||
import android.content.ContentResolver
|
||||
import android.content.ContentUris
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.MediaStore
|
||||
import android.util.Log
|
||||
import deckers.thibault.aves.utils.UriUtils.tryParseId
|
||||
|
@ -24,19 +21,6 @@ object ContextUtils {
|
|||
.build()
|
||||
}
|
||||
|
||||
fun Context.isMyServiceRunning(serviceClass: Class<out Service>): Boolean {
|
||||
val am = this.getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager
|
||||
am ?: return false
|
||||
@Suppress("deprecation")
|
||||
return am.getRunningServices(Integer.MAX_VALUE).any { it.service.className == serviceClass.name }
|
||||
}
|
||||
|
||||
// `flag`: `DocumentsContract.Document.FLAG_SUPPORTS_COPY`, etc.
|
||||
fun Context.queryDocumentProviderFlag(docUri: Uri, flag: Int): Boolean {
|
||||
val flags = queryContentPropValue(docUri, "", DocumentsContract.Document.COLUMN_FLAGS) as Long?
|
||||
return if (flags != null) (flags.toInt() and flag) == flag else false
|
||||
}
|
||||
|
||||
fun Context.queryContentPropValue(uri: Uri, mimeType: String, column: String): Any? {
|
||||
var contentUri: Uri = uri
|
||||
if (StorageUtils.isMediaStoreContentUri(uri)) {
|
||||
|
|
|
@ -11,6 +11,7 @@ import io.flutter.embedding.engine.FlutterEngine
|
|||
import io.flutter.embedding.engine.dart.DartExecutor
|
||||
import io.flutter.embedding.engine.loader.FlutterLoader
|
||||
import io.flutter.view.FlutterCallbackInformation
|
||||
import kotlin.coroutines.Continuation
|
||||
import kotlin.coroutines.resume
|
||||
import kotlin.coroutines.suspendCoroutine
|
||||
|
||||
|
@ -60,7 +61,7 @@ object FlutterUtils {
|
|||
suspend fun runOnUiThread(r: Runnable) {
|
||||
val mainLooper = Looper.getMainLooper()
|
||||
if (Looper.myLooper() != mainLooper) {
|
||||
suspendCoroutine<Boolean> { cont ->
|
||||
suspendCoroutine { cont: Continuation<Boolean> ->
|
||||
Handler(mainLooper).post {
|
||||
r.run()
|
||||
cont.resume(true)
|
||||
|
|
|
@ -716,6 +716,18 @@ object StorageUtils {
|
|||
|
||||
// convenience methods
|
||||
|
||||
fun getFolderSize(f: File): Long {
|
||||
var size: Long = 0
|
||||
if (f.isDirectory) {
|
||||
for (file in f.listFiles()!!) {
|
||||
size += getFolderSize(file)
|
||||
}
|
||||
} else {
|
||||
size = f.length()
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
fun ensureTrailingSeparator(dirPath: String): String {
|
||||
return if (dirPath.endsWith(File.separator)) dirPath else dirPath + File.separator
|
||||
}
|
||||
|
|
|
@ -1,2 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources></resources>
|
||||
<resources>
|
||||
<string name="analysis_channel_name">Сканаванне носьбітаў</string>
|
||||
<string name="videos_shortcut_short_label">Відэа</string>
|
||||
<string name="wallpaper">Шпалеры</string>
|
||||
<string name="analysis_notification_default_title">Сканаванне носьбітаў</string>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="app_widget_label">Фотарамка</string>
|
||||
<string name="safe_mode_shortcut_short_label">Бяспечны рэжым</string>
|
||||
<string name="search_shortcut_short_label">Пошук</string>
|
||||
<string name="analysis_notification_action_stop">Стоп</string>
|
||||
</resources>
|
|
@ -3,7 +3,7 @@
|
|||
<string name="app_name">Aves</string>
|
||||
<string name="app_widget_label">Marco de foto</string>
|
||||
<string name="wallpaper">Fondo de pantalla</string>
|
||||
<string name="search_shortcut_short_label">Búsqueda</string>
|
||||
<string name="search_shortcut_short_label">Buscar</string>
|
||||
<string name="videos_shortcut_short_label">Vídeos</string>
|
||||
<string name="analysis_channel_name">Explorar medios</string>
|
||||
<string name="analysis_notification_default_title">Explorando medios</string>
|
||||
|
|
12
android/app/src/main/res/values-kn/strings.xml
Normal file
12
android/app/src/main/res/values-kn/strings.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="app_widget_label">ಫೋಟೋ ಫ್ರೇಮ್</string>
|
||||
<string name="wallpaper">ವಾಲ್ಪೇಪರ್</string>
|
||||
<string name="safe_mode_shortcut_short_label">ಸುರಕ್ಷಿತ ಮೋಡ್</string>
|
||||
<string name="videos_shortcut_short_label">ವೀಡಿಯೊಗಳು</string>
|
||||
<string name="analysis_channel_name">ಮೀಡಿಯಾ ಸ್ಕ್ಯಾನ್</string>
|
||||
<string name="analysis_notification_default_title">ಮೀಡಿಯಾ ಸ್ಕ್ಯಾನ್ ಮಾಡಲಾಗುತ್ತಿದೆ</string>
|
||||
<string name="analysis_notification_action_stop">ನಿಲ್ಲಿಸಿ</string>
|
||||
<string name="search_shortcut_short_label">ಹುಡುಕಿ</string>
|
||||
</resources>
|
12
android/app/src/main/res/values-my/strings.xml
Normal file
12
android/app/src/main/res/values-my/strings.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="app_widget_label">ရုပ်ပုံဘောင်</string>
|
||||
<string name="wallpaper">နောက်ခံ ရုပ်ပုံ</string>
|
||||
<string name="safe_mode_shortcut_short_label">လုံခြုံရေးလုပ်ဆောင်ချက်</string>
|
||||
<string name="analysis_channel_name">မီဒီယာ စကင်</string>
|
||||
<string name="search_shortcut_short_label">ရှာရန်</string>
|
||||
<string name="videos_shortcut_short_label">ဗီဒီယိုများ</string>
|
||||
<string name="analysis_notification_default_title">မီဒီယာ ကိုစကင်ဖတ်နေသည်</string>
|
||||
<string name="analysis_notification_action_stop">ရပ်ရန်</string>
|
||||
</resources>
|
12
android/app/src/main/res/values-sl/strings.xml
Normal file
12
android/app/src/main/res/values-sl/strings.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_widget_label">Okvir za sliko</string>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="safe_mode_shortcut_short_label">Varni način</string>
|
||||
<string name="videos_shortcut_short_label">Videoposnetki</string>
|
||||
<string name="wallpaper">Ozadje</string>
|
||||
<string name="search_shortcut_short_label">Iskanje</string>
|
||||
<string name="analysis_notification_default_title">Skeniram medijske datoteke</string>
|
||||
<string name="analysis_notification_action_stop">Ustavi</string>
|
||||
<string name="analysis_channel_name">Sken za medijske datoteke</string>
|
||||
</resources>
|
|
@ -1,11 +1,11 @@
|
|||
buildscript {
|
||||
ext {
|
||||
kotlin_version = '1.8.21'
|
||||
agp_version = '8.0.1'
|
||||
ksp_version = "$kotlin_version-1.0.11"
|
||||
agp_version = '7.4.2'
|
||||
glide_version = '4.15.1'
|
||||
// AppGallery Connect plugin versions: https://developer.huawei.com/consumer/en/doc/development/AppGallery-connect-Guides/agc-sdk-changenotes-0000001058732550
|
||||
// TODO TLAD AppGallery Connect plugin v1.9.0.300 does not support Gradle 8+
|
||||
huawei_agconnect_version = '1.9.0.300'
|
||||
huawei_agconnect_version = '1.9.1.300'
|
||||
abiCodes = ['armeabi-v7a': 1, 'arm64-v8a': 2, 'x86': 3, 'x86_64': 4]
|
||||
useCrashlytics = gradle.startParameter.taskNames.any { task -> task.containsIgnoreCase("play") }
|
||||
useHms = gradle.startParameter.taskNames.any { task -> task.containsIgnoreCase("huawei") }
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<i>Aves</i> can handle all sorts of images and videos, including your typical JPEGs and MP4s, but also more exotic things like <b>multi-page TIFFs, SVGs, old AVIs and more</b>! It scans your media collection to identify <b>motion photos</b>, <b>panoramas</b> (aka photo spheres), <b>360° videos</b>, as well as <b>GeoTIFF</b> files.
|
||||
<i>أيفيس</i> يمكنه التعامل مع جميع أنواع الصور ومقاطع الفيديو ، بما في ذلك ملفات JPEG و MP4 النموذجية ، ولكن أيضًا أشياء أكثر غرابة مثل <b>ملفات TIFF و SVG و AVI القديمة متعددة الصفحات والمزيد</b>! يقوم بمسح مجموعة الوسائط الخاصة بك لتحديد <b> الصور المتحركة</b>, <b>الإستعراضات</b> (المعروف أيضًا باسم الصور البانورامية), <b>360 درجة مقاطع الفيديو</b>, إلى جانب <b>GeoTIFF</b> الملفات.
|
||||
|
||||
<b>Navigation and search</b> is an important part of <i>Aves</i>. The goal is for users to easily flow from albums to photos to tags to maps, etc.
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
<i>Aves</i> can handle all sorts of images and videos, including your typical JPEGs and MP4s, but also more exotic things like <b>multi-page TIFFs, SVGs, old AVIs and more</b>! It scans your media collection to identify <b>motion photos</b>, <b>panoramas</b> (aka photo spheres), <b>360° videos</b>, as well as <b>GeoTIFF</b> files.
|
||||
<i>Aves</i> можа апрацоўваць разнастайныя выявы і відэа, у тым ліку звычайныя файлы JPEG і MP4, а таксама больш экзатычныя рэчы, такія як <b>шматстаронкавыя файлы TIFF, SVG, старыя файлы AVI і іншае</b>! Ён скануе вашу калекцыю мультымедыя для ідэнтыфікацыі <b>фотаздымкаў з рухам</b>, <b>панарам</b> (ён жа панарам), <b>360° відэа</b>, а таксама <b>GeoTIFF</b> файлы.
|
||||
|
||||
<b>Navigation and search</b> is an important part of <i>Aves</i>. The goal is for users to easily flow from albums to photos to tags to maps, etc.
|
||||
<b>Навігацыя і пошук</b> з'яўляюцца важнай часткай <i>Aves</i>. Мэта складаецца ў тым, каб карыстальнікі лёгка пераходзілі ад альбомаў да фатаграфій да тэгаў да карт і г.д.
|
||||
|
||||
<i>Aves</i> integrates with Android (from KitKat to Android 13, including Android TV) with features such as <b>widgets</b>, <b>app shortcuts</b>, <b>screen saver</b> and <b>global search</b> handling. It also works as a <b>media viewer and picker</b>.
|
||||
<i>Aves</i> інтэгруецца з Android (ад KitKat да Android 13, уключаючы Android TV) з такімі функцыямі, як <b>віджэты</b>, <b>ярлыкі праграм</b>, <b>застаўка</b> і апрацоўка <b>глабальнага пошуку</b>. Ён таксама працуе як <b>сродак прагляду і выбару мультымедыя</b>.
|
|
@ -1 +1 @@
|
|||
Gallery and metadata explorer
|
||||
Галерэя і правадыр метададзеных
|
5
fastlane/metadata/android/en-US/changelogs/101.txt
Normal file
5
fastlane/metadata/android/en-US/changelogs/101.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
In v1.9.0:
|
||||
- play your animated AVIF, AV1, and HDR videos
|
||||
- filter by rating ranges
|
||||
- judge tonal distributions with the viewer histogram
|
||||
Full changelog available on GitHub
|
5
fastlane/metadata/android/en-US/changelogs/10101.txt
Normal file
5
fastlane/metadata/android/en-US/changelogs/10101.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
In v1.9.0:
|
||||
- play your animated AVIF, AV1, and HDR videos
|
||||
- filter by rating ranges
|
||||
- judge tonal distributions with the viewer histogram
|
||||
Full changelog available on GitHub
|
|
@ -1,4 +1,4 @@
|
|||
<i>Aves</i> aplikazioak mota guztitako irudi eta bideoak, nahiz ohiko zure JPEG eta MP4 fitxategiak eta exotikoagoak diren <b>orri ugaritako TIFF, SVG, AVI zaharrak eta are gehiago</b> maneiatzen ditu! Zure media-bilduma eskaneatzen du <b>mugimendu-argazkiak</b>, <b>panoramikak</b> (argazki esferikoak bezala ere ezagunak), <b>360°-ko bideoak</b>, baita <b>GeoTIFF</b> fitxategiak ere.
|
||||
<i>Aves</i> aplikazioak mota guztitako irudi eta bideoak, nahiz zure ohiko JPEG eta MP4 fitxategiak, eta exotikoagoak diren <b>orri ugaritako TIFF, SVG, AVI zaharrak eta are gehiago</b> maneiatzen ditu! Zure media-bilduma eskaneatzen du <b>mugimendu-argazkiak</b>, <b>panoramikak</b> (argazki esferikoak bezala ere ezagunak), <b>360°-ko bideoak</b>, baita <b>GeoTIFF</b> fitxategiak ere.
|
||||
|
||||
<b>Nabigazioa eta bilaketa</b> <i>Aves</i> aplikazioaren zati garrantzitsu bat da. Helburua, erabiltzaileek albumetatik argazkietara, etiketetara, mapetara, etab. modu errazean mugi ahal izatea da.
|
||||
|
||||
|
|
5
fastlane/metadata/android/kn/full_description.txt
Normal file
5
fastlane/metadata/android/kn/full_description.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
<i>Aves</i> can handle all sorts of images and videos, including your typical JPEGs and MP4s, but also more exotic things like <b>multi-page TIFFs, SVGs, old AVIs and more</b>! It scans your media collection to identify <b>motion photos</b>, <b>panoramas</b> (aka photo spheres), <b>360° videos</b>, as well as <b>GeoTIFF</b> files.
|
||||
|
||||
<b>Navigation and search</b> is an important part of <i>Aves</i>. The goal is for users to easily flow from albums to photos to tags to maps, etc.
|
||||
|
||||
<i>Aves</i> integrates with Android (from KitKat to Android 13, including Android TV) with features such as <b>widgets</b>, <b>app shortcuts</b>, <b>screen saver</b> and <b>global search</b> handling. It also works as a <b>media viewer and picker</b>.
|
1
fastlane/metadata/android/kn/short_description.txt
Normal file
1
fastlane/metadata/android/kn/short_description.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Gallery and metadata explorer
|
5
fastlane/metadata/android/my/full_description.txt
Normal file
5
fastlane/metadata/android/my/full_description.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
<i>Aves</i> can handle all sorts of images and videos, including your typical JPEGs and MP4s, but also more exotic things like <b>multi-page TIFFs, SVGs, old AVIs and more</b>! It scans your media collection to identify <b>motion photos</b>, <b>panoramas</b> (aka photo spheres), <b>360° videos</b>, as well as <b>GeoTIFF</b> files.
|
||||
|
||||
<b>Navigation and search</b> is an important part of <i>Aves</i>. The goal is for users to easily flow from albums to photos to tags to maps, etc.
|
||||
|
||||
<i>Aves</i> integrates with Android (from KitKat to Android 13, including Android TV) with features such as <b>widgets</b>, <b>app shortcuts</b>, <b>screen saver</b> and <b>global search</b> handling. It also works as a <b>media viewer and picker</b>.
|
1
fastlane/metadata/android/my/short_description.txt
Normal file
1
fastlane/metadata/android/my/short_description.txt
Normal file
|
@ -0,0 +1 @@
|
|||
ဂယ်လာရီနဲ့metadataအက်ပ်
|
5
fastlane/metadata/android/sl/full_description.txt
Normal file
5
fastlane/metadata/android/sl/full_description.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
<i>Aves</i> can handle all sorts of images and videos, including your typical JPEGs and MP4s, but also more exotic things like <b>multi-page TIFFs, SVGs, old AVIs and more</b>! It scans your media collection to identify <b>motion photos</b>, <b>panoramas</b> (aka photo spheres), <b>360° videos</b>, as well as <b>GeoTIFF</b> files.
|
||||
|
||||
<b>Navigation and search</b> is an important part of <i>Aves</i>. The goal is for users to easily flow from albums to photos to tags to maps, etc.
|
||||
|
||||
<i>Aves</i> integrates with Android (from KitKat to Android 13, including Android TV) with features such as <b>widgets</b>, <b>app shortcuts</b>, <b>screen saver</b> and <b>global search</b> handling. It also works as a <b>media viewer and picker</b>.
|
1
fastlane/metadata/android/sl/short_description.txt
Normal file
1
fastlane/metadata/android/sl/short_description.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Gallery and metadata explorer
|
|
@ -111,5 +111,564 @@
|
|||
"chipActionCreateAlbum": "Стварыць альбом",
|
||||
"@chipActionCreateAlbum": {},
|
||||
"entryActionConvert": "Канвертаваць",
|
||||
"@entryActionConvert": {}
|
||||
"@entryActionConvert": {},
|
||||
"entryActionRotateCCW": "Круціць супраць гадзінны стрэлкі",
|
||||
"@entryActionRotateCCW": {},
|
||||
"entryActionRestore": "Аднавіць",
|
||||
"@entryActionRestore": {},
|
||||
"entryActionRotateScreen": "Паварот экрана",
|
||||
"@entryActionRotateScreen": {},
|
||||
"entryActionViewSource": "Паглядзець крыніцу",
|
||||
"@entryActionViewSource": {},
|
||||
"entryActionConvertMotionPhotoToStillImage": "Пераўтварыць у нерухомую выяву",
|
||||
"@entryActionConvertMotionPhotoToStillImage": {},
|
||||
"entryActionViewMotionPhotoVideo": "Адкрыць відэа",
|
||||
"@entryActionViewMotionPhotoVideo": {},
|
||||
"entryActionSetAs": "Усталяваць як",
|
||||
"@entryActionSetAs": {},
|
||||
"entryActionAddFavourite": "Дадаць у абранае",
|
||||
"@entryActionAddFavourite": {},
|
||||
"videoActionUnmute": "Уключыць гук",
|
||||
"@videoActionUnmute": {},
|
||||
"videoActionCaptureFrame": "Захоп кадра",
|
||||
"@videoActionCaptureFrame": {},
|
||||
"viewerActionSettings": "Налады",
|
||||
"@viewerActionSettings": {},
|
||||
"videoActionSkip10": "Перамотка наперад на 10 секунд",
|
||||
"@videoActionSkip10": {},
|
||||
"videoActionReplay10": "Перамотка назад на 10 секунд",
|
||||
"@videoActionReplay10": {},
|
||||
"entryInfoActionEditTitleDescription": "Рэдагаваць назву і апісанне",
|
||||
"@entryInfoActionEditTitleDescription": {},
|
||||
"entryInfoActionRemoveMetadata": "Выдаліць метададзеныя",
|
||||
"@entryInfoActionRemoveMetadata": {},
|
||||
"editorTransformRotate": "Павярнуць",
|
||||
"@editorTransformRotate": {},
|
||||
"editorTransformCrop": "Абрэзаць",
|
||||
"@editorTransformCrop": {},
|
||||
"entryActionShowGeoTiffOnMap": "Паказаць як накладанне на карту",
|
||||
"@entryActionShowGeoTiffOnMap": {},
|
||||
"videoActionSelectStreams": "Выберыце трэкі",
|
||||
"@videoActionSelectStreams": {},
|
||||
"entryInfoActionEditLocation": "Рэдагаваць месцазнаходжанне",
|
||||
"@entryInfoActionEditLocation": {},
|
||||
"entryActionRemoveFavourite": "Выдаліць з абранага",
|
||||
"@entryActionRemoveFavourite": {},
|
||||
"videoActionPause": "Паўза",
|
||||
"@videoActionPause": {},
|
||||
"videoActionPlay": "Прайграць",
|
||||
"@videoActionPlay": {},
|
||||
"videoActionSetSpeed": "Хуткасць прайгравання",
|
||||
"@videoActionSetSpeed": {},
|
||||
"viewerActionLock": "Блакіроўка прагляду",
|
||||
"@viewerActionLock": {},
|
||||
"slideshowActionResume": "Аднавіць",
|
||||
"@slideshowActionResume": {},
|
||||
"viewerActionUnlock": "Разблакіроўка прагляду",
|
||||
"@viewerActionUnlock": {},
|
||||
"columnCount": "{count, plural, =1{1 column} other{{count} columns}}",
|
||||
"@columnCount": {
|
||||
"placeholders": {
|
||||
"count": {}
|
||||
}
|
||||
},
|
||||
"timeSeconds": "{seconds, plural, =1{1 second} other{{seconds} seconds}}",
|
||||
"@timeSeconds": {
|
||||
"placeholders": {
|
||||
"seconds": {}
|
||||
}
|
||||
},
|
||||
"timeMinutes": "{minutes, plural, =1{1 minute} other{{minutes} minutes}}",
|
||||
"@timeMinutes": {
|
||||
"placeholders": {
|
||||
"minutes": {}
|
||||
}
|
||||
},
|
||||
"timeDays": "{days, plural, =1{1 day} other{{days} days}}",
|
||||
"@timeDays": {
|
||||
"placeholders": {
|
||||
"days": {}
|
||||
}
|
||||
},
|
||||
"entryActionExport": "Экспарт",
|
||||
"@entryActionExport": {},
|
||||
"entryActionInfo": "Інфармацыя",
|
||||
"@entryActionInfo": {},
|
||||
"entryActionRename": "Перайменаваць",
|
||||
"@entryActionRename": {},
|
||||
"entryActionRotateCW": "Круціць па гадзіннікавай стрэлцы",
|
||||
"@entryActionRotateCW": {},
|
||||
"entryActionFlip": "Перавярнуць па гарызанталі",
|
||||
"@entryActionFlip": {},
|
||||
"entryActionPrint": "Друк",
|
||||
"@entryActionPrint": {},
|
||||
"entryActionShare": "Падзяліцца",
|
||||
"@entryActionShare": {},
|
||||
"entryActionShareImageOnly": "Падзяліцца толькі выявай",
|
||||
"@entryActionShareImageOnly": {},
|
||||
"entryActionShareVideoOnly": "Падзяліцца толькі відэа",
|
||||
"@entryActionShareVideoOnly": {},
|
||||
"entryActionEdit": "Рэдагаваць",
|
||||
"@entryActionEdit": {},
|
||||
"entryActionOpen": "Адкрыць з дапамогай",
|
||||
"@entryActionOpen": {},
|
||||
"entryActionOpenMap": "Паказаць у праграме карты",
|
||||
"@entryActionOpenMap": {},
|
||||
"videoActionMute": "Адключыць гук",
|
||||
"@videoActionMute": {},
|
||||
"slideshowActionShowInCollection": "Паказаць у калекцыі",
|
||||
"@slideshowActionShowInCollection": {},
|
||||
"entryInfoActionEditDate": "Рэдагаваць дату і час",
|
||||
"@entryInfoActionEditDate": {},
|
||||
"entryInfoActionEditRating": "Рэдагаваць рэйтынг",
|
||||
"@entryInfoActionEditRating": {},
|
||||
"entryInfoActionEditTags": "Рэдагаваць тэгі",
|
||||
"@entryInfoActionEditTags": {},
|
||||
"entryInfoActionExportMetadata": "Экспарт метададзеных",
|
||||
"@entryInfoActionExportMetadata": {},
|
||||
"entryInfoActionRemoveLocation": "Выдаліць месцазнаходжанне",
|
||||
"@entryInfoActionRemoveLocation": {},
|
||||
"editorActionTransform": "Трансфармаваць",
|
||||
"@editorActionTransform": {},
|
||||
"cropAspectRatioFree": "Свабодныя",
|
||||
"@cropAspectRatioFree": {},
|
||||
"cropAspectRatioOriginal": "Першапачатковае",
|
||||
"@cropAspectRatioOriginal": {},
|
||||
"cropAspectRatioSquare": "Квадратнае",
|
||||
"@cropAspectRatioSquare": {},
|
||||
"coordinateDmsNorth": "Поўнач",
|
||||
"@coordinateDmsNorth": {},
|
||||
"filterAspectRatioPortraitLabel": "Партрэтныя",
|
||||
"@filterAspectRatioPortraitLabel": {},
|
||||
"filterTypeMotionPhotoLabel": "Фота з рухам",
|
||||
"@filterTypeMotionPhotoLabel": {},
|
||||
"filterRecentlyAddedLabel": "Нядаўна дададзены",
|
||||
"@filterRecentlyAddedLabel": {},
|
||||
"filterTypeAnimatedLabel": "Аніміраваныя",
|
||||
"@filterTypeAnimatedLabel": {},
|
||||
"filterTypeRawLabel": "Без апрацоўкі",
|
||||
"@filterTypeRawLabel": {},
|
||||
"filterTypeSphericalVideoLabel": "Відэа 360°",
|
||||
"@filterTypeSphericalVideoLabel": {},
|
||||
"filterNoTitleLabel": "Без назвы",
|
||||
"@filterNoTitleLabel": {},
|
||||
"filterOnThisDayLabel": "У гэты дзень",
|
||||
"@filterOnThisDayLabel": {},
|
||||
"filterRatingRejectedLabel": "Адхілена",
|
||||
"@filterRatingRejectedLabel": {},
|
||||
"albumTierRegular": "Іншыя",
|
||||
"@albumTierRegular": {},
|
||||
"filterTypeGeotiffLabel": "GeoTIFF",
|
||||
"@filterTypeGeotiffLabel": {},
|
||||
"coordinateDms": "{coordinate} {direction}",
|
||||
"@coordinateDms": {
|
||||
"placeholders": {
|
||||
"coordinate": {
|
||||
"type": "String",
|
||||
"example": "38° 41′ 47.72″"
|
||||
},
|
||||
"direction": {
|
||||
"type": "String",
|
||||
"example": "S"
|
||||
}
|
||||
}
|
||||
},
|
||||
"coordinateFormatDms": "Градусы, хвіліны і секунды",
|
||||
"@coordinateFormatDms": {},
|
||||
"mapStyleGoogleHybrid": "Карты Google (гібрыд)",
|
||||
"@mapStyleGoogleHybrid": {},
|
||||
"coordinateFormatDecimal": "Дзесятковы градус",
|
||||
"@coordinateFormatDecimal": {},
|
||||
"subtitlePositionBottom": "Ніз",
|
||||
"@subtitlePositionBottom": {},
|
||||
"videoControlsPlaySeek": "Прайграванне і перамотка назад/уперад",
|
||||
"@videoControlsPlaySeek": {},
|
||||
"nameConflictStrategyReplace": "Замяніць",
|
||||
"@nameConflictStrategyReplace": {},
|
||||
"filterAspectRatioLandscapeLabel": "Ландшафтныя",
|
||||
"@filterAspectRatioLandscapeLabel": {},
|
||||
"filterBinLabel": "Кошык",
|
||||
"@filterBinLabel": {},
|
||||
"filterFavouriteLabel": "Выбранае",
|
||||
"@filterFavouriteLabel": {},
|
||||
"filterNoDateLabel": "Без даты",
|
||||
"@filterNoDateLabel": {},
|
||||
"filterNoAddressLabel": "Без адрасу",
|
||||
"@filterNoAddressLabel": {},
|
||||
"filterLocatedLabel": "Месцазнаходжанне",
|
||||
"@filterLocatedLabel": {},
|
||||
"filterNoLocationLabel": "Без месцазнаходжання",
|
||||
"@filterNoLocationLabel": {},
|
||||
"filterNoRatingLabel": "Без рэйтынгу",
|
||||
"@filterNoRatingLabel": {},
|
||||
"filterTaggedLabel": "З тэгамі",
|
||||
"@filterTaggedLabel": {},
|
||||
"filterNoTagLabel": "Без тэгаў",
|
||||
"@filterNoTagLabel": {},
|
||||
"filterTypePanoramaLabel": "Панарама",
|
||||
"@filterTypePanoramaLabel": {},
|
||||
"filterMimeImageLabel": "Малюнак",
|
||||
"@filterMimeImageLabel": {},
|
||||
"filterMimeVideoLabel": "Відэа",
|
||||
"@filterMimeVideoLabel": {},
|
||||
"accessibilityAnimationsRemove": "Прадухіленне экранных эфектаў",
|
||||
"@accessibilityAnimationsRemove": {},
|
||||
"accessibilityAnimationsKeep": "Захаваць экранныя эфекты",
|
||||
"@accessibilityAnimationsKeep": {},
|
||||
"albumTierNew": "Новы",
|
||||
"@albumTierNew": {},
|
||||
"albumTierPinned": "Замацаваны",
|
||||
"@albumTierPinned": {},
|
||||
"albumTierApps": "Праграмы",
|
||||
"@albumTierApps": {},
|
||||
"albumTierVaults": "Сховішчы",
|
||||
"@albumTierVaults": {},
|
||||
"albumTierSpecial": "Стандартныя",
|
||||
"@albumTierSpecial": {},
|
||||
"coordinateDmsSouth": "Поўдзень",
|
||||
"@coordinateDmsSouth": {},
|
||||
"coordinateDmsEast": "Усход",
|
||||
"@coordinateDmsEast": {},
|
||||
"coordinateDmsWest": "Захад",
|
||||
"@coordinateDmsWest": {},
|
||||
"displayRefreshRatePreferHighest": "Найвышэйшая частата",
|
||||
"@displayRefreshRatePreferHighest": {},
|
||||
"displayRefreshRatePreferLowest": "Найменшая частата",
|
||||
"@displayRefreshRatePreferLowest": {},
|
||||
"keepScreenOnNever": "Ніколі",
|
||||
"@keepScreenOnNever": {},
|
||||
"keepScreenOnVideoPlayback": "Падчас прайгравання відэа",
|
||||
"@keepScreenOnVideoPlayback": {},
|
||||
"keepScreenOnViewerOnly": "Толькі ў праглядніку",
|
||||
"@keepScreenOnViewerOnly": {},
|
||||
"keepScreenOnAlways": "Заўсёды",
|
||||
"@keepScreenOnAlways": {},
|
||||
"lengthUnitPixel": "px",
|
||||
"@lengthUnitPixel": {},
|
||||
"lengthUnitPercent": "%",
|
||||
"@lengthUnitPercent": {},
|
||||
"mapStyleGoogleNormal": "Карты Google",
|
||||
"@mapStyleGoogleNormal": {},
|
||||
"mapStyleGoogleTerrain": "Карты Google (Рэльеф мясцовасці)",
|
||||
"@mapStyleGoogleTerrain": {},
|
||||
"mapStyleHuaweiNormal": "Карты Petal",
|
||||
"@mapStyleHuaweiNormal": {},
|
||||
"mapStyleHuaweiTerrain": "Карты Petal (Рэльеф мясцовасці)",
|
||||
"@mapStyleHuaweiTerrain": {},
|
||||
"mapStyleOsmHot": "Гуманітарная ОСМ",
|
||||
"@mapStyleOsmHot": {},
|
||||
"mapStyleStamenToner": "Тычынкавы тонер",
|
||||
"@mapStyleStamenToner": {},
|
||||
"mapStyleStamenWatercolor": "Тычынка Акварэль",
|
||||
"@mapStyleStamenWatercolor": {},
|
||||
"maxBrightnessNever": "Ніколі",
|
||||
"@maxBrightnessNever": {},
|
||||
"maxBrightnessAlways": "Заўсёды",
|
||||
"@maxBrightnessAlways": {},
|
||||
"nameConflictStrategyRename": "Перайменаваць",
|
||||
"@nameConflictStrategyRename": {},
|
||||
"nameConflictStrategySkip": "Прапусціць",
|
||||
"@nameConflictStrategySkip": {},
|
||||
"subtitlePositionTop": "Верх",
|
||||
"@subtitlePositionTop": {},
|
||||
"themeBrightnessLight": "Светлая",
|
||||
"@themeBrightnessLight": {},
|
||||
"themeBrightnessDark": "Цёмная",
|
||||
"@themeBrightnessDark": {},
|
||||
"themeBrightnessBlack": "Чорная",
|
||||
"@themeBrightnessBlack": {},
|
||||
"unitSystemMetric": "Метрычныя адзінкі вымярэння",
|
||||
"@unitSystemMetric": {},
|
||||
"unitSystemImperial": "Імперская",
|
||||
"@unitSystemImperial": {},
|
||||
"vaultLockTypePattern": "Шаблон",
|
||||
"@vaultLockTypePattern": {},
|
||||
"vaultLockTypePin": "PIN-код",
|
||||
"@vaultLockTypePin": {},
|
||||
"vaultLockTypePassword": "Пароль",
|
||||
"@vaultLockTypePassword": {},
|
||||
"settingsVideoEnablePip": "Карцінка ў карцінцы",
|
||||
"@settingsVideoEnablePip": {},
|
||||
"videoControlsPlayOutside": "Адкрыць у іншым прайгравальніку",
|
||||
"@videoControlsPlayOutside": {},
|
||||
"videoControlsPlay": "Прайграць",
|
||||
"@videoControlsPlay": {},
|
||||
"videoLoopModeNever": "Ніколі",
|
||||
"@videoLoopModeNever": {},
|
||||
"videoLoopModeShortOnly": "Толькі для кароткіх відэа",
|
||||
"@videoLoopModeShortOnly": {},
|
||||
"videoPlaybackSkip": "Прапусціць",
|
||||
"@videoPlaybackSkip": {},
|
||||
"videoPlaybackMuted": "Гуляць без гука",
|
||||
"@videoPlaybackMuted": {},
|
||||
"videoPlaybackWithSound": "Гуляць з гукам",
|
||||
"@videoPlaybackWithSound": {},
|
||||
"videoResumptionModeNever": "Ніколі",
|
||||
"@videoResumptionModeNever": {},
|
||||
"videoResumptionModeAlways": "Заўсёды",
|
||||
"@videoResumptionModeAlways": {},
|
||||
"viewerTransitionParallax": "Паралакс",
|
||||
"@viewerTransitionParallax": {},
|
||||
"videoLoopModeAlways": "Заўсёды",
|
||||
"@videoLoopModeAlways": {},
|
||||
"widgetDisplayedItemMostRecent": "Самы апошні",
|
||||
"@widgetDisplayedItemMostRecent": {},
|
||||
"storageVolumeDescriptionFallbackNonPrimary": "SD-карта",
|
||||
"@storageVolumeDescriptionFallbackNonPrimary": {},
|
||||
"rootDirectoryDescription": "каранёвы каталог",
|
||||
"@rootDirectoryDescription": {},
|
||||
"otherDirectoryDescription": "Каталог “{name}”",
|
||||
"@otherDirectoryDescription": {
|
||||
"placeholders": {
|
||||
"name": {
|
||||
"type": "String",
|
||||
"example": "Pictures",
|
||||
"description": "the name of a specific directory"
|
||||
}
|
||||
}
|
||||
},
|
||||
"storageAccessDialogMessage": "Калі ласка, выберыце {directory} «{volume}» на наступным экране, каб даць гэтай праграме доступ да яго.",
|
||||
"@storageAccessDialogMessage": {
|
||||
"placeholders": {
|
||||
"directory": {
|
||||
"type": "String",
|
||||
"description": "the name of a directory, using the output of `rootDirectoryDescription` or `otherDirectoryDescription`"
|
||||
},
|
||||
"volume": {
|
||||
"type": "String",
|
||||
"example": "SD card",
|
||||
"description": "the name of a storage volume"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notEnoughSpaceDialogMessage": "Для завяршэння гэтай аперацыі патрабуецца {neededSize} вольнага месца на “{volume}”, але засталося толькі {freeSize}.",
|
||||
"@notEnoughSpaceDialogMessage": {
|
||||
"placeholders": {
|
||||
"neededSize": {
|
||||
"type": "String",
|
||||
"example": "314 MB"
|
||||
},
|
||||
"freeSize": {
|
||||
"type": "String",
|
||||
"example": "123 MB"
|
||||
},
|
||||
"volume": {
|
||||
"type": "String",
|
||||
"example": "SD card",
|
||||
"description": "the name of a storage volume"
|
||||
}
|
||||
}
|
||||
},
|
||||
"nameConflictDialogSingleSourceMessage": "Некаторыя файлы ў тэчцы прызначэння маюць аднолькавыя назвы.",
|
||||
"@nameConflictDialogSingleSourceMessage": {},
|
||||
"nameConflictDialogMultipleSourceMessage": "Некаторыя файлы маюць аднолькавыя назвы.",
|
||||
"@nameConflictDialogMultipleSourceMessage": {},
|
||||
"setCoverDialogLatest": "Апошні элемент",
|
||||
"@setCoverDialogLatest": {},
|
||||
"vaultDialogLockModeWhenScreenOff": "Блакіроўка пры выключэнні экрана",
|
||||
"@vaultDialogLockModeWhenScreenOff": {},
|
||||
"wallpaperTargetHome": "Галоўны экран",
|
||||
"@wallpaperTargetHome": {},
|
||||
"wallpaperTargetLock": "Экран блакіроўкі",
|
||||
"@wallpaperTargetLock": {},
|
||||
"wallpaperTargetHomeLock": "На абодва экраны",
|
||||
"@wallpaperTargetHomeLock": {},
|
||||
"widgetTapUpdateWidget": "Абнавіць віджэт",
|
||||
"@widgetTapUpdateWidget": {},
|
||||
"storageVolumeDescriptionFallbackPrimary": "Унутраная памяць",
|
||||
"@storageVolumeDescriptionFallbackPrimary": {},
|
||||
"restrictedAccessDialogMessage": "Гэтай праграме забаронена змяняць файлы ў {directory} «{volume}».\n\nКаб перамясціць элементы ў іншую дырэкторыю, выкарыстоўвайце папярэдне ўсталяваны дыспетчар файлаў або праграму галерэі.",
|
||||
"@restrictedAccessDialogMessage": {
|
||||
"placeholders": {
|
||||
"directory": {
|
||||
"type": "String",
|
||||
"description": "the name of a directory, using the output of `rootDirectoryDescription` or `otherDirectoryDescription`"
|
||||
},
|
||||
"volume": {
|
||||
"type": "String",
|
||||
"example": "SD card",
|
||||
"description": "the name of a storage volume"
|
||||
}
|
||||
}
|
||||
},
|
||||
"missingSystemFilePickerDialogMessage": "Сродак выбару сістэмных файлаў адсутнічае або адключаны. Уключыце яго і паўтарыце спробу.",
|
||||
"@missingSystemFilePickerDialogMessage": {},
|
||||
"unsupportedTypeDialogMessage": "{count, plural, =1{Гэта аперацыя не падтрымліваецца для элементаў наступнага тыпу: {types}.} other{Гэта аперацыя не падтрымліваецца для элементаў наступных тыпаў: {types}.}}",
|
||||
"@unsupportedTypeDialogMessage": {
|
||||
"placeholders": {
|
||||
"count": {},
|
||||
"types": {
|
||||
"type": "String",
|
||||
"example": "GIF, TIFF, MP4",
|
||||
"description": "a list of unsupported types"
|
||||
}
|
||||
}
|
||||
},
|
||||
"addShortcutDialogLabel": "Ярлык хуткага доступу",
|
||||
"@addShortcutDialogLabel": {},
|
||||
"addShortcutButtonLabel": "ДАДАЦЬ",
|
||||
"@addShortcutButtonLabel": {},
|
||||
"noMatchingAppDialogMessage": "Няма праграм, якія б з гэтым справіліся.",
|
||||
"@noMatchingAppDialogMessage": {},
|
||||
"moveUndatedConfirmationDialogMessage": "Захаваць даты элементаў, перш чым працягнуць?",
|
||||
"@moveUndatedConfirmationDialogMessage": {},
|
||||
"moveUndatedConfirmationDialogSetDate": "Захаваць даты",
|
||||
"@moveUndatedConfirmationDialogSetDate": {},
|
||||
"videoResumeDialogMessage": "Вы хочаце аднавіць гульню ў {time}?",
|
||||
"@videoResumeDialogMessage": {
|
||||
"placeholders": {
|
||||
"time": {
|
||||
"type": "String",
|
||||
"example": "13:37"
|
||||
}
|
||||
}
|
||||
},
|
||||
"videoStartOverButtonLabel": "ПАЧАЦЬ НАНАВА",
|
||||
"@videoStartOverButtonLabel": {},
|
||||
"videoResumeButtonLabel": "АДНАВІЦЬ",
|
||||
"@videoResumeButtonLabel": {},
|
||||
"setCoverDialogAuto": "Аўто",
|
||||
"@setCoverDialogAuto": {},
|
||||
"newAlbumDialogTitle": "Новы альбом",
|
||||
"@newAlbumDialogTitle": {},
|
||||
"newAlbumDialogNameLabel": "Назва альбома",
|
||||
"@newAlbumDialogNameLabel": {},
|
||||
"newAlbumDialogNameLabelAlreadyExistsHelper": "Каталог ужо існуе",
|
||||
"@newAlbumDialogNameLabelAlreadyExistsHelper": {},
|
||||
"newAlbumDialogStorageLabel": "Захоўванне:",
|
||||
"@newAlbumDialogStorageLabel": {},
|
||||
"newVaultDialogTitle": "Новае сховішча",
|
||||
"@newVaultDialogTitle": {},
|
||||
"configureVaultDialogTitle": "Наладзьце сховішча",
|
||||
"@configureVaultDialogTitle": {},
|
||||
"vaultDialogLockTypeLabel": "Тып блакіроўкі",
|
||||
"@vaultDialogLockTypeLabel": {},
|
||||
"pinDialogEnter": "Увядзіце PIN-код",
|
||||
"@pinDialogEnter": {},
|
||||
"patternDialogEnter": "Увядзіце графічны ключ",
|
||||
"@patternDialogEnter": {},
|
||||
"patternDialogConfirm": "Пацвердзіце графічны ключ",
|
||||
"@patternDialogConfirm": {},
|
||||
"pinDialogConfirm": "Пацвердзіце PIN-код",
|
||||
"@pinDialogConfirm": {},
|
||||
"passwordDialogEnter": "Увядзіце пароль",
|
||||
"@passwordDialogEnter": {},
|
||||
"passwordDialogConfirm": "Пацвердзіце пароль",
|
||||
"@passwordDialogConfirm": {},
|
||||
"authenticateToConfigureVault": "Прайдзіце аўтэнтыфікацыю, каб наладзіць сховішча",
|
||||
"@authenticateToConfigureVault": {},
|
||||
"authenticateToUnlockVault": "Прайдзіце аўтэнтыфікацыю, каб разблакіраваць сховішча",
|
||||
"@authenticateToUnlockVault": {},
|
||||
"statsTopTagsSectionTitle": "Лепшыя тэгі",
|
||||
"@statsTopTagsSectionTitle": {},
|
||||
"statsTopPlacesSectionTitle": "Лепшыя месцы",
|
||||
"@statsTopPlacesSectionTitle": {},
|
||||
"viewerInfoSearchSuggestionDescription": "Апісанне",
|
||||
"@viewerInfoSearchSuggestionDescription": {},
|
||||
"viewerInfoSearchSuggestionDate": "Дата і час",
|
||||
"@viewerInfoSearchSuggestionDate": {},
|
||||
"viewerInfoViewXmlLinkText": "Прагляд XML",
|
||||
"@viewerInfoViewXmlLinkText": {},
|
||||
"viewerInfoOpenLinkText": "Адкрыць",
|
||||
"@viewerInfoOpenLinkText": {},
|
||||
"mapAttributionStamen": "Даныя карты © [OpenStreetMap](https://www.openstreetmap.org/copyright) удзельнікі • Пліткі ад [Stamen Design](https://stamen.com), [CC BY 3.0](https://creativecommons.org/licenses/by/3.0)",
|
||||
"@mapAttributionStamen": {},
|
||||
"mapPointNorthUpTooltip": "Пакажыце поўнач уверх",
|
||||
"@mapPointNorthUpTooltip": {},
|
||||
"viewerInfoLabelCoordinates": "Каардынаты",
|
||||
"@viewerInfoLabelCoordinates": {},
|
||||
"viewerInfoLabelOwner": "Уладальнік",
|
||||
"@viewerInfoLabelOwner": {},
|
||||
"viewerInfoLabelDuration": "Працягласць",
|
||||
"@viewerInfoLabelDuration": {},
|
||||
"viewerInfoLabelPath": "Шлях",
|
||||
"@viewerInfoLabelPath": {},
|
||||
"viewerInfoLabelResolution": "Дазвол",
|
||||
"@viewerInfoLabelResolution": {},
|
||||
"viewerInfoBackToViewerTooltip": "Вярнуцца да аглядальніка",
|
||||
"@viewerInfoBackToViewerTooltip": {},
|
||||
"viewerInfoPageTitle": "Інфармацыя",
|
||||
"@viewerInfoPageTitle": {},
|
||||
"viewerErrorDoesNotExist": "Файл больш не існуе.",
|
||||
"@viewerErrorDoesNotExist": {},
|
||||
"filePickerUseThisFolder": "Выкарыстоўваць гэтую тэчку",
|
||||
"@filePickerUseThisFolder": {},
|
||||
"filePickerNoItems": "Няма элементаў",
|
||||
"@filePickerNoItems": {},
|
||||
"filePickerOpenFrom": "Адкрыць з",
|
||||
"@filePickerOpenFrom": {},
|
||||
"filePickerShowHiddenFiles": "Паказаць схаваныя файлы",
|
||||
"@filePickerShowHiddenFiles": {},
|
||||
"sourceViewerPageTitle": "Крыніца",
|
||||
"@sourceViewerPageTitle": {},
|
||||
"panoramaDisableSensorControl": "Адключыць сэнсарнае кіраванне",
|
||||
"@panoramaDisableSensorControl": {},
|
||||
"panoramaEnableSensorControl": "Уключыць сэнсарнае кіраванне",
|
||||
"@panoramaEnableSensorControl": {},
|
||||
"tagPlaceholderPlace": "Месца",
|
||||
"@tagPlaceholderPlace": {},
|
||||
"tagPlaceholderState": "Дзяржава",
|
||||
"@tagPlaceholderState": {},
|
||||
"tagEditorSectionPlaceholders": "Запаўняльнікі",
|
||||
"@tagEditorSectionPlaceholders": {},
|
||||
"tagEditorSectionRecent": "Апошнія",
|
||||
"@tagEditorSectionRecent": {},
|
||||
"tagEditorPageAddTagTooltip": "Дадаць тэг",
|
||||
"@tagEditorPageAddTagTooltip": {},
|
||||
"tagEditorPageNewTagFieldLabel": "Новы тэг",
|
||||
"@tagEditorPageNewTagFieldLabel": {},
|
||||
"wallpaperUseScrollEffect": "Выкарыстоўвайце эфект пракруткі на галоўным экране",
|
||||
"@wallpaperUseScrollEffect": {},
|
||||
"viewerInfoSearchSuggestionResolution": "Дазвол",
|
||||
"@viewerInfoSearchSuggestionResolution": {},
|
||||
"viewerInfoSearchSuggestionDimensions": "Памеры",
|
||||
"@viewerInfoSearchSuggestionDimensions": {},
|
||||
"videoControlsNone": "Без",
|
||||
"@videoControlsNone": {},
|
||||
"viewerErrorUnknown": "Ой!",
|
||||
"@viewerErrorUnknown": {},
|
||||
"viewerSetWallpaperButtonLabel": "УСТАНАВІЦЬ ШПАЛЕРЫ",
|
||||
"@viewerSetWallpaperButtonLabel": {},
|
||||
"statsTopAlbumsSectionTitle": "Лепшыя альбомы",
|
||||
"@statsTopAlbumsSectionTitle": {},
|
||||
"viewerInfoUnknown": "невядома",
|
||||
"@viewerInfoUnknown": {},
|
||||
"viewerInfoLabelTitle": "Назва",
|
||||
"@viewerInfoLabelTitle": {},
|
||||
"viewerInfoLabelDescription": "Апісанне",
|
||||
"@viewerInfoLabelDescription": {},
|
||||
"viewerInfoLabelDate": "Дата",
|
||||
"@viewerInfoLabelDate": {},
|
||||
"viewerInfoLabelAddress": "Адрас",
|
||||
"@viewerInfoLabelAddress": {},
|
||||
"mapZoomInTooltip": "Павелічэнне",
|
||||
"@mapZoomInTooltip": {},
|
||||
"mapStyleTooltip": "Выберыце стыль карты",
|
||||
"@mapStyleTooltip": {},
|
||||
"mapStyleDialogTitle": "Стыль карты",
|
||||
"@mapStyleDialogTitle": {},
|
||||
"mapZoomOutTooltip": "Змяншэння",
|
||||
"@mapZoomOutTooltip": {},
|
||||
"openMapPageTooltip": "Паглядзець на старонцы карты",
|
||||
"@openMapPageTooltip": {},
|
||||
"mapEmptyRegion": "У гэтым рэгіёне няма малюнкаў",
|
||||
"@mapEmptyRegion": {},
|
||||
"viewerInfoSearchEmpty": "Няма адпаведных ключоў",
|
||||
"@viewerInfoSearchEmpty": {},
|
||||
"viewerInfoSearchFieldLabel": "Пошук метададзеных",
|
||||
"@viewerInfoSearchFieldLabel": {},
|
||||
"tagEditorPageTitle": "Рэдагаваць тэгі",
|
||||
"@tagEditorPageTitle": {},
|
||||
"tagEditorDiscardDialogMessage": "Вы хочаце адмяніць змены?",
|
||||
"@tagEditorDiscardDialogMessage": {},
|
||||
"tagPlaceholderCountry": "Краіна",
|
||||
"@tagPlaceholderCountry": {},
|
||||
"filePickerDoNotShowHiddenFiles": "Не паказваць схаваныя файлы",
|
||||
"@filePickerDoNotShowHiddenFiles": {},
|
||||
"viewerInfoOpenEmbeddedFailureFeedback": "Не ўдалося атрымаць убудаваныя даныя",
|
||||
"@viewerInfoOpenEmbeddedFailureFeedback": {},
|
||||
"mapAttributionOsmHot": "Даныя карты © [OpenStreetMap](https://www.openstreetmap.org/copyright) удзельнікі • Пліткі ад [HOT](https://www.hotosm.org/) • Арганізаваны [OSM France](https://openstreetmap.fr/)",
|
||||
"@mapAttributionOsmHot": {},
|
||||
"viewerInfoLabelSize": "Памер",
|
||||
"@viewerInfoLabelSize": {}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"@@locale" : "ckb",
|
||||
"@@locale": "ckb",
|
||||
"welcomeOptional": "ئارەزومەندانە",
|
||||
"@welcomeOptional": {},
|
||||
"welcomeTermsToggle": "ڕازیم بە مەرج و یاساکانی بەکارهێنان",
|
||||
|
|
|
@ -1486,5 +1486,25 @@
|
|||
"settingsVideoPlaybackPageTitle": "Přehrávání",
|
||||
"@settingsVideoPlaybackPageTitle": {},
|
||||
"settingsVideoResumptionModeTile": "Obnovit přehrávání",
|
||||
"@settingsVideoResumptionModeTile": {}
|
||||
"@settingsVideoResumptionModeTile": {},
|
||||
"editorActionTransform": "Transformovat",
|
||||
"@editorActionTransform": {},
|
||||
"cropAspectRatioSquare": "Čtverec",
|
||||
"@cropAspectRatioSquare": {},
|
||||
"cropAspectRatioFree": "Volný",
|
||||
"@cropAspectRatioFree": {},
|
||||
"aboutDataUsageSectionTitle": "Využitý prostor",
|
||||
"@aboutDataUsageSectionTitle": {},
|
||||
"aboutDataUsageData": "Data",
|
||||
"@aboutDataUsageData": {},
|
||||
"aboutDataUsageCache": "Mezipaměť",
|
||||
"@aboutDataUsageCache": {},
|
||||
"aboutDataUsageDatabase": "Databáze",
|
||||
"@aboutDataUsageDatabase": {},
|
||||
"aboutDataUsageMisc": "Různé",
|
||||
"@aboutDataUsageMisc": {},
|
||||
"aboutDataUsageInternal": "Interní",
|
||||
"@aboutDataUsageInternal": {},
|
||||
"aboutDataUsageExternal": "Externí",
|
||||
"@aboutDataUsageExternal": {}
|
||||
}
|
||||
|
|
|
@ -1334,5 +1334,19 @@
|
|||
"cropAspectRatioSquare": "Quadrat",
|
||||
"@cropAspectRatioSquare": {},
|
||||
"widgetTapUpdateWidget": "Widget öffnen",
|
||||
"@widgetTapUpdateWidget": {}
|
||||
"@widgetTapUpdateWidget": {},
|
||||
"aboutDataUsageSectionTitle": "Datennutzung",
|
||||
"@aboutDataUsageSectionTitle": {},
|
||||
"aboutDataUsageData": "Daten",
|
||||
"@aboutDataUsageData": {},
|
||||
"aboutDataUsageCache": "Cache",
|
||||
"@aboutDataUsageCache": {},
|
||||
"aboutDataUsageMisc": "Sonstiges",
|
||||
"@aboutDataUsageMisc": {},
|
||||
"aboutDataUsageInternal": "Intern",
|
||||
"@aboutDataUsageInternal": {},
|
||||
"aboutDataUsageExternal": "Extern",
|
||||
"@aboutDataUsageExternal": {},
|
||||
"aboutDataUsageDatabase": "Datenbank",
|
||||
"@aboutDataUsageDatabase": {}
|
||||
}
|
||||
|
|
|
@ -233,6 +233,10 @@
|
|||
"nameConflictStrategyReplace": "Replace",
|
||||
"nameConflictStrategySkip": "Skip",
|
||||
|
||||
"overlayHistogramNone": "None",
|
||||
"overlayHistogramRGB": "RGB",
|
||||
"overlayHistogramLuminance": "Luminance",
|
||||
|
||||
"subtitlePositionTop": "Top",
|
||||
"subtitlePositionBottom": "Bottom",
|
||||
|
||||
|
@ -533,6 +537,14 @@
|
|||
"aboutBugReportInstruction": "Report on GitHub with the logs and system information",
|
||||
"aboutBugReportButton": "Report",
|
||||
|
||||
"aboutDataUsageSectionTitle": "Data Usage",
|
||||
"aboutDataUsageData": "Data",
|
||||
"aboutDataUsageCache": "Cache",
|
||||
"aboutDataUsageDatabase": "Database",
|
||||
"aboutDataUsageMisc": "Misc",
|
||||
"aboutDataUsageInternal": "Internal",
|
||||
"aboutDataUsageExternal": "External",
|
||||
|
||||
"aboutCreditsSectionTitle": "Credits",
|
||||
"aboutCreditsWorldAtlas1": "This app uses a TopoJSON file from",
|
||||
"aboutCreditsWorldAtlas2": "under ISC License.",
|
||||
|
@ -801,6 +813,7 @@
|
|||
"settingsViewerOverlayTile": "Overlay",
|
||||
"settingsViewerOverlayPageTitle": "Overlay",
|
||||
"settingsViewerShowOverlayOnOpening": "Show on opening",
|
||||
"settingsViewerShowHistogram": "Show histogram",
|
||||
"settingsViewerShowMinimap": "Show minimap",
|
||||
"settingsViewerShowInformation": "Show information",
|
||||
"settingsViewerShowInformationSubtitle": "Show title, date, location, etc.",
|
||||
|
|
|
@ -1334,5 +1334,27 @@
|
|||
"cropAspectRatioOriginal": "Original",
|
||||
"@cropAspectRatioOriginal": {},
|
||||
"widgetTapUpdateWidget": "Actualizar el widget",
|
||||
"@widgetTapUpdateWidget": {}
|
||||
"@widgetTapUpdateWidget": {},
|
||||
"aboutDataUsageData": "Datos",
|
||||
"@aboutDataUsageData": {},
|
||||
"aboutDataUsageCache": "Cache",
|
||||
"@aboutDataUsageCache": {},
|
||||
"aboutDataUsageDatabase": "Base de datos",
|
||||
"@aboutDataUsageDatabase": {},
|
||||
"aboutDataUsageInternal": "Interno",
|
||||
"@aboutDataUsageInternal": {},
|
||||
"aboutDataUsageExternal": "Exterior",
|
||||
"@aboutDataUsageExternal": {},
|
||||
"aboutDataUsageSectionTitle": "Uso de los datos",
|
||||
"@aboutDataUsageSectionTitle": {},
|
||||
"aboutDataUsageMisc": "Varios",
|
||||
"@aboutDataUsageMisc": {},
|
||||
"overlayHistogramLuminance": "Luminancia",
|
||||
"@overlayHistogramLuminance": {},
|
||||
"overlayHistogramNone": "Ninguna",
|
||||
"@overlayHistogramNone": {},
|
||||
"overlayHistogramRGB": "RGB",
|
||||
"@overlayHistogramRGB": {},
|
||||
"settingsViewerShowHistogram": "Mostrar el histograma",
|
||||
"@settingsViewerShowHistogram": {}
|
||||
}
|
||||
|
|
|
@ -1492,5 +1492,27 @@
|
|||
"cropAspectRatioSquare": "Karratua",
|
||||
"@cropAspectRatioSquare": {},
|
||||
"widgetTapUpdateWidget": "Eguneratu widgeta",
|
||||
"@widgetTapUpdateWidget": {}
|
||||
"@widgetTapUpdateWidget": {},
|
||||
"aboutDataUsageSectionTitle": "Datuen erabilera",
|
||||
"@aboutDataUsageSectionTitle": {},
|
||||
"aboutDataUsageData": "Datuak",
|
||||
"@aboutDataUsageData": {},
|
||||
"aboutDataUsageDatabase": "Datu-basea",
|
||||
"@aboutDataUsageDatabase": {},
|
||||
"aboutDataUsageMisc": "Askotariko",
|
||||
"@aboutDataUsageMisc": {},
|
||||
"aboutDataUsageInternal": "Barnekoa",
|
||||
"@aboutDataUsageInternal": {},
|
||||
"aboutDataUsageExternal": "Kanpokoa",
|
||||
"@aboutDataUsageExternal": {},
|
||||
"aboutDataUsageCache": "Cachea",
|
||||
"@aboutDataUsageCache": {},
|
||||
"overlayHistogramLuminance": "Luminantzia",
|
||||
"@overlayHistogramLuminance": {},
|
||||
"overlayHistogramNone": "Bat ere ez",
|
||||
"@overlayHistogramNone": {},
|
||||
"overlayHistogramRGB": "RGB",
|
||||
"@overlayHistogramRGB": {},
|
||||
"settingsViewerShowHistogram": "Erakutsi histograma",
|
||||
"@settingsViewerShowHistogram": {}
|
||||
}
|
||||
|
|
|
@ -1334,5 +1334,27 @@
|
|||
"cropAspectRatioOriginal": "Photo d’origine",
|
||||
"@cropAspectRatioOriginal": {},
|
||||
"widgetTapUpdateWidget": "Mettre à jour le widget",
|
||||
"@widgetTapUpdateWidget": {}
|
||||
"@widgetTapUpdateWidget": {},
|
||||
"aboutDataUsageDatabase": "Base de données",
|
||||
"@aboutDataUsageDatabase": {},
|
||||
"aboutDataUsageMisc": "Divers",
|
||||
"@aboutDataUsageMisc": {},
|
||||
"aboutDataUsageInternal": "Interne",
|
||||
"@aboutDataUsageInternal": {},
|
||||
"aboutDataUsageExternal": "Externe",
|
||||
"@aboutDataUsageExternal": {},
|
||||
"aboutDataUsageSectionTitle": "Espace utilisé",
|
||||
"@aboutDataUsageSectionTitle": {},
|
||||
"aboutDataUsageData": "Données",
|
||||
"@aboutDataUsageData": {},
|
||||
"aboutDataUsageCache": "Cache",
|
||||
"@aboutDataUsageCache": {},
|
||||
"overlayHistogramRGB": "RVB",
|
||||
"@overlayHistogramRGB": {},
|
||||
"overlayHistogramLuminance": "Luminance",
|
||||
"@overlayHistogramLuminance": {},
|
||||
"settingsViewerShowHistogram": "Afficher l'histogramme",
|
||||
"@settingsViewerShowHistogram": {},
|
||||
"overlayHistogramNone": "Aucun",
|
||||
"@overlayHistogramNone": {}
|
||||
}
|
||||
|
|
|
@ -1492,5 +1492,19 @@
|
|||
"cropAspectRatioFree": "Kötetlen",
|
||||
"@cropAspectRatioFree": {},
|
||||
"widgetTapUpdateWidget": "Widget frissítése",
|
||||
"@widgetTapUpdateWidget": {}
|
||||
"@widgetTapUpdateWidget": {},
|
||||
"aboutDataUsageData": "Adat",
|
||||
"@aboutDataUsageData": {},
|
||||
"aboutDataUsageDatabase": "Adatbázis",
|
||||
"@aboutDataUsageDatabase": {},
|
||||
"aboutDataUsageMisc": "Egyéb",
|
||||
"@aboutDataUsageMisc": {},
|
||||
"aboutDataUsageInternal": "Belső",
|
||||
"@aboutDataUsageInternal": {},
|
||||
"aboutDataUsageExternal": "Külső",
|
||||
"@aboutDataUsageExternal": {},
|
||||
"aboutDataUsageSectionTitle": "Adatforgalom",
|
||||
"@aboutDataUsageSectionTitle": {},
|
||||
"aboutDataUsageCache": "Gyorsítótár",
|
||||
"@aboutDataUsageCache": {}
|
||||
}
|
||||
|
|
|
@ -1334,5 +1334,27 @@
|
|||
"cropAspectRatioFree": "Bebas",
|
||||
"@cropAspectRatioFree": {},
|
||||
"widgetTapUpdateWidget": "Perbarui widget",
|
||||
"@widgetTapUpdateWidget": {}
|
||||
"@widgetTapUpdateWidget": {},
|
||||
"aboutDataUsageData": "Data",
|
||||
"@aboutDataUsageData": {},
|
||||
"aboutDataUsageDatabase": "Basis Data",
|
||||
"@aboutDataUsageDatabase": {},
|
||||
"aboutDataUsageSectionTitle": "Penggunaan Data",
|
||||
"@aboutDataUsageSectionTitle": {},
|
||||
"aboutDataUsageCache": "Tembolok",
|
||||
"@aboutDataUsageCache": {},
|
||||
"aboutDataUsageExternal": "Eksternal",
|
||||
"@aboutDataUsageExternal": {},
|
||||
"aboutDataUsageMisc": "Lainnya",
|
||||
"@aboutDataUsageMisc": {},
|
||||
"aboutDataUsageInternal": "Internal",
|
||||
"@aboutDataUsageInternal": {},
|
||||
"overlayHistogramNone": "Tidak ada",
|
||||
"@overlayHistogramNone": {},
|
||||
"overlayHistogramRGB": "RGB",
|
||||
"@overlayHistogramRGB": {},
|
||||
"overlayHistogramLuminance": "Kecerahan",
|
||||
"@overlayHistogramLuminance": {},
|
||||
"settingsViewerShowHistogram": "Tampilkan histogram",
|
||||
"@settingsViewerShowHistogram": {}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
"@focalLength": {},
|
||||
"applyButtonLabel": "APPLICA",
|
||||
"@applyButtonLabel": {},
|
||||
"deleteButtonLabel": "CANCELLA",
|
||||
"deleteButtonLabel": "ELIMINA",
|
||||
"@deleteButtonLabel": {},
|
||||
"nextButtonLabel": "AVANTI",
|
||||
"@nextButtonLabel": {},
|
||||
|
@ -345,7 +345,7 @@
|
|||
"@noMatchingAppDialogMessage": {},
|
||||
"binEntriesConfirmationDialogMessage": "{count, plural, =1{Spostare questo elemento nel cestino?} other{Spostare questi {count} elementi nel cestino?}}",
|
||||
"@binEntriesConfirmationDialogMessage": {},
|
||||
"deleteEntriesConfirmationDialogMessage": "{count, plural, =1{Cancellare questo elemento?} other{Cancellare questi {count} elementi?}}",
|
||||
"deleteEntriesConfirmationDialogMessage": "{count, plural, =1{Eliminare questo elemento?} other{Eliminare questi {count} elementi?}}",
|
||||
"@deleteEntriesConfirmationDialogMessage": {},
|
||||
"moveUndatedConfirmationDialogMessage": "Salvare le date degli elementi prima di procedere?",
|
||||
"@moveUndatedConfirmationDialogMessage": {},
|
||||
|
@ -389,9 +389,9 @@
|
|||
"@renameProcessorCounter": {},
|
||||
"renameProcessorName": "Nome",
|
||||
"@renameProcessorName": {},
|
||||
"deleteSingleAlbumConfirmationDialogMessage": "{count, plural, =1{Cancellare questo album e l’elemento in esso?} other{Cancellare questo album e i {count} elementi in esso?}}",
|
||||
"deleteSingleAlbumConfirmationDialogMessage": "{count, plural, =1{Eliminare questo album e l’elemento in esso?} other{Eliminare questo album e i {count} elementi in esso?}}",
|
||||
"@deleteSingleAlbumConfirmationDialogMessage": {},
|
||||
"deleteMultiAlbumConfirmationDialogMessage": "{count, plural, =1{Cancellare questi album e l’elemento in essi?} other{Cancellare questi album e i {count} elementi in essi?}}",
|
||||
"deleteMultiAlbumConfirmationDialogMessage": "{count, plural, =1{Eliminare questi album e l’elemento in essi?} other{Eliminare questi album e i {count} elementi in essi?}}",
|
||||
"@deleteMultiAlbumConfirmationDialogMessage": {},
|
||||
"exportEntryDialogFormat": "Formato:",
|
||||
"@exportEntryDialogFormat": {},
|
||||
|
@ -579,7 +579,7 @@
|
|||
"@dateYesterday": {},
|
||||
"dateThisMonth": "Questo mese",
|
||||
"@dateThisMonth": {},
|
||||
"collectionDeleteFailureFeedback": "{count, plural, =1{Impossibile cancellare 1 elemento} other{Impossibile cancellare {count} elementi}}",
|
||||
"collectionDeleteFailureFeedback": "{count, plural, =1{Impossibile eliminare 1 elemento} other{Impossibile eliminare {count} elementi}}",
|
||||
"@collectionDeleteFailureFeedback": {},
|
||||
"collectionCopyFailureFeedback": "{count, plural, =1{Impossibile copiare 1 elemento} other{Impossibile copiare {count} elementi}}",
|
||||
"@collectionCopyFailureFeedback": {},
|
||||
|
@ -775,7 +775,7 @@
|
|||
"@settingsConfirmationTile": {},
|
||||
"settingsConfirmationDialogTitle": "Richieste di conferma",
|
||||
"@settingsConfirmationDialogTitle": {},
|
||||
"settingsConfirmationBeforeDeleteItems": "Chiedi prima di cancellare gli elementi definitivamente",
|
||||
"settingsConfirmationBeforeDeleteItems": "Chiedi prima di eliminare gli elementi definitivamente",
|
||||
"@settingsConfirmationBeforeDeleteItems": {},
|
||||
"settingsConfirmationBeforeMoveToBinItems": "Chiedi prima di spostare gli elementi nel cestino",
|
||||
"@settingsConfirmationBeforeMoveToBinItems": {},
|
||||
|
@ -955,7 +955,7 @@
|
|||
"@settingsSaveSearchHistory": {},
|
||||
"settingsEnableBin": "Usa il cestino",
|
||||
"@settingsEnableBin": {},
|
||||
"settingsEnableBinSubtitle": "Conserva gli elementi cancellati per 30 giorni",
|
||||
"settingsEnableBinSubtitle": "Conserva gli elementi eliminati per 30 giorni",
|
||||
"@settingsEnableBinSubtitle": {},
|
||||
"settingsHiddenItemsTile": "Elementi nascosti",
|
||||
"@settingsHiddenItemsTile": {},
|
||||
|
@ -1316,5 +1316,37 @@
|
|||
"settingsVideoPlaybackTile": "Riproduzione",
|
||||
"@settingsVideoPlaybackTile": {},
|
||||
"settingsCollectionBurstPatternsTile": "Modelli di burst",
|
||||
"@settingsCollectionBurstPatternsTile": {}
|
||||
"@settingsCollectionBurstPatternsTile": {},
|
||||
"saveCopyButtonLabel": "SALVA COPIA",
|
||||
"@saveCopyButtonLabel": {},
|
||||
"applyTooltip": "Applica",
|
||||
"@applyTooltip": {},
|
||||
"editorActionTransform": "Trasforma",
|
||||
"@editorActionTransform": {},
|
||||
"editorTransformCrop": "Ritaglia",
|
||||
"@editorTransformCrop": {},
|
||||
"editorTransformRotate": "Ruota",
|
||||
"@editorTransformRotate": {},
|
||||
"cropAspectRatioFree": "Libero",
|
||||
"@cropAspectRatioFree": {},
|
||||
"cropAspectRatioOriginal": "Originale",
|
||||
"@cropAspectRatioOriginal": {},
|
||||
"cropAspectRatioSquare": "Quadrato",
|
||||
"@cropAspectRatioSquare": {},
|
||||
"widgetTapUpdateWidget": "Aggiorna widget",
|
||||
"@widgetTapUpdateWidget": {},
|
||||
"aboutDataUsageCache": "Cache",
|
||||
"@aboutDataUsageCache": {},
|
||||
"aboutDataUsageMisc": "Varie",
|
||||
"@aboutDataUsageMisc": {},
|
||||
"aboutDataUsageDatabase": "Database",
|
||||
"@aboutDataUsageDatabase": {},
|
||||
"aboutDataUsageSectionTitle": "Utilizzo dati",
|
||||
"@aboutDataUsageSectionTitle": {},
|
||||
"aboutDataUsageData": "Dati",
|
||||
"@aboutDataUsageData": {},
|
||||
"aboutDataUsageInternal": "Interno",
|
||||
"@aboutDataUsageInternal": {},
|
||||
"aboutDataUsageExternal": "Esterno",
|
||||
"@aboutDataUsageExternal": {}
|
||||
}
|
||||
|
|
64
lib/l10n/app_kn.arb
Normal file
64
lib/l10n/app_kn.arb
Normal file
|
@ -0,0 +1,64 @@
|
|||
{
|
||||
"appName": "Aves",
|
||||
"@appName": {},
|
||||
"welcomeOptional": "ಐಚ್ಛಿಕ",
|
||||
"@welcomeOptional": {},
|
||||
"welcomeTermsToggle": "ನಾನು ನಿಯಮಗಳು ಮತ್ತು ಷರತ್ತುಗಳನ್ನು ಒಪ್ಪುತ್ತೇನೆ",
|
||||
"@welcomeTermsToggle": {},
|
||||
"applyButtonLabel": "ಅನ್ವಯಿಸು",
|
||||
"@applyButtonLabel": {},
|
||||
"deleteButtonLabel": "ಅಳಿಸಿ",
|
||||
"@deleteButtonLabel": {},
|
||||
"nextButtonLabel": "ಮುಂದೆ",
|
||||
"@nextButtonLabel": {},
|
||||
"showButtonLabel": "ತೋರಿಸು",
|
||||
"@showButtonLabel": {},
|
||||
"hideButtonLabel": "ಮುಚ್ಚಿಡು",
|
||||
"@hideButtonLabel": {},
|
||||
"continueButtonLabel": "ಮುಂದುವರಿಸು",
|
||||
"@continueButtonLabel": {},
|
||||
"saveCopyButtonLabel": "ನಕಲು ಉಳಿಸಿ",
|
||||
"@saveCopyButtonLabel": {},
|
||||
"applyTooltip": "ಅನ್ವಯಿಸು",
|
||||
"@applyTooltip": {},
|
||||
"cancelTooltip": "ರದ್ದುಗೊಳಿಸಿ",
|
||||
"@cancelTooltip": {},
|
||||
"changeTooltip": "ಬದಲಾಯಿಸು",
|
||||
"@changeTooltip": {},
|
||||
"clearTooltip": "ಸ್ಪಷ್ಟ ಮಾಡು",
|
||||
"@clearTooltip": {},
|
||||
"previousTooltip": "ಹಿಂದಿನ",
|
||||
"@previousTooltip": {},
|
||||
"nextTooltip": "ಮುಂದಿನ",
|
||||
"@nextTooltip": {},
|
||||
"showTooltip": "ತೋರಿಸು",
|
||||
"@showTooltip": {},
|
||||
"actionRemove": "ತೆಗೆದುಹಾಕಿ",
|
||||
"@actionRemove": {},
|
||||
"resetTooltip": "ರೀಸೆಟ್ ಮಾಡಿ",
|
||||
"@resetTooltip": {},
|
||||
"saveTooltip": "ಉಳಿಸಿ",
|
||||
"@saveTooltip": {},
|
||||
"pickTooltip": "ಆಯ್ಕೆ",
|
||||
"@pickTooltip": {},
|
||||
"doubleBackExitMessage": "ನಿರ್ಗಮಿಸಲು ಮತ್ತೆ \"ಹಿಂದೆ\" ಟ್ಯಾಪ್ ಮಾಡಿ.",
|
||||
"@doubleBackExitMessage": {},
|
||||
"doNotAskAgain": "ಇನ್ನೊಮ್ಮೆ ಕೇಳಬೇಡಿ",
|
||||
"@doNotAskAgain": {},
|
||||
"sourceStateLoading": "ಲೋಡ್ ಆಗುತ್ತಿದೆ",
|
||||
"@sourceStateLoading": {},
|
||||
"sourceStateCataloguing": "ಪಟ್ಟಿಮಾಡುವುದು",
|
||||
"@sourceStateCataloguing": {},
|
||||
"sourceStateLocatingCountries": "ದೇಶಗಳನ್ನು ಪತ್ತೆ ಮಾಡಲಾಗುತ್ತಿದೆ",
|
||||
"@sourceStateLocatingCountries": {},
|
||||
"sourceStateLocatingPlaces": "ಸ್ಥಳಗಳನ್ನು ಪತ್ತೆ ಮಾಡಲಾಗುತ್ತಿದೆ",
|
||||
"@sourceStateLocatingPlaces": {},
|
||||
"chipActionDelete": "ಅಳಿಸಿ",
|
||||
"@chipActionDelete": {},
|
||||
"chipActionGoToAlbumPage": "ಆಲ್ಬಮ್ಗಳಲ್ಲಿ ತೋರಿಸು",
|
||||
"@chipActionGoToAlbumPage": {},
|
||||
"hideTooltip": "ಮರೆಮಾಡಿ",
|
||||
"@hideTooltip": {},
|
||||
"welcomeMessage": "Aves ಗೆ ಸ್ವಾಗತ",
|
||||
"@welcomeMessage": {}
|
||||
}
|
|
@ -1334,5 +1334,27 @@
|
|||
"editorActionTransform": "변형",
|
||||
"@editorActionTransform": {},
|
||||
"widgetTapUpdateWidget": "위젯 갱신",
|
||||
"@widgetTapUpdateWidget": {}
|
||||
"@widgetTapUpdateWidget": {},
|
||||
"aboutDataUsageDatabase": "데이터베이스",
|
||||
"@aboutDataUsageDatabase": {},
|
||||
"aboutDataUsageMisc": "기타",
|
||||
"@aboutDataUsageMisc": {},
|
||||
"aboutDataUsageSectionTitle": "사용 중인 저장공간",
|
||||
"@aboutDataUsageSectionTitle": {},
|
||||
"aboutDataUsageExternal": "외부 저장소",
|
||||
"@aboutDataUsageExternal": {},
|
||||
"aboutDataUsageInternal": "내부 저장소",
|
||||
"@aboutDataUsageInternal": {},
|
||||
"aboutDataUsageData": "데이터",
|
||||
"@aboutDataUsageData": {},
|
||||
"aboutDataUsageCache": "캐시",
|
||||
"@aboutDataUsageCache": {},
|
||||
"overlayHistogramRGB": "RGB",
|
||||
"@overlayHistogramRGB": {},
|
||||
"overlayHistogramNone": "없음",
|
||||
"@overlayHistogramNone": {},
|
||||
"overlayHistogramLuminance": "휘도",
|
||||
"@overlayHistogramLuminance": {},
|
||||
"settingsViewerShowHistogram": "히스토그램 표시",
|
||||
"@settingsViewerShowHistogram": {}
|
||||
}
|
||||
|
|
119
lib/l10n/app_my.arb
Normal file
119
lib/l10n/app_my.arb
Normal file
|
@ -0,0 +1,119 @@
|
|||
{
|
||||
"sourceStateLocatingPlaces": "နေရာ တည်နေရာများ",
|
||||
"@sourceStateLocatingPlaces": {},
|
||||
"chipActionDelete": "ဖျက်",
|
||||
"@chipActionDelete": {},
|
||||
"welcomeMessage": "Avesမှကြိုဆိုပါတယ်",
|
||||
"@welcomeMessage": {},
|
||||
"columnCount": "{count, plural, =1{1 ကော်လံ} other{{count} ကော်လံများ}}",
|
||||
"@columnCount": {
|
||||
"placeholders": {
|
||||
"count": {}
|
||||
}
|
||||
},
|
||||
"appName": "Aves",
|
||||
"@appName": {},
|
||||
"welcomeOptional": "ရွေးချယ်နိုင်သော။",
|
||||
"@welcomeOptional": {},
|
||||
"welcomeTermsToggle": "ကျွနု်ပ်",
|
||||
"@welcomeTermsToggle": {},
|
||||
"itemCount": "{count, plural, =1{1 အိုင်တမ်} other{{count} အိုင်တမ်များ}}",
|
||||
"@itemCount": {
|
||||
"placeholders": {
|
||||
"count": {}
|
||||
}
|
||||
},
|
||||
"timeSeconds": "{seconds, plural, =1{1 စက္ကန့်} other{{seconds} စက္ကန့်များ}}",
|
||||
"@timeSeconds": {
|
||||
"placeholders": {
|
||||
"seconds": {}
|
||||
}
|
||||
},
|
||||
"timeMinutes": "{minutes, plural, =1{1 မိနစ်} other{{minutes} မိနစ်များ}}",
|
||||
"@timeMinutes": {
|
||||
"placeholders": {
|
||||
"minutes": {}
|
||||
}
|
||||
},
|
||||
"timeDays": "{days, plural, =1{1 ရက်} other{{days} ရက်များ}}",
|
||||
"@timeDays": {
|
||||
"placeholders": {
|
||||
"days": {}
|
||||
}
|
||||
},
|
||||
"focalLength": "{length} မီလီမီတာ",
|
||||
"@focalLength": {
|
||||
"placeholders": {
|
||||
"length": {
|
||||
"type": "String",
|
||||
"example": "5.4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"applyButtonLabel": "အတည်ပြု",
|
||||
"@applyButtonLabel": {},
|
||||
"cancelTooltip": "ပယ်ဖျက်မည်",
|
||||
"@cancelTooltip": {},
|
||||
"previousTooltip": "နောက်သို့",
|
||||
"@previousTooltip": {},
|
||||
"deleteButtonLabel": "ဖျက်မည်",
|
||||
"@deleteButtonLabel": {},
|
||||
"nextButtonLabel": "ရှေ့သို့",
|
||||
"@nextButtonLabel": {},
|
||||
"showButtonLabel": "ပြရန်",
|
||||
"@showButtonLabel": {},
|
||||
"hideButtonLabel": "ဝှက်ရန်",
|
||||
"@hideButtonLabel": {},
|
||||
"resetTooltip": "ပြန်ပြုပြင်မည်",
|
||||
"@resetTooltip": {},
|
||||
"continueButtonLabel": "ရှေ့ဆက်သွားရန်",
|
||||
"@continueButtonLabel": {},
|
||||
"clearTooltip": "ရှင်းလင်းမည်",
|
||||
"@clearTooltip": {},
|
||||
"saveCopyButtonLabel": "မိတ္တူကိုသိမ်းမည်",
|
||||
"@saveCopyButtonLabel": {},
|
||||
"showTooltip": "ပြရန်",
|
||||
"@showTooltip": {},
|
||||
"actionRemove": "ဖယ်ရှားမည်",
|
||||
"@actionRemove": {},
|
||||
"sourceStateLoading": "ခဏစောင့်ပါ",
|
||||
"@sourceStateLoading": {},
|
||||
"chipActionGoToCountryPage": "နိုင်ငံတွေထဲမှာပြရန်",
|
||||
"@chipActionGoToCountryPage": {},
|
||||
"applyTooltip": "အတည်ပြု",
|
||||
"@applyTooltip": {},
|
||||
"changeTooltip": "ပြောင်းမည်",
|
||||
"@changeTooltip": {},
|
||||
"nextTooltip": "ရှေ့သို့",
|
||||
"@nextTooltip": {},
|
||||
"chipActionGoToPlacePage": "နေရာများထဲတွင်ပြရန်",
|
||||
"@chipActionGoToPlacePage": {},
|
||||
"chipActionGoToTagPage": "အမှတ်အသားများတွင်ပြရန်",
|
||||
"@chipActionGoToTagPage": {},
|
||||
"chipActionFilterOut": "စစ်ထုတ်ရန်",
|
||||
"@chipActionFilterOut": {},
|
||||
"chipActionFilterIn": "စစ်သွင်းရန်",
|
||||
"@chipActionFilterIn": {},
|
||||
"chipActionHide": "ဝှက်",
|
||||
"@chipActionHide": {},
|
||||
"chipActionLock": "သော့ခတ်",
|
||||
"@chipActionLock": {},
|
||||
"chipActionPin": "အပေါ်ဆုံးများပင်တွဲရန်",
|
||||
"@chipActionPin": {},
|
||||
"chipActionUnpin": "အပေါ်ဆုံးမှပင်ဖြုတ်ရန်",
|
||||
"@chipActionUnpin": {},
|
||||
"hideTooltip": "ဝှက်ရန်",
|
||||
"@hideTooltip": {},
|
||||
"pickTooltip": "ရွေးရန်",
|
||||
"@pickTooltip": {},
|
||||
"saveTooltip": "သိမ်းမည်",
|
||||
"@saveTooltip": {},
|
||||
"doubleBackExitMessage": "ထွက်ရန်\"နောက်\"ကိုထပ်နိှပ်ပါ။",
|
||||
"@doubleBackExitMessage": {},
|
||||
"doNotAskAgain": "ထပ်မမေးပါနှင့်",
|
||||
"@doNotAskAgain": {},
|
||||
"sourceStateLocatingCountries": "နိုင်ငံတည်နေရာများ",
|
||||
"@sourceStateLocatingCountries": {},
|
||||
"chipActionGoToAlbumPage": "အယ်လ်ဘမ်တွေထဲမှာပြရန်",
|
||||
"@chipActionGoToAlbumPage": {}
|
||||
}
|
|
@ -1420,5 +1420,65 @@
|
|||
"settingsConfirmationVaultDataLoss": "Vis advarsel om hvelv-datatap",
|
||||
"@settingsConfirmationVaultDataLoss": {},
|
||||
"lengthUnitPercent": "%",
|
||||
"@lengthUnitPercent": {}
|
||||
"@lengthUnitPercent": {},
|
||||
"editorTransformCrop": "Beskjær",
|
||||
"@editorTransformCrop": {},
|
||||
"editorTransformRotate": "Roter",
|
||||
"@editorTransformRotate": {},
|
||||
"statePageTitle": "Tilstander",
|
||||
"@statePageTitle": {},
|
||||
"stateEmpty": "Ingen tilstander",
|
||||
"@stateEmpty": {},
|
||||
"settingsVideoPlaybackTile": "Avspilling",
|
||||
"@settingsVideoPlaybackTile": {},
|
||||
"settingsVideoResumptionModeTile": "Gjenoppta avspilling",
|
||||
"@settingsVideoResumptionModeTile": {},
|
||||
"settingsVideoResumptionModeDialogTitle": "Gjenoppta avspilling",
|
||||
"@settingsVideoResumptionModeDialogTitle": {},
|
||||
"maxBrightnessAlways": "Alltid",
|
||||
"@maxBrightnessAlways": {},
|
||||
"videoResumptionModeNever": "Aldri",
|
||||
"@videoResumptionModeNever": {},
|
||||
"maxBrightnessNever": "Aldri",
|
||||
"@maxBrightnessNever": {},
|
||||
"videoResumptionModeAlways": "Alltid",
|
||||
"@videoResumptionModeAlways": {},
|
||||
"exportEntryDialogQuality": "Kvalitet",
|
||||
"@exportEntryDialogQuality": {},
|
||||
"aboutDataUsageSectionTitle": "Databruk",
|
||||
"@aboutDataUsageSectionTitle": {},
|
||||
"aboutDataUsageData": "Data",
|
||||
"@aboutDataUsageData": {},
|
||||
"aboutDataUsageMisc": "Ymse",
|
||||
"@aboutDataUsageMisc": {},
|
||||
"settingsVideoPlaybackPageTitle": "Avspilling",
|
||||
"@settingsVideoPlaybackPageTitle": {},
|
||||
"settingsVideoBackgroundMode": "Bakgrunnsmodus",
|
||||
"@settingsVideoBackgroundMode": {},
|
||||
"saveCopyButtonLabel": "Lagre kopi",
|
||||
"@saveCopyButtonLabel": {},
|
||||
"applyTooltip": "Bruk",
|
||||
"@applyTooltip": {},
|
||||
"overlayHistogramRGB": "RGB",
|
||||
"@overlayHistogramRGB": {},
|
||||
"patternDialogConfirm": "Bekreft mønster",
|
||||
"@patternDialogConfirm": {},
|
||||
"aboutDataUsageCache": "Hurtiglager",
|
||||
"@aboutDataUsageCache": {},
|
||||
"aboutDataUsageDatabase": "Database",
|
||||
"@aboutDataUsageDatabase": {},
|
||||
"settingsAskEverytime": "Spør hver gang",
|
||||
"@settingsAskEverytime": {},
|
||||
"settingsVideoBackgroundModeDialogTitle": "Bakgrunnsmodus",
|
||||
"@settingsVideoBackgroundModeDialogTitle": {},
|
||||
"tagEditorDiscardDialogMessage": "Forkast endringer?",
|
||||
"@tagEditorDiscardDialogMessage": {},
|
||||
"tagPlaceholderState": "Tilstand?",
|
||||
"@tagPlaceholderState": {},
|
||||
"chipActionShowCountryStates": "Vis tilstander",
|
||||
"@chipActionShowCountryStates": {},
|
||||
"searchStatesSectionTitle": "Tilstander",
|
||||
"@searchStatesSectionTitle": {},
|
||||
"vaultLockTypePattern": "Mønster",
|
||||
"@vaultLockTypePattern": {}
|
||||
}
|
||||
|
|
|
@ -1492,5 +1492,27 @@
|
|||
"cropAspectRatioSquare": "Kwadrat",
|
||||
"@cropAspectRatioSquare": {},
|
||||
"widgetTapUpdateWidget": "Zaktualizuj widżet",
|
||||
"@widgetTapUpdateWidget": {}
|
||||
"@widgetTapUpdateWidget": {},
|
||||
"aboutDataUsageExternal": "Zewnętrzny",
|
||||
"@aboutDataUsageExternal": {},
|
||||
"aboutDataUsageSectionTitle": "Wykorzystanie danych",
|
||||
"@aboutDataUsageSectionTitle": {},
|
||||
"aboutDataUsageInternal": "Wewnętrzny",
|
||||
"@aboutDataUsageInternal": {},
|
||||
"aboutDataUsageData": "Dane",
|
||||
"@aboutDataUsageData": {},
|
||||
"aboutDataUsageDatabase": "Baza danych",
|
||||
"@aboutDataUsageDatabase": {},
|
||||
"aboutDataUsageCache": "Pamięć podręczna",
|
||||
"@aboutDataUsageCache": {},
|
||||
"aboutDataUsageMisc": "Różne",
|
||||
"@aboutDataUsageMisc": {},
|
||||
"overlayHistogramNone": "Brak",
|
||||
"@overlayHistogramNone": {},
|
||||
"overlayHistogramRGB": "RGB",
|
||||
"@overlayHistogramRGB": {},
|
||||
"overlayHistogramLuminance": "Jasność",
|
||||
"@overlayHistogramLuminance": {},
|
||||
"settingsViewerShowHistogram": "Pokaż histogram",
|
||||
"@settingsViewerShowHistogram": {}
|
||||
}
|
||||
|
|
|
@ -1316,5 +1316,23 @@
|
|||
"videoResumptionModeNever": "Nunca",
|
||||
"@videoResumptionModeNever": {},
|
||||
"tagEditorDiscardDialogMessage": "Pretende rejeitar as alterações?",
|
||||
"@tagEditorDiscardDialogMessage": {}
|
||||
"@tagEditorDiscardDialogMessage": {},
|
||||
"saveCopyButtonLabel": "SALVAR CÓPIA",
|
||||
"@saveCopyButtonLabel": {},
|
||||
"applyTooltip": "Aplicar",
|
||||
"@applyTooltip": {},
|
||||
"editorActionTransform": "Transformar",
|
||||
"@editorActionTransform": {},
|
||||
"editorTransformCrop": "Cortar",
|
||||
"@editorTransformCrop": {},
|
||||
"editorTransformRotate": "Girar",
|
||||
"@editorTransformRotate": {},
|
||||
"cropAspectRatioFree": "Livre",
|
||||
"@cropAspectRatioFree": {},
|
||||
"cropAspectRatioOriginal": "Original",
|
||||
"@cropAspectRatioOriginal": {},
|
||||
"cropAspectRatioSquare": "Quadrada",
|
||||
"@cropAspectRatioSquare": {},
|
||||
"widgetTapUpdateWidget": "Atualizar o widget",
|
||||
"@widgetTapUpdateWidget": {}
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@
|
|||
"@videoActionSkip10": {},
|
||||
"videoActionSelectStreams": "Выбрать дорожку",
|
||||
"@videoActionSelectStreams": {},
|
||||
"videoActionSetSpeed": "Скорость вопспроизведения",
|
||||
"videoActionSetSpeed": "Скорость воспроизведения",
|
||||
"@videoActionSetSpeed": {},
|
||||
"viewerActionSettings": "Настройки",
|
||||
"@viewerActionSettings": {},
|
||||
|
@ -1334,5 +1334,27 @@
|
|||
"searchStatesSectionTitle": "Регионы",
|
||||
"@searchStatesSectionTitle": {},
|
||||
"settingsCollectionBurstPatternsNone": "Без вспышки",
|
||||
"@settingsCollectionBurstPatternsNone": {}
|
||||
"@settingsCollectionBurstPatternsNone": {},
|
||||
"aboutDataUsageSectionTitle": "Использование данных",
|
||||
"@aboutDataUsageSectionTitle": {},
|
||||
"aboutDataUsageData": "Данные",
|
||||
"@aboutDataUsageData": {},
|
||||
"aboutDataUsageCache": "Кэш",
|
||||
"@aboutDataUsageCache": {},
|
||||
"aboutDataUsageDatabase": "База данных",
|
||||
"@aboutDataUsageDatabase": {},
|
||||
"aboutDataUsageMisc": "Разнообразное",
|
||||
"@aboutDataUsageMisc": {},
|
||||
"aboutDataUsageInternal": "Внутреннее",
|
||||
"@aboutDataUsageInternal": {},
|
||||
"aboutDataUsageExternal": "Внешнее",
|
||||
"@aboutDataUsageExternal": {},
|
||||
"overlayHistogramNone": "Откл.",
|
||||
"@overlayHistogramNone": {},
|
||||
"overlayHistogramRGB": "RGB",
|
||||
"@overlayHistogramRGB": {},
|
||||
"settingsViewerShowHistogram": "Показать гистограмму",
|
||||
"@settingsViewerShowHistogram": {},
|
||||
"overlayHistogramLuminance": "Яркость",
|
||||
"@overlayHistogramLuminance": {}
|
||||
}
|
||||
|
|
10
lib/l10n/app_sl.arb
Normal file
10
lib/l10n/app_sl.arb
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"appName": "Aves",
|
||||
"@appName": {},
|
||||
"welcomeMessage": "Dobrodošli v Avesu",
|
||||
"@welcomeMessage": {},
|
||||
"welcomeOptional": "Izbirno",
|
||||
"@welcomeOptional": {},
|
||||
"welcomeTermsToggle": "Sprejmem pogoje uporabe",
|
||||
"@welcomeTermsToggle": {}
|
||||
}
|
|
@ -341,7 +341,7 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"videoResumeButtonLabel": "ПРОДОВЖИТИ",
|
||||
"videoResumeButtonLabel": "ВІДНОВИТИ",
|
||||
"@videoResumeButtonLabel": {},
|
||||
"setCoverDialogAuto": "Авто",
|
||||
"@setCoverDialogAuto": {},
|
||||
|
@ -1492,5 +1492,27 @@
|
|||
"cropAspectRatioSquare": "Площа",
|
||||
"@cropAspectRatioSquare": {},
|
||||
"widgetTapUpdateWidget": "Оновити віджет",
|
||||
"@widgetTapUpdateWidget": {}
|
||||
"@widgetTapUpdateWidget": {},
|
||||
"aboutDataUsageSectionTitle": "Використання даних",
|
||||
"@aboutDataUsageSectionTitle": {},
|
||||
"aboutDataUsageMisc": "Різне",
|
||||
"@aboutDataUsageMisc": {},
|
||||
"aboutDataUsageInternal": "Внутрішній",
|
||||
"@aboutDataUsageInternal": {},
|
||||
"aboutDataUsageExternal": "Зовнішній",
|
||||
"@aboutDataUsageExternal": {},
|
||||
"aboutDataUsageCache": "Кеш",
|
||||
"@aboutDataUsageCache": {},
|
||||
"aboutDataUsageDatabase": "База даних",
|
||||
"@aboutDataUsageDatabase": {},
|
||||
"aboutDataUsageData": "Дані",
|
||||
"@aboutDataUsageData": {},
|
||||
"overlayHistogramNone": "Нічого",
|
||||
"@overlayHistogramNone": {},
|
||||
"overlayHistogramRGB": "RGB",
|
||||
"@overlayHistogramRGB": {},
|
||||
"overlayHistogramLuminance": "Яскравість",
|
||||
"@overlayHistogramLuminance": {},
|
||||
"settingsViewerShowHistogram": "Показати гістограму",
|
||||
"@settingsViewerShowHistogram": {}
|
||||
}
|
||||
|
|
|
@ -130,7 +130,7 @@
|
|||
"@slideshowActionResume": {},
|
||||
"entryInfoActionEditLocation": "編輯座標",
|
||||
"@entryInfoActionEditLocation": {},
|
||||
"entryInfoActionEditTitleDescription": "編輯標題和敘述",
|
||||
"entryInfoActionEditTitleDescription": "編輯標題和描述",
|
||||
"@entryInfoActionEditTitleDescription": {},
|
||||
"entryInfoActionExportMetadata": "匯出元資料",
|
||||
"@entryInfoActionExportMetadata": {},
|
||||
|
@ -671,11 +671,11 @@
|
|||
"@settingsConfirmationTile": {},
|
||||
"settingsConfirmationDialogTitle": "確認對話框",
|
||||
"@settingsConfirmationDialogTitle": {},
|
||||
"settingsConfirmationBeforeDeleteItems": "永久刪除項目前詢問",
|
||||
"settingsConfirmationBeforeDeleteItems": "永久刪除項目前先詢問",
|
||||
"@settingsConfirmationBeforeDeleteItems": {},
|
||||
"settingsConfirmationBeforeMoveToBinItems": "移動項目到資源回收桶前詢問",
|
||||
"settingsConfirmationBeforeMoveToBinItems": "移動項目到資源回收桶前先詢問",
|
||||
"@settingsConfirmationBeforeMoveToBinItems": {},
|
||||
"settingsConfirmationBeforeMoveUndatedItems": "移動沒有日期項目前詢問",
|
||||
"settingsConfirmationBeforeMoveUndatedItems": "日期不詳的項目移動前先詢問",
|
||||
"@settingsConfirmationBeforeMoveUndatedItems": {},
|
||||
"settingsConfirmationAfterMoveToBinItems": "移動項目到資源回收桶後顯示訊息",
|
||||
"@settingsConfirmationAfterMoveToBinItems": {},
|
||||
|
@ -1297,7 +1297,7 @@
|
|||
"@viewerInfoSearchEmpty": {},
|
||||
"viewerInfoSearchSuggestionDate": "日期和時間",
|
||||
"@viewerInfoSearchSuggestionDate": {},
|
||||
"viewerInfoSearchSuggestionDescription": "敘述",
|
||||
"viewerInfoSearchSuggestionDescription": "描述",
|
||||
"@viewerInfoSearchSuggestionDescription": {},
|
||||
"viewerInfoSearchSuggestionDimensions": "範圍",
|
||||
"@viewerInfoSearchSuggestionDimensions": {},
|
||||
|
|
|
@ -49,18 +49,28 @@ class Contributors {
|
|||
Contributor('Макар Разин', 'makarrazin14@gmail.com'),
|
||||
Contributor('Leon', 'leonhoog@outlook.com'),
|
||||
Contributor('stephen-cusi', 'magiskcurry@qq.com'),
|
||||
Contributor('atilluF', '110931720+atilluF@users.noreply.github.com'),
|
||||
Contributor('Davide Neri', 'davnerix@gmail.com'),
|
||||
Contributor('ShiftCtrlAltDel', 'who--is@yandex.ru'),
|
||||
Contributor('lol lol', 'besonderspositiverpanda@ji5.de'),
|
||||
Contributor('Fabian Rennebeck', 'propago47@posteo.org'),
|
||||
Contributor('Henry The Mole', 'htmole@gmail.com'),
|
||||
// Contributor('SAMIRAH AIL', 'samiratalzahrani@gmail.com'), // Arabic
|
||||
// Contributor('Salih Ail', 'rrrfff444@gmail.com'), // Arabic
|
||||
// Contributor('nasreddineloukriz', 'nasreddineloukriz@gmail.com'), // Arabic
|
||||
// Contributor('Mohamed Zeroug', 'mzeroug19@gmail.com'), // Arabic
|
||||
// Contributor('Htet Oo Hlaing', 'htetoh2006@outlook.com'), // Burmese
|
||||
// Contributor('Idj', 'joneltmp+goahn@gmail.com'), // Hebrew
|
||||
// Contributor('Rohit Burman', 'rohitburman31p@rediffmail.com'), // Hindi
|
||||
// Contributor('Chethan', 'chethan@users.noreply.hosted.weblate.org'), // Kannada
|
||||
// Contributor('GoRaN', 'gorangharib.909@gmail.com'), // Kurdish (Central)
|
||||
// Contributor('Raman', 'xysed@tutanota.com'), // Malayalam
|
||||
// Contributor('Subham Jena', 'subhamjena8465@gmail.com'), // Odia
|
||||
// Contributor('امیر جهانگرد', 'ijahangard.a@gmail.com'), // Persian
|
||||
// Contributor('slasb37', 'p84haghi@gmail.com'), // Persian
|
||||
// Contributor('Nattapong K', 'mixer5056@gmail.com'), // Thai
|
||||
// Contributor('Idj', 'joneltmp+goahn@gmail.com'), // Hebrew
|
||||
// Contributor('Martin Frandel', 'martinko.fr@gmail.com'), // Slovak
|
||||
// Contributor('GoRaN', 'gorangharib.909@gmail.com'), // Kurdish (Central)
|
||||
// Contributor('Rohit Burman', 'rohitburman31p@rediffmail.com'), // Hindi
|
||||
// Contributor('Subham Jena', 'subhamjena8465@gmail.com'), // Odia
|
||||
// Contributor('Raman', 'xysed@tutanota.com'), // Malayalam
|
||||
// Contributor('mytja', 'mamnju21@gmail.com'), // Slovenian
|
||||
// Contributor('Nattapong K', 'mixer5056@gmail.com'), // Thai
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -92,6 +92,11 @@ class Dependencies {
|
|||
licenseUrl: 'https://github.com/flutter/packages/blob/main/packages/local_auth/local_auth/LICENSE',
|
||||
sourceUrl: 'https://github.com/flutter/packages/tree/main/packages/local_auth/local_auth',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Media Kit',
|
||||
license: mit,
|
||||
sourceUrl: 'https://github.com/media-kit/media-kit',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Package Info Plus',
|
||||
license: bsd3,
|
||||
|
@ -365,11 +370,6 @@ class Dependencies {
|
|||
license: mit,
|
||||
sourceUrl: 'https://github.com/brianegan/transparent_image',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Tuple',
|
||||
license: bsd2,
|
||||
sourceUrl: 'https://github.com/google/tuple.dart',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Vector Math',
|
||||
license: '$zlib, $bsd3',
|
||||
|
|
|
@ -13,7 +13,6 @@ import 'package:collection/collection.dart';
|
|||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/painting.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
final Covers covers = Covers._private();
|
||||
|
||||
|
@ -40,11 +39,11 @@ class Covers {
|
|||
|
||||
Set<CoverRow> get all => Set.unmodifiable(_rows);
|
||||
|
||||
Tuple3<int?, String?, Color?>? of(CollectionFilter filter) {
|
||||
(int? entryId, String? packageName, Color? color)? of(CollectionFilter filter) {
|
||||
if (filter is AlbumFilter && vaults.isLocked(filter.album)) return null;
|
||||
|
||||
final row = _rows.firstWhereOrNull((row) => row.filter == filter);
|
||||
return row != null ? Tuple3(row.entryId, row.packageName, row.color) : null;
|
||||
return row != null ? (row.entryId, row.packageName, row.color) : null;
|
||||
}
|
||||
|
||||
Future<void> set({
|
||||
|
@ -113,7 +112,7 @@ class Covers {
|
|||
}
|
||||
|
||||
AlbumType effectiveAlbumType(String albumPath) {
|
||||
final filterPackage = of(AlbumFilter(albumPath, null))?.item2;
|
||||
final filterPackage = of(AlbumFilter(albumPath, null))?.$2;
|
||||
if (filterPackage != null) {
|
||||
return filterPackage.isEmpty ? AlbumType.regular : AlbumType.app;
|
||||
} else {
|
||||
|
@ -122,7 +121,7 @@ class Covers {
|
|||
}
|
||||
|
||||
String? effectiveAlbumPackage(String albumPath) {
|
||||
final filterPackage = of(AlbumFilter(albumPath, null))?.item2;
|
||||
final filterPackage = of(AlbumFilter(albumPath, null))?.$2;
|
||||
return filterPackage ?? appInventory.getAlbumAppPackageName(albumPath);
|
||||
}
|
||||
|
||||
|
|
|
@ -200,6 +200,7 @@ class AvesEntry with AvesEntryBase {
|
|||
_bestTitle = null;
|
||||
}
|
||||
|
||||
@override
|
||||
String? get path => _path;
|
||||
|
||||
// directory path, without the trailing separator
|
||||
|
@ -293,6 +294,9 @@ class AvesEntry with AvesEntryBase {
|
|||
return d == null ? null : DateTime(d.year, d.month, d.day);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isAnimated => catalogMetadata?.isAnimated ?? false;
|
||||
|
||||
@override
|
||||
int? get durationMillis => _durationMillis;
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ import 'package:aves/model/entry/extensions/props.dart';
|
|||
import 'package:aves/model/geotiff.dart';
|
||||
import 'package:aves/model/metadata/catalog.dart';
|
||||
import 'package:aves/model/video/metadata.dart';
|
||||
import 'package:aves/ref/mime_types.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves/services/metadata/svg_metadata_service.dart';
|
||||
|
||||
|
@ -23,7 +24,7 @@ extension ExtraAvesEntryCatalog on AvesEntry {
|
|||
catalogMetadata = CatalogMetadata(id: id);
|
||||
} else {
|
||||
// pre-processing
|
||||
if (isVideo && (!isSized || durationMillis == 0)) {
|
||||
if ((isVideo && (!isSized || durationMillis == 0)) || mimeType == MimeTypes.avif) {
|
||||
// exotic video that is not sized during loading
|
||||
final fields = await VideoMetadataFormatter.getLoadingMetadata(this);
|
||||
await applyNewFields(fields, persist: persist);
|
||||
|
@ -33,7 +34,7 @@ extension ExtraAvesEntryCatalog on AvesEntry {
|
|||
catalogMetadata = await metadataFetchService.getCatalogMetadata(this, background: background);
|
||||
|
||||
// post-processing
|
||||
if (isVideo && (catalogMetadata?.dateMillis ?? 0) == 0) {
|
||||
if ((isVideo && (catalogMetadata?.dateMillis ?? 0) == 0) || (mimeType == MimeTypes.avif && durationMillis != null)) {
|
||||
catalogMetadata = await VideoMetadataFormatter.getCatalogMetadata(this);
|
||||
}
|
||||
if (isGeotiff && !hasGps) {
|
||||
|
|
|
@ -71,7 +71,7 @@ extension ExtraAvesEntryInfo on AvesEntry {
|
|||
|
||||
Future<List<MetadataDirectory>> _getStreamDirectories(BuildContext context) async {
|
||||
final directories = <MetadataDirectory>[];
|
||||
final mediaInfo = await VideoMetadataFormatter.getVideoMetadata(this);
|
||||
final mediaInfo = await videoMetadataFetcher.getMetadata(this);
|
||||
|
||||
final formattedMediaTags = VideoMetadataFormatter.formatInfo(mediaInfo);
|
||||
if (formattedMediaTags.isNotEmpty) {
|
||||
|
|
|
@ -8,8 +8,8 @@ import 'package:aves/model/entry/extensions/props.dart';
|
|||
import 'package:aves/model/metadata/date_modifier.dart';
|
||||
import 'package:aves/ref/metadata/exif.dart';
|
||||
import 'package:aves/ref/metadata/iptc.dart';
|
||||
import 'package:aves/ref/mime_types.dart';
|
||||
import 'package:aves/ref/metadata/xmp.dart';
|
||||
import 'package:aves/ref/mime_types.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves/services/metadata/xmp.dart';
|
||||
import 'package:aves/utils/time_utils.dart';
|
||||
|
@ -82,7 +82,7 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
|
|||
return dataTypes;
|
||||
}
|
||||
|
||||
static final removalLocation = LatLng(0, 0);
|
||||
static const removalLocation = LatLng(0, 0);
|
||||
|
||||
Future<Set<EntryDataType>> editLocation(LatLng? latLng) async {
|
||||
final dataTypes = <EntryDataType>{};
|
||||
|
|
|
@ -26,7 +26,9 @@ extension ExtraAvesEntryProps on AvesEntry {
|
|||
|
||||
bool get isImage => MimeTypes.isImage(mimeType);
|
||||
|
||||
bool get isVideo => MimeTypes.isVideo(mimeType);
|
||||
bool get isVideo => MimeTypes.isVideo(mimeType) || (mimeType == MimeTypes.avif && isAnimated);
|
||||
|
||||
bool get isPureVideo => isVideo && !isAnimated;
|
||||
|
||||
// size
|
||||
|
||||
|
@ -34,9 +36,9 @@ extension ExtraAvesEntryProps on AvesEntry {
|
|||
|
||||
bool get isSized => width > 0 && height > 0;
|
||||
|
||||
Size videoDisplaySize(double sar) {
|
||||
Size videoDisplaySize(double? sar) {
|
||||
final size = displaySize;
|
||||
if (sar != 1) {
|
||||
if (sar != null && sar != 1) {
|
||||
final dar = displayAspectRatio * sar;
|
||||
final w = size.width;
|
||||
final h = size.height;
|
||||
|
@ -68,8 +70,6 @@ extension ExtraAvesEntryProps on AvesEntry {
|
|||
|
||||
// catalog
|
||||
|
||||
bool get isAnimated => catalogMetadata?.isAnimated ?? false;
|
||||
|
||||
bool get isGeotiff => catalogMetadata?.isGeotiff ?? false;
|
||||
|
||||
bool get is360 => catalogMetadata?.is360 ?? false;
|
||||
|
|
|
@ -64,7 +64,7 @@ class AlbumFilter extends CoveredCollectionFilter {
|
|||
@override
|
||||
Future<Color> color(BuildContext context) {
|
||||
// custom color has precedence over others, even custom app color
|
||||
final customColor = covers.of(this)?.item3;
|
||||
final customColor = covers.of(this)?.$3;
|
||||
if (customColor != null) return SynchronousFuture(customColor);
|
||||
|
||||
final colors = context.read<AvesColorsData>();
|
||||
|
|
|
@ -157,7 +157,7 @@ abstract class CoveredCollectionFilter extends CollectionFilter {
|
|||
|
||||
@override
|
||||
Future<Color> color(BuildContext context) {
|
||||
final customColor = covers.of(this)?.item3;
|
||||
final customColor = covers.of(this)?.$3;
|
||||
if (customColor != null) {
|
||||
return SynchronousFuture(customColor);
|
||||
}
|
||||
|
|
|
@ -68,14 +68,12 @@ class MimeFilter extends CollectionFilter {
|
|||
|
||||
@override
|
||||
String getLabel(BuildContext context) {
|
||||
switch (mime) {
|
||||
case MimeTypes.anyImage:
|
||||
return context.l10n.filterMimeImageLabel;
|
||||
case MimeTypes.anyVideo:
|
||||
return context.l10n.filterMimeVideoLabel;
|
||||
default:
|
||||
return _label;
|
||||
}
|
||||
final l10n = context.l10n;
|
||||
return switch (mime) {
|
||||
MimeTypes.anyImage => l10n.filterMimeImageLabel,
|
||||
MimeTypes.anyVideo => l10n.filterMimeVideoLabel,
|
||||
_ => _label,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -60,16 +60,13 @@ class MissingFilter extends CollectionFilter {
|
|||
|
||||
@override
|
||||
String getLabel(BuildContext context) {
|
||||
switch (metadataType) {
|
||||
case _date:
|
||||
return context.l10n.filterNoDateLabel;
|
||||
case _fineAddress:
|
||||
return context.l10n.filterNoAddressLabel;
|
||||
case _title:
|
||||
return context.l10n.filterNoTitleLabel;
|
||||
default:
|
||||
return metadataType;
|
||||
}
|
||||
final l10n = context.l10n;
|
||||
return switch (metadataType) {
|
||||
_date => l10n.filterNoDateLabel,
|
||||
_fineAddress => l10n.filterNoAddressLabel,
|
||||
_title => l10n.filterNoTitleLabel,
|
||||
_ => metadataType,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -86,16 +86,13 @@ class PlaceholderFilter extends CollectionFilter {
|
|||
|
||||
@override
|
||||
String getLabel(BuildContext context) {
|
||||
switch (placeholder) {
|
||||
case _country:
|
||||
return context.l10n.tagPlaceholderCountry;
|
||||
case _state:
|
||||
return context.l10n.tagPlaceholderState;
|
||||
case _place:
|
||||
return context.l10n.tagPlaceholderPlace;
|
||||
default:
|
||||
return placeholder;
|
||||
}
|
||||
final l10n = context.l10n;
|
||||
return switch (placeholder) {
|
||||
_country => l10n.tagPlaceholderCountry,
|
||||
_state => l10n.tagPlaceholderState,
|
||||
_place => l10n.tagPlaceholderPlace,
|
||||
_ => placeholder,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -8,18 +8,34 @@ class RatingFilter extends CollectionFilter {
|
|||
static const type = 'rating';
|
||||
|
||||
final int rating;
|
||||
final String op;
|
||||
late final EntryFilter _test;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [rating, reversed];
|
||||
static const opEqual = '=';
|
||||
static const opOrLower = '<=';
|
||||
static const opOrGreater = '>=';
|
||||
|
||||
RatingFilter(this.rating, {super.reversed = false}) {
|
||||
_test = (entry) => entry.rating == rating;
|
||||
@override
|
||||
List<Object?> get props => [rating, op, reversed];
|
||||
|
||||
RatingFilter(this.rating, {this.op = opEqual, super.reversed = false}) {
|
||||
_test = switch (op) {
|
||||
opOrLower => (entry) => entry.rating <= rating && entry.rating > 0,
|
||||
opOrGreater => (entry) => entry.rating >= rating,
|
||||
opEqual || _ => (entry) => entry.rating == rating,
|
||||
};
|
||||
}
|
||||
|
||||
RatingFilter copyWith(String op) => RatingFilter(
|
||||
rating,
|
||||
op: op,
|
||||
reversed: reversed,
|
||||
);
|
||||
|
||||
factory RatingFilter.fromMap(Map<String, dynamic> json) {
|
||||
return RatingFilter(
|
||||
json['rating'] ?? 0,
|
||||
op: json['op'] ?? opEqual,
|
||||
reversed: json['reversed'] ?? false,
|
||||
);
|
||||
}
|
||||
|
@ -28,6 +44,7 @@ class RatingFilter extends CollectionFilter {
|
|||
Map<String, dynamic> toMap() => {
|
||||
'type': type,
|
||||
'rating': rating,
|
||||
'op': op,
|
||||
'reversed': reversed,
|
||||
};
|
||||
|
||||
|
@ -38,37 +55,42 @@ class RatingFilter extends CollectionFilter {
|
|||
bool get exclusiveProp => true;
|
||||
|
||||
@override
|
||||
String get universalLabel => '$rating';
|
||||
String get universalLabel => '$op $rating';
|
||||
|
||||
@override
|
||||
String getLabel(BuildContext context) => formatRating(context, rating);
|
||||
String getLabel(BuildContext context) => switch (op) {
|
||||
opOrLower || opOrGreater => '${UniChars.whiteMediumStar} ${formatRatingRange(context, rating, op)}',
|
||||
opEqual || _ => formatRating(context, rating),
|
||||
};
|
||||
|
||||
@override
|
||||
Widget? iconBuilder(BuildContext context, double size, {bool showGenericIcon = true}) {
|
||||
switch (rating) {
|
||||
case -1:
|
||||
return Icon(AIcons.ratingRejected, size: size);
|
||||
case 0:
|
||||
return Icon(AIcons.ratingUnrated, size: size);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
return switch (rating) {
|
||||
-1 => Icon(AIcons.ratingRejected, size: size),
|
||||
0 => Icon(AIcons.ratingUnrated, size: size),
|
||||
_ => null,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
String get category => type;
|
||||
|
||||
@override
|
||||
String get key => '$type-$reversed-$rating';
|
||||
String get key => '$type-$reversed-$rating-$op';
|
||||
|
||||
static String formatRating(BuildContext context, int rating) {
|
||||
switch (rating) {
|
||||
case -1:
|
||||
return context.l10n.filterRatingRejectedLabel;
|
||||
case 0:
|
||||
return context.l10n.filterNoRatingLabel;
|
||||
default:
|
||||
return UniChars.whiteMediumStar * rating;
|
||||
}
|
||||
return switch (rating) {
|
||||
-1 => context.l10n.filterRatingRejectedLabel,
|
||||
0 => context.l10n.filterNoRatingLabel,
|
||||
_ => UniChars.whiteMediumStar * rating,
|
||||
};
|
||||
}
|
||||
|
||||
static String formatRatingRange(BuildContext context, int rating, String op) {
|
||||
return switch (op) {
|
||||
opOrLower => '1~$rating',
|
||||
opOrGreater => '$rating~5',
|
||||
opEqual || _ => '$rating',
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -80,22 +80,16 @@ class TypeFilter extends CollectionFilter {
|
|||
|
||||
@override
|
||||
String getLabel(BuildContext context) {
|
||||
switch (itemType) {
|
||||
case _animated:
|
||||
return context.l10n.filterTypeAnimatedLabel;
|
||||
case _motionPhoto:
|
||||
return context.l10n.filterTypeMotionPhotoLabel;
|
||||
case _panorama:
|
||||
return context.l10n.filterTypePanoramaLabel;
|
||||
case _raw:
|
||||
return context.l10n.filterTypeRawLabel;
|
||||
case _sphericalVideo:
|
||||
return context.l10n.filterTypeSphericalVideoLabel;
|
||||
case _geotiff:
|
||||
return context.l10n.filterTypeGeotiffLabel;
|
||||
default:
|
||||
return itemType;
|
||||
}
|
||||
final l10n = context.l10n;
|
||||
return switch (itemType) {
|
||||
_animated => l10n.filterTypeAnimatedLabel,
|
||||
_motionPhoto => l10n.filterTypeMotionPhotoLabel,
|
||||
_panorama => l10n.filterTypePanoramaLabel,
|
||||
_raw => l10n.filterTypeRawLabel,
|
||||
_sphericalVideo => l10n.filterTypeSphericalVideoLabel,
|
||||
_geotiff => l10n.filterTypeGeotiffLabel,
|
||||
_ => itemType,
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -55,6 +55,7 @@ class CatalogMetadata {
|
|||
int? id,
|
||||
String? mimeType,
|
||||
int? dateMillis,
|
||||
bool? isAnimated,
|
||||
bool? isMultiPage,
|
||||
int? rotationDegrees,
|
||||
double? latitude,
|
||||
|
@ -64,7 +65,7 @@ class CatalogMetadata {
|
|||
id: id ?? this.id,
|
||||
mimeType: mimeType ?? this.mimeType,
|
||||
dateMillis: dateMillis ?? this.dateMillis,
|
||||
isAnimated: isAnimated,
|
||||
isAnimated: isAnimated ?? this.isAnimated,
|
||||
isFlipped: isFlipped,
|
||||
isGeotiff: isGeotiff,
|
||||
is360: is360,
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
import 'dart:ui';
|
||||
|
||||
import 'package:aves/model/filters/recent.dart';
|
||||
import 'package:aves/model/naming_pattern.dart';
|
||||
import 'package:aves/ref/mime_types.dart';
|
||||
import 'package:aves/utils/colors.dart';
|
||||
import 'package:aves/widgets/filter_grids/albums_page.dart';
|
||||
import 'package:aves/widgets/filter_grids/countries_page.dart';
|
||||
import 'package:aves/widgets/filter_grids/tags_page.dart';
|
||||
|
@ -75,6 +72,7 @@ class SettingsDefaults {
|
|||
];
|
||||
static const showOverlayOnOpening = true;
|
||||
static const showOverlayMinimap = false;
|
||||
static const overlayHistogramStyle = OverlayHistogramStyle.none;
|
||||
static const showOverlayInfo = true;
|
||||
static const showOverlayDescription = false;
|
||||
static const showOverlayRatingTags = false;
|
||||
|
@ -84,26 +82,6 @@ class SettingsDefaults {
|
|||
static const viewerUseCutout = true;
|
||||
static const enableMotionPhotoAutoPlay = false;
|
||||
|
||||
// video
|
||||
static const enableVideoHardwareAcceleration = true;
|
||||
static const videoAutoPlayMode = VideoAutoPlayMode.disabled;
|
||||
static const videoBackgroundMode = VideoBackgroundMode.disabled;
|
||||
static const videoLoopMode = VideoLoopMode.shortOnly;
|
||||
static const videoResumptionMode = VideoResumptionMode.ask;
|
||||
static const videoShowRawTimedText = false;
|
||||
static const videoControls = VideoControls.play;
|
||||
static const videoGestureDoubleTapTogglePlay = false;
|
||||
static const videoGestureSideDoubleTapSeek = true;
|
||||
static const videoGestureVerticalDragBrightnessVolume = false;
|
||||
|
||||
// subtitles
|
||||
static const subtitleFontSize = 20.0;
|
||||
static const subtitleTextAlignment = TextAlign.center;
|
||||
static const subtitleTextPosition = SubtitlePosition.bottom;
|
||||
static const subtitleShowOutline = true;
|
||||
static const subtitleTextColor = Color(0xFFFFFFFF);
|
||||
static const subtitleBackgroundColor = ColorUtils.transparentBlack;
|
||||
|
||||
// info
|
||||
static const infoMapZoom = 12.0;
|
||||
static const coordinateFormat = CoordinateFormat.dms;
|
||||
|
|
|
@ -7,9 +7,9 @@ extension ExtraAccessibilityTimeout on AccessibilityTimeout {
|
|||
switch (this) {
|
||||
case AccessibilityTimeout.system:
|
||||
if (hasAction) {
|
||||
return Duration(milliseconds: await (AccessibilityService.getRecommendedTimeToTakeAction(Durations.opToastActionDisplay)));
|
||||
return Duration(milliseconds: await (AccessibilityService.getRecommendedTimeToTakeAction(ADurations.opToastActionDisplay)));
|
||||
} else {
|
||||
return Duration(milliseconds: await (AccessibilityService.getRecommendedTimeToRead(Durations.opToastTextDisplay)));
|
||||
return Duration(milliseconds: await (AccessibilityService.getRecommendedTimeToRead(ADurations.opToastTextDisplay)));
|
||||
}
|
||||
case AccessibilityTimeout.s1:
|
||||
return const Duration(seconds: 1);
|
||||
|
|
109
lib/model/settings/modules/app.dart
Normal file
109
lib/model/settings/modules/app.dart
Normal file
|
@ -0,0 +1,109 @@
|
|||
import 'package:aves/model/filters/filters.dart';
|
||||
import 'package:aves/model/settings/defaults.dart';
|
||||
import 'package:aves/widgets/aves_app.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
mixin AppSettings on SettingsAccess {
|
||||
static const int _recentFilterHistoryMax = 10;
|
||||
|
||||
bool get hasAcceptedTerms => getBool(SettingKeys.hasAcceptedTermsKey) ?? SettingsDefaults.hasAcceptedTerms;
|
||||
|
||||
set hasAcceptedTerms(bool newValue) => set(SettingKeys.hasAcceptedTermsKey, newValue);
|
||||
|
||||
bool get canUseAnalysisService => getBool(SettingKeys.canUseAnalysisServiceKey) ?? SettingsDefaults.canUseAnalysisService;
|
||||
|
||||
set canUseAnalysisService(bool newValue) => set(SettingKeys.canUseAnalysisServiceKey, newValue);
|
||||
|
||||
bool get isInstalledAppAccessAllowed => getBool(SettingKeys.isInstalledAppAccessAllowedKey) ?? SettingsDefaults.isInstalledAppAccessAllowed;
|
||||
|
||||
set isInstalledAppAccessAllowed(bool newValue) => set(SettingKeys.isInstalledAppAccessAllowedKey, newValue);
|
||||
|
||||
bool get isErrorReportingAllowed => getBool(SettingKeys.isErrorReportingAllowedKey) ?? SettingsDefaults.isErrorReportingAllowed;
|
||||
|
||||
set isErrorReportingAllowed(bool newValue) => set(SettingKeys.isErrorReportingAllowedKey, newValue);
|
||||
|
||||
static const localeSeparator = '-';
|
||||
|
||||
Locale? get locale {
|
||||
// exceptionally allow getting locale before settings are initialized
|
||||
final tag = initialized ? getString(SettingKeys.localeKey) : null;
|
||||
if (tag != null) {
|
||||
final codes = tag.split(localeSeparator);
|
||||
return Locale.fromSubtags(
|
||||
languageCode: codes[0],
|
||||
scriptCode: codes[1] == '' ? null : codes[1],
|
||||
countryCode: codes[2] == '' ? null : codes[2],
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
set locale(Locale? newValue) {
|
||||
String? tag;
|
||||
if (newValue != null) {
|
||||
tag = [
|
||||
newValue.languageCode,
|
||||
newValue.scriptCode ?? '',
|
||||
newValue.countryCode ?? '',
|
||||
].join(localeSeparator);
|
||||
}
|
||||
set(SettingKeys.localeKey, tag);
|
||||
_appliedLocale = null;
|
||||
}
|
||||
|
||||
List<Locale> _systemLocalesFallback = [];
|
||||
|
||||
set systemLocalesFallback(List<Locale> locales) => _systemLocalesFallback = locales;
|
||||
|
||||
Locale? _appliedLocale;
|
||||
|
||||
void resetAppliedLocale() => _appliedLocale = null;
|
||||
|
||||
Locale get appliedLocale {
|
||||
if (_appliedLocale == null) {
|
||||
final _locale = locale;
|
||||
final preferredLocales = <Locale>[];
|
||||
if (_locale != null) {
|
||||
preferredLocales.add(_locale);
|
||||
} else {
|
||||
preferredLocales.addAll(WidgetsBinding.instance.platformDispatcher.locales);
|
||||
if (preferredLocales.isEmpty) {
|
||||
// the `window` locales may be empty in a window-less service context
|
||||
preferredLocales.addAll(_systemLocalesFallback);
|
||||
}
|
||||
}
|
||||
_appliedLocale = basicLocaleListResolution(preferredLocales, AvesApp.supportedLocales);
|
||||
}
|
||||
return _appliedLocale!;
|
||||
}
|
||||
|
||||
int get catalogTimeZoneRawOffsetMillis => getInt(SettingKeys.catalogTimeZoneRawOffsetMillisKey) ?? 0;
|
||||
|
||||
set catalogTimeZoneRawOffsetMillis(int newValue) => set(SettingKeys.catalogTimeZoneRawOffsetMillisKey, newValue);
|
||||
|
||||
double getTileExtent(String routeName) => getDouble(SettingKeys.tileExtentPrefixKey + routeName) ?? 0;
|
||||
|
||||
void setTileExtent(String routeName, double newValue) => set(SettingKeys.tileExtentPrefixKey + routeName, newValue);
|
||||
|
||||
TileLayout getTileLayout(String routeName) => getEnumOrDefault(SettingKeys.tileLayoutPrefixKey + routeName, SettingsDefaults.tileLayout, TileLayout.values);
|
||||
|
||||
void setTileLayout(String routeName, TileLayout newValue) => set(SettingKeys.tileLayoutPrefixKey + routeName, newValue.toString());
|
||||
|
||||
String get entryRenamingPattern => getString(SettingKeys.entryRenamingPatternKey) ?? SettingsDefaults.entryRenamingPattern;
|
||||
|
||||
set entryRenamingPattern(String newValue) => set(SettingKeys.entryRenamingPatternKey, newValue);
|
||||
|
||||
List<int>? get topEntryIds => getStringList(SettingKeys.topEntryIdsKey)?.map(int.tryParse).whereNotNull().toList();
|
||||
|
||||
set topEntryIds(List<int>? newValue) => set(SettingKeys.topEntryIdsKey, newValue?.map((id) => id.toString()).whereNotNull().toList());
|
||||
|
||||
List<String> get recentDestinationAlbums => getStringList(SettingKeys.recentDestinationAlbumsKey) ?? [];
|
||||
|
||||
set recentDestinationAlbums(List<String> newValue) => set(SettingKeys.recentDestinationAlbumsKey, newValue.take(_recentFilterHistoryMax).toList());
|
||||
|
||||
List<CollectionFilter> get recentTags => (getStringList(SettingKeys.recentTagsKey) ?? []).map(CollectionFilter.fromJson).whereNotNull().toList();
|
||||
|
||||
set recentTags(List<CollectionFilter> newValue) => set(SettingKeys.recentTagsKey, newValue.take(_recentFilterHistoryMax).map((filter) => filter.toJson()).toList());
|
||||
}
|
56
lib/model/settings/modules/collection.dart
Normal file
56
lib/model/settings/modules/collection.dart
Normal file
|
@ -0,0 +1,56 @@
|
|||
import 'package:aves/model/settings/defaults.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
|
||||
mixin CollectionSettings on SettingsAccess {
|
||||
List<String> get collectionBurstPatterns => getStringList(SettingKeys.collectionBurstPatternsKey) ?? [];
|
||||
|
||||
set collectionBurstPatterns(List<String> newValue) => set(SettingKeys.collectionBurstPatternsKey, newValue);
|
||||
|
||||
EntryGroupFactor get collectionSectionFactor => getEnumOrDefault(SettingKeys.collectionGroupFactorKey, SettingsDefaults.collectionSectionFactor, EntryGroupFactor.values);
|
||||
|
||||
set collectionSectionFactor(EntryGroupFactor newValue) => set(SettingKeys.collectionGroupFactorKey, newValue.toString());
|
||||
|
||||
EntrySortFactor get collectionSortFactor => getEnumOrDefault(SettingKeys.collectionSortFactorKey, SettingsDefaults.collectionSortFactor, EntrySortFactor.values);
|
||||
|
||||
set collectionSortFactor(EntrySortFactor newValue) => set(SettingKeys.collectionSortFactorKey, newValue.toString());
|
||||
|
||||
bool get collectionSortReverse => getBool(SettingKeys.collectionSortReverseKey) ?? false;
|
||||
|
||||
set collectionSortReverse(bool newValue) => set(SettingKeys.collectionSortReverseKey, newValue);
|
||||
|
||||
List<EntrySetAction> get collectionBrowsingQuickActions => getEnumListOrDefault(SettingKeys.collectionBrowsingQuickActionsKey, SettingsDefaults.collectionBrowsingQuickActions, EntrySetAction.values);
|
||||
|
||||
set collectionBrowsingQuickActions(List<EntrySetAction> newValue) => set(SettingKeys.collectionBrowsingQuickActionsKey, newValue.map((v) => v.toString()).toList());
|
||||
|
||||
List<EntrySetAction> get collectionSelectionQuickActions => getEnumListOrDefault(SettingKeys.collectionSelectionQuickActionsKey, SettingsDefaults.collectionSelectionQuickActions, EntrySetAction.values);
|
||||
|
||||
set collectionSelectionQuickActions(List<EntrySetAction> newValue) => set(SettingKeys.collectionSelectionQuickActionsKey, newValue.map((v) => v.toString()).toList());
|
||||
|
||||
bool get showThumbnailFavourite => getBool(SettingKeys.showThumbnailFavouriteKey) ?? SettingsDefaults.showThumbnailFavourite;
|
||||
|
||||
set showThumbnailFavourite(bool newValue) => set(SettingKeys.showThumbnailFavouriteKey, newValue);
|
||||
|
||||
ThumbnailOverlayLocationIcon get thumbnailLocationIcon => getEnumOrDefault(SettingKeys.thumbnailLocationIconKey, SettingsDefaults.thumbnailLocationIcon, ThumbnailOverlayLocationIcon.values);
|
||||
|
||||
set thumbnailLocationIcon(ThumbnailOverlayLocationIcon newValue) => set(SettingKeys.thumbnailLocationIconKey, newValue.toString());
|
||||
|
||||
ThumbnailOverlayTagIcon get thumbnailTagIcon => getEnumOrDefault(SettingKeys.thumbnailTagIconKey, SettingsDefaults.thumbnailTagIcon, ThumbnailOverlayTagIcon.values);
|
||||
|
||||
set thumbnailTagIcon(ThumbnailOverlayTagIcon newValue) => set(SettingKeys.thumbnailTagIconKey, newValue.toString());
|
||||
|
||||
bool get showThumbnailMotionPhoto => getBool(SettingKeys.showThumbnailMotionPhotoKey) ?? SettingsDefaults.showThumbnailMotionPhoto;
|
||||
|
||||
set showThumbnailMotionPhoto(bool newValue) => set(SettingKeys.showThumbnailMotionPhotoKey, newValue);
|
||||
|
||||
bool get showThumbnailRating => getBool(SettingKeys.showThumbnailRatingKey) ?? SettingsDefaults.showThumbnailRating;
|
||||
|
||||
set showThumbnailRating(bool newValue) => set(SettingKeys.showThumbnailRatingKey, newValue);
|
||||
|
||||
bool get showThumbnailRaw => getBool(SettingKeys.showThumbnailRawKey) ?? SettingsDefaults.showThumbnailRaw;
|
||||
|
||||
set showThumbnailRaw(bool newValue) => set(SettingKeys.showThumbnailRawKey, newValue);
|
||||
|
||||
bool get showThumbnailVideoDuration => getBool(SettingKeys.showThumbnailVideoDurationKey) ?? SettingsDefaults.showThumbnailVideoDuration;
|
||||
|
||||
set showThumbnailVideoDuration(bool newValue) => set(SettingKeys.showThumbnailVideoDurationKey, newValue);
|
||||
}
|
37
lib/model/settings/modules/display.dart
Normal file
37
lib/model/settings/modules/display.dart
Normal file
|
@ -0,0 +1,37 @@
|
|||
import 'package:aves/model/device.dart';
|
||||
import 'package:aves/model/settings/defaults.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
|
||||
mixin DisplaySettings on SettingsAccess {
|
||||
DisplayRefreshRateMode get displayRefreshRateMode => getEnumOrDefault(SettingKeys.displayRefreshRateModeKey, SettingsDefaults.displayRefreshRateMode, DisplayRefreshRateMode.values);
|
||||
|
||||
set displayRefreshRateMode(DisplayRefreshRateMode newValue) => set(SettingKeys.displayRefreshRateModeKey, newValue.toString());
|
||||
|
||||
AvesThemeBrightness get themeBrightness => getEnumOrDefault(SettingKeys.themeBrightnessKey, SettingsDefaults.themeBrightness, AvesThemeBrightness.values);
|
||||
|
||||
set themeBrightness(AvesThemeBrightness newValue) => set(SettingKeys.themeBrightnessKey, newValue.toString());
|
||||
|
||||
AvesThemeColorMode get themeColorMode => getEnumOrDefault(SettingKeys.themeColorModeKey, SettingsDefaults.themeColorMode, AvesThemeColorMode.values);
|
||||
|
||||
set themeColorMode(AvesThemeColorMode newValue) => set(SettingKeys.themeColorModeKey, newValue.toString());
|
||||
|
||||
bool get enableDynamicColor => getBool(SettingKeys.enableDynamicColorKey) ?? SettingsDefaults.enableDynamicColor;
|
||||
|
||||
set enableDynamicColor(bool newValue) => set(SettingKeys.enableDynamicColorKey, newValue);
|
||||
|
||||
bool get enableBlurEffect => getBool(SettingKeys.enableBlurEffectKey) ?? SettingsDefaults.enableBlurEffect;
|
||||
|
||||
set enableBlurEffect(bool newValue) => set(SettingKeys.enableBlurEffectKey, newValue);
|
||||
|
||||
MaxBrightness get maxBrightness => getEnumOrDefault(SettingKeys.maxBrightnessKey, SettingsDefaults.maxBrightness, MaxBrightness.values);
|
||||
|
||||
set maxBrightness(MaxBrightness newValue) => set(SettingKeys.maxBrightnessKey, newValue.toString());
|
||||
|
||||
bool get forceTvLayout => getBool(SettingKeys.forceTvLayoutKey) ?? SettingsDefaults.forceTvLayout;
|
||||
|
||||
set forceTvLayout(bool newValue) => set(SettingKeys.forceTvLayoutKey, newValue);
|
||||
|
||||
bool get useTvLayout => device.isTelevision || forceTvLayout;
|
||||
|
||||
bool get isReadOnly => useTvLayout;
|
||||
}
|
74
lib/model/settings/modules/filter_grids.dart
Normal file
74
lib/model/settings/modules/filter_grids.dart
Normal file
|
@ -0,0 +1,74 @@
|
|||
import 'package:aves/model/filters/filters.dart';
|
||||
import 'package:aves/model/settings/defaults.dart';
|
||||
import 'package:aves/model/settings/modules/search.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
mixin FilterGridsSettings on SettingsAccess, SearchSettings {
|
||||
AlbumChipGroupFactor get albumGroupFactor => getEnumOrDefault(SettingKeys.albumGroupFactorKey, SettingsDefaults.albumGroupFactor, AlbumChipGroupFactor.values);
|
||||
|
||||
set albumGroupFactor(AlbumChipGroupFactor newValue) => set(SettingKeys.albumGroupFactorKey, newValue.toString());
|
||||
|
||||
ChipSortFactor get albumSortFactor => getEnumOrDefault(SettingKeys.albumSortFactorKey, SettingsDefaults.chipListSortFactor, ChipSortFactor.values);
|
||||
|
||||
set albumSortFactor(ChipSortFactor newValue) => set(SettingKeys.albumSortFactorKey, newValue.toString());
|
||||
|
||||
ChipSortFactor get countrySortFactor => getEnumOrDefault(SettingKeys.countrySortFactorKey, SettingsDefaults.chipListSortFactor, ChipSortFactor.values);
|
||||
|
||||
set countrySortFactor(ChipSortFactor newValue) => set(SettingKeys.countrySortFactorKey, newValue.toString());
|
||||
|
||||
ChipSortFactor get stateSortFactor => getEnumOrDefault(SettingKeys.stateSortFactorKey, SettingsDefaults.chipListSortFactor, ChipSortFactor.values);
|
||||
|
||||
set stateSortFactor(ChipSortFactor newValue) => set(SettingKeys.stateSortFactorKey, newValue.toString());
|
||||
|
||||
ChipSortFactor get placeSortFactor => getEnumOrDefault(SettingKeys.placeSortFactorKey, SettingsDefaults.chipListSortFactor, ChipSortFactor.values);
|
||||
|
||||
set placeSortFactor(ChipSortFactor newValue) => set(SettingKeys.placeSortFactorKey, newValue.toString());
|
||||
|
||||
ChipSortFactor get tagSortFactor => getEnumOrDefault(SettingKeys.tagSortFactorKey, SettingsDefaults.chipListSortFactor, ChipSortFactor.values);
|
||||
|
||||
set tagSortFactor(ChipSortFactor newValue) => set(SettingKeys.tagSortFactorKey, newValue.toString());
|
||||
|
||||
bool get albumSortReverse => getBool(SettingKeys.albumSortReverseKey) ?? false;
|
||||
|
||||
set albumSortReverse(bool newValue) => set(SettingKeys.albumSortReverseKey, newValue);
|
||||
|
||||
bool get countrySortReverse => getBool(SettingKeys.countrySortReverseKey) ?? false;
|
||||
|
||||
set countrySortReverse(bool newValue) => set(SettingKeys.countrySortReverseKey, newValue);
|
||||
|
||||
bool get stateSortReverse => getBool(SettingKeys.stateSortReverseKey) ?? false;
|
||||
|
||||
set stateSortReverse(bool newValue) => set(SettingKeys.stateSortReverseKey, newValue);
|
||||
|
||||
bool get placeSortReverse => getBool(SettingKeys.placeSortReverseKey) ?? false;
|
||||
|
||||
set placeSortReverse(bool newValue) => set(SettingKeys.placeSortReverseKey, newValue);
|
||||
|
||||
bool get tagSortReverse => getBool(SettingKeys.tagSortReverseKey) ?? false;
|
||||
|
||||
set tagSortReverse(bool newValue) => set(SettingKeys.tagSortReverseKey, newValue);
|
||||
|
||||
Set<CollectionFilter> get pinnedFilters => (getStringList(SettingKeys.pinnedFiltersKey) ?? []).map(CollectionFilter.fromJson).whereNotNull().toSet();
|
||||
|
||||
set pinnedFilters(Set<CollectionFilter> newValue) => set(SettingKeys.pinnedFiltersKey, newValue.map((filter) => filter.toJson()).toList());
|
||||
|
||||
Set<CollectionFilter> get hiddenFilters => (getStringList(SettingKeys.hiddenFiltersKey) ?? []).map(CollectionFilter.fromJson).whereNotNull().toSet();
|
||||
|
||||
set hiddenFilters(Set<CollectionFilter> newValue) => set(SettingKeys.hiddenFiltersKey, newValue.map((filter) => filter.toJson()).toList());
|
||||
|
||||
void changeFilterVisibility(Set<CollectionFilter> filters, bool visible) {
|
||||
final _hiddenFilters = hiddenFilters;
|
||||
if (visible) {
|
||||
_hiddenFilters.removeAll(filters);
|
||||
} else {
|
||||
_hiddenFilters.addAll(filters);
|
||||
searchHistory = searchHistory..removeWhere(filters.contains);
|
||||
}
|
||||
hiddenFilters = _hiddenFilters;
|
||||
}
|
||||
|
||||
bool get showAlbumPickQuery => getBool(SettingKeys.showAlbumPickQueryKey) ?? false;
|
||||
|
||||
set showAlbumPickQuery(bool newValue) => set(SettingKeys.showAlbumPickQueryKey, newValue);
|
||||
}
|
16
lib/model/settings/modules/info.dart
Normal file
16
lib/model/settings/modules/info.dart
Normal file
|
@ -0,0 +1,16 @@
|
|||
import 'package:aves/model/settings/defaults.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
|
||||
mixin InfoSettings on SettingsAccess {
|
||||
double get infoMapZoom => getDouble(SettingKeys.infoMapZoomKey) ?? SettingsDefaults.infoMapZoom;
|
||||
|
||||
set infoMapZoom(double newValue) => set(SettingKeys.infoMapZoomKey, newValue);
|
||||
|
||||
CoordinateFormat get coordinateFormat => getEnumOrDefault(SettingKeys.coordinateFormatKey, SettingsDefaults.coordinateFormat, CoordinateFormat.values);
|
||||
|
||||
set coordinateFormat(CoordinateFormat newValue) => set(SettingKeys.coordinateFormatKey, newValue.toString());
|
||||
|
||||
UnitSystem get unitSystem => getEnumOrDefault(SettingKeys.unitSystemKey, SettingsDefaults.unitSystem, UnitSystem.values);
|
||||
|
||||
set unitSystem(UnitSystem newValue) => set(SettingKeys.unitSystemKey, newValue.toString());
|
||||
}
|
62
lib/model/settings/modules/navigation.dart
Normal file
62
lib/model/settings/modules/navigation.dart
Normal file
|
@ -0,0 +1,62 @@
|
|||
import 'package:aves/model/filters/filters.dart';
|
||||
import 'package:aves/model/settings/defaults.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
|
||||
mixin NavigationSettings on SettingsAccess {
|
||||
bool get mustBackTwiceToExit => getBool(SettingKeys.mustBackTwiceToExitKey) ?? SettingsDefaults.mustBackTwiceToExit;
|
||||
|
||||
set mustBackTwiceToExit(bool newValue) => set(SettingKeys.mustBackTwiceToExitKey, newValue);
|
||||
|
||||
KeepScreenOn get keepScreenOn => getEnumOrDefault(SettingKeys.keepScreenOnKey, SettingsDefaults.keepScreenOn, KeepScreenOn.values);
|
||||
|
||||
set keepScreenOn(KeepScreenOn newValue) => set(SettingKeys.keepScreenOnKey, newValue.toString());
|
||||
|
||||
HomePageSetting get homePage => getEnumOrDefault(SettingKeys.homePageKey, SettingsDefaults.homePage, HomePageSetting.values);
|
||||
|
||||
set homePage(HomePageSetting newValue) => set(SettingKeys.homePageKey, newValue.toString());
|
||||
|
||||
bool get enableBottomNavigationBar => getBool(SettingKeys.enableBottomNavigationBarKey) ?? SettingsDefaults.enableBottomNavigationBar;
|
||||
|
||||
set enableBottomNavigationBar(bool newValue) => set(SettingKeys.enableBottomNavigationBarKey, newValue);
|
||||
|
||||
bool get confirmCreateVault => getBool(SettingKeys.confirmCreateVaultKey) ?? SettingsDefaults.confirm;
|
||||
|
||||
set confirmCreateVault(bool newValue) => set(SettingKeys.confirmCreateVaultKey, newValue);
|
||||
|
||||
bool get confirmDeleteForever => getBool(SettingKeys.confirmDeleteForeverKey) ?? SettingsDefaults.confirm;
|
||||
|
||||
set confirmDeleteForever(bool newValue) => set(SettingKeys.confirmDeleteForeverKey, newValue);
|
||||
|
||||
bool get confirmMoveToBin => getBool(SettingKeys.confirmMoveToBinKey) ?? SettingsDefaults.confirm;
|
||||
|
||||
set confirmMoveToBin(bool newValue) => set(SettingKeys.confirmMoveToBinKey, newValue);
|
||||
|
||||
bool get confirmMoveUndatedItems => getBool(SettingKeys.confirmMoveUndatedItemsKey) ?? SettingsDefaults.confirm;
|
||||
|
||||
set confirmMoveUndatedItems(bool newValue) => set(SettingKeys.confirmMoveUndatedItemsKey, newValue);
|
||||
|
||||
bool get confirmAfterMoveToBin => getBool(SettingKeys.confirmAfterMoveToBinKey) ?? SettingsDefaults.confirm;
|
||||
|
||||
set confirmAfterMoveToBin(bool newValue) => set(SettingKeys.confirmAfterMoveToBinKey, newValue);
|
||||
|
||||
bool get setMetadataDateBeforeFileOp => getBool(SettingKeys.setMetadataDateBeforeFileOpKey) ?? SettingsDefaults.setMetadataDateBeforeFileOp;
|
||||
|
||||
set setMetadataDateBeforeFileOp(bool newValue) => set(SettingKeys.setMetadataDateBeforeFileOpKey, newValue);
|
||||
|
||||
List<CollectionFilter?> get drawerTypeBookmarks =>
|
||||
(getStringList(SettingKeys.drawerTypeBookmarksKey))?.map((v) {
|
||||
if (v.isEmpty) return null;
|
||||
return CollectionFilter.fromJson(v);
|
||||
}).toList() ??
|
||||
SettingsDefaults.drawerTypeBookmarks;
|
||||
|
||||
set drawerTypeBookmarks(List<CollectionFilter?> newValue) => set(SettingKeys.drawerTypeBookmarksKey, newValue.map((filter) => filter?.toJson() ?? '').toList());
|
||||
|
||||
List<String>? get drawerAlbumBookmarks => getStringList(SettingKeys.drawerAlbumBookmarksKey);
|
||||
|
||||
set drawerAlbumBookmarks(List<String>? newValue) => set(SettingKeys.drawerAlbumBookmarksKey, newValue);
|
||||
|
||||
List<String> get drawerPageBookmarks => getStringList(SettingKeys.drawerPageBookmarksKey) ?? SettingsDefaults.drawerPageBookmarks;
|
||||
|
||||
set drawerPageBookmarks(List<String> newValue) => set(SettingKeys.drawerPageBookmarksKey, newValue);
|
||||
}
|
14
lib/model/settings/modules/search.dart
Normal file
14
lib/model/settings/modules/search.dart
Normal file
|
@ -0,0 +1,14 @@
|
|||
import 'package:aves/model/filters/filters.dart';
|
||||
import 'package:aves/model/settings/defaults.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
mixin SearchSettings on SettingsAccess {
|
||||
bool get saveSearchHistory => getBool(SettingKeys.saveSearchHistoryKey) ?? SettingsDefaults.saveSearchHistory;
|
||||
|
||||
set saveSearchHistory(bool newValue) => set(SettingKeys.saveSearchHistoryKey, newValue);
|
||||
|
||||
List<CollectionFilter> get searchHistory => (getStringList(SettingKeys.searchHistoryKey) ?? []).map(CollectionFilter.fromJson).whereNotNull().toList();
|
||||
|
||||
set searchHistory(List<CollectionFilter> newValue) => set(SettingKeys.searchHistoryKey, newValue.map((filter) => filter.toJson()).toList());
|
||||
}
|
56
lib/model/settings/modules/viewer.dart
Normal file
56
lib/model/settings/modules/viewer.dart
Normal file
|
@ -0,0 +1,56 @@
|
|||
import 'package:aves/model/settings/defaults.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
|
||||
mixin ViewerSettings on SettingsAccess {
|
||||
List<EntryAction> get viewerQuickActions => getEnumListOrDefault(SettingKeys.viewerQuickActionsKey, SettingsDefaults.viewerQuickActions, EntryAction.values);
|
||||
|
||||
set viewerQuickActions(List<EntryAction> newValue) => set(SettingKeys.viewerQuickActionsKey, newValue.map((v) => v.toString()).toList());
|
||||
|
||||
bool get showOverlayOnOpening => getBool(SettingKeys.showOverlayOnOpeningKey) ?? SettingsDefaults.showOverlayOnOpening;
|
||||
|
||||
set showOverlayOnOpening(bool newValue) => set(SettingKeys.showOverlayOnOpeningKey, newValue);
|
||||
|
||||
bool get showOverlayMinimap => getBool(SettingKeys.showOverlayMinimapKey) ?? SettingsDefaults.showOverlayMinimap;
|
||||
|
||||
set showOverlayMinimap(bool newValue) => set(SettingKeys.showOverlayMinimapKey, newValue);
|
||||
|
||||
OverlayHistogramStyle get overlayHistogramStyle => getEnumOrDefault(SettingKeys.overlayHistogramStyleKey, SettingsDefaults.overlayHistogramStyle, OverlayHistogramStyle.values);
|
||||
|
||||
set overlayHistogramStyle(OverlayHistogramStyle newValue) => set(SettingKeys.overlayHistogramStyleKey, newValue.toString());
|
||||
|
||||
bool get showOverlayInfo => getBool(SettingKeys.showOverlayInfoKey) ?? SettingsDefaults.showOverlayInfo;
|
||||
|
||||
set showOverlayInfo(bool newValue) => set(SettingKeys.showOverlayInfoKey, newValue);
|
||||
|
||||
bool get showOverlayDescription => getBool(SettingKeys.showOverlayDescriptionKey) ?? SettingsDefaults.showOverlayDescription;
|
||||
|
||||
set showOverlayDescription(bool newValue) => set(SettingKeys.showOverlayDescriptionKey, newValue);
|
||||
|
||||
bool get showOverlayRatingTags => getBool(SettingKeys.showOverlayRatingTagsKey) ?? SettingsDefaults.showOverlayRatingTags;
|
||||
|
||||
set showOverlayRatingTags(bool newValue) => set(SettingKeys.showOverlayRatingTagsKey, newValue);
|
||||
|
||||
bool get showOverlayShootingDetails => getBool(SettingKeys.showOverlayShootingDetailsKey) ?? SettingsDefaults.showOverlayShootingDetails;
|
||||
|
||||
set showOverlayShootingDetails(bool newValue) => set(SettingKeys.showOverlayShootingDetailsKey, newValue);
|
||||
|
||||
bool get showOverlayThumbnailPreview => getBool(SettingKeys.showOverlayThumbnailPreviewKey) ?? SettingsDefaults.showOverlayThumbnailPreview;
|
||||
|
||||
set showOverlayThumbnailPreview(bool newValue) => set(SettingKeys.showOverlayThumbnailPreviewKey, newValue);
|
||||
|
||||
bool get viewerGestureSideTapNext => getBool(SettingKeys.viewerGestureSideTapNextKey) ?? SettingsDefaults.viewerGestureSideTapNext;
|
||||
|
||||
set viewerGestureSideTapNext(bool newValue) => set(SettingKeys.viewerGestureSideTapNextKey, newValue);
|
||||
|
||||
bool get viewerUseCutout => getBool(SettingKeys.viewerUseCutoutKey) ?? SettingsDefaults.viewerUseCutout;
|
||||
|
||||
set viewerUseCutout(bool newValue) => set(SettingKeys.viewerUseCutoutKey, newValue);
|
||||
|
||||
bool get enableMotionPhotoAutoPlay => getBool(SettingKeys.enableMotionPhotoAutoPlayKey) ?? SettingsDefaults.enableMotionPhotoAutoPlay;
|
||||
|
||||
set enableMotionPhotoAutoPlay(bool newValue) => set(SettingKeys.enableMotionPhotoAutoPlayKey, newValue);
|
||||
|
||||
EntryBackground get imageBackground => getEnumOrDefault(SettingKeys.imageBackgroundKey, SettingsDefaults.imageBackground, EntryBackground.values);
|
||||
|
||||
set imageBackground(EntryBackground newValue) => set(SettingKeys.imageBackgroundKey, newValue.toString());
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -1,4 +1,4 @@
|
|||
import 'package:aves/model/settings/store/store.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
|
@ -19,7 +19,9 @@ mixin AlbumMixin on SourceBase {
|
|||
|
||||
Set<AlbumFilter> getNewAlbumFilters(BuildContext context) => Set.unmodifiable(_newAlbums.map((v) => AlbumFilter(v, getAlbumDisplayName(context, v))));
|
||||
|
||||
int compareAlbumsByName(String a, String b) {
|
||||
int compareAlbumsByName(String? a, String? b) {
|
||||
a ??= '';
|
||||
b ??= '';
|
||||
final ua = getAlbumDisplayName(null, a);
|
||||
final ub = getAlbumDisplayName(null, b);
|
||||
final c = compareAsciiUpperCaseNatural(ua, ub);
|
||||
|
|
|
@ -84,10 +84,10 @@ class CollectionLens with ChangeNotifier {
|
|||
}
|
||||
_subscriptions.add(settings.updateStream
|
||||
.where((event) => [
|
||||
Settings.collectionBurstPatternsKey,
|
||||
Settings.collectionSortFactorKey,
|
||||
Settings.collectionGroupFactorKey,
|
||||
Settings.collectionSortReverseKey,
|
||||
SettingKeys.collectionBurstPatternsKey,
|
||||
SettingKeys.collectionSortFactorKey,
|
||||
SettingKeys.collectionGroupFactorKey,
|
||||
SettingKeys.collectionSortReverseKey,
|
||||
].contains(event.key))
|
||||
.listen((_) => _onSettingsChanged()));
|
||||
refresh();
|
||||
|
@ -245,7 +245,7 @@ class CollectionLens with ChangeNotifier {
|
|||
}
|
||||
case EntrySortFactor.name:
|
||||
final byAlbum = groupBy<AvesEntry, EntryAlbumSectionKey>(_filteredSortedEntries, (entry) => EntryAlbumSectionKey(entry.directory));
|
||||
final compare = sortReverse ? (a, b) => source.compareAlbumsByName(b.directory!, a.directory!) : (a, b) => source.compareAlbumsByName(a.directory!, b.directory!);
|
||||
final int Function(EntryAlbumSectionKey, EntryAlbumSectionKey) compare = sortReverse ? (a, b) => source.compareAlbumsByName(b.directory, a.directory) : (a, b) => source.compareAlbumsByName(a.directory, b.directory);
|
||||
sections = SplayTreeMap<EntryAlbumSectionKey, List<AvesEntry>>.of(byAlbum, compare);
|
||||
case EntrySortFactor.rating:
|
||||
sections = groupBy<AvesEntry, EntryRatingSectionKey>(_filteredSortedEntries, (entry) => EntryRatingSectionKey(entry.rating));
|
||||
|
|
|
@ -61,8 +61,8 @@ mixin SourceBase {
|
|||
|
||||
abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, PlaceMixin, StateMixin, LocationMixin, TagMixin, TrashMixin {
|
||||
CollectionSource() {
|
||||
settings.updateStream.where((event) => event.key == Settings.localeKey).listen((_) => invalidateAlbumDisplayNames());
|
||||
settings.updateStream.where((event) => event.key == Settings.hiddenFiltersKey).listen((event) {
|
||||
settings.updateStream.where((event) => event.key == SettingKeys.localeKey).listen((_) => invalidateAlbumDisplayNames());
|
||||
settings.updateStream.where((event) => event.key == SettingKeys.hiddenFiltersKey).listen((event) {
|
||||
final oldValue = event.oldValue;
|
||||
if (oldValue is List<String>?) {
|
||||
final oldHiddenFilters = (oldValue ?? []).map(CollectionFilter.fromJson).whereNotNull().toSet();
|
||||
|
@ -274,9 +274,9 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
|
|||
final existingCover = covers.of(oldFilter);
|
||||
await covers.set(
|
||||
filter: newFilter,
|
||||
entryId: existingCover?.item1,
|
||||
packageName: existingCover?.item2,
|
||||
color: existingCover?.item3,
|
||||
entryId: existingCover?.$1,
|
||||
packageName: existingCover?.$2,
|
||||
color: existingCover?.$3,
|
||||
);
|
||||
|
||||
renameNewAlbum(sourceAlbum, destinationAlbum);
|
||||
|
@ -547,7 +547,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
|
|||
}
|
||||
|
||||
AvesEntry? coverEntry(CollectionFilter filter) {
|
||||
final id = covers.of(filter)?.item1;
|
||||
final id = covers.of(filter)?.$1;
|
||||
if (id != null) {
|
||||
final entry = visibleEntries.firstWhereOrNull((entry) => entry.id == id);
|
||||
if (entry != null) return entry;
|
||||
|
|
|
@ -14,7 +14,6 @@ import 'package:aves/services/common/services.dart';
|
|||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
mixin LocationMixin on CountryMixin, StateMixin {
|
||||
static const commitCountThreshold = 200;
|
||||
|
@ -96,16 +95,16 @@ mixin LocationMixin on CountryMixin, StateMixin {
|
|||
// - 652 calls (22%) when approximating to 2 decimal places (~1km - town or village)
|
||||
// cf https://en.wikipedia.org/wiki/Decimal_degrees#Precision
|
||||
final latLngFactor = pow(10, 2);
|
||||
Tuple2<int, int> approximateLatLng(AvesEntry entry) {
|
||||
(int latitude, int longitude) approximateLatLng(AvesEntry entry) {
|
||||
// entry has coordinates
|
||||
final catalogMetadata = entry.catalogMetadata!;
|
||||
final lat = catalogMetadata.latitude!;
|
||||
final lng = catalogMetadata.longitude!;
|
||||
return Tuple2<int, int>((lat * latLngFactor).round(), (lng * latLngFactor).round());
|
||||
return ((lat * latLngFactor).round(), (lng * latLngFactor).round());
|
||||
}
|
||||
|
||||
final located = visibleEntries.where((entry) => entry.hasGps).toSet().difference(todo);
|
||||
final knownLocations = <Tuple2<int, int>, AddressDetails?>{};
|
||||
final knownLocations = <(int, int), AddressDetails?>{};
|
||||
located.forEach((entry) {
|
||||
knownLocations.putIfAbsent(approximateLatLng(entry), () => entry.addressDetails);
|
||||
});
|
||||
|
|
|
@ -8,6 +8,7 @@ import 'package:aves/model/video/profiles/aac.dart';
|
|||
import 'package:aves/model/video/profiles/h264.dart';
|
||||
import 'package:aves/model/video/profiles/hevc.dart';
|
||||
import 'package:aves/ref/languages.dart';
|
||||
import 'package:aves/ref/mime_types.dart';
|
||||
import 'package:aves/ref/mp4.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves/theme/format.dart';
|
||||
|
@ -15,10 +16,8 @@ import 'package:aves/utils/file_utils.dart';
|
|||
import 'package:aves/utils/math_utils.dart';
|
||||
import 'package:aves/utils/string_utils.dart';
|
||||
import 'package:aves/utils/time_utils.dart';
|
||||
import 'package:aves/widgets/viewer/video/fijkplayer.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:fijkplayer/fijkplayer.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class VideoMetadataFormatter {
|
||||
|
@ -26,7 +25,8 @@ class VideoMetadataFormatter {
|
|||
static final _ambiguousDatePatterns = {
|
||||
RegExp(r'^\d{2}[-/]\d{2}[-/]\d{4}$'),
|
||||
};
|
||||
static final _durationPattern = RegExp(r'(\d+):(\d+):(\d+)(.\d+)');
|
||||
static final _durationHmsmPattern = RegExp(r'(\d+):(\d+):(\d+)(.\d+)');
|
||||
static final _durationSmPattern = RegExp(r'(\d+)(.\d+)');
|
||||
static final _locationPattern = RegExp(r'([+-][.0-9]+)');
|
||||
static final Map<String, String> _codecNames = {
|
||||
Codecs.ac3: 'AC-3',
|
||||
|
@ -44,20 +44,8 @@ class VideoMetadataFormatter {
|
|||
Codecs.webm: 'WebM',
|
||||
};
|
||||
|
||||
static Future<Map> getVideoMetadata(AvesEntry entry) async {
|
||||
final player = FijkPlayer();
|
||||
final info = await player.setDataSourceUntilPrepared(entry.uri).then((v) {
|
||||
return player.getInfo();
|
||||
}).catchError((error) {
|
||||
debugPrint('failed to get video metadata for entry=$entry, error=$error');
|
||||
return {};
|
||||
});
|
||||
await player.release();
|
||||
return info;
|
||||
}
|
||||
|
||||
static Future<Map<String, int>> getLoadingMetadata(AvesEntry entry) async {
|
||||
final mediaInfo = await getVideoMetadata(entry);
|
||||
final mediaInfo = await videoMetadataFetcher.getMetadata(entry);
|
||||
final fields = <String, int>{};
|
||||
|
||||
final streams = mediaInfo[Keys.streams];
|
||||
|
@ -77,12 +65,26 @@ class VideoMetadataFormatter {
|
|||
final durationMicros = mediaInfo[Keys.durationMicros];
|
||||
if (durationMicros is num) {
|
||||
fields['durationMillis'] = (durationMicros / 1000).round();
|
||||
} else {
|
||||
final duration = _parseDuration(mediaInfo[Keys.duration]);
|
||||
if (duration != null) {
|
||||
fields['durationMillis'] = duration.inMilliseconds;
|
||||
}
|
||||
}
|
||||
return fields;
|
||||
}
|
||||
|
||||
static Future<CatalogMetadata?> getCatalogMetadata(AvesEntry entry) async {
|
||||
final mediaInfo = await getVideoMetadata(entry);
|
||||
var catalogMetadata = entry.catalogMetadata ?? CatalogMetadata(id: entry.id);
|
||||
|
||||
final mediaInfo = await videoMetadataFetcher.getMetadata(entry);
|
||||
|
||||
if (entry.mimeType == MimeTypes.avif) {
|
||||
final duration = _parseDuration(mediaInfo[Keys.duration]);
|
||||
if (duration == null) return null;
|
||||
|
||||
catalogMetadata = catalogMetadata.copyWith(isAnimated: true);
|
||||
}
|
||||
|
||||
// only consider values with at least 8 characters (yyyymmdd),
|
||||
// ignoring unset values like `0`, as well as year values like `2021`
|
||||
|
@ -102,12 +104,12 @@ class VideoMetadataFormatter {
|
|||
|
||||
// exclude date if it is suspiciously close to epoch
|
||||
if (dateMillis != null && !DateTime.fromMillisecondsSinceEpoch(dateMillis).isAtSameDayAs(epoch)) {
|
||||
return (entry.catalogMetadata ?? CatalogMetadata(id: entry.id)).copyWith(
|
||||
catalogMetadata = catalogMetadata.copyWith(
|
||||
dateMillis: dateMillis,
|
||||
);
|
||||
}
|
||||
|
||||
return entry.catalogMetadata;
|
||||
return catalogMetadata;
|
||||
}
|
||||
|
||||
static bool isAmbiguousDate(String dateString) {
|
||||
|
@ -194,14 +196,21 @@ class VideoMetadataFormatter {
|
|||
|
||||
switch (key) {
|
||||
case Keys.codecLevel:
|
||||
case Keys.codecTag:
|
||||
case Keys.codecTagString:
|
||||
case Keys.durationTs:
|
||||
case Keys.fpsNum:
|
||||
case Keys.handlerName:
|
||||
case Keys.index:
|
||||
case Keys.isAvc:
|
||||
case Keys.probeScore:
|
||||
case Keys.programCount:
|
||||
case Keys.refs:
|
||||
case Keys.sarNum:
|
||||
case Keys.selectedAudioStream:
|
||||
case Keys.selectedTextStream:
|
||||
case Keys.selectedVideoStream:
|
||||
case Keys.statisticsTags:
|
||||
case Keys.streamCount:
|
||||
case Keys.streams:
|
||||
case Keys.streamType:
|
||||
case Keys.tbrNum:
|
||||
|
@ -219,10 +228,14 @@ class VideoMetadataFormatter {
|
|||
case Keys.bitrate:
|
||||
case Keys.bps:
|
||||
save('Bit Rate', _formatMetric(value, 'b/s'));
|
||||
case Keys.bitsPerRawSample:
|
||||
save('Bits Per Raw Sample', value);
|
||||
case Keys.byteCount:
|
||||
save('Size', _formatFilesize(value));
|
||||
case Keys.channelLayout:
|
||||
save('Channel Layout', _formatChannelLayout(value));
|
||||
case Keys.chromaLocation:
|
||||
save('Chroma Location', value);
|
||||
case Keys.codecName:
|
||||
if (value != 'none') {
|
||||
save('Format', _formatCodecName(value));
|
||||
|
@ -233,6 +246,18 @@ class VideoMetadataFormatter {
|
|||
// user-friendly descriptions for related enums are defined in libavutil/pixfmt.h
|
||||
save('Pixel Format', (value as String).toUpperCase());
|
||||
}
|
||||
case Keys.codedHeight:
|
||||
save('Coded Height', '$value pixels');
|
||||
case Keys.codedWidth:
|
||||
save('Coded Width', '$value pixels');
|
||||
case Keys.colorPrimaries:
|
||||
save('Color Primaries', (value as String).toUpperCase());
|
||||
case Keys.colorRange:
|
||||
save('Color Range', (value as String).toUpperCase());
|
||||
case Keys.colorSpace:
|
||||
save('Color Space', (value as String).toUpperCase());
|
||||
case Keys.colorTransfer:
|
||||
save('Color Transfer', (value as String).toUpperCase());
|
||||
case Keys.codecProfileId:
|
||||
{
|
||||
final profile = int.tryParse(value);
|
||||
|
@ -242,9 +267,9 @@ class VideoMetadataFormatter {
|
|||
case Codecs.h264:
|
||||
case Codecs.hevc:
|
||||
{
|
||||
final levelString = info[Keys.codecLevel];
|
||||
if (levelString != null) {
|
||||
final level = int.tryParse(levelString) ?? 0;
|
||||
final levelValue = info[Keys.codecLevel];
|
||||
if (levelValue != null) {
|
||||
final level = levelValue is int ? levelValue : int.tryParse(levelValue) ?? 0;
|
||||
if (codec == Codecs.h264) {
|
||||
profileString = H264.formatProfile(profile, level);
|
||||
} else {
|
||||
|
@ -268,6 +293,8 @@ class VideoMetadataFormatter {
|
|||
save('Compatible Brands', formattedBrands);
|
||||
case Keys.creationTime:
|
||||
save('Creation Time', _formatDate(value));
|
||||
case Keys.dar:
|
||||
save('Display Aspect Ratio', value);
|
||||
case Keys.date:
|
||||
if (value is String && value != '0') {
|
||||
final charCount = value.length;
|
||||
|
@ -277,10 +304,18 @@ class VideoMetadataFormatter {
|
|||
save('Duration', _formatDuration(value));
|
||||
case Keys.durationMicros:
|
||||
if (value != 0) save('Duration', formatPreciseDuration(Duration(microseconds: value)));
|
||||
case Keys.extraDataSize:
|
||||
save('Extra Data Size', _formatFilesize(value));
|
||||
case Keys.fieldOrder:
|
||||
save('Field Order', value);
|
||||
case Keys.fpsDen:
|
||||
save('Frame Rate', '${roundToPrecision(info[Keys.fpsNum] / info[Keys.fpsDen], decimals: 3).toString()} FPS');
|
||||
case Keys.frameCount:
|
||||
save('Frame Count', value);
|
||||
case Keys.handlerName:
|
||||
save('Handler Name', value);
|
||||
case Keys.hasBFrames:
|
||||
save('Has B-Frames', value);
|
||||
case Keys.height:
|
||||
save('Height', '$value pixels');
|
||||
case Keys.language:
|
||||
|
@ -295,6 +330,8 @@ class VideoMetadataFormatter {
|
|||
save('Media Type', value);
|
||||
case Keys.minorVersion:
|
||||
if (value != '0') save('Minor Version', value);
|
||||
case Keys.nalLengthSize:
|
||||
save('NAL Length Size', _formatFilesize(value));
|
||||
case Keys.quicktimeLocationAccuracyHorizontal:
|
||||
save('QuickTime Location Horizontal Accuracy', value);
|
||||
case Keys.quicktimeCreationDate:
|
||||
|
@ -304,25 +341,41 @@ class VideoMetadataFormatter {
|
|||
case Keys.quicktimeSoftware:
|
||||
// redundant with `QuickTime Metadata` directory
|
||||
break;
|
||||
case Keys.rFrameRate:
|
||||
save('R Frame Rate', value);
|
||||
case Keys.rotate:
|
||||
save('Rotation', '$value°');
|
||||
case Keys.sampleFormat:
|
||||
save('Sample Format', (value as String).toUpperCase());
|
||||
case Keys.sampleRate:
|
||||
save('Sample Rate', _formatMetric(value, 'Hz'));
|
||||
case Keys.sar:
|
||||
save('Sample Aspect Ratio', value);
|
||||
case Keys.sarDen:
|
||||
final sarNum = info[Keys.sarNum];
|
||||
final sarDen = info[Keys.sarDen];
|
||||
// skip common square pixels (1:1)
|
||||
if (sarNum != sarDen) save('SAR', '$sarNum:$sarDen');
|
||||
case Keys.segmentCount:
|
||||
save('Segment Count', value);
|
||||
case Keys.sourceOshash:
|
||||
save('Source OSHash', value);
|
||||
case Keys.startMicros:
|
||||
if (value != 0) save('Start', formatPreciseDuration(Duration(microseconds: value)));
|
||||
case Keys.startPts:
|
||||
save('Start PTS', value);
|
||||
case Keys.startTime:
|
||||
save('Start', _formatDuration(value));
|
||||
case Keys.statisticsWritingApp:
|
||||
save('Stats Writing App', value);
|
||||
case Keys.statisticsWritingDateUtc:
|
||||
save('Stats Writing Date', _formatDate(value));
|
||||
case Keys.timeBase:
|
||||
save('Time Base', value);
|
||||
case Keys.track:
|
||||
if (value != '0') save('Track', value);
|
||||
case Keys.vendorId:
|
||||
save('Vendor ID', value);
|
||||
case Keys.width:
|
||||
save('Width', '$value pixels');
|
||||
case Keys.xiaomiSlowMoment:
|
||||
|
@ -340,7 +393,12 @@ class VideoMetadataFormatter {
|
|||
|
||||
static String _formatBrand(String value) => Mp4.brands[value] ?? value;
|
||||
|
||||
static String _formatChannelLayout(value) => ChannelLayouts.names[value] ?? 'unknown ($value)';
|
||||
static String _formatChannelLayout(dynamic value) {
|
||||
if (value is int) {
|
||||
return ChannelLayouts.names[value] ?? 'unknown ($value)';
|
||||
}
|
||||
return '$value';
|
||||
}
|
||||
|
||||
static String _formatCodecName(String value) => _codecNames[value] ?? value.toUpperCase().replaceAll('_', ' ');
|
||||
|
||||
|
@ -352,28 +410,49 @@ class VideoMetadataFormatter {
|
|||
return date.toIso8601String();
|
||||
}
|
||||
|
||||
// input example: '00:00:05.408000000'
|
||||
static String _formatDuration(String value) {
|
||||
final match = _durationPattern.firstMatch(value);
|
||||
// input example: '00:00:05.408000000' or '5.408000'
|
||||
static Duration? _parseDuration(String? value) {
|
||||
if (value == null) return null;
|
||||
|
||||
var match = _durationHmsmPattern.firstMatch(value);
|
||||
if (match != null) {
|
||||
final h = int.tryParse(match.group(1)!);
|
||||
final m = int.tryParse(match.group(2)!);
|
||||
final s = int.tryParse(match.group(3)!);
|
||||
final millis = double.tryParse(match.group(4)!);
|
||||
if (h != null && m != null && s != null && millis != null) {
|
||||
return formatPreciseDuration(Duration(
|
||||
return Duration(
|
||||
hours: h,
|
||||
minutes: m,
|
||||
seconds: s,
|
||||
milliseconds: (millis * 1000).toInt(),
|
||||
));
|
||||
);
|
||||
}
|
||||
}
|
||||
return value;
|
||||
|
||||
match = _durationSmPattern.firstMatch(value);
|
||||
if (match != null) {
|
||||
final s = int.tryParse(match.group(1)!);
|
||||
final millis = double.tryParse(match.group(2)!);
|
||||
if (s != null && millis != null) {
|
||||
return Duration(
|
||||
seconds: s,
|
||||
milliseconds: (millis * 1000).toInt(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
static String _formatFilesize(String value) {
|
||||
final size = int.tryParse(value);
|
||||
// input example: '00:00:05.408000000' or '5.408000'
|
||||
static String _formatDuration(String value) {
|
||||
final duration = _parseDuration(value);
|
||||
return duration != null ? formatPreciseDuration(duration) : value;
|
||||
}
|
||||
|
||||
static String _formatFilesize(dynamic value) {
|
||||
final size = value is int ? value : int.tryParse(value);
|
||||
return size != null ? formatFileSize('en_US', size) : value;
|
||||
}
|
||||
|
||||
|
|
|
@ -10,25 +10,19 @@ class BurstPatterns {
|
|||
];
|
||||
|
||||
static String getName(String pattern) {
|
||||
switch (pattern) {
|
||||
case samsung:
|
||||
return 'Samsung';
|
||||
case sony:
|
||||
return 'Sony';
|
||||
default:
|
||||
return pattern;
|
||||
}
|
||||
return switch (pattern) {
|
||||
samsung => 'Samsung',
|
||||
sony => 'Sony',
|
||||
_ => pattern,
|
||||
};
|
||||
}
|
||||
|
||||
static String getExample(String pattern) {
|
||||
switch (pattern) {
|
||||
case samsung:
|
||||
return '20151021_072800_007';
|
||||
case sony:
|
||||
return 'DSC_0007_BURST20151021072800123';
|
||||
default:
|
||||
return '?';
|
||||
}
|
||||
return switch (pattern) {
|
||||
samsung => '20151021_072800_007',
|
||||
sony => 'DSC_0007_BURST20151021072800123',
|
||||
_ => '?',
|
||||
};
|
||||
}
|
||||
|
||||
static const byManufacturer = {
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import 'package:latlong2/latlong.dart';
|
||||
|
||||
class PointsOfInterest {
|
||||
static final pointNemo = LatLng(-48.876667, -123.393333);
|
||||
static const pointNemo = LatLng(-48.876667, -123.393333);
|
||||
|
||||
static final wonders = [
|
||||
static const wonders = [
|
||||
LatLng(29.979167, 31.134167),
|
||||
LatLng(36.451000, 28.223615),
|
||||
LatLng(32.5355, 44.4275),
|
||||
|
|
|
@ -10,7 +10,6 @@ import 'package:aves/services/common/services.dart';
|
|||
import 'package:aves/utils/android_file_utils.dart';
|
||||
import 'package:aves/view/view.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:fijkplayer/fijkplayer.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
|
@ -52,7 +51,6 @@ Future<void> _init() async {
|
|||
await device.init();
|
||||
await mobileServices.init();
|
||||
await settings.init(monitorPlatformSettings: false);
|
||||
FijkLog.setLevel(FijkLogLevel.Warn);
|
||||
await reportService.init();
|
||||
|
||||
final analyzer = Analyzer();
|
||||
|
|
|
@ -3,13 +3,12 @@ import 'dart:collection';
|
|||
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
final ServicePolicy servicePolicy = ServicePolicy._private();
|
||||
|
||||
class ServicePolicy {
|
||||
final StreamController<QueueState> _queueStreamController = StreamController.broadcast();
|
||||
final Map<Object, Tuple2<int, _Task>> _paused = {};
|
||||
final Map<Object, (int, _Task)> _paused = {};
|
||||
final SplayTreeMap<int, LinkedHashMap<Object, _Task>> _queues = SplayTreeMap();
|
||||
final LinkedHashMap<Object, _Task> _runningQueue = LinkedHashMap();
|
||||
|
||||
|
@ -30,8 +29,8 @@ class ServicePolicy {
|
|||
key ??= platformCall.hashCode;
|
||||
final toResume = _paused.remove(key);
|
||||
if (toResume != null) {
|
||||
priority = toResume.item1;
|
||||
task = toResume.item2 as _Task<T>;
|
||||
priority = toResume.$1;
|
||||
task = toResume.$2 as _Task<T>;
|
||||
completer = task.completer;
|
||||
} else {
|
||||
completer = Completer<T>();
|
||||
|
@ -56,8 +55,8 @@ class ServicePolicy {
|
|||
Future<T>? resume<T>(Object key) {
|
||||
final toResume = _paused.remove(key);
|
||||
if (toResume != null) {
|
||||
final priority = toResume.item1;
|
||||
final task = toResume.item2 as _Task<T>;
|
||||
final priority = toResume.$1;
|
||||
final task = toResume.$2 as _Task<T>;
|
||||
_getQueue(priority)[key] = task;
|
||||
_pickNext();
|
||||
return task.completer.future;
|
||||
|
@ -97,7 +96,7 @@ class ServicePolicy {
|
|||
}
|
||||
|
||||
bool pause(Object key, Iterable<int> priorities) {
|
||||
return _takeOut(key, priorities, (priority, task) => _paused.putIfAbsent(key, () => Tuple2(priority, task)));
|
||||
return _takeOut(key, priorities, (priority, task) => _paused.putIfAbsent(key, () => (priority, task)));
|
||||
}
|
||||
|
||||
bool isPaused(Object key) => _paused.containsKey(key);
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
import 'package:aves/model/availability.dart';
|
||||
import 'package:aves/model/db/db_metadata.dart';
|
||||
import 'package:aves/model/db/db_metadata_sqflite.dart';
|
||||
import 'package:aves/model/settings/store/store.dart';
|
||||
import 'package:aves/model/settings/store/store_shared_pref.dart';
|
||||
import 'package:aves/model/settings/store_shared_pref.dart';
|
||||
import 'package:aves/services/app_service.dart';
|
||||
import 'package:aves/services/device_service.dart';
|
||||
import 'package:aves/services/media/embedded_data_service.dart';
|
||||
|
@ -15,10 +14,14 @@ import 'package:aves/services/metadata/metadata_fetch_service.dart';
|
|||
import 'package:aves/services/security_service.dart';
|
||||
import 'package:aves/services/storage_service.dart';
|
||||
import 'package:aves/services/window_service.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:aves_report/aves_report.dart';
|
||||
import 'package:aves_report_platform/aves_report_platform.dart';
|
||||
import 'package:aves_services/aves_services.dart';
|
||||
import 'package:aves_services_platform/aves_services_platform.dart';
|
||||
import 'package:aves_video/aves_video.dart';
|
||||
import 'package:aves_video_ffmpeg/aves_video_ffmpeg.dart';
|
||||
import 'package:aves_video_mpv/aves_video_mpv.dart';
|
||||
import 'package:get_it/get_it.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
|
||||
|
@ -30,6 +33,8 @@ final SettingsStore settingsStore = SharedPrefSettingsStore();
|
|||
final p.Context pContext = getIt<p.Context>();
|
||||
final AvesAvailability availability = getIt<AvesAvailability>();
|
||||
final MetadataDb metadataDb = getIt<MetadataDb>();
|
||||
final AvesVideoControllerFactory videoControllerFactory = getIt<AvesVideoControllerFactory>();
|
||||
final AvesVideoMetadataFetcher videoMetadataFetcher = getIt<AvesVideoMetadataFetcher>();
|
||||
|
||||
final AppService appService = getIt<AppService>();
|
||||
final DeviceService deviceService = getIt<DeviceService>();
|
||||
|
@ -50,6 +55,8 @@ void initPlatformServices() {
|
|||
getIt.registerLazySingleton<p.Context>(p.Context.new);
|
||||
getIt.registerLazySingleton<AvesAvailability>(LiveAvesAvailability.new);
|
||||
getIt.registerLazySingleton<MetadataDb>(SqfliteMetadataDb.new);
|
||||
getIt.registerLazySingleton<AvesVideoControllerFactory>(MpvVideoControllerFactory.new);
|
||||
getIt.registerLazySingleton<AvesVideoMetadataFetcher>(FfmpegVideoMetadataFetcher.new);
|
||||
|
||||
getIt.registerLazySingleton<AppService>(PlatformAppService.new);
|
||||
getIt.registerLazySingleton<DeviceService>(PlatformDeviceService.new);
|
||||
|
|
|
@ -8,13 +8,11 @@ extension ExtraNameConflictStrategy on NameConflictStrategy {
|
|||
String toPlatform() => name;
|
||||
|
||||
String getName(BuildContext context) {
|
||||
switch (this) {
|
||||
case NameConflictStrategy.rename:
|
||||
return context.l10n.nameConflictStrategyRename;
|
||||
case NameConflictStrategy.replace:
|
||||
return context.l10n.nameConflictStrategyReplace;
|
||||
case NameConflictStrategy.skip:
|
||||
return context.l10n.nameConflictStrategySkip;
|
||||
}
|
||||
final l10n = context.l10n;
|
||||
return switch (this) {
|
||||
NameConflictStrategy.rename => l10n.nameConflictStrategyRename,
|
||||
NameConflictStrategy.replace => l10n.nameConflictStrategyReplace,
|
||||
NameConflictStrategy.skip => l10n.nameConflictStrategySkip,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves_utils/aves_utils.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves_utils/aves_utils.dart';
|
||||
import 'package:aves_video/aves_video.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
|
|
@ -8,6 +8,8 @@ import 'package:flutter/services.dart';
|
|||
import 'package:streams_channel/streams_channel.dart';
|
||||
|
||||
abstract class StorageService {
|
||||
Future<Map<String, int>> getDataUsage();
|
||||
|
||||
Future<Set<StorageVolume>> getStorageVolumes();
|
||||
|
||||
Future<String> getVaultRoot();
|
||||
|
@ -45,6 +47,17 @@ class PlatformStorageService implements StorageService {
|
|||
static const _platform = MethodChannel('deckers.thibault/aves/storage');
|
||||
static final _stream = StreamsChannel('deckers.thibault/aves/activity_result_stream');
|
||||
|
||||
@override
|
||||
Future<Map<String, int>> getDataUsage() async {
|
||||
try {
|
||||
final result = await _platform.invokeMethod('getDataUsage');
|
||||
if (result != null) return (result as Map).cast<String, int>();
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Set<StorageVolume>> getStorageVolumes() async {
|
||||
try {
|
||||
|
|
|
@ -18,10 +18,12 @@ class AColors {
|
|||
}
|
||||
|
||||
class AvesColorsProvider extends StatelessWidget {
|
||||
final bool allowMonochrome;
|
||||
final Widget child;
|
||||
|
||||
const AvesColorsProvider({
|
||||
super.key,
|
||||
this.allowMonochrome = true,
|
||||
required this.child,
|
||||
});
|
||||
|
||||
|
@ -30,12 +32,14 @@ class AvesColorsProvider extends StatelessWidget {
|
|||
return ProxyProvider<Settings, AvesColorsData>(
|
||||
update: (context, settings, __) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
switch (settings.themeColorMode) {
|
||||
case AvesThemeColorMode.monochrome:
|
||||
return isDark ? _MonochromeOnDark() : _MonochromeOnLight();
|
||||
case AvesThemeColorMode.polychrome:
|
||||
return isDark ? NeonOnDark() : PastelOnLight();
|
||||
var mode = settings.themeColorMode;
|
||||
if (!allowMonochrome && mode == AvesThemeColorMode.monochrome) {
|
||||
mode = AvesThemeColorMode.polychrome;
|
||||
}
|
||||
return switch (mode) {
|
||||
AvesThemeColorMode.monochrome => isDark ? _MonochromeOnDark() : _MonochromeOnLight(),
|
||||
AvesThemeColorMode.polychrome => isDark ? NeonOnDark() : PastelOnLight(),
|
||||
};
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:flutter/foundation.dart';
|
||||
|
||||
class Durations {
|
||||
class ADurations {
|
||||
// Flutter animations (with margin)
|
||||
static const popupMenuAnimation = Duration(milliseconds: 300 + 20); // ref `_kMenuDuration` used in `_PopupMenuRoute`
|
||||
// page transition duration also available via `ModalRoute.of(context)!.transitionDuration * timeDilation`
|
||||
|
|
|
@ -17,7 +17,7 @@ class AIcons {
|
|||
static const brightnessMin = Icons.brightness_low_outlined;
|
||||
static const brightnessMax = Icons.brightness_high_outlined;
|
||||
static const checked = Icons.done_outlined;
|
||||
static const count = MdiIcons.counter;
|
||||
static final count = MdiIcons.counter;
|
||||
static const counter = Icons.plus_one_outlined;
|
||||
static const date = Icons.calendar_today_outlined;
|
||||
static const dateByDay = Icons.today_outlined;
|
||||
|
@ -42,11 +42,11 @@ class AIcons {
|
|||
static const mainStorage = Icons.smartphone_outlined;
|
||||
static const mimeType = Icons.code_outlined;
|
||||
static const opacity = Icons.opacity;
|
||||
static const privacy = MdiIcons.shieldAccountOutline;
|
||||
static final privacy = MdiIcons.shieldAccountOutline;
|
||||
static const rating = Icons.star_border_outlined;
|
||||
static const ratingFull = Icons.star;
|
||||
static const ratingRejected = MdiIcons.starMinusOutline;
|
||||
static const ratingUnrated = MdiIcons.starOffOutline;
|
||||
static final ratingRejected = MdiIcons.starMinusOutline;
|
||||
static final ratingUnrated = MdiIcons.starOffOutline;
|
||||
static const raw = Icons.raw_on_outlined;
|
||||
static const shooting = Icons.camera_outlined;
|
||||
static const removableStorage = Icons.sd_storage_outlined;
|
||||
|
@ -56,7 +56,7 @@ class AIcons {
|
|||
static const size = Icons.data_usage_outlined;
|
||||
static const text = Icons.format_quote_outlined;
|
||||
static const tag = Icons.local_offer_outlined;
|
||||
static const tagUntagged = MdiIcons.tagOffOutline;
|
||||
static final tagUntagged = MdiIcons.tagOffOutline;
|
||||
static const volumeMin = Icons.volume_mute_outlined;
|
||||
static const volumeMax = Icons.volume_up_outlined;
|
||||
|
||||
|
@ -79,35 +79,35 @@ class AIcons {
|
|||
static const clear = Icons.clear_outlined;
|
||||
static const clipboard = Icons.content_copy_outlined;
|
||||
static const convert = Icons.transform_outlined;
|
||||
static const convertToStillImage = MdiIcons.movieRemoveOutline;
|
||||
static final convertToStillImage = MdiIcons.movieRemoveOutline;
|
||||
static const copy = Icons.file_copy_outlined;
|
||||
static const debug = Icons.whatshot_outlined;
|
||||
static const delete = Icons.delete_outlined;
|
||||
static const edit = Icons.edit_outlined;
|
||||
static const emptyBin = Icons.delete_sweep_outlined;
|
||||
static const export = Icons.open_with_outlined;
|
||||
static const fileExport = MdiIcons.fileExportOutline;
|
||||
static const fileImport = MdiIcons.fileImportOutline;
|
||||
static final fileExport = MdiIcons.fileExportOutline;
|
||||
static final fileImport = MdiIcons.fileImportOutline;
|
||||
static const flip = Icons.flip_outlined;
|
||||
static const favourite = Icons.favorite_border;
|
||||
static const favouriteActive = Icons.favorite;
|
||||
static const filter = MdiIcons.filterOutline;
|
||||
static const filterOff = MdiIcons.filterOffOutline;
|
||||
static final filter = MdiIcons.filterOutline;
|
||||
static final filterOff = MdiIcons.filterOffOutline;
|
||||
static const geoBounds = Icons.public_outlined;
|
||||
static const goUp = Icons.arrow_upward_outlined;
|
||||
static const hide = Icons.visibility_off_outlined;
|
||||
static const info = Icons.info_outlined;
|
||||
static const layers = Icons.layers_outlined;
|
||||
static const map = Icons.map_outlined;
|
||||
static const move = MdiIcons.fileMoveOutline;
|
||||
static final move = MdiIcons.fileMoveOutline;
|
||||
static const mute = Icons.volume_off_outlined;
|
||||
static const unmute = Icons.volume_up_outlined;
|
||||
static const name = Icons.abc_outlined;
|
||||
static const newTier = Icons.fiber_new_outlined;
|
||||
static const openOutside = Icons.open_in_new_outlined;
|
||||
static const openVideo = MdiIcons.moviePlayOutline;
|
||||
static final openVideo = MdiIcons.moviePlayOutline;
|
||||
static const pin = Icons.push_pin_outlined;
|
||||
static const unpin = MdiIcons.pinOffOutline;
|
||||
static final unpin = MdiIcons.pinOffOutline;
|
||||
static const play = Icons.play_arrow;
|
||||
static const pause = Icons.pause;
|
||||
static const print = Icons.print_outlined;
|
||||
|
@ -123,10 +123,10 @@ class AIcons {
|
|||
static const search = Icons.search_outlined;
|
||||
static const select = Icons.select_all_outlined;
|
||||
static const setAs = Icons.wallpaper_outlined;
|
||||
static const setCover = MdiIcons.imageEditOutline;
|
||||
static final setCover = MdiIcons.imageEditOutline;
|
||||
static const share = Icons.share_outlined;
|
||||
static const show = Icons.visibility_outlined;
|
||||
static const showFullscreen = MdiIcons.arrowExpand;
|
||||
static final showFullscreen = MdiIcons.arrowExpand;
|
||||
static const slideshow = Icons.slideshow_outlined;
|
||||
static const speed = Icons.speed_outlined;
|
||||
static const stats = Icons.donut_small_outlined;
|
||||
|
@ -136,7 +136,7 @@ class AIcons {
|
|||
static const streamText = Icons.closed_caption_outlined;
|
||||
static const vaultLock = Icons.lock_outline;
|
||||
static const vaultAdd = Icons.enhanced_encryption_outlined;
|
||||
static const vaultConfigure = MdiIcons.shieldLockOutline;
|
||||
static final vaultConfigure = MdiIcons.shieldLockOutline;
|
||||
static const videoSettings = Icons.video_settings_outlined;
|
||||
static const view = Icons.grid_view_outlined;
|
||||
static const viewerLock = Icons.lock_outline;
|
||||
|
@ -176,6 +176,6 @@ class AIcons {
|
|||
static const selected = Icons.check_circle_outline;
|
||||
static const unselected = Icons.radio_button_unchecked;
|
||||
|
||||
static const github = MdiIcons.github;
|
||||
static const legal = MdiIcons.scaleBalance;
|
||||
static final github = MdiIcons.github;
|
||||
static final legal = MdiIcons.scaleBalance;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
import 'dart:math';
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
int highestPowerOf2(num x) => x < 1 ? 0 : pow(2, (log(x) / ln2).floor()).toInt();
|
||||
|
||||
int smallestPowerOf2(num x) => x < 1 ? 1 : pow(2, (log(x) / ln2).ceil()).toInt();
|
||||
|
@ -10,16 +8,16 @@ int smallestPowerOf2(num x) => x < 1 ? 1 : pow(2, (log(x) / ln2).ceil()).toInt()
|
|||
double roundToPrecision(final double value, {required final int decimals}) => (value * pow(10, decimals)).round() / pow(10, decimals);
|
||||
|
||||
// cf https://en.wikipedia.org/wiki/Intersection_(geometry)#Two_line_segments
|
||||
Offset? segmentIntersection(Tuple2<Offset, Offset> s1, Tuple2<Offset, Offset> s2) {
|
||||
final x1 = s1.item1.dx;
|
||||
final y1 = s1.item1.dy;
|
||||
final x2 = s1.item2.dx;
|
||||
final y2 = s1.item2.dy;
|
||||
Offset? segmentIntersection((Offset, Offset) s1, (Offset, Offset) s2) {
|
||||
final x1 = s1.$1.dx;
|
||||
final y1 = s1.$1.dy;
|
||||
final x2 = s1.$2.dx;
|
||||
final y2 = s1.$2.dy;
|
||||
|
||||
final x3 = s2.item1.dx;
|
||||
final y3 = s2.item1.dy;
|
||||
final x4 = s2.item2.dx;
|
||||
final y4 = s2.item2.dy;
|
||||
final x3 = s2.$1.dx;
|
||||
final y3 = s2.$1.dy;
|
||||
final x4 = s2.$2.dx;
|
||||
final y4 = s2.$2.dy;
|
||||
|
||||
final a1 = x2 - x1;
|
||||
final b1 = -(x4 - x3);
|
||||
|
|
|
@ -5,43 +5,34 @@ import 'package:flutter/widgets.dart';
|
|||
|
||||
extension ExtraChipActionView on ChipAction {
|
||||
String getText(BuildContext context) {
|
||||
switch (this) {
|
||||
case ChipAction.goToAlbumPage:
|
||||
return context.l10n.chipActionGoToAlbumPage;
|
||||
case ChipAction.goToCountryPage:
|
||||
return context.l10n.chipActionGoToCountryPage;
|
||||
case ChipAction.goToPlacePage:
|
||||
return context.l10n.chipActionGoToPlacePage;
|
||||
case ChipAction.goToTagPage:
|
||||
return context.l10n.chipActionGoToTagPage;
|
||||
case ChipAction.reverse:
|
||||
final l10n = context.l10n;
|
||||
return switch (this) {
|
||||
ChipAction.goToAlbumPage => l10n.chipActionGoToAlbumPage,
|
||||
ChipAction.goToCountryPage => l10n.chipActionGoToCountryPage,
|
||||
ChipAction.goToPlacePage => l10n.chipActionGoToPlacePage,
|
||||
ChipAction.goToTagPage => l10n.chipActionGoToTagPage,
|
||||
ChipAction.ratingOrGreater ||
|
||||
ChipAction.ratingOrLower =>
|
||||
// different data depending on state
|
||||
return context.l10n.chipActionFilterOut;
|
||||
case ChipAction.hide:
|
||||
return context.l10n.chipActionHide;
|
||||
case ChipAction.lockVault:
|
||||
return context.l10n.chipActionLock;
|
||||
}
|
||||
toString(),
|
||||
ChipAction.reverse =>
|
||||
// different data depending on state
|
||||
l10n.chipActionFilterOut,
|
||||
ChipAction.hide => l10n.chipActionHide,
|
||||
ChipAction.lockVault => l10n.chipActionLock,
|
||||
};
|
||||
}
|
||||
|
||||
Widget getIcon() => Icon(_getIconData());
|
||||
|
||||
IconData _getIconData() {
|
||||
switch (this) {
|
||||
case ChipAction.goToAlbumPage:
|
||||
return AIcons.album;
|
||||
case ChipAction.goToCountryPage:
|
||||
return AIcons.country;
|
||||
case ChipAction.goToPlacePage:
|
||||
return AIcons.place;
|
||||
case ChipAction.goToTagPage:
|
||||
return AIcons.tag;
|
||||
case ChipAction.reverse:
|
||||
return AIcons.reverse;
|
||||
case ChipAction.hide:
|
||||
return AIcons.hide;
|
||||
case ChipAction.lockVault:
|
||||
return AIcons.vaultLock;
|
||||
}
|
||||
}
|
||||
IconData _getIconData() => switch (this) {
|
||||
ChipAction.goToAlbumPage => AIcons.album,
|
||||
ChipAction.goToCountryPage => AIcons.country,
|
||||
ChipAction.goToPlacePage => AIcons.place,
|
||||
ChipAction.goToTagPage => AIcons.tag,
|
||||
ChipAction.ratingOrGreater || ChipAction.ratingOrLower => AIcons.rating,
|
||||
ChipAction.reverse => AIcons.reverse,
|
||||
ChipAction.hide => AIcons.hide,
|
||||
ChipAction.lockVault => AIcons.vaultLock,
|
||||
};
|
||||
}
|
||||
|
|
|
@ -5,106 +5,69 @@ import 'package:flutter/material.dart';
|
|||
|
||||
extension ExtraChipSetActionView on ChipSetAction {
|
||||
String getText(BuildContext context) {
|
||||
switch (this) {
|
||||
final l10n = context.l10n;
|
||||
return switch (this) {
|
||||
// general
|
||||
case ChipSetAction.configureView:
|
||||
return context.l10n.menuActionConfigureView;
|
||||
case ChipSetAction.select:
|
||||
return context.l10n.menuActionSelect;
|
||||
case ChipSetAction.selectAll:
|
||||
return context.l10n.menuActionSelectAll;
|
||||
case ChipSetAction.selectNone:
|
||||
return context.l10n.menuActionSelectNone;
|
||||
ChipSetAction.configureView => l10n.menuActionConfigureView,
|
||||
ChipSetAction.select => l10n.menuActionSelect,
|
||||
ChipSetAction.selectAll => l10n.menuActionSelectAll,
|
||||
ChipSetAction.selectNone => l10n.menuActionSelectNone,
|
||||
// browsing
|
||||
case ChipSetAction.search:
|
||||
return MaterialLocalizations.of(context).searchFieldLabel;
|
||||
case ChipSetAction.toggleTitleSearch:
|
||||
ChipSetAction.search => MaterialLocalizations.of(context).searchFieldLabel,
|
||||
ChipSetAction.toggleTitleSearch =>
|
||||
// different data depending on toggle state
|
||||
return context.l10n.collectionActionShowTitleSearch;
|
||||
case ChipSetAction.createAlbum:
|
||||
return context.l10n.chipActionCreateAlbum;
|
||||
case ChipSetAction.createVault:
|
||||
return context.l10n.chipActionCreateVault;
|
||||
l10n.collectionActionShowTitleSearch,
|
||||
ChipSetAction.createAlbum => l10n.chipActionCreateAlbum,
|
||||
ChipSetAction.createVault => l10n.chipActionCreateVault,
|
||||
// browsing or selecting
|
||||
case ChipSetAction.map:
|
||||
return context.l10n.menuActionMap;
|
||||
case ChipSetAction.slideshow:
|
||||
return context.l10n.menuActionSlideshow;
|
||||
case ChipSetAction.stats:
|
||||
return context.l10n.menuActionStats;
|
||||
ChipSetAction.map => l10n.menuActionMap,
|
||||
ChipSetAction.slideshow => l10n.menuActionSlideshow,
|
||||
ChipSetAction.stats => l10n.menuActionStats,
|
||||
// selecting (single/multiple filters)
|
||||
case ChipSetAction.delete:
|
||||
return context.l10n.chipActionDelete;
|
||||
case ChipSetAction.hide:
|
||||
return context.l10n.chipActionHide;
|
||||
case ChipSetAction.pin:
|
||||
return context.l10n.chipActionPin;
|
||||
case ChipSetAction.unpin:
|
||||
return context.l10n.chipActionUnpin;
|
||||
case ChipSetAction.lockVault:
|
||||
return context.l10n.chipActionLock;
|
||||
case ChipSetAction.showCountryStates:
|
||||
return context.l10n.chipActionShowCountryStates;
|
||||
ChipSetAction.delete => l10n.chipActionDelete,
|
||||
ChipSetAction.hide => l10n.chipActionHide,
|
||||
ChipSetAction.pin => l10n.chipActionPin,
|
||||
ChipSetAction.unpin => l10n.chipActionUnpin,
|
||||
ChipSetAction.lockVault => l10n.chipActionLock,
|
||||
ChipSetAction.showCountryStates => l10n.chipActionShowCountryStates,
|
||||
// selecting (single filter)
|
||||
case ChipSetAction.rename:
|
||||
return context.l10n.chipActionRename;
|
||||
case ChipSetAction.setCover:
|
||||
return context.l10n.chipActionSetCover;
|
||||
case ChipSetAction.configureVault:
|
||||
return context.l10n.chipActionConfigureVault;
|
||||
}
|
||||
ChipSetAction.rename => l10n.chipActionRename,
|
||||
ChipSetAction.setCover => l10n.chipActionSetCover,
|
||||
ChipSetAction.configureVault => l10n.chipActionConfigureVault,
|
||||
};
|
||||
}
|
||||
|
||||
Widget getIcon() => Icon(_getIconData());
|
||||
|
||||
IconData _getIconData() {
|
||||
switch (this) {
|
||||
return switch (this) {
|
||||
// general
|
||||
case ChipSetAction.configureView:
|
||||
return AIcons.view;
|
||||
case ChipSetAction.select:
|
||||
return AIcons.select;
|
||||
case ChipSetAction.selectAll:
|
||||
return AIcons.selected;
|
||||
case ChipSetAction.selectNone:
|
||||
return AIcons.unselected;
|
||||
ChipSetAction.configureView => AIcons.view,
|
||||
ChipSetAction.select => AIcons.select,
|
||||
ChipSetAction.selectAll => AIcons.selected,
|
||||
ChipSetAction.selectNone => AIcons.unselected,
|
||||
// browsing
|
||||
case ChipSetAction.search:
|
||||
return AIcons.search;
|
||||
case ChipSetAction.toggleTitleSearch:
|
||||
ChipSetAction.search => AIcons.search,
|
||||
ChipSetAction.toggleTitleSearch =>
|
||||
// different data depending on toggle state
|
||||
return AIcons.filter;
|
||||
case ChipSetAction.createAlbum:
|
||||
return AIcons.add;
|
||||
case ChipSetAction.createVault:
|
||||
return AIcons.vaultAdd;
|
||||
AIcons.filter,
|
||||
ChipSetAction.createAlbum => AIcons.add,
|
||||
ChipSetAction.createVault => AIcons.vaultAdd,
|
||||
// browsing or selecting
|
||||
case ChipSetAction.map:
|
||||
return AIcons.map;
|
||||
case ChipSetAction.slideshow:
|
||||
return AIcons.slideshow;
|
||||
case ChipSetAction.stats:
|
||||
return AIcons.stats;
|
||||
ChipSetAction.map => AIcons.map,
|
||||
ChipSetAction.slideshow => AIcons.slideshow,
|
||||
ChipSetAction.stats => AIcons.stats,
|
||||
// selecting (single/multiple filters)
|
||||
case ChipSetAction.delete:
|
||||
return AIcons.delete;
|
||||
case ChipSetAction.hide:
|
||||
return AIcons.hide;
|
||||
case ChipSetAction.pin:
|
||||
return AIcons.pin;
|
||||
case ChipSetAction.unpin:
|
||||
return AIcons.unpin;
|
||||
case ChipSetAction.lockVault:
|
||||
return AIcons.vaultLock;
|
||||
case ChipSetAction.showCountryStates:
|
||||
return AIcons.state;
|
||||
ChipSetAction.delete => AIcons.delete,
|
||||
ChipSetAction.hide => AIcons.hide,
|
||||
ChipSetAction.pin => AIcons.pin,
|
||||
ChipSetAction.unpin => AIcons.unpin,
|
||||
ChipSetAction.lockVault => AIcons.vaultLock,
|
||||
ChipSetAction.showCountryStates => AIcons.state,
|
||||
// selecting (single filter)
|
||||
case ChipSetAction.rename:
|
||||
return AIcons.name;
|
||||
case ChipSetAction.setCover:
|
||||
return AIcons.setCover;
|
||||
case ChipSetAction.configureVault:
|
||||
return AIcons.vaultConfigure;
|
||||
}
|
||||
ChipSetAction.rename => AIcons.name,
|
||||
ChipSetAction.setCover => AIcons.setCover,
|
||||
ChipSetAction.configureVault => AIcons.vaultConfigure,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,216 +6,137 @@ import 'package:flutter/widgets.dart';
|
|||
|
||||
extension ExtraEntryActionView on EntryAction {
|
||||
String getText(BuildContext context) {
|
||||
switch (this) {
|
||||
case EntryAction.info:
|
||||
return context.l10n.entryActionInfo;
|
||||
case EntryAction.addShortcut:
|
||||
return context.l10n.collectionActionAddShortcut;
|
||||
case EntryAction.copyToClipboard:
|
||||
return context.l10n.entryActionCopyToClipboard;
|
||||
case EntryAction.delete:
|
||||
return context.l10n.entryActionDelete;
|
||||
case EntryAction.restore:
|
||||
return context.l10n.entryActionRestore;
|
||||
case EntryAction.convert:
|
||||
return context.l10n.entryActionConvert;
|
||||
case EntryAction.print:
|
||||
return context.l10n.entryActionPrint;
|
||||
case EntryAction.rename:
|
||||
return context.l10n.entryActionRename;
|
||||
case EntryAction.copy:
|
||||
return context.l10n.collectionActionCopy;
|
||||
case EntryAction.move:
|
||||
return context.l10n.collectionActionMove;
|
||||
case EntryAction.share:
|
||||
return context.l10n.entryActionShare;
|
||||
case EntryAction.toggleFavourite:
|
||||
final l10n = context.l10n;
|
||||
return switch (this) {
|
||||
EntryAction.info => l10n.entryActionInfo,
|
||||
EntryAction.addShortcut => l10n.collectionActionAddShortcut,
|
||||
EntryAction.copyToClipboard => l10n.entryActionCopyToClipboard,
|
||||
EntryAction.delete => l10n.entryActionDelete,
|
||||
EntryAction.restore => l10n.entryActionRestore,
|
||||
EntryAction.convert => l10n.entryActionConvert,
|
||||
EntryAction.print => l10n.entryActionPrint,
|
||||
EntryAction.rename => l10n.entryActionRename,
|
||||
EntryAction.copy => l10n.collectionActionCopy,
|
||||
EntryAction.move => l10n.collectionActionMove,
|
||||
EntryAction.share => l10n.entryActionShare,
|
||||
EntryAction.toggleFavourite =>
|
||||
// different data depending on toggle state
|
||||
return context.l10n.entryActionAddFavourite;
|
||||
l10n.entryActionAddFavourite,
|
||||
// raster
|
||||
case EntryAction.rotateCCW:
|
||||
return context.l10n.entryActionRotateCCW;
|
||||
case EntryAction.rotateCW:
|
||||
return context.l10n.entryActionRotateCW;
|
||||
case EntryAction.flip:
|
||||
return context.l10n.entryActionFlip;
|
||||
EntryAction.rotateCCW => l10n.entryActionRotateCCW,
|
||||
EntryAction.rotateCW => l10n.entryActionRotateCW,
|
||||
EntryAction.flip => l10n.entryActionFlip,
|
||||
// vector
|
||||
case EntryAction.viewSource:
|
||||
return context.l10n.entryActionViewSource;
|
||||
EntryAction.viewSource => l10n.entryActionViewSource,
|
||||
// video
|
||||
case EntryAction.lockViewer:
|
||||
return context.l10n.viewerActionLock;
|
||||
case EntryAction.videoCaptureFrame:
|
||||
return context.l10n.videoActionCaptureFrame;
|
||||
case EntryAction.videoToggleMute:
|
||||
EntryAction.lockViewer => l10n.viewerActionLock,
|
||||
EntryAction.videoCaptureFrame => l10n.videoActionCaptureFrame,
|
||||
EntryAction.videoToggleMute =>
|
||||
// different data depending on toggle state
|
||||
return context.l10n.videoActionMute;
|
||||
case EntryAction.videoSelectStreams:
|
||||
return context.l10n.videoActionSelectStreams;
|
||||
case EntryAction.videoSetSpeed:
|
||||
return context.l10n.videoActionSetSpeed;
|
||||
case EntryAction.videoSettings:
|
||||
return context.l10n.viewerActionSettings;
|
||||
case EntryAction.videoTogglePlay:
|
||||
l10n.videoActionMute,
|
||||
EntryAction.videoSelectStreams => l10n.videoActionSelectStreams,
|
||||
EntryAction.videoSetSpeed => l10n.videoActionSetSpeed,
|
||||
EntryAction.videoSettings => l10n.viewerActionSettings,
|
||||
EntryAction.videoTogglePlay =>
|
||||
// different data depending on toggle state
|
||||
return context.l10n.videoActionPlay;
|
||||
case EntryAction.videoReplay10:
|
||||
return context.l10n.videoActionReplay10;
|
||||
case EntryAction.videoSkip10:
|
||||
return context.l10n.videoActionSkip10;
|
||||
l10n.videoActionPlay,
|
||||
EntryAction.videoReplay10 => l10n.videoActionReplay10,
|
||||
EntryAction.videoSkip10 => l10n.videoActionSkip10,
|
||||
// external
|
||||
case EntryAction.edit:
|
||||
return context.l10n.entryActionEdit;
|
||||
case EntryAction.open:
|
||||
case EntryAction.openVideo:
|
||||
return context.l10n.entryActionOpen;
|
||||
case EntryAction.openMap:
|
||||
return context.l10n.entryActionOpenMap;
|
||||
case EntryAction.setAs:
|
||||
return context.l10n.entryActionSetAs;
|
||||
EntryAction.edit => l10n.entryActionEdit,
|
||||
EntryAction.open || EntryAction.openVideo => l10n.entryActionOpen,
|
||||
EntryAction.openMap => l10n.entryActionOpenMap,
|
||||
EntryAction.setAs => l10n.entryActionSetAs,
|
||||
// platform
|
||||
case EntryAction.rotateScreen:
|
||||
return context.l10n.entryActionRotateScreen;
|
||||
EntryAction.rotateScreen => l10n.entryActionRotateScreen,
|
||||
// metadata
|
||||
case EntryAction.editDate:
|
||||
return context.l10n.entryInfoActionEditDate;
|
||||
case EntryAction.editLocation:
|
||||
return context.l10n.entryInfoActionEditLocation;
|
||||
case EntryAction.editTitleDescription:
|
||||
return context.l10n.entryInfoActionEditTitleDescription;
|
||||
case EntryAction.editRating:
|
||||
return context.l10n.entryInfoActionEditRating;
|
||||
case EntryAction.editTags:
|
||||
return context.l10n.entryInfoActionEditTags;
|
||||
case EntryAction.removeMetadata:
|
||||
return context.l10n.entryInfoActionRemoveMetadata;
|
||||
case EntryAction.exportMetadata:
|
||||
return context.l10n.entryInfoActionExportMetadata;
|
||||
EntryAction.editDate => l10n.entryInfoActionEditDate,
|
||||
EntryAction.editLocation => l10n.entryInfoActionEditLocation,
|
||||
EntryAction.editTitleDescription => l10n.entryInfoActionEditTitleDescription,
|
||||
EntryAction.editRating => l10n.entryInfoActionEditRating,
|
||||
EntryAction.editTags => l10n.entryInfoActionEditTags,
|
||||
EntryAction.removeMetadata => l10n.entryInfoActionRemoveMetadata,
|
||||
EntryAction.exportMetadata => l10n.entryInfoActionExportMetadata,
|
||||
// metadata / GeoTIFF
|
||||
case EntryAction.showGeoTiffOnMap:
|
||||
return context.l10n.entryActionShowGeoTiffOnMap;
|
||||
EntryAction.showGeoTiffOnMap => l10n.entryActionShowGeoTiffOnMap,
|
||||
// metadata / motion photo
|
||||
case EntryAction.convertMotionPhotoToStillImage:
|
||||
return context.l10n.entryActionConvertMotionPhotoToStillImage;
|
||||
case EntryAction.viewMotionPhotoVideo:
|
||||
return context.l10n.entryActionViewMotionPhotoVideo;
|
||||
EntryAction.convertMotionPhotoToStillImage => l10n.entryActionConvertMotionPhotoToStillImage,
|
||||
EntryAction.viewMotionPhotoVideo => l10n.entryActionViewMotionPhotoVideo,
|
||||
// debug
|
||||
case EntryAction.debug:
|
||||
return 'Debug';
|
||||
}
|
||||
EntryAction.debug => 'Debug',
|
||||
};
|
||||
}
|
||||
|
||||
Widget getIcon() {
|
||||
final child = Icon(getIconData());
|
||||
switch (this) {
|
||||
case EntryAction.debug:
|
||||
return ShaderMask(
|
||||
return switch (this) {
|
||||
EntryAction.debug => ShaderMask(
|
||||
shaderCallback: AvesColorsData.debugGradient.createShader,
|
||||
blendMode: BlendMode.srcIn,
|
||||
child: child,
|
||||
);
|
||||
default:
|
||||
return child;
|
||||
}
|
||||
),
|
||||
_ => child,
|
||||
};
|
||||
}
|
||||
|
||||
IconData getIconData() {
|
||||
switch (this) {
|
||||
case EntryAction.info:
|
||||
return AIcons.info;
|
||||
case EntryAction.addShortcut:
|
||||
return AIcons.addShortcut;
|
||||
case EntryAction.copyToClipboard:
|
||||
return AIcons.clipboard;
|
||||
case EntryAction.delete:
|
||||
return AIcons.delete;
|
||||
case EntryAction.restore:
|
||||
return AIcons.restore;
|
||||
case EntryAction.convert:
|
||||
return AIcons.convert;
|
||||
case EntryAction.print:
|
||||
return AIcons.print;
|
||||
case EntryAction.rename:
|
||||
return AIcons.name;
|
||||
case EntryAction.copy:
|
||||
return AIcons.copy;
|
||||
case EntryAction.move:
|
||||
return AIcons.move;
|
||||
case EntryAction.share:
|
||||
return AIcons.share;
|
||||
case EntryAction.toggleFavourite:
|
||||
return switch (this) {
|
||||
EntryAction.info => AIcons.info,
|
||||
EntryAction.addShortcut => AIcons.addShortcut,
|
||||
EntryAction.copyToClipboard => AIcons.clipboard,
|
||||
EntryAction.delete => AIcons.delete,
|
||||
EntryAction.restore => AIcons.restore,
|
||||
EntryAction.convert => AIcons.convert,
|
||||
EntryAction.print => AIcons.print,
|
||||
EntryAction.rename => AIcons.name,
|
||||
EntryAction.copy => AIcons.copy,
|
||||
EntryAction.move => AIcons.move,
|
||||
EntryAction.share => AIcons.share,
|
||||
EntryAction.toggleFavourite =>
|
||||
// different data depending on toggle state
|
||||
return AIcons.favourite;
|
||||
AIcons.favourite,
|
||||
// raster
|
||||
case EntryAction.rotateCCW:
|
||||
return AIcons.rotateLeft;
|
||||
case EntryAction.rotateCW:
|
||||
return AIcons.rotateRight;
|
||||
case EntryAction.flip:
|
||||
return AIcons.flip;
|
||||
EntryAction.rotateCCW => AIcons.rotateLeft,
|
||||
EntryAction.rotateCW => AIcons.rotateRight,
|
||||
EntryAction.flip => AIcons.flip,
|
||||
// vector
|
||||
case EntryAction.viewSource:
|
||||
return AIcons.vector;
|
||||
EntryAction.viewSource => AIcons.vector,
|
||||
// video
|
||||
case EntryAction.lockViewer:
|
||||
return AIcons.viewerLock;
|
||||
case EntryAction.videoCaptureFrame:
|
||||
return AIcons.captureFrame;
|
||||
case EntryAction.videoToggleMute:
|
||||
EntryAction.lockViewer => AIcons.viewerLock,
|
||||
EntryAction.videoCaptureFrame => AIcons.captureFrame,
|
||||
EntryAction.videoToggleMute =>
|
||||
// different data depending on toggle state
|
||||
return AIcons.mute;
|
||||
case EntryAction.videoSelectStreams:
|
||||
return AIcons.streams;
|
||||
case EntryAction.videoSetSpeed:
|
||||
return AIcons.speed;
|
||||
case EntryAction.videoSettings:
|
||||
return AIcons.videoSettings;
|
||||
case EntryAction.videoTogglePlay:
|
||||
AIcons.mute,
|
||||
EntryAction.videoSelectStreams => AIcons.streams,
|
||||
EntryAction.videoSetSpeed => AIcons.speed,
|
||||
EntryAction.videoSettings => AIcons.videoSettings,
|
||||
EntryAction.videoTogglePlay =>
|
||||
// different data depending on toggle state
|
||||
return AIcons.play;
|
||||
case EntryAction.videoReplay10:
|
||||
return AIcons.replay10;
|
||||
case EntryAction.videoSkip10:
|
||||
return AIcons.skip10;
|
||||
AIcons.play,
|
||||
EntryAction.videoReplay10 => AIcons.replay10,
|
||||
EntryAction.videoSkip10 => AIcons.skip10,
|
||||
// external
|
||||
case EntryAction.edit:
|
||||
return AIcons.edit;
|
||||
case EntryAction.open:
|
||||
case EntryAction.openVideo:
|
||||
return AIcons.openOutside;
|
||||
case EntryAction.openMap:
|
||||
return AIcons.map;
|
||||
case EntryAction.setAs:
|
||||
return AIcons.setAs;
|
||||
EntryAction.edit => AIcons.edit,
|
||||
EntryAction.open || EntryAction.openVideo => AIcons.openOutside,
|
||||
EntryAction.openMap => AIcons.map,
|
||||
EntryAction.setAs => AIcons.setAs,
|
||||
// platform
|
||||
case EntryAction.rotateScreen:
|
||||
return AIcons.rotateScreen;
|
||||
EntryAction.rotateScreen => AIcons.rotateScreen,
|
||||
// metadata
|
||||
case EntryAction.editDate:
|
||||
return AIcons.date;
|
||||
case EntryAction.editLocation:
|
||||
return AIcons.location;
|
||||
case EntryAction.editTitleDescription:
|
||||
return AIcons.description;
|
||||
case EntryAction.editRating:
|
||||
return AIcons.rating;
|
||||
case EntryAction.editTags:
|
||||
return AIcons.tag;
|
||||
case EntryAction.removeMetadata:
|
||||
return AIcons.clear;
|
||||
case EntryAction.exportMetadata:
|
||||
return AIcons.fileExport;
|
||||
EntryAction.editDate => AIcons.date,
|
||||
EntryAction.editLocation => AIcons.location,
|
||||
EntryAction.editTitleDescription => AIcons.description,
|
||||
EntryAction.editRating => AIcons.rating,
|
||||
EntryAction.editTags => AIcons.tag,
|
||||
EntryAction.removeMetadata => AIcons.clear,
|
||||
EntryAction.exportMetadata => AIcons.fileExport,
|
||||
// metadata / GeoTIFF
|
||||
case EntryAction.showGeoTiffOnMap:
|
||||
return AIcons.map;
|
||||
EntryAction.showGeoTiffOnMap => AIcons.map,
|
||||
// metadata / motion photo
|
||||
case EntryAction.convertMotionPhotoToStillImage:
|
||||
return AIcons.convertToStillImage;
|
||||
case EntryAction.viewMotionPhotoVideo:
|
||||
return AIcons.openVideo;
|
||||
EntryAction.convertMotionPhotoToStillImage => AIcons.convertToStillImage,
|
||||
EntryAction.viewMotionPhotoVideo => AIcons.openVideo,
|
||||
// debug
|
||||
case EntryAction.debug:
|
||||
return AIcons.debug;
|
||||
}
|
||||
EntryAction.debug => AIcons.debug,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue