diff --git a/.github/workflows/quality-check.yml b/.github/workflows/quality-check.yml index 6f573c642..d448a848a 100644 --- a/.github/workflows/quality-check.yml +++ b/.github/workflows/quality-check.yml @@ -69,7 +69,7 @@ jobs: # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5 + uses: github/codeql-action/init@babb554ede22fd5605947329c4d04d8e7a0b8155 # v3.27.7 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} @@ -83,6 +83,6 @@ jobs: ./flutterw build apk --profile -t lib/main_play.dart --flavor play - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5 + uses: github/codeql-action/analyze@babb554ede22fd5605947329c4d04d8e7a0b8155 # v3.27.7 with: category: "/language:${{matrix.language}}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e82037478..d647f6cf9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -75,7 +75,7 @@ jobs: AVES_GOOGLE_API_KEY: ${{ secrets.AVES_GOOGLE_API_KEY }} - name: Generate artifact attestation - uses: actions/attest-build-provenance@ef244123eb79f2f7a7e75d99086184180e6d0018 # v1.4.4 + uses: actions/attest-build-provenance@7668571508540a607bdfd90a87a560489fe372eb # v2.1.0 with: subject-path: 'outputs/*' diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml index 781659b9f..d0a842855 100644 --- a/.github/workflows/scorecards.yml +++ b/.github/workflows/scorecards.yml @@ -71,6 +71,6 @@ jobs: # Upload the results to GitHub's code scanning dashboard. - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@f09c1c0a94de965c15400f5634aa42fac8fb8f88 # v3.27.5 + uses: github/codeql-action/upload-sarif@babb554ede22fd5605947329c4d04d8e7a0b8155 # v3.27.7 with: sarif_file: results.sarif diff --git a/CHANGELOG.md b/CHANGELOG.md index c67508ef8..c48e6e13d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file. ## [Unreleased] +## [v1.11.20] - 2024-12-11 + +### Added + +- Albums: dynamic albums from filter sets +- Bulgarian translation (thanks Petrov) +- Tamil translation (thanks தமிழ்நேரம்) + ## [v1.11.19] - 2024-11-24 ### Added diff --git a/android/app/build.gradle b/android/app/build.gradle index 2eade1cc3..2caf851ae 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -151,7 +151,7 @@ repositories { } dependencies { - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.1' + implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0' implementation "androidx.appcompat:appcompat:1.7.0" implementation 'androidx.core:core-ktx:1.15.0' @@ -167,7 +167,7 @@ dependencies { implementation "com.github.bumptech.glide:glide:$glide_version" implementation 'com.google.android.material:material:1.12.0' // SLF4J implementation for `mp4parser` - implementation 'org.slf4j:slf4j-simple:2.0.14' + implementation 'org.slf4j:slf4j-simple:2.0.16' // forked, built by JitPack: // - https://jitpack.io/p/deckerst/Android-TiffBitmapFactory @@ -179,7 +179,7 @@ dependencies { implementation 'com.github.deckerst:pixymeta-android:9ec7097f17' implementation project(':exifinterface') - testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.3' + testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.11.3' kapt 'androidx.annotation:annotation:1.9.1' ksp "com.github.bumptech.glide:ksp:$glide_version" diff --git a/android/app/src/main/res/values-bg/strings.xml b/android/app/src/main/res/values-bg/strings.xml new file mode 100644 index 000000000..d33ecff37 --- /dev/null +++ b/android/app/src/main/res/values-bg/strings.xml @@ -0,0 +1,12 @@ + + + Aves + Фоторамка + Тапет + Карта + Търсене + Сканиране медия + Сканиране медия + Стоп + Видео + \ No newline at end of file diff --git a/android/app/src/main/res/values-it/strings.xml b/android/app/src/main/res/values-it/strings.xml index 4d3bc899d..519f15893 100644 --- a/android/app/src/main/res/values-it/strings.xml +++ b/android/app/src/main/res/values-it/strings.xml @@ -8,4 +8,5 @@ Scansione media Scansione in corso Annulla + Mappa \ No newline at end of file diff --git a/android/app/src/main/res/values-iw/strings.xml b/android/app/src/main/res/values-iw/strings.xml index 8cee373a7..842cc5530 100644 --- a/android/app/src/main/res/values-iw/strings.xml +++ b/android/app/src/main/res/values-iw/strings.xml @@ -8,4 +8,5 @@ סריקת מדיה סורק מדיה הפסק + מפה \ No newline at end of file diff --git a/android/app/src/main/res/values-ta/strings.xml b/android/app/src/main/res/values-ta/strings.xml new file mode 100644 index 000000000..ca82090f9 --- /dev/null +++ b/android/app/src/main/res/values-ta/strings.xml @@ -0,0 +1,12 @@ + + + ஏவ்ச் + புகைப்பட சட்டகம் + வால்பேப்பர் + வரைபடம் + தேடல் + ஊடக ச்கேனிங் + வீடியோக்கள் + மீடியா ச்கேன் + நிறுத்து + \ No newline at end of file diff --git a/android/app/src/main/res/values-zh/strings.xml b/android/app/src/main/res/values-zh/strings.xml index c7b9b7185..70b8d1c33 100644 --- a/android/app/src/main/res/values-zh/strings.xml +++ b/android/app/src/main/res/values-zh/strings.xml @@ -8,4 +8,5 @@ 媒体扫描 正在扫描媒体 停止 + 地图 \ No newline at end of file diff --git a/fastlane/metadata/android/ar/full_description.txt b/fastlane/metadata/android/ar/full_description.txt index eb0b4ab29..fe7e208cf 100644 --- a/fastlane/metadata/android/ar/full_description.txt +++ b/fastlane/metadata/android/ar/full_description.txt @@ -2,4 +2,4 @@ الملاحة والبحث جزء مهم من Avesالهدف هو أن يكون بإمكان المستخدمين التنقل بسهولة من الألبومات إلى الصور إلى الوسوم إلى الخرائط، وما إلى ذلك.. -Avesتتكامل مع نظام Android (من KitKat إلى Android 14، بما في ذلك Android TV) مع ميزات مثلs الودجتس, اختصارات التطبيق, حافظة الشاشة والبحث العالمي التعامل معه. كما أنه يعمل كـ عارض الوسائط ومنتقيها. +Avesتتكامل مع نظام Android (بما في ذلك Android TV) مع ميزات مثلs الودجتس, اختصارات التطبيق, حافظة الشاشة والبحث العالمي التعامل معه. كما أنه يعمل كـ عارض الوسائط ومنتقيها. diff --git a/fastlane/metadata/android/az/full_description.txt b/fastlane/metadata/android/az/full_description.txt index 6b96ec3ea..23a3d6235 100644 --- a/fastlane/metadata/android/az/full_description.txt +++ b/fastlane/metadata/android/az/full_description.txt @@ -2,4 +2,4 @@ Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. -Aves integrates with Android (from KitKat to Android 14, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. +Aves integrates with Android (including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. diff --git a/fastlane/metadata/android/be/full_description.txt b/fastlane/metadata/android/be/full_description.txt index 9be4265ce..a4395811d 100644 --- a/fastlane/metadata/android/be/full_description.txt +++ b/fastlane/metadata/android/be/full_description.txt @@ -2,4 +2,4 @@ Навігацыя і пошук з'яўляюцца важнай часткай Aves. Мэта складаецца ў тым, каб карыстальнікі лёгка пераходзілі ад альбомаў да фатаграфій да тэгаў да карт і г.д. -Aves інтэгруецца з Android (ад KitKat да Android 14, уключаючы Android TV) з такімі функцыямі, як віджэты, ярлыкі праграм, застаўка і апрацоўка глабальнага пошуку. Ён таксама працуе як сродак прагляду і выбару мультымедыя. +Aves інтэгруецца з Android (уключаючы Android TV) з такімі функцыямі, як віджэты, ярлыкі праграм, застаўка і апрацоўка глабальнага пошуку. Ён таксама працуе як сродак прагляду і выбару мультымедыя. diff --git a/fastlane/metadata/android/bg/full_description.txt b/fastlane/metadata/android/bg/full_description.txt new file mode 100644 index 000000000..13898aeb4 --- /dev/null +++ b/fastlane/metadata/android/bg/full_description.txt @@ -0,0 +1,5 @@ +Aves може да обработва всякакви изображения и видеоклипове, включително вашите типични JPEG и MP4 файлове, но също и по-екзотични неща като многостранични TIFF файлове, SVG файлове, стари AVI файлове и други! Той сканира медийната ви колекция, за да идентифицира снимки с движение, панорами (известни още като фото 360°), 360° видеоклипове, както и GeoTIFF файлове. + +Навигация и търсене са важна част от Aves. Целта е потребителите лесно да преминават от албуми към снимки, към етикети, към карти и т.н. + +Aves се интегрира с Android (включително Android TV) с функции като джаджи, преки пътища за приложения, скрийнсейвър и обработка на глобално търсене. Работи и като диалогов прозорец за преглед и избор на медии. diff --git a/fastlane/metadata/android/bg/images/featureGraphic.png b/fastlane/metadata/android/bg/images/featureGraphic.png new file mode 100644 index 000000000..1aa61389f Binary files /dev/null and b/fastlane/metadata/android/bg/images/featureGraphic.png differ diff --git a/fastlane/metadata/android/bg/images/phoneScreenshots/1.png b/fastlane/metadata/android/bg/images/phoneScreenshots/1.png new file mode 100644 index 000000000..3ce322072 Binary files /dev/null and b/fastlane/metadata/android/bg/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/bg/images/phoneScreenshots/2.png b/fastlane/metadata/android/bg/images/phoneScreenshots/2.png new file mode 100644 index 000000000..7f94da64b Binary files /dev/null and b/fastlane/metadata/android/bg/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/bg/images/phoneScreenshots/3.png b/fastlane/metadata/android/bg/images/phoneScreenshots/3.png new file mode 100644 index 000000000..551cf3a4b Binary files /dev/null and b/fastlane/metadata/android/bg/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/bg/images/phoneScreenshots/4.png b/fastlane/metadata/android/bg/images/phoneScreenshots/4.png new file mode 100644 index 000000000..4a1a13500 Binary files /dev/null and b/fastlane/metadata/android/bg/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/bg/images/phoneScreenshots/5.png b/fastlane/metadata/android/bg/images/phoneScreenshots/5.png new file mode 100644 index 000000000..1ddde86df Binary files /dev/null and b/fastlane/metadata/android/bg/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/bg/images/phoneScreenshots/6.png b/fastlane/metadata/android/bg/images/phoneScreenshots/6.png new file mode 100644 index 000000000..631ba4ded Binary files /dev/null and b/fastlane/metadata/android/bg/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/bg/images/phoneScreenshots/7.png b/fastlane/metadata/android/bg/images/phoneScreenshots/7.png new file mode 100644 index 000000000..8f468cd9d Binary files /dev/null and b/fastlane/metadata/android/bg/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/bg/short_description.txt b/fastlane/metadata/android/bg/short_description.txt new file mode 100644 index 000000000..aa3da964f --- /dev/null +++ b/fastlane/metadata/android/bg/short_description.txt @@ -0,0 +1 @@ +Галерия и мениджър на данни \ No newline at end of file diff --git a/fastlane/metadata/android/bn/full_description.txt b/fastlane/metadata/android/bn/full_description.txt index bae3cb8be..5003114e4 100644 --- a/fastlane/metadata/android/bn/full_description.txt +++ b/fastlane/metadata/android/bn/full_description.txt @@ -2,4 +2,4 @@ নেভিগেশন এবং সার্চ আভেস এর একটি গুরুত্বপূর্ণ অংশ। লক্ষ্য হল ইউসার যাতে সহজেই অ্যালবাম, ফটো, ট্যাগ, ম্যাপ ইত্যাদিতে স্থানান্তর করতে পারে। -উইজেট, অ্যাপ শর্টকাট, স্ক্রিন সেভারের এবং গ্লোবাল সার্চ এর মতো বৈশিষ্ট্য সহ, আভেস অ্যান্ড্রয়েড এর সাথে সংহত হতে পারে (কিট ক্যাট থেকে অ্যান্ড্রয়েড ১৩, অ্যান্ড্রয়েড টিভি সহ)। এটি একটি মিডিয়া উপস্থাপক এবং বাছাইকারী হিসেবেও কাজ করে। +উইজেট, অ্যাপ শর্টকাট, স্ক্রিন সেভারের এবং গ্লোবাল সার্চ এর মতো বৈশিষ্ট্য সহ, আভেস অ্যান্ড্রয়েড এর সাথে সংহত হতে পারে (অ্যান্ড্রয়েড টিভি সহ)। এটি একটি মিডিয়া উপস্থাপক এবং বাছাইকারী হিসেবেও কাজ করে। diff --git a/fastlane/metadata/android/ca/full_description.txt b/fastlane/metadata/android/ca/full_description.txt index 6b96ec3ea..23a3d6235 100644 --- a/fastlane/metadata/android/ca/full_description.txt +++ b/fastlane/metadata/android/ca/full_description.txt @@ -2,4 +2,4 @@ Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. -Aves integrates with Android (from KitKat to Android 14, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. +Aves integrates with Android (including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. diff --git a/fastlane/metadata/android/ckb/full_description.txt b/fastlane/metadata/android/ckb/full_description.txt index 6b96ec3ea..23a3d6235 100644 --- a/fastlane/metadata/android/ckb/full_description.txt +++ b/fastlane/metadata/android/ckb/full_description.txt @@ -2,4 +2,4 @@ Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. -Aves integrates with Android (from KitKat to Android 14, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. +Aves integrates with Android (including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. diff --git a/fastlane/metadata/android/cs/full_description.txt b/fastlane/metadata/android/cs/full_description.txt index 63000d41a..70c0ebefa 100644 --- a/fastlane/metadata/android/cs/full_description.txt +++ b/fastlane/metadata/android/cs/full_description.txt @@ -2,4 +2,4 @@ Navigace a vyhledávání jsou důležitou součástí aplikace Aves. Cílem je, aby uživatelé jednoduše přecházeli z alb k fotografiím, albům, mapám, atd. -Aves podporuje Android (od verze KitKat po Android 14, včetně Android TV) s funkcemi jako jsou widgety, zkratky aplikací, spořič displeje a globální vyhledávání. Rovněž jej lze použít pro prohlížení a výběr médií. +Aves podporuje Android (včetně Android TV) s funkcemi jako jsou widgety, zkratky aplikací, spořič displeje a globální vyhledávání. Rovněž jej lze použít pro prohlížení a výběr médií. diff --git a/fastlane/metadata/android/da/full_description.txt b/fastlane/metadata/android/da/full_description.txt index 6b96ec3ea..23a3d6235 100644 --- a/fastlane/metadata/android/da/full_description.txt +++ b/fastlane/metadata/android/da/full_description.txt @@ -2,4 +2,4 @@ Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. -Aves integrates with Android (from KitKat to Android 14, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. +Aves integrates with Android (including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. diff --git a/fastlane/metadata/android/de/full_description.txt b/fastlane/metadata/android/de/full_description.txt index 3b78e40f8..820329540 100644 --- a/fastlane/metadata/android/de/full_description.txt +++ b/fastlane/metadata/android/de/full_description.txt @@ -2,4 +2,4 @@ Navigation und Suche ist ein wichtiger Bestandteil von Aves. Das Ziel besteht darin, dass Benutzer problemlos von Alben zu Fotos zu Tags zu Karten usw. wechseln können. -Aves lässt sich mit Android (von API 19 bis 34, d. h. von KitKat bis Android 14) mit Funktionen wie App-Verknüpfungen und globaler Suche integrieren. Es funktioniert auch als Medienbetrachter und -auswahl. +Aves lässt sich mit Android mit Funktionen wie App-Verknüpfungen und globaler Suche integrieren. Es funktioniert auch als Medienbetrachter und -auswahl. diff --git a/fastlane/metadata/android/el/full_description.txt b/fastlane/metadata/android/el/full_description.txt index c7ee2b244..1d6b6e61e 100644 --- a/fastlane/metadata/android/el/full_description.txt +++ b/fastlane/metadata/android/el/full_description.txt @@ -2,4 +2,4 @@ Η πλοήγηση και η αναζήτηση αποτελούν σημαντικό μέρος της εφαρμογής Aves. Ο στόχος της εφαρμογής είναι να παρέχει στους χρήστες εύκολη και γρήγορη πρόσβαση στα άλμπουμ, σε φωτογραφίες, ετικέτες, χάρτες κ.λπ. -Η εφαρμογή Aves εγκαθίσταται στο λογισμικό Android (συμβατότητα από API 19 έως 34, δηλαδή από KitKat έως Android 14) με δυνατότητες όπως γραφικά στοιχεία, συντομεύσεις, προφύλαξη οθόνης και global search. Μπορεί επίσης να χρησιμοποιηθεί ως εφαρμογή προβολής και επιλογής πολυμέσων. +Η εφαρμογή Aves εγκαθίσταται στο λογισμικό Android με δυνατότητες όπως γραφικά στοιχεία, συντομεύσεις, προφύλαξη οθόνης και global search. Μπορεί επίσης να χρησιμοποιηθεί ως εφαρμογή προβολής και επιλογής πολυμέσων. diff --git a/fastlane/metadata/android/en-US/changelogs/130.txt b/fastlane/metadata/android/en-US/changelogs/130.txt deleted file mode 100644 index 43e1c1fce..000000000 --- a/fastlane/metadata/android/en-US/changelogs/130.txt +++ /dev/null @@ -1,3 +0,0 @@ -In v1.11.11: -- review photos from the lock screen -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/13001.txt b/fastlane/metadata/android/en-US/changelogs/13001.txt deleted file mode 100644 index 43e1c1fce..000000000 --- a/fastlane/metadata/android/en-US/changelogs/13001.txt +++ /dev/null @@ -1,3 +0,0 @@ -In v1.11.11: -- review photos from the lock screen -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/131.txt b/fastlane/metadata/android/en-US/changelogs/131.txt deleted file mode 100644 index d76189ea3..000000000 --- a/fastlane/metadata/android/en-US/changelogs/131.txt +++ /dev/null @@ -1,3 +0,0 @@ -In v1.11.12: -- review photos from the lock screen -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/13101.txt b/fastlane/metadata/android/en-US/changelogs/13101.txt deleted file mode 100644 index d76189ea3..000000000 --- a/fastlane/metadata/android/en-US/changelogs/13101.txt +++ /dev/null @@ -1,3 +0,0 @@ -In v1.11.12: -- review photos from the lock screen -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/132.txt b/fastlane/metadata/android/en-US/changelogs/132.txt deleted file mode 100644 index c0c7c4762..000000000 --- a/fastlane/metadata/android/en-US/changelogs/132.txt +++ /dev/null @@ -1,3 +0,0 @@ -In v1.11.13: -- review photos from the lock screen -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/13201.txt b/fastlane/metadata/android/en-US/changelogs/13201.txt deleted file mode 100644 index c0c7c4762..000000000 --- a/fastlane/metadata/android/en-US/changelogs/13201.txt +++ /dev/null @@ -1,3 +0,0 @@ -In v1.11.13: -- review photos from the lock screen -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/133.txt b/fastlane/metadata/android/en-US/changelogs/133.txt deleted file mode 100644 index c3e79cc42..000000000 --- a/fastlane/metadata/android/en-US/changelogs/133.txt +++ /dev/null @@ -1,4 +0,0 @@ -In v1.11.14: -- enjoy new map layers -- share "geo" addresses to Aves and see your collection in that area -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/13301.txt b/fastlane/metadata/android/en-US/changelogs/13301.txt deleted file mode 100644 index c3e79cc42..000000000 --- a/fastlane/metadata/android/en-US/changelogs/13301.txt +++ /dev/null @@ -1,4 +0,0 @@ -In v1.11.14: -- enjoy new map layers -- share "geo" addresses to Aves and see your collection in that area -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/134.txt b/fastlane/metadata/android/en-US/changelogs/134.txt deleted file mode 100644 index 6d1b2633e..000000000 --- a/fastlane/metadata/android/en-US/changelogs/134.txt +++ /dev/null @@ -1,4 +0,0 @@ -In v1.11.15: -- enjoy new map layers -- share "geo" addresses to Aves and see your collection in that area -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/13401.txt b/fastlane/metadata/android/en-US/changelogs/13401.txt deleted file mode 100644 index 6d1b2633e..000000000 --- a/fastlane/metadata/android/en-US/changelogs/13401.txt +++ /dev/null @@ -1,4 +0,0 @@ -In v1.11.15: -- enjoy new map layers -- share "geo" addresses to Aves and see your collection in that area -Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/139.txt b/fastlane/metadata/android/en-US/changelogs/139.txt new file mode 100644 index 000000000..00cd62158 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/139.txt @@ -0,0 +1,4 @@ +In v1.11.20: +- save your filtered collection as dynamic albums +- enjoy the app in Tamil and Bulgarian +Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/changelogs/13901.txt b/fastlane/metadata/android/en-US/changelogs/13901.txt new file mode 100644 index 000000000..00cd62158 --- /dev/null +++ b/fastlane/metadata/android/en-US/changelogs/13901.txt @@ -0,0 +1,4 @@ +In v1.11.20: +- save your filtered collection as dynamic albums +- enjoy the app in Tamil and Bulgarian +Full changelog available on GitHub \ No newline at end of file diff --git a/fastlane/metadata/android/en-US/full_description.txt b/fastlane/metadata/android/en-US/full_description.txt index 6b96ec3ea..23a3d6235 100644 --- a/fastlane/metadata/android/en-US/full_description.txt +++ b/fastlane/metadata/android/en-US/full_description.txt @@ -2,4 +2,4 @@ Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. -Aves integrates with Android (from KitKat to Android 14, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. +Aves integrates with Android (including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. diff --git a/fastlane/metadata/android/en-XW-Shaw/full_description.txt b/fastlane/metadata/android/en-XW-Shaw/full_description.txt index 443fb2726..ebbc3b9a2 100644 --- a/fastlane/metadata/android/en-XW-Shaw/full_description.txt +++ b/fastlane/metadata/android/en-XW-Shaw/full_description.txt @@ -2,4 +2,4 @@ 𐑯𐑨𐑝𐑦𐑜𐑱𐑖𐑩𐑯 𐑯 𐑕𐑻𐑗 𐑦𐑟 𐑩𐑯 𐑦𐑥𐑐𐑹𐑑𐑩𐑯𐑑 𐑐𐑸𐑑 𐑝 ·𐑱𐑝𐑰𐑟. 𐑞 𐑜𐑴𐑤 𐑦𐑟 𐑓 𐑿𐑟𐑼𐑟 𐑑 𐑰𐑟𐑦𐑤𐑦 𐑓𐑤𐑴 𐑓𐑮𐑪𐑥 𐑨𐑤𐑚𐑩𐑥𐑟 𐑑 𐑓𐑴𐑑𐑴𐑟 𐑑 𐑑𐑨𐑜𐑟 𐑑 𐑥𐑨𐑐𐑕, 𐑯𐑯𐑯. -·𐑱𐑝𐑰𐑟 𐑦𐑯𐑑𐑦𐑜𐑮𐑱𐑑𐑕 𐑢𐑦𐑞 ·𐑨𐑯𐑛𐑮𐑶𐑛 (𐑓𐑮𐑪𐑥 ·𐑒𐑦𐑑𐑒𐑨𐑑 𐑑 ·𐑨𐑯𐑛𐑮𐑶𐑛 14, 𐑦𐑯𐑒𐑤𐑵𐑛𐑦𐑙 ·𐑨𐑯𐑛𐑮𐑶𐑛 ⸰𐑑𐑝) 𐑢𐑦𐑞 𐑓𐑰𐑗𐑼𐑟 𐑕𐑳𐑗 𐑨𐑟 𐑢𐑦𐑡𐑩𐑑𐑕, 𐑨𐑐 𐑖𐑹𐑑𐑒𐑳𐑑𐑕, 𐑕𐑒𐑮𐑰𐑯 𐑕𐑱𐑝𐑼 𐑯 𐑜𐑤𐑴𐑚𐑩𐑤 𐑕𐑻𐑗 𐑣𐑨𐑯𐑛𐑤𐑦𐑙. 𐑦𐑑 𐑷𐑤𐑕𐑴 𐑢𐑻𐑒𐑕 𐑨𐑟 𐑩 𐑥𐑰𐑛𐑾 𐑝𐑿𐑼 𐑯 𐑐𐑦𐑒𐑼. +·𐑱𐑝𐑰𐑟 𐑦𐑯𐑑𐑦𐑜𐑮𐑱𐑑𐑕 𐑢𐑦𐑞 ·𐑨𐑯𐑛𐑮𐑶𐑛 (𐑦𐑯𐑒𐑤𐑵𐑛𐑦𐑙 ·𐑨𐑯𐑛𐑮𐑶𐑛 ⸰𐑑𐑝) 𐑢𐑦𐑞 𐑓𐑰𐑗𐑼𐑟 𐑕𐑳𐑗 𐑨𐑟 𐑢𐑦𐑡𐑩𐑑𐑕, 𐑨𐑐 𐑖𐑹𐑑𐑒𐑳𐑑𐑕, 𐑕𐑒𐑮𐑰𐑯 𐑕𐑱𐑝𐑼 𐑯 𐑜𐑤𐑴𐑚𐑩𐑤 𐑕𐑻𐑗 𐑣𐑨𐑯𐑛𐑤𐑦𐑙. 𐑦𐑑 𐑷𐑤𐑕𐑴 𐑢𐑻𐑒𐑕 𐑨𐑟 𐑩 𐑥𐑰𐑛𐑾 𐑝𐑿𐑼 𐑯 𐑐𐑦𐑒𐑼. diff --git a/fastlane/metadata/android/es-MX/full_description.txt b/fastlane/metadata/android/es-MX/full_description.txt index 9d58d9cfb..3bb053d3b 100644 --- a/fastlane/metadata/android/es-MX/full_description.txt +++ b/fastlane/metadata/android/es-MX/full_description.txt @@ -2,4 +2,4 @@ La navegación y búsqueda son las partes más importantes de Aves. Su propósito es que los usuarios puedan fácilmente ir de álbumes a fotos, etiquetas, mapas, etc. -Aves se integra con Android (desde KitKat hasta Android 14, incluido Android TV) con funciones como complementos, accesos directos a aplicaciones, protector de pantalla y búsqueda global. También funciona como un visor y selector de medios. +Aves se integra con Android (incluido Android TV) con funciones como complementos, accesos directos a aplicaciones, protector de pantalla y búsqueda global. También funciona como un visor y selector de medios. diff --git a/fastlane/metadata/android/et/full_description.txt b/fastlane/metadata/android/et/full_description.txt index 6b96ec3ea..5459b87ac 100644 --- a/fastlane/metadata/android/et/full_description.txt +++ b/fastlane/metadata/android/et/full_description.txt @@ -1,5 +1,5 @@ -Aves can handle all sorts of images and videos, including your typical JPEGs and MP4s, but also more exotic things like multi-page TIFFs, SVGs, old AVIs and more! It scans your media collection to identify motion photos, panoramas (aka photo spheres), 360° videos, as well as GeoTIFF files. +Aves oskab kasutada eri sorti pildi- ja videofaile, alates tavalistest JPEG-piltidest kuni MP4-videoteni, aga samuti harvaesinevaid mitmelehelisi TIFF-faile, SVG-graafikafaile, vanu AVI-videoid ja palju muud! Skaneerides sinu meediakogu tuvastab ta animeeritud pildifailid, panoraamfotod, 360° videod, aga ka GeoTIFFi-failid. -Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. +Meedias liikumine ja otsing on oluline osa Avesest. Meie eesmärgiks on see, et rakenduses liikumine oleks sujuv - alates albumitest kuni fotode, siltide, kaartideni jne. -Aves integrates with Android (from KitKat to Android 14, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. +Aves lõimub sujuvalt Androidiga (sh Android TV) kasutades muu hulgas võimalusi nagu vidinad, rakenduse viited, ekraanisäästja ja üldotsing. Ta toimib ka meediaesitajana ja -valijana. diff --git a/fastlane/metadata/android/eu/full_description.txt b/fastlane/metadata/android/eu/full_description.txt index 63af644c6..cb7b3828c 100644 --- a/fastlane/metadata/android/eu/full_description.txt +++ b/fastlane/metadata/android/eu/full_description.txt @@ -2,4 +2,4 @@ Nabigazioa eta bilaketa Aves aplikazioaren zati garrantzitsu bat da. Helburua, erabiltzaileek albumetatik argazkietara, etiketetara, mapetara, etab. modu errazean mugi ahal izatea da. -Aves Androidera (KitKatetik Android 14ra, Android TV barne) egiten da ezaugarri ugarirekin: widgetak, aplikazioko lasterbideak, pantaila-babeslea eta bilaketa globala. Baita ere, media-bisore edo -hautagailu bezala erabil daiteke. +Aves Androidera (Android TV barne) egiten da ezaugarri ugarirekin: widgetak, aplikazioko lasterbideak, pantaila-babeslea eta bilaketa globala. Baita ere, media-bisore edo -hautagailu bezala erabil daiteke. diff --git a/fastlane/metadata/android/fa/full_description.txt b/fastlane/metadata/android/fa/full_description.txt index 87c81b673..26f556410 100644 --- a/fastlane/metadata/android/fa/full_description.txt +++ b/fastlane/metadata/android/fa/full_description.txt @@ -2,4 +2,4 @@ پیمایش و جستجویک بخش مهم از اِیوْز است. هدف این است که کاربران به راحتی از آلبوم ها به عکس ها به برچسب ها به نقشه ها و غیره دست پیدا کنند. -اِیوْز با اندروید سازگار است (از نسخه KitKat تا اندروید 14, شامل تلوزیون اندرویدی) با قابلیت هایی مانند ابزارک ها, میانبر ها, ذخیره نیرو و جستجو عمومی و همچنین میتوان از آن به عنوان نمایشگر و انتخابگر رسانه استفاده کرد. +اِیوْز با اندروید سازگار است (شامل تلوزیون اندرویدی) با قابلیت هایی مانند ابزارک ها, میانبر ها, ذخیره نیرو و جستجو عمومی و همچنین میتوان از آن به عنوان نمایشگر و انتخابگر رسانه استفاده کرد. diff --git a/fastlane/metadata/android/fi/full_description.txt b/fastlane/metadata/android/fi/full_description.txt index 6b96ec3ea..23a3d6235 100644 --- a/fastlane/metadata/android/fi/full_description.txt +++ b/fastlane/metadata/android/fi/full_description.txt @@ -2,4 +2,4 @@ Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. -Aves integrates with Android (from KitKat to Android 14, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. +Aves integrates with Android (including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. diff --git a/fastlane/metadata/android/fr/full_description.txt b/fastlane/metadata/android/fr/full_description.txt index 9358d7cf1..e4bff9552 100644 --- a/fastlane/metadata/android/fr/full_description.txt +++ b/fastlane/metadata/android/fr/full_description.txt @@ -2,4 +2,4 @@ La navigation et la recherche sont une partie importante d’Aves. Le but est que les utilisateurs puissent passer facilement des albums aux photos, des photos aux tags, des tags aux cartes, etc. -Aves s’intègre avec Android (de KitKat à Android 14, y compris Android TV) avec des fonctionnalités telles que les widgets, les raccourcis d’application, économiseur d’écran et la recherche globale. Il est également possible de l’utiliser comme visionneuse et sélecteur de médias. +Aves s’intègre avec Android (y compris Android TV) avec des fonctionnalités telles que les widgets, les raccourcis d’application, économiseur d’écran et la recherche globale. Il est également possible de l’utiliser comme visionneuse et sélecteur de médias. diff --git a/fastlane/metadata/android/gl/full_description.txt b/fastlane/metadata/android/gl/full_description.txt index 6e3b9f2cf..06201158a 100644 --- a/fastlane/metadata/android/gl/full_description.txt +++ b/fastlane/metadata/android/gl/full_description.txt @@ -2,4 +2,4 @@ A navegación e busca é unha parte importante de Aves. O obxectivo é que os usuarios poidan fluír facilmente de álbums a fotos, a etiquetas a mapas, etc. -Aves intégrase con Android (desde KitKat ata Android 14, incluíndo Android TV) con funcións como complementos, atallos de aplicacións, salvapantallas e busca global. Tamén funciona como visor e selector de medios. +Aves intégrase con Android (incluíndo Android TV) con funcións como complementos, atallos de aplicacións, salvapantallas e busca global. Tamén funciona como visor e selector de medios. diff --git a/fastlane/metadata/android/he/full_description.txt b/fastlane/metadata/android/he/full_description.txt index 6b96ec3ea..23a3d6235 100644 --- a/fastlane/metadata/android/he/full_description.txt +++ b/fastlane/metadata/android/he/full_description.txt @@ -2,4 +2,4 @@ Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. -Aves integrates with Android (from KitKat to Android 14, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. +Aves integrates with Android (including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. diff --git a/fastlane/metadata/android/hi/full_description.txt b/fastlane/metadata/android/hi/full_description.txt index ec60c4d99..704559101 100644 --- a/fastlane/metadata/android/hi/full_description.txt +++ b/fastlane/metadata/android/hi/full_description.txt @@ -2,4 +2,4 @@ Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. -Aves integrates with Android (from KitKat to Android 14, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. +Aves integrates with Android (including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. diff --git a/fastlane/metadata/android/hu/full_description.txt b/fastlane/metadata/android/hu/full_description.txt index 614f6266e..2787d97ae 100644 --- a/fastlane/metadata/android/hu/full_description.txt +++ b/fastlane/metadata/android/hu/full_description.txt @@ -2,4 +2,4 @@ Az Aves mindenféle képet és videót képes kezelni, beleértve a tipik A navigáció és a keresés fontos része az Aves-nek. A cél, hogy a felhasználók egyszerűen tudjanak váltani az albumokról a fotókra, címkézni vagy helyadatokkal ellátni az elemeket, stb. -Az Aves beépül az Android-ba (a KitKat-tól az Android 14-ig, beleértve az Android TV-t) olyan funkciókkal, mint a widgetek, alkalmazás parancsikonok, képernyővédő és globális keresés kezelése. Média fájlok megnyitására és kezelésére is használható. +Az Aves beépül az Android-ba (beleértve az Android TV-t) olyan funkciókkal, mint a widgetek, alkalmazás parancsikonok, képernyővédő és globális keresés kezelése. Média fájlok megnyitására és kezelésére is használható. diff --git a/fastlane/metadata/android/id/full_description.txt b/fastlane/metadata/android/id/full_description.txt index c0d9d1c9a..7f9ef0ebc 100644 --- a/fastlane/metadata/android/id/full_description.txt +++ b/fastlane/metadata/android/id/full_description.txt @@ -2,4 +2,4 @@ Navigasi dan pencarian merupakan bagian penting dari Aves. Tujuannya adalah agar pengguna dengan mudah mengalir dari album ke foto ke tag ke peta, dll. -Aves mengintegrasi dengan Android (dari Kitkat ke Android 14) dengan fitur-fitur seperti pintasan aplikasi, jalan pintas aplikasi, screen saver dan pencarian global penanganan. Ini juga berfungsi sebagai penampil dan pemilih media. +Aves mengintegrasi dengan Android dengan fitur-fitur seperti pintasan aplikasi, jalan pintas aplikasi, screen saver dan pencarian global penanganan. Ini juga berfungsi sebagai penampil dan pemilih media. diff --git a/fastlane/metadata/android/is/full_description.txt b/fastlane/metadata/android/is/full_description.txt index acf30d43d..3ef3b542f 100644 --- a/fastlane/metadata/android/is/full_description.txt +++ b/fastlane/metadata/android/is/full_description.txt @@ -2,4 +2,4 @@ Flakk og leit eru mikilvægir hlutar Aves. Markmiðið er að notendur eigi auðvelt með að flæða úr albúmum yfir í ljósmyndir yfir í merki eða landakort, o.s.frv. -Aves samtvinnast við Android (frá KitKat til Android 14, að meðtöldu Android TV) með eiginleikum á borð við viðmótshluta, flýtileiðir í forrit, skjáhvílu og víðværa leit. Það virkar einnig sem margmiðlunarskoðari og veljari. +Aves samtvinnast við Android (að meðtöldu Android TV) með eiginleikum á borð við viðmótshluta, flýtileiðir í forrit, skjáhvílu og víðværa leit. Það virkar einnig sem margmiðlunarskoðari og veljari. diff --git a/fastlane/metadata/android/it/full_description.txt b/fastlane/metadata/android/it/full_description.txt index 0b1afa53f..dd7fdfe5a 100644 --- a/fastlane/metadata/android/it/full_description.txt +++ b/fastlane/metadata/android/it/full_description.txt @@ -2,4 +2,4 @@ Navigazione e ricerca sono una parte importante di Aves. L'obiettivo è che gli utenti passino facilmente dagli album alle foto, ai tag, alle mappe, ecc. -Aves si integra con Android (da KitKat ad Android 14, anche Android TV) con funzionalità come widget, scorciatoie app, salvaschermo e gestione della ricerca globale. Funziona anche come visualizzazione e raccolta di media. +Aves si integra con Android (anche Android TV) con funzionalità come widget, scorciatoie app, salvaschermo e gestione della ricerca globale. Funziona anche come visualizzazione e raccolta di media. diff --git a/fastlane/metadata/android/ja/full_description.txt b/fastlane/metadata/android/ja/full_description.txt index bd93f6622..c5dd599a2 100644 --- a/fastlane/metadata/android/ja/full_description.txt +++ b/fastlane/metadata/android/ja/full_description.txt @@ -2,4 +2,4 @@ ナビゲーションと検索は、Avesの重要な部分です。アルバムから写真、タグ、地図などへ簡単に移動できます。 -Avesは、アプリショートカットグローバル検索などの機能を、Android(API 19から34まで、つまりAndroid 4.4から14 Lまで)と統合しています。また、メディアビューワーメディアピッカーとしても機能します。 +Avesは、アプリショートカットグローバル検索などの機能を、Androidと統合しています。また、メディアビューワーメディアピッカーとしても機能します。 diff --git a/fastlane/metadata/android/kn/full_description.txt b/fastlane/metadata/android/kn/full_description.txt index 6b96ec3ea..23a3d6235 100644 --- a/fastlane/metadata/android/kn/full_description.txt +++ b/fastlane/metadata/android/kn/full_description.txt @@ -2,4 +2,4 @@ Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. -Aves integrates with Android (from KitKat to Android 14, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. +Aves integrates with Android (including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. diff --git a/fastlane/metadata/android/ko/full_description.txt b/fastlane/metadata/android/ko/full_description.txt index 4773b06bc..f4479d84a 100644 --- a/fastlane/metadata/android/ko/full_description.txt +++ b/fastlane/metadata/android/ko/full_description.txt @@ -2,4 +2,4 @@ 네비게이션과 검색Aves의 중요한 부분입니다. 사용자가 앨범에서 사진, 태그에서 지도 등으로 쉽게 흘러갈 수 있도록 하는 것이 목표입니다. -Aves는 Android(KitKat에서 Android 14까지)에서 사용할 수 있습니다. 앱 바로 가기글로벌 검색과 같은 기능과 통합됩니다. 또한 미디어 뷰어 및 선택기로도 작동합니다. +Aves는 Android(Android TV 포함)에서 사용할 수 있습니다. 앱 바로 가기글로벌 검색과 같은 기능과 통합됩니다. 또한 미디어 뷰어 및 선택기로도 작동합니다. diff --git a/fastlane/metadata/android/lt/full_description.txt b/fastlane/metadata/android/lt/full_description.txt index 246bda3c0..d5807e873 100644 --- a/fastlane/metadata/android/lt/full_description.txt +++ b/fastlane/metadata/android/lt/full_description.txt @@ -2,4 +2,4 @@ Naršymas ir paieška yra svarbi „Aves” dalis. Tikslas yra, kad vartotojai galėtų lengvai pereiti nuo albumų prie nuotraukų, žymų, žemėlapių ir kt. -„Aves” integruojasi su „Android” (nuo 19 iki 34 API, t. y. nuo „KitKat” iki 14 versijos „Android”) su tokiomis funkcijomis kaip valdiklių, programėlių nuorodų , ekrano užsklandų ir globalios paieškos palaikymu. Ji taip pat veikia kaip medijos peržiūros priemonė ir rinkiklis. +„Aves” integruojasi su „Android” su tokiomis funkcijomis kaip valdiklių, programėlių nuorodų , ekrano užsklandų ir globalios paieškos palaikymu. Ji taip pat veikia kaip medijos peržiūros priemonė ir rinkiklis. diff --git a/fastlane/metadata/android/ml/full_description.txt b/fastlane/metadata/android/ml/full_description.txt index 6b96ec3ea..23a3d6235 100644 --- a/fastlane/metadata/android/ml/full_description.txt +++ b/fastlane/metadata/android/ml/full_description.txt @@ -2,4 +2,4 @@ Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. -Aves integrates with Android (from KitKat to Android 14, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. +Aves integrates with Android (including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. diff --git a/fastlane/metadata/android/my/full_description.txt b/fastlane/metadata/android/my/full_description.txt index 44d1a23cf..e12680e41 100644 --- a/fastlane/metadata/android/my/full_description.txt +++ b/fastlane/metadata/android/my/full_description.txt @@ -2,4 +2,4 @@ အလွယ်တကူ ရှာဖွေကြည့်ရှုနိုင်တာက Aves ရဲ့ အဓိကကျတဲ့အစိတ်အပိုင်းတစ်ခုဖြစ်ပါတယ်။ အသုံးပြုသူတွေကို အယ်လ်ဘမ်တွေကနေ ပုံတွေ၊ ပုံတွေကနေ tag တွေ၊ tag တွေကနေ မြေပုံတွေ၊ အစရှိသဖြင့် အလွယ်လေးကူးပြောင်းနိုင်စေဖို့က Aves ရဲ့ ပန်းတိုင်ဖြစ်ပါတယ်။ -Aves ဟာ Android (KitKat ကနေ Android 14 အထိ၊ Android တီဗီအပါအဝင်) ဖြင့်ပေါင်းစပ်ပြီး ဝစ်ဂျက်တွေအက်ပ်ဖြတ်လမ်းတိုတွေscreen saver တွေနဲ့ နေရာအနှံ့ရှာဖွေမှုတွေကိုပါ ကိုင်တွယ်နိုင်ပါတယ်။ ဒါ့အပြင် မီဒီယာကြည့်ဖို့၊ မီဒီယာရွေးချယ်ဖို့အတွက်လည်း သုံးလို့ရပါသေးတယ်။ +Aves ဟာ Android (Android တီဗီအပါအဝင်) ဖြင့်ပေါင်းစပ်ပြီး ဝစ်ဂျက်တွေအက်ပ်ဖြတ်လမ်းတိုတွေscreen saver တွေနဲ့ နေရာအနှံ့ရှာဖွေမှုတွေကိုပါ ကိုင်တွယ်နိုင်ပါတယ်။ ဒါ့အပြင် မီဒီယာကြည့်ဖို့၊ မီဒီယာရွေးချယ်ဖို့အတွက်လည်း သုံးလို့ရပါသေးတယ်။ diff --git a/fastlane/metadata/android/nb-NO/full_description.txt b/fastlane/metadata/android/nb-NO/full_description.txt index 9f4e596b0..994c607d7 100644 --- a/fastlane/metadata/android/nb-NO/full_description.txt +++ b/fastlane/metadata/android/nb-NO/full_description.txt @@ -2,4 +2,4 @@ Navigasjon og søk er en viktig del av Aves. Målet er at brukere enkelt skal kunne ta seg fra album, til bilder, til etiketter, til kart, osv. -Aves integrerer seg med Android (fra API 19 til 34, altså fra KitKat til Android 14) med funksjoner som f.eks. miniprogrammer, programsnarveier, skjermsparer og søk i hele programmet. Det fungerer også som mediaviser og utvelger. +Aves integrerer seg med Android med funksjoner som f.eks. miniprogrammer, programsnarveier, skjermsparer og søk i hele programmet. Det fungerer også som mediaviser og utvelger. diff --git a/fastlane/metadata/android/nl/full_description.txt b/fastlane/metadata/android/nl/full_description.txt index b09754ce3..09ae9ee93 100644 --- a/fastlane/metadata/android/nl/full_description.txt +++ b/fastlane/metadata/android/nl/full_description.txt @@ -2,4 +2,4 @@ Navigatie en zoeken is een belangrijk onderdeel van Aves. Het doel is dat gebruikers eenvoudig kunnen wisselen van albums naar foto's naar labels naar kaarten enz. -Aves integreert met Android (van KitKat t/m Android 14, inclusief Android TV) met functies zoals widgets, app-snelkoppelingen, screensaver en algemene zoekopdrachten. Het werkt ook als een mediaviewer en -kiezer. +Aves integreert met Android (inclusief Android TV) met functies zoals widgets, app-snelkoppelingen, screensaver en algemene zoekopdrachten. Het werkt ook als een mediaviewer en -kiezer. diff --git a/fastlane/metadata/android/nn/full_description.txt b/fastlane/metadata/android/nn/full_description.txt index 0ecaa08de..8e8725f5c 100644 --- a/fastlane/metadata/android/nn/full_description.txt +++ b/fastlane/metadata/android/nn/full_description.txt @@ -2,4 +2,4 @@ Navigering og søk har mykje å sei i Aves. Målet er at ein skal lett kunne gå ifrå album, til bilete, til merkelappar, til kart, osv. -Aves integrates with Android (from KitKat to Android 14, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. +Aves integrates with Android (including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. diff --git a/fastlane/metadata/android/or/full_description.txt b/fastlane/metadata/android/or/full_description.txt index 6b96ec3ea..23a3d6235 100644 --- a/fastlane/metadata/android/or/full_description.txt +++ b/fastlane/metadata/android/or/full_description.txt @@ -2,4 +2,4 @@ Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. -Aves integrates with Android (from KitKat to Android 14, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. +Aves integrates with Android (including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. diff --git a/fastlane/metadata/android/pl/full_description.txt b/fastlane/metadata/android/pl/full_description.txt index e0f0b7f36..da65aabce 100644 --- a/fastlane/metadata/android/pl/full_description.txt +++ b/fastlane/metadata/android/pl/full_description.txt @@ -2,4 +2,4 @@ Nawigacja i wyszukiwanie jest ważną częścią Aves. Celem jest aby użytkownicy mogli łatwo przechodzić od albumów do zdjęć, tagów, map itd. -Aves integruje się z Androidem (od API 19 do 34, czyli od KitKata do Androida 14) z funkcjami takimi jak widżety, skróty do aplikacji, wygaszacz ekranu i obsługa globalnego wyszukiwania. Działa również jako przeglądarka i selektor mediów. +Aves integruje się z Androidem z funkcjami takimi jak widżety, skróty do aplikacji, wygaszacz ekranu i obsługa globalnego wyszukiwania. Działa również jako przeglądarka i selektor mediów. diff --git a/fastlane/metadata/android/pt-BR/full_description.txt b/fastlane/metadata/android/pt-BR/full_description.txt index a0550b001..a602edf52 100644 --- a/fastlane/metadata/android/pt-BR/full_description.txt +++ b/fastlane/metadata/android/pt-BR/full_description.txt @@ -2,4 +2,4 @@ Navegação e pesquisa é uma parte importante do Aves. O objetivo é que os usuários fluam facilmente de álbuns para fotos, etiquetas, mapas, etc. -Aves integra com Android (de KitKat até Android 14, incluindo TVs Android) com recursos como widgets, atalhos de apps, protetor de tela e pesquisa global. Também funciona como um visualizador e selecionador de mídia. +Aves integra com Android (incluindo TVs Android) com recursos como widgets, atalhos de apps, protetor de tela e pesquisa global. Também funciona como um visualizador e selecionador de mídia. diff --git a/fastlane/metadata/android/ro/full_description.txt b/fastlane/metadata/android/ro/full_description.txt index e76ce6d7c..4ca99911e 100644 --- a/fastlane/metadata/android/ro/full_description.txt +++ b/fastlane/metadata/android/ro/full_description.txt @@ -2,4 +2,4 @@ Navigația și căutarea sunt o parte importantă a Aves. Scopul este ca utilizatorii să treacă cu ușurință de la albume la fotografii la etichete la hărți etc. -Aves se integrează cu Android (începând de la API 19 la 34, adică de la KitKat la Android 14) cu funcții precum widget-uri, comenzi rapide pentru aplicații, protector de ecran și gestionarea căutării globale. De asemenea, funcționează ca vizionator și selector de conținut media. +Aves se integrează cu Android cu funcții precum widget-uri, comenzi rapide pentru aplicații, protector de ecran și gestionarea căutării globale. De asemenea, funcționează ca vizionator și selector de conținut media. diff --git a/fastlane/metadata/android/ru/full_description.txt b/fastlane/metadata/android/ru/full_description.txt index d09a7d4de..b33ceb262 100644 --- a/fastlane/metadata/android/ru/full_description.txt +++ b/fastlane/metadata/android/ru/full_description.txt @@ -2,4 +2,4 @@ Навигация и поиск важные части Aves. Пользователи могут легко переходить от альбомов к фотографиям, тэгам, картам и т.д. -Aves интегрируется с Android (от KitKat до Android 14, включая Android TV) предлагая такие возможности как виджеты, пользовательские ярлыки, скринсейвер и поддержку глобального поиска. Он так же работает как диалоговое окно для просмотра и выбора медиа. +Aves интегрируется с Android (включая Android TV) предлагая такие возможности как виджеты, пользовательские ярлыки, скринсейвер и поддержку глобального поиска. Он так же работает как диалоговое окно для просмотра и выбора медиа. diff --git a/fastlane/metadata/android/sat/full_description.txt b/fastlane/metadata/android/sat/full_description.txt index 6b96ec3ea..23a3d6235 100644 --- a/fastlane/metadata/android/sat/full_description.txt +++ b/fastlane/metadata/android/sat/full_description.txt @@ -2,4 +2,4 @@ Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. -Aves integrates with Android (from KitKat to Android 14, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. +Aves integrates with Android (including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. diff --git a/fastlane/metadata/android/sk/full_description.txt b/fastlane/metadata/android/sk/full_description.txt index 1e9788de0..0c3a37174 100644 --- a/fastlane/metadata/android/sk/full_description.txt +++ b/fastlane/metadata/android/sk/full_description.txt @@ -2,4 +2,4 @@ Navigácia a vyhľadávanie je dôležitou súčasťou aplikácie Aves. Jej cieľom je poskytnúť užívateľom jednoduchý prechod z albumov, do fotiek, tagov, máp, atď. -Aves je schopný pracovať s Android (od KitKat do Android 14, včetne Android TV) a ponúka rozšírenia ako miniaplikácie (widgety), skratky aplikácie, šetrič obrazovky a globálne vyhľadávanie. Rovnako poskytuje prehľadávnie médií. +Aves je schopný pracovať s Android (včetne Android TV) a ponúka rozšírenia ako miniaplikácie (widgety), skratky aplikácie, šetrič obrazovky a globálne vyhľadávanie. Rovnako poskytuje prehľadávnie médií. diff --git a/fastlane/metadata/android/sl/full_description.txt b/fastlane/metadata/android/sl/full_description.txt index 6b96ec3ea..23a3d6235 100644 --- a/fastlane/metadata/android/sl/full_description.txt +++ b/fastlane/metadata/android/sl/full_description.txt @@ -2,4 +2,4 @@ Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. -Aves integrates with Android (from KitKat to Android 14, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. +Aves integrates with Android (including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. diff --git a/fastlane/metadata/android/sr/full_description.txt b/fastlane/metadata/android/sr/full_description.txt index 6b96ec3ea..23a3d6235 100644 --- a/fastlane/metadata/android/sr/full_description.txt +++ b/fastlane/metadata/android/sr/full_description.txt @@ -2,4 +2,4 @@ Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. -Aves integrates with Android (from KitKat to Android 14, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. +Aves integrates with Android (including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. diff --git a/fastlane/metadata/android/sv/full_description.txt b/fastlane/metadata/android/sv/full_description.txt index 999ed1764..22f47c9a6 100644 --- a/fastlane/metadata/android/sv/full_description.txt +++ b/fastlane/metadata/android/sv/full_description.txt @@ -2,4 +2,4 @@ Navigering och sökhantering är än viktigt del av Aves. Målet är att användarna på ett smidigt sätt ska kunna gå från album till foton till etiketter till kartor, osv. -Aves integrerar med Android (från KitKat till Android 14, inklusive Android TV) med funktioner som widgetar, appgenvägar, skärmsläckare och global sökhantering. Den fungerar också som en mediavisare och mediaväljare. +Aves integrerar med Android (inklusive Android TV) med funktioner som widgetar, appgenvägar, skärmsläckare och global sökhantering. Den fungerar också som en mediavisare och mediaväljare. diff --git a/fastlane/metadata/android/ta/full_description.txt b/fastlane/metadata/android/ta/full_description.txt new file mode 100644 index 000000000..23a3d6235 --- /dev/null +++ b/fastlane/metadata/android/ta/full_description.txt @@ -0,0 +1,5 @@ +Aves can handle all sorts of images and videos, including your typical JPEGs and MP4s, but also more exotic things like multi-page TIFFs, SVGs, old AVIs and more! It scans your media collection to identify motion photos, panoramas (aka photo spheres), 360° videos, as well as GeoTIFF files. + +Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. + +Aves integrates with Android (including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. diff --git a/fastlane/metadata/android/ta/images/featureGraphic.png b/fastlane/metadata/android/ta/images/featureGraphic.png new file mode 100644 index 000000000..651729292 Binary files /dev/null and b/fastlane/metadata/android/ta/images/featureGraphic.png differ diff --git a/fastlane/metadata/android/ta/images/phoneScreenshots/1.png b/fastlane/metadata/android/ta/images/phoneScreenshots/1.png new file mode 100644 index 000000000..db5a2625e Binary files /dev/null and b/fastlane/metadata/android/ta/images/phoneScreenshots/1.png differ diff --git a/fastlane/metadata/android/ta/images/phoneScreenshots/2.png b/fastlane/metadata/android/ta/images/phoneScreenshots/2.png new file mode 100644 index 000000000..643f990d0 Binary files /dev/null and b/fastlane/metadata/android/ta/images/phoneScreenshots/2.png differ diff --git a/fastlane/metadata/android/ta/images/phoneScreenshots/3.png b/fastlane/metadata/android/ta/images/phoneScreenshots/3.png new file mode 100644 index 000000000..9d5fd1909 Binary files /dev/null and b/fastlane/metadata/android/ta/images/phoneScreenshots/3.png differ diff --git a/fastlane/metadata/android/ta/images/phoneScreenshots/4.png b/fastlane/metadata/android/ta/images/phoneScreenshots/4.png new file mode 100644 index 000000000..5bd3c6052 Binary files /dev/null and b/fastlane/metadata/android/ta/images/phoneScreenshots/4.png differ diff --git a/fastlane/metadata/android/ta/images/phoneScreenshots/5.png b/fastlane/metadata/android/ta/images/phoneScreenshots/5.png new file mode 100644 index 000000000..423e383e3 Binary files /dev/null and b/fastlane/metadata/android/ta/images/phoneScreenshots/5.png differ diff --git a/fastlane/metadata/android/ta/images/phoneScreenshots/6.png b/fastlane/metadata/android/ta/images/phoneScreenshots/6.png new file mode 100644 index 000000000..82f9de360 Binary files /dev/null and b/fastlane/metadata/android/ta/images/phoneScreenshots/6.png differ diff --git a/fastlane/metadata/android/ta/images/phoneScreenshots/7.png b/fastlane/metadata/android/ta/images/phoneScreenshots/7.png new file mode 100644 index 000000000..91c6f2dfe Binary files /dev/null and b/fastlane/metadata/android/ta/images/phoneScreenshots/7.png differ diff --git a/fastlane/metadata/android/ta/short_description.txt b/fastlane/metadata/android/ta/short_description.txt new file mode 100644 index 000000000..1f72a7ca5 --- /dev/null +++ b/fastlane/metadata/android/ta/short_description.txt @@ -0,0 +1 @@ +தொகுப்பு மற்றும் தரவுத்தகவல் உலாவி \ No newline at end of file diff --git a/fastlane/metadata/android/th/full_description.txt b/fastlane/metadata/android/th/full_description.txt index 6b96ec3ea..23a3d6235 100644 --- a/fastlane/metadata/android/th/full_description.txt +++ b/fastlane/metadata/android/th/full_description.txt @@ -2,4 +2,4 @@ Navigation and search is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc. -Aves integrates with Android (from KitKat to Android 14, including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. +Aves integrates with Android (including Android TV) with features such as widgets, app shortcuts, screen saver and global search handling. It also works as a media viewer and picker. diff --git a/fastlane/metadata/android/tr/full_description.txt b/fastlane/metadata/android/tr/full_description.txt index 96fd599be..b6fb2b537 100644 --- a/fastlane/metadata/android/tr/full_description.txt +++ b/fastlane/metadata/android/tr/full_description.txt @@ -2,4 +2,4 @@ Gezinme ve arama Aves'in önemli bir parçasıdır. Amaç, kullanıcıların albümlerden fotoğraflara, etiketlerden haritalara vb. kolayca geçmesini sağlamaktır. -Aves, uygulama kısayolları ve global arama işleme gibi özelliklerle Android (API 19'dan 34'ye, yani KitKat'tan Android 14'ye kadar) ile entegre olur. Ayrıca bir medya görüntüleyici ve alıcı olarak da çalışır. +Aves, uygulama kısayolları ve global arama işleme gibi özelliklerle Android ile entegre olur. Ayrıca bir medya görüntüleyici ve alıcı olarak da çalışır. diff --git a/fastlane/metadata/android/uk/full_description.txt b/fastlane/metadata/android/uk/full_description.txt index cc81f9cd5..6b6aa78ad 100644 --- a/fastlane/metadata/android/uk/full_description.txt +++ b/fastlane/metadata/android/uk/full_description.txt @@ -2,4 +2,4 @@ Навігація та пошук є важливою частиною Aves. Мета полягає в тому, щоб користувачі легко переходили від альбомів до фотографій, від тегів до карт тощо. -Aves інтегрується з Android (від KitKat до Android 14, включаючи Android TV) пропонуючи такі можливості як віджети, ярлики додатка, заставка екрану та работа з глобальним пошуком. Він також працює як переглядач і засоб вибору медіа. +Aves інтегрується з Android (включаючи Android TV) пропонуючи такі можливості як віджети, ярлики додатка, заставка екрану та работа з глобальним пошуком. Він також працює як переглядач і засоб вибору медіа. diff --git a/fastlane/metadata/android/vi/full_description.txt b/fastlane/metadata/android/vi/full_description.txt index fa415ff0c..77ebcb8da 100644 --- a/fastlane/metadata/android/vi/full_description.txt +++ b/fastlane/metadata/android/vi/full_description.txt @@ -2,4 +2,4 @@ Điều hướng và tìm kiếm là một phần quan trọng của Aves. Mục tiêu là để người dùng dễ dàng chuyển từ album sang ảnh sang thẻ sang bản đồ, v.v. -Aves tương thích với Android (từ KitKat đến Android 14, bao gồm cả Android TV) với các tính năng như widget, phím tắt ứng dụng, trình bảo vệ màn hình và xử lý tìm kiếm toàn hệ thống. Nó cũng hoạt động như một trình xem và lựa chọn phương tiện. +Aves tương thích với Android (bao gồm cả Android TV) với các tính năng như widget, phím tắt ứng dụng, trình bảo vệ màn hình và xử lý tìm kiếm toàn hệ thống. Nó cũng hoạt động như một trình xem và lựa chọn phương tiện. diff --git a/fastlane/metadata/android/zh-CN/full_description.txt b/fastlane/metadata/android/zh-CN/full_description.txt index 429d20c02..406d501af 100644 --- a/fastlane/metadata/android/zh-CN/full_description.txt +++ b/fastlane/metadata/android/zh-CN/full_description.txt @@ -2,4 +2,4 @@ 导航与搜索Aves 的核心功能之一,旨在帮助用户在相册、照片、标签、地图等之间轻松切换。 -Aves 与 Android(API 19-34,即从 KitKat 到 Android 14)集成,具有快捷方式全局搜索等功能。它还可用作媒体查看器和选择器。 +Aves 与 Android 集成,具有快捷方式全局搜索等功能。它还可用作媒体查看器和选择器。 diff --git a/fastlane/metadata/android/zh-Hant/full_description.txt b/fastlane/metadata/android/zh-Hant/full_description.txt index 96c231562..850a64373 100644 --- a/fastlane/metadata/android/zh-Hant/full_description.txt +++ b/fastlane/metadata/android/zh-Hant/full_description.txt @@ -2,4 +2,4 @@ 瀏覽和搜尋Aves重要的一部份. 目的是為了讓使用者能夠輕易的從相簿, 相片, 標籤, 地圖, 等等之間順暢切換. -Aves 與 Android (從 KitKat 到 Android 14, 包括 Android TV) 整合, 具有特色像是 小工具, app 捷徑, 螢幕保護程式全域搜尋 等功能. 還可以當成 媒體檢視和選擇器. +Aves 與 Android (包括 Android TV) 整合, 具有特色像是 小工具, app 捷徑, 螢幕保護程式全域搜尋 等功能. 還可以當成 媒體檢視和選擇器. diff --git a/lib/l10n/app_ar.arb b/lib/l10n/app_ar.arb index 815c167c3..0efe3eb23 100644 --- a/lib/l10n/app_ar.arb +++ b/lib/l10n/app_ar.arb @@ -1550,5 +1550,17 @@ "videoActionShowNextFrame": "إظهار الإطار التالي", "@videoActionShowNextFrame": {}, "newAlbumDialogAlbumAlreadyExistsHelper": "الألبوم موجود بالفعل", - "@newAlbumDialogAlbumAlreadyExistsHelper": {} + "@newAlbumDialogAlbumAlreadyExistsHelper": {}, + "albumTierDynamic": "ديناميكي", + "@albumTierDynamic": {}, + "collectionActionAddDynamicAlbum": "إضافة ألبوم ديناميكي", + "@collectionActionAddDynamicAlbum": {}, + "chipActionRemove": "إزالة", + "@chipActionRemove": {}, + "dynamicAlbumAlreadyExists": "الألبوم الديناميكي موجود بالفعل", + "@dynamicAlbumAlreadyExists": {}, + "appExportDynamicAlbums": "الألبومات الديناميكية", + "@appExportDynamicAlbums": {}, + "newDynamicAlbumDialogTitle": "ألبوم ديناميكي جديد", + "@newDynamicAlbumDialogTitle": {} } diff --git a/lib/l10n/app_az.arb b/lib/l10n/app_az.arb index 0b96ffecb..3461be7f9 100644 --- a/lib/l10n/app_az.arb +++ b/lib/l10n/app_az.arb @@ -117,5 +117,25 @@ "appName": "Aves", "@appName": {}, "welcomeMessage": "Aves-ə Xoş Gəlmisiniz", - "@welcomeMessage": {} + "@welcomeMessage": {}, + "chipActionRemove": "Təmizlə", + "@chipActionRemove": {}, + "chipActionGoToTagPage": "Etiketlərdə Göstər", + "@chipActionGoToTagPage": {}, + "chipActionGoToExplorerPage": "Nəticədə Göstər", + "@chipActionGoToExplorerPage": {}, + "chipActionFilterOut": "Ayırın", + "@chipActionFilterOut": {}, + "chipActionFilterIn": "Qoyun", + "@chipActionFilterIn": {}, + "chipActionHide": "Gizlət", + "@chipActionHide": {}, + "chipActionLock": "Kilidlə", + "@chipActionLock": {}, + "chipActionPin": "Yuxarıya sabitlə", + "@chipActionPin": {}, + "chipActionUnpin": "Sabitləməyin", + "@chipActionUnpin": {}, + "chipActionRename": "Bir də adlandır", + "@chipActionRename": {} } diff --git a/lib/l10n/app_bg.arb b/lib/l10n/app_bg.arb new file mode 100644 index 000000000..c2c209d46 --- /dev/null +++ b/lib/l10n/app_bg.arb @@ -0,0 +1,1606 @@ +{ + "nameConflictStrategyReplace": "Замени", + "@nameConflictStrategyReplace": {}, + "overlayHistogramRGB": "RGB", + "@overlayHistogramRGB": {}, + "subtitlePositionBottom": "Отдолу", + "@subtitlePositionBottom": {}, + "subtitlePositionTop": "Най-отгоре", + "@subtitlePositionTop": {}, + "vaultLockTypePassword": "Парола", + "@vaultLockTypePassword": {}, + "videoLoopModeShortOnly": "Само за кратки видеа", + "@videoLoopModeShortOnly": {}, + "videoResumptionModeAlways": "Винаги", + "@videoResumptionModeAlways": {}, + "viewerTransitionZoomIn": "Приближения", + "@viewerTransitionZoomIn": {}, + "viewerTransitionNone": "Не", + "@viewerTransitionNone": {}, + "widgetOpenPageCollection": "Отвори колекция", + "@widgetOpenPageCollection": {}, + "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" + } + } + }, + "nameConflictDialogMultipleSourceMessage": "Някои файлове имат еднакво име.", + "@nameConflictDialogMultipleSourceMessage": {}, + "addShortcutButtonLabel": "ДОБАВИ", + "@addShortcutButtonLabel": {}, + "binEntriesConfirmationDialogMessage": "{count, plural, =1{Преместване на този елемент в кошчето?} other{Преместване на тези {count} елемента в кошчето?}}", + "@binEntriesConfirmationDialogMessage": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "moveUndatedConfirmationDialogMessage": "Запазвам ли датите на елементите преди продължаване?", + "@moveUndatedConfirmationDialogMessage": {}, + "setCoverDialogAuto": "Авто", + "@setCoverDialogAuto": {}, + "hideFilterConfirmationDialogMessage": "Съответстващите снимки и видеоклипове ще бъдат скрити от вашата колекция. Можете да ги покажете отново от настройките „Поверителност“.\n\nСигурни ли сте, че искате да ги скриете?", + "@hideFilterConfirmationDialogMessage": {}, + "newVaultWarningDialogMessage": "Елементите в трезорите са достъпни само за това приложение и за никое друго.\n\nАко деинсталирате това приложение или изчистите данните на това приложение, ще загубите всички тези елементи.", + "@newVaultWarningDialogMessage": {}, + "authenticateToConfigureVault": "Удостоверете се, за да конфигурирате трезора", + "@authenticateToConfigureVault": {}, + "renameAlbumDialogLabelAlreadyExistsHelper": "Съществуваща директория", + "@renameAlbumDialogLabelAlreadyExistsHelper": {}, + "renameEntrySetPageTitle": "Преименувай", + "@renameEntrySetPageTitle": {}, + "deleteSingleAlbumConfirmationDialogMessage": "{count, plural, =1{Изтриване на този албум и елемента в него?} other{Изтриване на този албум и {count} елементите в него?}}", + "@deleteSingleAlbumConfirmationDialogMessage": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "deleteMultiAlbumConfirmationDialogMessage": "{count, plural, =1{Изтриване на тези албуми и елемента в тях?} other{Изтриване на тези албуми и {count} елементите в тях?}}", + "@deleteMultiAlbumConfirmationDialogMessage": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "editEntryDateDialogSourceFileModifiedDate": "Дата на промяна на файла", + "@editEntryDateDialogSourceFileModifiedDate": {}, + "editEntryRatingDialogTitle": "Рейтинг", + "@editEntryRatingDialogTitle": {}, + "editEntryLocationDialogLatitude": "Широчина", + "@editEntryLocationDialogLatitude": {}, + "locationPickerUseThisLocationButton": "Използвай това местоположение", + "@locationPickerUseThisLocationButton": {}, + "videoStreamSelectionDialogText": "Субтитри", + "@videoStreamSelectionDialogText": {}, + "removeEntryMetadataMotionPhotoXmpWarningDialogMessage": "XMP е необходим за възпроизвеждане на видеоклипа в снимка с движение.\n\nСигурни ли сте, че искате да го премахнете?", + "@removeEntryMetadataMotionPhotoXmpWarningDialogMessage": {}, + "videoStreamSelectionDialogVideo": "Видео", + "@videoStreamSelectionDialogVideo": {}, + "menuActionSelect": "Избери", + "@menuActionSelect": {}, + "menuActionSelectAll": "Изберете всички", + "@menuActionSelectAll": {}, + "menuActionMap": "Карта", + "@menuActionMap": {}, + "menuActionStats": "Статистика", + "@menuActionStats": {}, + "viewDialogGroupSectionTitle": "Групиране", + "@viewDialogGroupSectionTitle": {}, + "appPickDialogNone": "Нищо", + "@appPickDialogNone": {}, + "aboutBugCopyInfoButton": "Копирай", + "@aboutBugCopyInfoButton": {}, + "aboutDataUsageSectionTitle": "Използване на данни", + "@aboutDataUsageSectionTitle": {}, + "aboutTranslatorsSectionTitle": "Преводачи", + "@aboutTranslatorsSectionTitle": {}, + "aboutLicensesBanner": "Това приложение използва следните пакети и библиотеки с отворен код.", + "@aboutLicensesBanner": {}, + "collectionPageTitle": "Колекция", + "@collectionPageTitle": {}, + "collectionPickPageTitle": "Избери", + "@collectionPickPageTitle": {}, + "collectionActionHideTitleSearch": "Скрий филтъра на заглавието", + "@collectionActionHideTitleSearch": {}, + "collectionActionSetHome": "Задай като главен", + "@collectionActionSetHome": {}, + "collectionActionRescan": "Сканирай наново", + "@collectionActionRescan": {}, + "collectionDeleteFailureFeedback": "{count, plural, =1{Неуспешно изтриване на 1 елемент} other{Неуспешно изтриване на {count} елемента}}", + "@collectionDeleteFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "dateThisMonth": "Този месец", + "@dateThisMonth": {}, + "collectionCopyFailureFeedback": "{count, plural, =1{Неуспешно копиране на 1 елемент} other{Неуспешно копиране на {count} елемента}}", + "@collectionCopyFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionMoveFailureFeedback": "{count, plural, =1{Неуспешно преместване на 1 елемент} other{Неуспешно преместване на {count} елемента}}", + "@collectionMoveFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionEditSuccessFeedback": "{count, plural, =1{Редактиран 1 елемент} other{Редактирани{count} елемента}}", + "@collectionEditSuccessFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionEditFailureFeedback": "{count, plural, =1{Неуспешно редактиране на 1 елемент} other{Неуспешно редактиране на {count} елемента}}", + "@collectionEditFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionExportFailureFeedback": "{count, plural, =1{Неуспешно експортиране на 1 страница} other{Неуспешно експортиране на {count} страници}}", + "@collectionExportFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "drawerCollectionVideos": "Видео", + "@drawerCollectionVideos": {}, + "collectionMoveSuccessFeedback": "{count, plural, =1{Преместен 1 елемент} other{Преместени{count} елемента}}", + "@collectionMoveSuccessFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "drawerCollectionSphericalVideos": "360° видео", + "@drawerCollectionSphericalVideos": {}, + "drawerCountryPage": "Държави", + "@drawerCountryPage": {}, + "newFilterBanner": "Нов", + "@newFilterBanner": {}, + "sortOrderShortestFirst": "Първо най-кратките", + "@sortOrderShortestFirst": {}, + "albumGroupTier": "По нива", + "@albumGroupTier": {}, + "explorerPageTitle": "Explorer", + "@explorerPageTitle": {}, + "explorerActionSelectStorageVolume": "Избери място за съхранение", + "@explorerActionSelectStorageVolume": {}, + "settingsAskEverytime": "Питай всеки път", + "@settingsAskEverytime": {}, + "settingsDefault": "По подразбиране", + "@settingsDefault": {}, + "settingsModificationWarningDialogMessage": "Другите настройки ще бъдат модифицирани.", + "@settingsModificationWarningDialogMessage": {}, + "settingsActionExport": "Експортиране", + "@settingsActionExport": {}, + "appExportCovers": "Тапети", + "@appExportCovers": {}, + "settingsNavigationSectionTitle": "Навигация", + "@settingsNavigationSectionTitle": {}, + "settingsShowBottomNavigationBar": "Показване на долната лента за навигация", + "@settingsShowBottomNavigationBar": {}, + "settingsConfirmationBeforeDeleteItems": "Попитай преди изтриване завинаги", + "@settingsConfirmationBeforeDeleteItems": {}, + "settingsConfirmationVaultDataLoss": "Показване на предупреждение за загуба на данни от трезора", + "@settingsConfirmationVaultDataLoss": {}, + "videoActionSkip10": "Превъртане 10 секунди напред", + "@videoActionSkip10": {}, + "entryActionRotateScreen": "Завъртане на екрана", + "@entryActionRotateScreen": {}, + "entryActionAddFavourite": "Добави в любими", + "@entryActionAddFavourite": {}, + "videoActionPause": "Пауза", + "@videoActionPause": {}, + "videoActionPlay": "Възпроизведи", + "@videoActionPlay": {}, + "videoActionCaptureFrame": "Кадър за заснемане", + "@videoActionCaptureFrame": {}, + "hideButtonLabel": "СКРИЙ", + "@hideButtonLabel": {}, + "videoActionUnmute": "Включи звук", + "@videoActionUnmute": {}, + "continueButtonLabel": "ПРОДЪЛЖИ", + "@continueButtonLabel": {}, + "clearTooltip": "Изчисти", + "@clearTooltip": {}, + "videoActionReplay10": "Превъртане 10 секунди назад", + "@videoActionReplay10": {}, + "saveCopyButtonLabel": "ЗАПАЗВАНЕ НА КОПИЕ", + "@saveCopyButtonLabel": {}, + "videoActionShowNextFrame": "Покажи следващия кадър", + "@videoActionShowNextFrame": {}, + "welcomeMessage": "Добре дошли в Aves", + "@welcomeMessage": {}, + "welcomeOptional": "Опционално", + "@welcomeOptional": {}, + "itemCount": "{count, plural, =1{{count} обект} few{{count} обекта} other{{count} обекти}}", + "@itemCount": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "focalLength": "{length} мм", + "@focalLength": { + "placeholders": { + "length": { + "type": "String", + "example": "5.4" + } + } + }, + "applyButtonLabel": "ПРИЕМАМ", + "@applyButtonLabel": {}, + "nextButtonLabel": "СЛЕДВАЩ", + "@nextButtonLabel": {}, + "showButtonLabel": "ПОКАЗВАНЕ", + "@showButtonLabel": {}, + "applyTooltip": "Приеми", + "@applyTooltip": {}, + "cancelTooltip": "Отмяна", + "@cancelTooltip": {}, + "changeTooltip": "Промени", + "@changeTooltip": {}, + "previousTooltip": "Предишен", + "@previousTooltip": {}, + "showTooltip": "Покажи", + "@showTooltip": {}, + "pickTooltip": "Изберете", + "@pickTooltip": {}, + "sourceStateLoading": "Зареждане", + "@sourceStateLoading": {}, + "sourceStateLocatingPlaces": "Местоположение на места", + "@sourceStateLocatingPlaces": {}, + "chipActionDelete": "Изтрий", + "@chipActionDelete": {}, + "chipActionShowCollection": "Показване в колекцията", + "@chipActionShowCollection": {}, + "chipActionGoToCountryPage": "Показване в държави", + "@chipActionGoToCountryPage": {}, + "chipActionGoToPlacePage": "Покажи в места", + "@chipActionGoToPlacePage": {}, + "chipActionGoToExplorerPage": "Показване в Експлорър", + "@chipActionGoToExplorerPage": {}, + "chipActionFilterOut": "Изключи", + "@chipActionFilterOut": {}, + "chipActionFilterIn": "Включи", + "@chipActionFilterIn": {}, + "chipActionLock": "Заключи", + "@chipActionLock": {}, + "chipActionUnpin": "Откачи от закачени", + "@chipActionUnpin": {}, + "chipActionSetCover": "Задайте тапет", + "@chipActionSetCover": {}, + "chipActionShowCountryStates": "Показване на държава", + "@chipActionShowCountryStates": {}, + "chipActionCreateAlbum": "Създайте албум", + "@chipActionCreateAlbum": {}, + "chipActionCreateVault": "Създаване на трезор", + "@chipActionCreateVault": {}, + "chipActionConfigureVault": "Настройване на трезор", + "@chipActionConfigureVault": {}, + "entryActionCopyToClipboard": "Копирайте в клипборд", + "@entryActionCopyToClipboard": {}, + "entryActionDelete": "Изтриване", + "@entryActionDelete": {}, + "entryActionExport": "Експортиране", + "@entryActionExport": {}, + "entryActionInfo": "Информация", + "@entryActionInfo": {}, + "entryActionRestore": "Възстановяване", + "@entryActionRestore": {}, + "entryActionRotateCCW": "Завъртете обратно на часовниковата стрелка", + "@entryActionRotateCCW": {}, + "entryActionRotateCW": "Завъртете по посока на часовниковата стрелка", + "@entryActionRotateCW": {}, + "entryActionFlip": "Хоризонтално превъртане", + "@entryActionFlip": {}, + "entryActionPrint": "Печат", + "@entryActionPrint": {}, + "entryActionShare": "Споделяне", + "@entryActionShare": {}, + "entryActionShareImageOnly": "Споделете само изображение", + "@entryActionShareImageOnly": {}, + "entryActionShareVideoOnly": "Сооделяне само на видео", + "@entryActionShareVideoOnly": {}, + "entryActionViewSource": "Вижте източника", + "@entryActionViewSource": {}, + "entryActionViewMotionPhotoVideo": "Отвори видео", + "@entryActionViewMotionPhotoVideo": {}, + "entryActionEdit": "Редактиране", + "@entryActionEdit": {}, + "entryActionOpen": "Отвори с", + "@entryActionOpen": {}, + "entryActionSetAs": "Задай като", + "@entryActionSetAs": {}, + "entryActionCast": "Излъчване", + "@entryActionCast": {}, + "entryActionOpenMap": "Покажи в приложение за карти", + "@entryActionOpenMap": {}, + "entryActionRemoveFavourite": "Премахни от любими", + "@entryActionRemoveFavourite": {}, + "videoActionMute": "Изключи звук", + "@videoActionMute": {}, + "videoActionShowPreviousFrame": "Покажи предишния кадър", + "@videoActionShowPreviousFrame": {}, + "videoActionSelectStreams": "Изберете песни", + "@videoActionSelectStreams": {}, + "videoActionSetSpeed": "Скорост на възпроизвеждане", + "@videoActionSetSpeed": {}, + "videoActionABRepeat": "Повторете от А до Б", + "@videoActionABRepeat": {}, + "videoRepeatActionSetStart": "Задайте начало", + "@videoRepeatActionSetStart": {}, + "videoRepeatActionSetEnd": "Задайте край", + "@videoRepeatActionSetEnd": {}, + "viewerActionSettings": "Настройки", + "@viewerActionSettings": {}, + "viewerActionLock": "Блокирай визуализатора", + "@viewerActionLock": {}, + "viewerActionUnlock": "Отблокирай визуализатора", + "@viewerActionUnlock": {}, + "slideshowActionResume": "Продължи", + "@slideshowActionResume": {}, + "slideshowActionShowInCollection": "Покажи в Колекции", + "@slideshowActionShowInCollection": {}, + "entryInfoActionEditDate": "Променете дата и час", + "@entryInfoActionEditDate": {}, + "entryInfoActionEditLocation": "Променете местоположение", + "@entryInfoActionEditLocation": {}, + "entryInfoActionEditRating": "Променяте рейтинга", + "@entryInfoActionEditRating": {}, + "entryInfoActionRemoveMetadata": "Изтриване на метаданни", + "@entryInfoActionRemoveMetadata": {}, + "entryInfoActionExportMetadata": "Експортиране на метаданни", + "@entryInfoActionExportMetadata": {}, + "entryInfoActionRemoveLocation": "Премахване на местоположение", + "@entryInfoActionRemoveLocation": {}, + "editorActionTransform": "Трансформиране", + "@editorActionTransform": {}, + "editorTransformCrop": "Изрязване", + "@editorTransformCrop": {}, + "editorTransformRotate": "Завъртане", + "@editorTransformRotate": {}, + "cropAspectRatioOriginal": "Оригинал", + "@cropAspectRatioOriginal": {}, + "filterAspectRatioLandscapeLabel": "Хоризонтално", + "@filterAspectRatioLandscapeLabel": {}, + "filterAspectRatioPortraitLabel": "Вертикално", + "@filterAspectRatioPortraitLabel": {}, + "filterBinLabel": "Кошче", + "@filterBinLabel": {}, + "filterFavouriteLabel": "Любими", + "@filterFavouriteLabel": {}, + "filterNoDateLabel": "Без дата", + "@filterNoDateLabel": {}, + "filterNoAddressLabel": "Без адрес", + "@filterNoAddressLabel": {}, + "filterLocatedLabel": "С местоположение", + "@filterLocatedLabel": {}, + "filterNoRatingLabel": "Без рейтинг", + "@filterNoRatingLabel": {}, + "filterTaggedLabel": "С тагове", + "@filterTaggedLabel": {}, + "filterNoTagLabel": "Без тагове", + "@filterNoTagLabel": {}, + "filterOnThisDayLabel": "Днес", + "@filterOnThisDayLabel": {}, + "filterRecentlyAddedLabel": "Наскоро добавен", + "@filterRecentlyAddedLabel": {}, + "filterRatingRejectedLabel": "Отхвърлени", + "@filterRatingRejectedLabel": {}, + "filterTypeAnimatedLabel": "Анимиран", + "@filterTypeAnimatedLabel": {}, + "filterTypeMotionPhotoLabel": "Снимка с движение", + "@filterTypeMotionPhotoLabel": {}, + "filterTypePanoramaLabel": "Панорама", + "@filterTypePanoramaLabel": {}, + "filterTypeRawLabel": "RAW", + "@filterTypeRawLabel": {}, + "filterTypeSphericalVideoLabel": "360° Видео", + "@filterTypeSphericalVideoLabel": {}, + "filterTypeGeotiffLabel": "GeoTIFF", + "@filterTypeGeotiffLabel": {}, + "filterMimeImageLabel": "Изображение", + "@filterMimeImageLabel": {}, + "filterMimeVideoLabel": "Видео", + "@filterMimeVideoLabel": {}, + "accessibilityAnimationsRemove": "Предотвратяване ефектите на екрана", + "@accessibilityAnimationsRemove": {}, + "accessibilityAnimationsKeep": "Запазване на екранните ефекти", + "@accessibilityAnimationsKeep": {}, + "albumTierPinned": "Закачени", + "@albumTierPinned": {}, + "albumTierSpecial": "Стандартни", + "@albumTierSpecial": {}, + "albumTierApps": "Приложения", + "@albumTierApps": {}, + "albumTierRegular": "Други", + "@albumTierRegular": {}, + "coordinateFormatDms": "Градуси, минути и секунди", + "@coordinateFormatDms": {}, + "coordinateFormatDecimal": "Десятични градуси", + "@coordinateFormatDecimal": {}, + "coordinateDms": "{coordinate} {direction}", + "@coordinateDms": { + "placeholders": { + "coordinate": { + "type": "String", + "example": "38° 41′ 47.72″" + }, + "direction": { + "type": "String", + "example": "S" + } + } + }, + "coordinateDmsEast": "Изток", + "@coordinateDmsEast": {}, + "coordinateDmsNorth": "Север", + "@coordinateDmsNorth": {}, + "coordinateDmsSouth": "Юг", + "@coordinateDmsSouth": {}, + "coordinateDmsWest": "Запад", + "@coordinateDmsWest": {}, + "displayRefreshRatePreferHighest": "Предпочете на висока честота на опресняване на дисплея", + "@displayRefreshRatePreferHighest": {}, + "displayRefreshRatePreferLowest": "Предпочете на ниска честота на опресняване на дисплея", + "@displayRefreshRatePreferLowest": {}, + "keepScreenOnNever": "Никога", + "@keepScreenOnNever": {}, + "keepScreenOnViewerOnly": "Само във визуализатора", + "@keepScreenOnViewerOnly": {}, + "keepScreenOnAlways": "Винаги", + "@keepScreenOnAlways": {}, + "lengthUnitPixel": "Пиксели", + "@lengthUnitPixel": {}, + "lengthUnitPercent": "%", + "@lengthUnitPercent": {}, + "mapStyleGoogleHybrid": "Google Карти (хибрид)", + "@mapStyleGoogleHybrid": {}, + "mapStyleGoogleTerrain": "Google Карти (терен)", + "@mapStyleGoogleTerrain": {}, + "mapStyleOsmLiberty": "OSM Liberty", + "@mapStyleOsmLiberty": {}, + "mapStyleOpenTopoMap": "OpenTopoMap", + "@mapStyleOpenTopoMap": {}, + "mapStyleOsmHot": "Humanitarian OSM", + "@mapStyleOsmHot": {}, + "mapStyleStamenWatercolor": "Stamen Акварел", + "@mapStyleStamenWatercolor": {}, + "maxBrightnessAlways": "Винаги", + "@maxBrightnessAlways": {}, + "nameConflictStrategyRename": "Преименувай", + "@nameConflictStrategyRename": {}, + "nameConflictStrategySkip": "Пропусни", + "@nameConflictStrategySkip": {}, + "overlayHistogramNone": "None", + "@overlayHistogramNone": {}, + "overlayHistogramLuminance": "Яркост", + "@overlayHistogramLuminance": {}, + "themeBrightnessLight": "Светла", + "@themeBrightnessLight": {}, + "themeBrightnessDark": "Тъмна", + "@themeBrightnessDark": {}, + "themeBrightnessBlack": "Черна", + "@themeBrightnessBlack": {}, + "unitSystemMetric": "Метрични", + "@unitSystemMetric": {}, + "unitSystemImperial": "Имперски", + "@unitSystemImperial": {}, + "vaultLockTypePattern": "Графичен ключ", + "@vaultLockTypePattern": {}, + "vaultLockTypePin": "PIN-код", + "@vaultLockTypePin": {}, + "settingsVideoEnablePip": "Картина в картината", + "@settingsVideoEnablePip": {}, + "videoControlsPlayOutside": "Отвори с друг видео плеър", + "@videoControlsPlayOutside": {}, + "videoLoopModeNever": "Никога", + "@videoLoopModeNever": {}, + "videoLoopModeAlways": "Винаги", + "@videoLoopModeAlways": {}, + "videoPlaybackSkip": "Пропусни", + "@videoPlaybackSkip": {}, + "videoPlaybackMuted": "Пусни без звук", + "@videoPlaybackMuted": {}, + "videoPlaybackWithSound": "Пусни със звук", + "@videoPlaybackWithSound": {}, + "videoResumptionModeNever": "Никога", + "@videoResumptionModeNever": {}, + "viewerTransitionSlide": "Слайд", + "@viewerTransitionSlide": {}, + "viewerTransitionParallax": "Паралакс", + "@viewerTransitionParallax": {}, + "viewerTransitionFade": "Избледняване на прехода", + "@viewerTransitionFade": {}, + "wallpaperTargetHome": "Начален екран", + "@wallpaperTargetHome": {}, + "wallpaperTargetLock": "Заключен екран", + "@wallpaperTargetLock": {}, + "wallpaperTargetHomeLock": "Начален и заключен екран", + "@wallpaperTargetHomeLock": {}, + "widgetDisplayedItemRandom": "Произволни", + "@widgetDisplayedItemRandom": {}, + "widgetDisplayedItemMostRecent": "Чести", + "@widgetDisplayedItemMostRecent": {}, + "widgetOpenPageHome": "Отвори начален екран", + "@widgetOpenPageHome": {}, + "widgetOpenPageViewer": "Преглед на текущия", + "@widgetOpenPageViewer": {}, + "widgetTapUpdateWidget": "Обнови Уиджет", + "@widgetTapUpdateWidget": {}, + "storageVolumeDescriptionFallbackPrimary": "Вътрешна памет", + "@storageVolumeDescriptionFallbackPrimary": {}, + "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": {}, + "addShortcutDialogLabel": "Етикет за пряк път", + "@addShortcutDialogLabel": {}, + "noMatchingAppDialogMessage": "Няма приложения, които да могат да се справят с това.", + "@noMatchingAppDialogMessage": {}, + "deleteEntriesConfirmationDialogMessage": "{count, plural, =1{Изтриване на този елемент?} other{Изтриване на тези {count} елемента?}}", + "@deleteEntriesConfirmationDialogMessage": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "moveUndatedConfirmationDialogSetDate": "Запазване дата", + "@moveUndatedConfirmationDialogSetDate": {}, + "videoResumeDialogMessage": "Да възобновени възпроизвеждането от {time}?", + "@videoResumeDialogMessage": { + "placeholders": { + "time": { + "type": "String", + "example": "13:37" + } + } + }, + "videoStartOverButtonLabel": "ЗАПОЧНИ ОТНАЧАЛО", + "@videoStartOverButtonLabel": {}, + "videoResumeButtonLabel": "ПРОДЪЛЖИ", + "@videoResumeButtonLabel": {}, + "setCoverDialogLatest": "Последен обект", + "@setCoverDialogLatest": {}, + "setCoverDialogCustom": "Персонален", + "@setCoverDialogCustom": {}, + "newAlbumDialogTitle": "Нов албум", + "@newAlbumDialogTitle": {}, + "newAlbumDialogNameLabel": "Име на албума", + "@newAlbumDialogNameLabel": {}, + "newAlbumDialogAlbumAlreadyExistsHelper": "Албумът вече съществува", + "@newAlbumDialogAlbumAlreadyExistsHelper": {}, + "newAlbumDialogNameLabelAlreadyExistsHelper": "Директорията вече съществува", + "@newAlbumDialogNameLabelAlreadyExistsHelper": {}, + "newAlbumDialogStorageLabel": "Хранилище:", + "@newAlbumDialogStorageLabel": {}, + "newVaultDialogTitle": "Нов трезор", + "@newVaultDialogTitle": {}, + "vaultDialogLockModeWhenScreenOff": "Заключване при изключен екран", + "@vaultDialogLockModeWhenScreenOff": {}, + "vaultDialogLockTypeLabel": "Вид заключване", + "@vaultDialogLockTypeLabel": {}, + "patternDialogEnter": "Въведете шаблон", + "@patternDialogEnter": {}, + "patternDialogConfirm": "Потвърдете шаблон", + "@patternDialogConfirm": {}, + "pinDialogEnter": "Въведете пин-код", + "@pinDialogEnter": {}, + "pinDialogConfirm": "Потвърдете PIN", + "@pinDialogConfirm": {}, + "passwordDialogEnter": "Въведете парола", + "@passwordDialogEnter": {}, + "passwordDialogConfirm": "Потвърдете парола", + "@passwordDialogConfirm": {}, + "authenticateToUnlockVault": "Удостоверете се, за да отключите трезора", + "@authenticateToUnlockVault": {}, + "vaultBinUsageDialogMessage": "Някой от трезорите използват кошче.", + "@vaultBinUsageDialogMessage": {}, + "renameAlbumDialogLabel": "Ново име", + "@renameAlbumDialogLabel": {}, + "renameEntrySetPagePatternFieldLabel": "Шаблон наименуван", + "@renameEntrySetPagePatternFieldLabel": {}, + "renameEntrySetPageInsertTooltip": "Вмъкване на поле", + "@renameEntrySetPageInsertTooltip": {}, + "renameEntrySetPagePreviewSectionTitle": "Преглед", + "@renameEntrySetPagePreviewSectionTitle": {}, + "renameProcessorCounter": "Брояч", + "@renameProcessorCounter": {}, + "renameProcessorHash": "Хеш", + "@renameProcessorHash": {}, + "renameProcessorName": "Име", + "@renameProcessorName": {}, + "exportEntryDialogFormat": "Формат:", + "@exportEntryDialogFormat": {}, + "exportEntryDialogWidth": "Ширина", + "@exportEntryDialogWidth": {}, + "exportEntryDialogHeight": "Височина", + "@exportEntryDialogHeight": {}, + "exportEntryDialogQuality": "Качество", + "@exportEntryDialogQuality": {}, + "exportEntryDialogWriteMetadata": "Запис на метаданни", + "@exportEntryDialogWriteMetadata": {}, + "renameEntryDialogLabel": "Ново име", + "@renameEntryDialogLabel": {}, + "editEntryDialogCopyFromItem": "Копиране от друг обект", + "@editEntryDialogCopyFromItem": {}, + "editEntryDialogTargetFieldsHeader": "Полета за промяна", + "@editEntryDialogTargetFieldsHeader": {}, + "editEntryDateDialogTitle": "Дата и час", + "@editEntryDateDialogTitle": {}, + "editEntryDateDialogSetCustom": "Задайте персонализирана дата", + "@editEntryDateDialogSetCustom": {}, + "editEntryDateDialogCopyField": "Копирайте от друга дата", + "@editEntryDateDialogCopyField": {}, + "editEntryDateDialogExtractFromTitle": "Извличане от заглавието", + "@editEntryDateDialogExtractFromTitle": {}, + "editEntryDateDialogShift": "Преместване с", + "@editEntryDateDialogShift": {}, + "durationDialogHours": "Часове", + "@durationDialogHours": {}, + "durationDialogMinutes": "Минути", + "@durationDialogMinutes": {}, + "durationDialogSeconds": "Секунди", + "@durationDialogSeconds": {}, + "editEntryLocationDialogTitle": "Местоположение", + "@editEntryLocationDialogTitle": {}, + "editEntryLocationDialogSetCustom": "Редактиране на местоположение", + "@editEntryLocationDialogSetCustom": {}, + "editEntryLocationDialogChooseOnMap": "Избери на картата", + "@editEntryLocationDialogChooseOnMap": {}, + "editEntryLocationDialogLongitude": "Дължина", + "@editEntryLocationDialogLongitude": {}, + "removeEntryMetadataDialogTitle": "Премахване на метаданни", + "@removeEntryMetadataDialogTitle": {}, + "removeEntryMetadataDialogMore": "Още", + "@removeEntryMetadataDialogMore": {}, + "videoSpeedDialogLabel": "Скорост на възпроизвеждане", + "@videoSpeedDialogLabel": {}, + "videoStreamSelectionDialogAudio": "Аудио", + "@videoStreamSelectionDialogAudio": {}, + "videoStreamSelectionDialogOff": "Изключено", + "@videoStreamSelectionDialogOff": {}, + "videoStreamSelectionDialogTrack": "Пътечка", + "@videoStreamSelectionDialogTrack": {}, + "videoStreamSelectionDialogNoSelection": "Няма други пътечки.", + "@videoStreamSelectionDialogNoSelection": {}, + "genericSuccessFeedback": "Готово!", + "@genericSuccessFeedback": {}, + "genericFailureFeedback": "Неуспешно", + "@genericFailureFeedback": {}, + "tooManyItemsErrorDialogMessage": "Опитай отново с по-малко елементи.", + "@tooManyItemsErrorDialogMessage": {}, + "menuActionConfigureView": "Вид", + "@menuActionConfigureView": {}, + "menuActionSelectNone": "Не избирай нищо", + "@menuActionSelectNone": {}, + "menuActionSlideshow": "Слайд-шоу", + "@menuActionSlideshow": {}, + "viewDialogSortSectionTitle": "Сортиране", + "@viewDialogSortSectionTitle": {}, + "viewDialogLayoutSectionTitle": "Оформление", + "@viewDialogLayoutSectionTitle": {}, + "viewDialogReverseSortOrder": "Сортиране в обратен ред", + "@viewDialogReverseSortOrder": {}, + "tileLayoutMosaic": "Мозайка", + "@tileLayoutMosaic": {}, + "tileLayoutGrid": "Решетка", + "@tileLayoutGrid": {}, + "tileLayoutList": "Списък", + "@tileLayoutList": {}, + "castDialogTitle": "Устройство за излъчване", + "@castDialogTitle": {}, + "coverDialogTabCover": "Тапет", + "@coverDialogTabCover": {}, + "coverDialogTabApp": "Приложение", + "@coverDialogTabApp": {}, + "coverDialogTabColor": "Цвят", + "@coverDialogTabColor": {}, + "appPickDialogTitle": "Изберете приложение", + "@appPickDialogTitle": {}, + "aboutPageTitle": "За нас", + "@aboutPageTitle": {}, + "aboutLinkLicense": "Лицензи", + "@aboutLinkLicense": {}, + "aboutLinkPolicy": "Политика за личните данни", + "@aboutLinkPolicy": {}, + "aboutBugSectionTitle": "Съобщи за грешка", + "@aboutBugSectionTitle": {}, + "aboutBugSaveLogInstruction": "Запазете лог във файл", + "@aboutBugSaveLogInstruction": {}, + "aboutBugCopyInfoInstruction": "Копирай системата информация", + "@aboutBugCopyInfoInstruction": {}, + "aboutBugReportInstruction": "Изпратете съобщение за грешка на GitHub, заедно с лог и системна информация", + "@aboutBugReportInstruction": {}, + "aboutBugReportButton": "Изпрати", + "@aboutBugReportButton": {}, + "aboutDataUsageData": "Данни", + "@aboutDataUsageData": {}, + "aboutDataUsageCache": "Кеш", + "@aboutDataUsageCache": {}, + "aboutDataUsageDatabase": "База данни", + "@aboutDataUsageDatabase": {}, + "aboutDataUsageMisc": "Разни", + "@aboutDataUsageMisc": {}, + "aboutDataUsageInternal": "Вътрешни", + "@aboutDataUsageInternal": {}, + "aboutDataUsageExternal": "Външни", + "@aboutDataUsageExternal": {}, + "aboutDataUsageClearCache": "Изчисти кеш", + "@aboutDataUsageClearCache": {}, + "aboutCreditsSectionTitle": "Благодарности", + "@aboutCreditsSectionTitle": {}, + "aboutCreditsWorldAtlas1": "Това приложение използва файл TopoJSON от", + "@aboutCreditsWorldAtlas1": {}, + "aboutCreditsWorldAtlas2": "под ISC License.", + "@aboutCreditsWorldAtlas2": {}, + "aboutLicensesSectionTitle": "Лиценз с отворен код", + "@aboutLicensesSectionTitle": {}, + "aboutLicensesAndroidLibrariesSectionTitle": "Библиотеки Android", + "@aboutLicensesAndroidLibrariesSectionTitle": {}, + "aboutLicensesFlutterPluginsSectionTitle": "Flutter плъгини", + "@aboutLicensesFlutterPluginsSectionTitle": {}, + "aboutLicensesFlutterPackagesSectionTitle": "Flutter пакети", + "@aboutLicensesFlutterPackagesSectionTitle": {}, + "aboutLicensesDartPackagesSectionTitle": "Dart пакети", + "@aboutLicensesDartPackagesSectionTitle": {}, + "aboutLicensesShowAllButtonLabel": "Покажи всички лицензи", + "@aboutLicensesShowAllButtonLabel": {}, + "policyPageTitle": "Политика за конфиденциалност", + "@policyPageTitle": {}, + "collectionSelectPageTitle": "Изберете елементи", + "@collectionSelectPageTitle": {}, + "collectionActionShowTitleSearch": "Покажи филтър на заглавията", + "@collectionActionShowTitleSearch": {}, + "collectionActionAddShortcut": "Добави кратък път", + "@collectionActionAddShortcut": {}, + "collectionActionEmptyBin": "Изпразни кошчето", + "@collectionActionEmptyBin": {}, + "collectionActionCopy": "Копирай в албум", + "@collectionActionCopy": {}, + "collectionActionMove": "Премести в албум", + "@collectionActionMove": {}, + "collectionActionEdit": "Промени", + "@collectionActionEdit": {}, + "collectionSearchTitlesHintText": "Търси заглавия", + "@collectionSearchTitlesHintText": {}, + "collectionGroupAlbum": "По албум", + "@collectionGroupAlbum": {}, + "collectionGroupMonth": "По месец", + "@collectionGroupMonth": {}, + "collectionGroupDay": "По дни", + "@collectionGroupDay": {}, + "collectionGroupNone": "Не групирай", + "@collectionGroupNone": {}, + "sectionUnknown": "Неизвестно", + "@sectionUnknown": {}, + "dateToday": "Днес", + "@dateToday": {}, + "dateYesterday": "Вчера", + "@dateYesterday": {}, + "collectionRenameFailureFeedback": "{count, plural, =1{Неуспешно преименуване на 1 елемент} other{Неуспешно преименуване на {count} елемента}}", + "@collectionRenameFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionCopySuccessFeedback": "{count, plural, =1{Копира 1 елемент} other{Копира {count} елемента}}", + "@collectionCopySuccessFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionRenameSuccessFeedback": "{count, plural, =1{Преименуван 1 елемент} other{Преименувани {count} елемента}}", + "@collectionRenameSuccessFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionEmptyFavourites": "Няма любими", + "@collectionEmptyFavourites": {}, + "collectionEmptyVideos": "Няма видеа", + "@collectionEmptyVideos": {}, + "collectionEmptyImages": "Няма изображения", + "@collectionEmptyImages": {}, + "collectionEmptyGrantAccessButtonLabel": "Упълномощете достъп", + "@collectionEmptyGrantAccessButtonLabel": {}, + "collectionSelectSectionTooltip": "Изберете раздел", + "@collectionSelectSectionTooltip": {}, + "collectionDeselectSectionTooltip": "Премахнете избрания раздел", + "@collectionDeselectSectionTooltip": {}, + "drawerAboutButton": "За нас", + "@drawerAboutButton": {}, + "drawerSettingsButton": "Настройки", + "@drawerSettingsButton": {}, + "drawerCollectionAll": "Цялата колекция", + "@drawerCollectionAll": {}, + "drawerCollectionFavourites": "Любими", + "@drawerCollectionFavourites": {}, + "drawerCollectionImages": "Изображения", + "@drawerCollectionImages": {}, + "drawerCollectionAnimated": "Анимиран", + "@drawerCollectionAnimated": {}, + "drawerCollectionMotionPhotos": "Снимки с движения", + "@drawerCollectionMotionPhotos": {}, + "drawerCollectionPanoramas": "Панорами", + "@drawerCollectionPanoramas": {}, + "drawerCollectionRaws": "RAW", + "@drawerCollectionRaws": {}, + "drawerAlbumPage": "Албуми", + "@drawerAlbumPage": {}, + "drawerTagPage": "Тагове", + "@drawerTagPage": {}, + "sortByDate": "По дати", + "@sortByDate": {}, + "sortByName": "По имена", + "@sortByName": {}, + "sortByItemCount": "По брой елементи", + "@sortByItemCount": {}, + "sortBySize": "По размер", + "@sortBySize": {}, + "sortByAlbumFileName": "По име на албум и файлови имена", + "@sortByAlbumFileName": {}, + "sortByRating": "По рейтинг", + "@sortByRating": {}, + "sortByDuration": "По продължителност", + "@sortByDuration": {}, + "sortOrderNewestFirst": "Първо най-новите", + "@sortOrderNewestFirst": {}, + "sortOrderOldestFirst": "Първо най-старите", + "@sortOrderOldestFirst": {}, + "sortOrderAtoZ": "От А до Я", + "@sortOrderAtoZ": {}, + "sortOrderZtoA": "От Я до А", + "@sortOrderZtoA": {}, + "sortOrderHighestFirst": "Първо най-високите", + "@sortOrderHighestFirst": {}, + "sortOrderLowestFirst": "Първо най-ниските", + "@sortOrderLowestFirst": {}, + "sortOrderLargestFirst": "Първо най-големите", + "@sortOrderLargestFirst": {}, + "sortOrderSmallestFirst": "Първо най-малките", + "@sortOrderSmallestFirst": {}, + "sortOrderLongestFirst": "Първо най-дългите", + "@sortOrderLongestFirst": {}, + "albumGroupType": "По тип", + "@albumGroupType": {}, + "albumGroupVolume": "По обем на съхранение", + "@albumGroupVolume": {}, + "albumGroupNone": "Без групиране", + "@albumGroupNone": {}, + "albumMimeTypeMixed": "Разни", + "@albumMimeTypeMixed": {}, + "albumPickPageTitleCopy": "Копирай в албум", + "@albumPickPageTitleCopy": {}, + "albumPickPageTitleExport": "Експортирай в албум", + "@albumPickPageTitleExport": {}, + "albumPickPageTitleMove": "Премести в албум", + "@albumPickPageTitleMove": {}, + "albumPickPageTitlePick": "Избери албум", + "@albumPickPageTitlePick": {}, + "albumCamera": "Камера", + "@albumCamera": {}, + "albumDownload": "Изтегляне", + "@albumDownload": {}, + "albumScreenshots": "Екранни снимки", + "@albumScreenshots": {}, + "albumScreenRecordings": "Екранни записи", + "@albumScreenRecordings": {}, + "albumVideoCaptures": "Видеозаписи", + "@albumVideoCaptures": {}, + "albumPageTitle": "Албуми", + "@albumPageTitle": {}, + "albumEmpty": "Няма албуми", + "@albumEmpty": {}, + "createAlbumButtonLabel": "СЪЗДАЙ", + "@createAlbumButtonLabel": {}, + "countryPageTitle": "Държави", + "@countryPageTitle": {}, + "countryEmpty": "Няма държави", + "@countryEmpty": {}, + "statePageTitle": "Региони", + "@statePageTitle": {}, + "stateEmpty": "Няма региони", + "@stateEmpty": {}, + "placePageTitle": "Места", + "@placePageTitle": {}, + "placeEmpty": "Няма места", + "@placeEmpty": {}, + "tagPageTitle": "Тагове", + "@tagPageTitle": {}, + "tagEmpty": "Без тагове", + "@tagEmpty": {}, + "binPageTitle": "Кошче", + "@binPageTitle": {}, + "selectStorageVolumeDialogTitle": "Избери място за съхранение", + "@selectStorageVolumeDialogTitle": {}, + "searchCollectionFieldHint": "Търсене по колекции", + "@searchCollectionFieldHint": {}, + "searchRecentSectionTitle": "Скорошни", + "@searchRecentSectionTitle": {}, + "searchDateSectionTitle": "Дата", + "@searchDateSectionTitle": {}, + "searchAlbumsSectionTitle": "Албуми", + "@searchAlbumsSectionTitle": {}, + "searchCountriesSectionTitle": "Държави", + "@searchCountriesSectionTitle": {}, + "searchStatesSectionTitle": "Региони", + "@searchStatesSectionTitle": {}, + "searchPlacesSectionTitle": "Места", + "@searchPlacesSectionTitle": {}, + "searchTagsSectionTitle": "Тагове", + "@searchTagsSectionTitle": {}, + "searchRatingSectionTitle": "Рейтинги", + "@searchRatingSectionTitle": {}, + "searchMetadataSectionTitle": "Метаданни", + "@searchMetadataSectionTitle": {}, + "settingsPageTitle": "Настройки", + "@settingsPageTitle": {}, + "settingsSystemDefault": "По подразбиране", + "@settingsSystemDefault": {}, + "settingsDisabled": "Изключено", + "@settingsDisabled": {}, + "settingsSearchFieldLabel": "Търсене настройки", + "@settingsSearchFieldLabel": {}, + "settingsSearchEmpty": "Не са открити настройки", + "@settingsSearchEmpty": {}, + "settingsActionExportDialogTitle": "Експорт", + "@settingsActionExportDialogTitle": {}, + "settingsActionImportDialogTitle": "Импорт", + "@settingsActionImportDialogTitle": {}, + "appExportFavourites": "Любими", + "@appExportFavourites": {}, + "appExportSettings": "Настройки", + "@appExportSettings": {}, + "settingsHomeTile": "Home", + "@settingsHomeTile": {}, + "settingsHomeDialogTitle": "Home", + "@settingsHomeDialogTitle": {}, + "setHomeCustom": "Custom", + "@setHomeCustom": {}, + "settingsKeepScreenOnTile": "Запази екрана включен", + "@settingsKeepScreenOnTile": {}, + "settingsKeepScreenOnDialogTitle": "Запази екрана включен", + "@settingsKeepScreenOnDialogTitle": {}, + "settingsDoubleBackExit": "Два пъти „назад“ за изход", + "@settingsDoubleBackExit": {}, + "settingsConfirmationTile": "Диалог за потвърждение", + "@settingsConfirmationTile": {}, + "settingsConfirmationDialogTitle": "Диалогови прозорци за потвърждение", + "@settingsConfirmationDialogTitle": {}, + "settingsConfirmationBeforeMoveToBinItems": "Попитай преди да преместиш в кошчето", + "@settingsConfirmationBeforeMoveToBinItems": {}, + "settingsConfirmationAfterMoveToBinItems": "Показване на съобщение след преместване на елементи в кошчето", + "@settingsConfirmationAfterMoveToBinItems": {}, + "settingsNavigationDrawerTile": "Навигационно меню", + "@settingsNavigationDrawerTile": {}, + "settingsNavigationDrawerEditorPageTitle": "Навигационно меню", + "@settingsNavigationDrawerEditorPageTitle": {}, + "settingsNavigationDrawerBanner": "Докоснете и задръжте, за да преместите и пренаредите елементи от менюто.", + "@settingsNavigationDrawerBanner": {}, + "appName": "Aves", + "@appName": {}, + "nextTooltip": "Следващ", + "@nextTooltip": {}, + "entryInfoActionEditTitleDescription": "Променете заглавието и описанието", + "@entryInfoActionEditTitleDescription": {}, + "albumTierVaults": "Трезори", + "@albumTierVaults": {}, + "keepScreenOnVideoPlayback": "По време на възпроизвеждане на видео", + "@keepScreenOnVideoPlayback": {}, + "mapStyleGoogleNormal": "Google Карти", + "@mapStyleGoogleNormal": {}, + "maxBrightnessNever": "Никога", + "@maxBrightnessNever": {}, + "entryInfoActionEditTags": "Променяне на тагове", + "@entryInfoActionEditTags": {}, + "filterNoTitleLabel": "Наименувани", + "@filterNoTitleLabel": {}, + "filterNoLocationLabel": "Без местоположение", + "@filterNoLocationLabel": {}, + "albumTierNew": "Нови", + "@albumTierNew": {}, + "chipActionRename": "Преименувайте", + "@chipActionRename": {}, + "hideTooltip": "Скрий", + "@hideTooltip": {}, + "saveTooltip": "Запишете", + "@saveTooltip": {}, + "actionRemove": "Премахване", + "@actionRemove": {}, + "resetTooltip": "Нулиране", + "@resetTooltip": {}, + "stopTooltip": "Стоп", + "@stopTooltip": {}, + "chipActionPin": "Закачете отгоре", + "@chipActionPin": {}, + "doubleBackExitMessage": "Натиснете отново „Назад“, за да излезете.", + "@doubleBackExitMessage": {}, + "doNotAskAgain": "Не питайте отново", + "@doNotAskAgain": {}, + "sourceStateLocatingCountries": "Местоположение на държави", + "@sourceStateLocatingCountries": {}, + "chipActionGoToTagPage": "Показване в тагове", + "@chipActionGoToTagPage": {}, + "sourceStateCataloguing": "Каталогизация", + "@sourceStateCataloguing": {}, + "chipActionHide": "Скрий", + "@chipActionHide": {}, + "entryActionConvert": "Конвертиране", + "@entryActionConvert": {}, + "entryActionRename": "Преименуване", + "@entryActionRename": {}, + "welcomeTermsToggle": "Съгласявам се с правилата и условията", + "@welcomeTermsToggle": {}, + "entryActionShowGeoTiffOnMap": "Покажи на картата", + "@entryActionShowGeoTiffOnMap": {}, + "columnCount": "{count, plural, =1{{count} колона} other{{count} колони}}", + "@columnCount": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "timeSeconds": "{count, plural, =1{{count} секунда} few{{count} секунди} other{{count} секунди}}", + "@timeSeconds": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "timeMinutes": "{count, plural, =1{{count} минута} few{{count} минути} other{{count} минути}}", + "@timeMinutes": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "entryActionConvertMotionPhotoToStillImage": "Конвертиране в статично изображение", + "@entryActionConvertMotionPhotoToStillImage": {}, + "deleteButtonLabel": "ИЗТРИЙ", + "@deleteButtonLabel": {}, + "timeDays": "{count, plural, =1{{count} ден} few{{count} дни} other{{count} дни}}", + "@timeDays": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "cropAspectRatioFree": "Свободни", + "@cropAspectRatioFree": {}, + "cropAspectRatioSquare": "Квадратно", + "@cropAspectRatioSquare": {}, + "settingsNavigationDrawerTabPages": "Страници", + "@settingsNavigationDrawerTabPages": {}, + "settingsNavigationDrawerAddAlbum": "Добави албум", + "@settingsNavigationDrawerAddAlbum": {}, + "settingsThumbnailSectionTitle": "Миниатюри", + "@settingsThumbnailSectionTitle": {}, + "settingsThumbnailOverlayTile": "Наслагване", + "@settingsThumbnailOverlayTile": {}, + "settingsThumbnailOverlayPageTitle": "Наслагване", + "@settingsThumbnailOverlayPageTitle": {}, + "settingsThumbnailShowHdrIcon": "Показване на HDR икона", + "@settingsThumbnailShowHdrIcon": {}, + "settingsThumbnailShowFavouriteIcon": "Показване на любима икона", + "@settingsThumbnailShowFavouriteIcon": {}, + "settingsThumbnailShowTagIcon": "Показване на иконата за таг", + "@settingsThumbnailShowTagIcon": {}, + "settingsThumbnailShowLocationIcon": "Показване на иконата за местоположение", + "@settingsThumbnailShowLocationIcon": {}, + "settingsThumbnailShowMotionPhotoIcon": "Показване на иконата на снимка с движение", + "@settingsThumbnailShowMotionPhotoIcon": {}, + "settingsThumbnailShowRawIcon": "Показване на необработена икона", + "@settingsThumbnailShowRawIcon": {}, + "settingsThumbnailShowVideoDuration": "Показване на продължителността на видеоклипа", + "@settingsThumbnailShowVideoDuration": {}, + "settingsCollectionQuickActionsTile": "Бързи действия", + "@settingsCollectionQuickActionsTile": {}, + "settingsCollectionQuickActionEditorPageTitle": "Бързи действия", + "@settingsCollectionQuickActionEditorPageTitle": {}, + "settingsCollectionQuickActionTabBrowsing": "Сърфиране", + "@settingsCollectionQuickActionTabBrowsing": {}, + "settingsCollectionQuickActionTabSelecting": "Избиране", + "@settingsCollectionQuickActionTabSelecting": {}, + "settingsCollectionBurstPatternsTile": "Burst patterns", + "@settingsCollectionBurstPatternsTile": {}, + "settingsCollectionBurstPatternsNone": "Няма", + "@settingsCollectionBurstPatternsNone": {}, + "settingsViewerSectionTitle": "Вюър", + "@settingsViewerSectionTitle": {}, + "settingsViewerGestureSideTapNext": "Докоснете ръбовете на екрана, за да покажете предишен/следващ елемент", + "@settingsViewerGestureSideTapNext": {}, + "settingsNavigationDrawerTabAlbums": "Албуми", + "@settingsNavigationDrawerTabAlbums": {}, + "settingsNavigationDrawerTabTypes": "Типове", + "@settingsNavigationDrawerTabTypes": {}, + "settingsThumbnailShowRating": "Покажи рейтинг", + "@settingsThumbnailShowRating": {}, + "settingsCollectionBrowsingQuickActionEditorBanner": "Докоснете и задръжте, за да преместите бутоните и избор кои действия да се показват при разглеждане на елементи.", + "@settingsCollectionBrowsingQuickActionEditorBanner": {}, + "settingsCollectionSelectionQuickActionEditorBanner": "Докоснете и задръжте, за да преместите бутоните и да изберете кои действия да се показват при избиране на елементи.", + "@settingsCollectionSelectionQuickActionEditorBanner": {}, + "chipActionGoToAlbumPage": "Покажи в албуми", + "@chipActionGoToAlbumPage": {}, + "configureVaultDialogTitle": "Конфигурирайте Трезор", + "@configureVaultDialogTitle": {}, + "genericDangerWarningDialogMessage": "Сигурен ли сте?", + "@genericDangerWarningDialogMessage": {}, + "drawerPlacePage": "Места", + "@drawerPlacePage": {}, + "settingsActionImport": "Импорт", + "@settingsActionImport": {}, + "settingsConfirmationBeforeMoveUndatedItems": "Попитайте, преди да преместите елементи без дата", + "@settingsConfirmationBeforeMoveUndatedItems": {}, + "settingsViewerUseCutout": "Използвай зоната на изрязване", + "@settingsViewerUseCutout": {}, + "settingsViewerMaximumBrightness": "Максимална яркост", + "@settingsViewerMaximumBrightness": {}, + "settingsMotionPhotoAutoPlay": "Автоматично възпроизвеждане на снимки с движение", + "@settingsMotionPhotoAutoPlay": {}, + "settingsViewerOverlayPageTitle": "Наслагване", + "@settingsViewerOverlayPageTitle": {}, + "settingsViewerShowHistogram": "Показвай хистограма", + "@settingsViewerShowHistogram": {}, + "settingsViewerShowOverlayOnOpening": "Показване при отваряне", + "@settingsViewerShowOverlayOnOpening": {}, + "settingsViewerShowMinimap": "Показване на миникарта", + "@settingsViewerShowMinimap": {}, + "settingsViewerShowInformation": "Показване на информация", + "@settingsViewerShowInformation": {}, + "settingsViewerQuickActionEditorAvailableButtonsSectionTitle": "Налични бутони", + "@settingsViewerQuickActionEditorAvailableButtonsSectionTitle": {}, + "settingsViewerQuickActionEmpty": "Без бутони", + "@settingsViewerQuickActionEmpty": {}, + "settingsViewerOverlayTile": "Наслагване", + "@settingsViewerOverlayTile": {}, + "settingsViewerShowRatingTags": "Показване на рейтинг и тагове", + "@settingsViewerShowRatingTags": {}, + "settingsViewerShowInformationSubtitle": "Показване на заглавие, дата, местоположение и др.", + "@settingsViewerShowInformationSubtitle": {}, + "settingsViewerSlideshowPageTitle": "Слайд-шоу", + "@settingsViewerSlideshowPageTitle": {}, + "settingsViewerSlideshowTile": "Слайд-шоу", + "@settingsViewerSlideshowTile": {}, + "settingsVideoSectionTitle": "Видео", + "@settingsVideoSectionTitle": {}, + "settingsVideoShowVideos": "Покажи видеа", + "@settingsVideoShowVideos": {}, + "settingsVideoPlaybackTile": "Възпроизвеждане", + "@settingsVideoPlaybackTile": {}, + "settingsVideoPlaybackPageTitle": "Възпроизвеждане", + "@settingsVideoPlaybackPageTitle": {}, + "settingsSlideshowVideoPlaybackDialogTitle": "Възпроизвеждане на видео", + "@settingsSlideshowVideoPlaybackDialogTitle": {}, + "settingsVideoPageTitle": "Видео настройки", + "@settingsVideoPageTitle": {}, + "settingsVideoLoopModeTile": "Цикличен режим", + "@settingsVideoLoopModeTile": {}, + "settingsVideoAutoPlay": "Автоматично възпроизвеждане", + "@settingsVideoAutoPlay": {}, + "settingsVideoBackgroundMode": "Фонов режим", + "@settingsVideoBackgroundMode": {}, + "settingsVideoBackgroundModeDialogTitle": "Фонов режим", + "@settingsVideoBackgroundModeDialogTitle": {}, + "settingsSubtitleThemeTile": "Субтитри", + "@settingsSubtitleThemeTile": {}, + "settingsSubtitleThemePageTitle": "Субтитри", + "@settingsSubtitleThemePageTitle": {}, + "settingsVideoGestureVerticalDragBrightnessVolume": "Плъзнете нагоре или надолу, за да регулирате яркостта/силата на звука", + "@settingsVideoGestureVerticalDragBrightnessVolume": {}, + "settingsSubtitleThemeTextAlignmentDialogTitle": "Подравняване на текст", + "@settingsSubtitleThemeTextAlignmentDialogTitle": {}, + "settingsSubtitleThemeBackgroundColor": "Цвят на фона", + "@settingsSubtitleThemeBackgroundColor": {}, + "settingsAllowInstalledAppAccessSubtitle": "Използва се за подобряване на показването на албума", + "@settingsAllowInstalledAppAccessSubtitle": {}, + "settingsHiddenFiltersBanner": "Снимките и видеоклиповете, съответстващи на скрити филтри, няма да се показват във вашата колекция.", + "@settingsHiddenFiltersBanner": {}, + "settingsHiddenFiltersEmpty": "Няма скрити филтри", + "@settingsHiddenFiltersEmpty": {}, + "settingsStorageAccessTile": "Достъп до хранилището", + "@settingsStorageAccessTile": {}, + "settingsAccessibilityShowPinchGestureAlternatives": "Показване на алтернативи за жестове с мултитъч", + "@settingsAccessibilityShowPinchGestureAlternatives": {}, + "settingsDisplaySectionTitle": "Изобразяване", + "@settingsDisplaySectionTitle": {}, + "settingsThemeBrightnessTile": "Тема", + "@settingsThemeBrightnessTile": {}, + "settingsRemoveAnimationsDialogTitle": "Премахване анимации", + "@settingsRemoveAnimationsDialogTitle": {}, + "settingsTimeToTakeActionTile": "Време на изпълнение на действието", + "@settingsTimeToTakeActionTile": {}, + "settingsThemeBrightnessDialogTitle": "Тема", + "@settingsThemeBrightnessDialogTitle": {}, + "settingsThemeColorHighlights": "Цветни акценти", + "@settingsThemeColorHighlights": {}, + "settingsThemeEnableDynamicColor": "Динамични цветове", + "@settingsThemeEnableDynamicColor": {}, + "settingsDisplayRefreshRateModeTile": "Покажи честотата на обновлението на дисплея", + "@settingsDisplayRefreshRateModeTile": {}, + "settingsWidgetPageTitle": "Фоторамка", + "@settingsWidgetPageTitle": {}, + "settingsScreenSaverPageTitle": "Скрийнсейвър", + "@settingsScreenSaverPageTitle": {}, + "statsPageTitle": "Статистика", + "@statsPageTitle": {}, + "settingsCollectionTile": "Колекция", + "@settingsCollectionTile": {}, + "statsTopPlacesSectionTitle": "Топ локации", + "@statsTopPlacesSectionTitle": {}, + "statsTopStatesSectionTitle": "Топ региони", + "@statsTopStatesSectionTitle": {}, + "statsTopAlbumsSectionTitle": "Топ албуми", + "@statsTopAlbumsSectionTitle": {}, + "viewerInfoLabelDescription": "Описание", + "@viewerInfoLabelDescription": {}, + "viewerInfoLabelTitle": "Заглавие", + "@viewerInfoLabelTitle": {}, + "viewerInfoLabelOwner": "Собственик", + "@viewerInfoLabelOwner": {}, + "mapAttributionOpenTopoMap": "[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | Tiles by [OpenTopoMap](https://opentopomap.org/), [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)", + "@mapAttributionOpenTopoMap": {}, + "mapEmptyRegion": "Няма изображения в този регион", + "@mapEmptyRegion": {}, + "viewerInfoOpenEmbeddedFailureFeedback": "Неуспешно извличане на вградени данни", + "@viewerInfoOpenEmbeddedFailureFeedback": {}, + "viewerInfoOpenLinkText": "Отвори", + "@viewerInfoOpenLinkText": {}, + "viewerInfoViewXmlLinkText": "Преглед на XML", + "@viewerInfoViewXmlLinkText": {}, + "tagEditorSectionRecent": "Скорошни", + "@tagEditorSectionRecent": {}, + "tagEditorPageNewTagFieldLabel": "Нов таг", + "@tagEditorPageNewTagFieldLabel": {}, + "tagEditorPageAddTagTooltip": "Добави таг", + "@tagEditorPageAddTagTooltip": {}, + "panoramaEnableSensorControl": "Активирайте сензорния контрол", + "@panoramaEnableSensorControl": {}, + "panoramaDisableSensorControl": "Изключете сензорния контрол", + "@panoramaDisableSensorControl": {}, + "sourceViewerPageTitle": "Источник", + "@sourceViewerPageTitle": {}, + "tagPlaceholderState": "Регион", + "@tagPlaceholderState": {}, + "tagPlaceholderPlace": "Локация", + "@tagPlaceholderPlace": {}, + "filePickerShowHiddenFiles": "Показване на скритите файлове", + "@filePickerShowHiddenFiles": {}, + "chipActionRemove": "Премахване", + "@chipActionRemove": {}, + "albumTierDynamic": "Dynamic", + "@albumTierDynamic": {}, + "collectionActionAddDynamicAlbum": "Добави динамичен албум", + "@collectionActionAddDynamicAlbum": {}, + "settingsImageBackground": "Фоново изображения", + "@settingsImageBackground": {}, + "settingsViewerQuickActionsTile": "Бързи действия", + "@settingsViewerQuickActionsTile": {}, + "settingsViewerQuickActionEditorPageTitle": "Бързи действия", + "@settingsViewerQuickActionEditorPageTitle": {}, + "settingsViewerQuickActionEditorBanner": "Докоснете и задръжте, за да преместите бутоните и да изберете кои действия да се показват във визуализатора.", + "@settingsViewerQuickActionEditorBanner": {}, + "settingsViewerQuickActionEditorDisplayedButtonsSectionTitle": "Показани бутони", + "@settingsViewerQuickActionEditorDisplayedButtonsSectionTitle": {}, + "settingsViewerShowShootingDetails": "Показване на подробности за снимане", + "@settingsViewerShowShootingDetails": {}, + "settingsViewerEnableOverlayBlurEffect": "Blur ефект", + "@settingsViewerEnableOverlayBlurEffect": {}, + "settingsSlideshowRepeat": "Повтори слайдшоу", + "@settingsSlideshowRepeat": {}, + "settingsSlideshowShuffle": "Разбъркайте", + "@settingsSlideshowShuffle": {}, + "settingsSlideshowAnimatedZoomEffect": "Анимиран ефект на увеличение", + "@settingsSlideshowAnimatedZoomEffect": {}, + "settingsSlideshowTransitionTile": "Преход ефекти", + "@settingsSlideshowTransitionTile": {}, + "settingsSlideshowVideoPlaybackTile": "Възпроизвеждане на видео", + "@settingsSlideshowVideoPlaybackTile": {}, + "settingsVideoEnableHardwareAcceleration": "Хардуерно ускорение", + "@settingsVideoEnableHardwareAcceleration": {}, + "settingsVideoResumptionModeTile": "Възобновяване на възпроизвеждането", + "@settingsVideoResumptionModeTile": {}, + "settingsVideoControlsTile": "Настройки видео", + "@settingsVideoControlsTile": {}, + "settingsVideoControlsPageTitle": "Настройки видео", + "@settingsVideoControlsPageTitle": {}, + "settingsVideoButtonsTile": "Бутони", + "@settingsVideoButtonsTile": {}, + "settingsVideoGestureDoubleTapTogglePlay": "Докоснете два пъти за възпроизвеждане/пауза", + "@settingsVideoGestureDoubleTapTogglePlay": {}, + "settingsSubtitleThemeSample": "Това е примерен текст.", + "@settingsSubtitleThemeSample": {}, + "settingsSubtitleThemeTextAlignmentTile": "Подравняване на текст", + "@settingsSubtitleThemeTextAlignmentTile": {}, + "settingsSubtitleThemeTextPositionTile": "Позиция на текст", + "@settingsSubtitleThemeTextPositionTile": {}, + "settingsSubtitleThemeTextPositionDialogTitle": "Позиция на текст", + "@settingsSubtitleThemeTextPositionDialogTitle": {}, + "settingsSubtitleThemeTextSize": "Размер на текст", + "@settingsSubtitleThemeTextSize": {}, + "settingsSubtitleThemeBackgroundOpacity": "Непрозрачност на фона", + "@settingsSubtitleThemeBackgroundOpacity": {}, + "settingsSubtitleThemeTextAlignmentLeft": "Подравняване отляво", + "@settingsSubtitleThemeTextAlignmentLeft": {}, + "settingsSubtitleThemeTextAlignmentRight": "Подравняване от дясно", + "@settingsSubtitleThemeTextAlignmentRight": {}, + "settingsSubtitleThemeTextAlignmentCenter": "Подравняване по центъра", + "@settingsSubtitleThemeTextAlignmentCenter": {}, + "settingsPrivacySectionTitle": "Поверителност", + "@settingsPrivacySectionTitle": {}, + "settingsAllowInstalledAppAccess": "Разрешете достъп до библиотеките на приложението", + "@settingsAllowInstalledAppAccess": {}, + "settingsAllowErrorReporting": "Разрешаване на анонимно докладване на грешки", + "@settingsAllowErrorReporting": {}, + "settingsEnableBin": "Използвайте кошчето", + "@settingsEnableBin": {}, + "settingsEnableBinSubtitle": "Съхранявайте изтритите обекти за 30 дни", + "@settingsEnableBinSubtitle": {}, + "settingsAllowMediaManagement": "Разрешаване на управление на медиите", + "@settingsAllowMediaManagement": {}, + "settingsHiddenItemsTile": "Скрити елементи", + "@settingsHiddenItemsTile": {}, + "settingsStorageAccessPageTitle": "Достъп до хранилището", + "@settingsStorageAccessPageTitle": {}, + "settingsStorageAccessEmpty": "Няма права за достъп", + "@settingsStorageAccessEmpty": {}, + "settingsStorageAccessRevokeTooltip": "Отмени", + "@settingsStorageAccessRevokeTooltip": {}, + "settingsAccessibilitySectionTitle": "Достъпност", + "@settingsAccessibilitySectionTitle": {}, + "settingsRemoveAnimationsTile": "Премахване анимация", + "@settingsRemoveAnimationsTile": {}, + "settingsDisplayRefreshRateModeDialogTitle": "Честота на опресняване", + "@settingsDisplayRefreshRateModeDialogTitle": {}, + "settingsDisplayUseTvInterface": "Интерфейс Android TV", + "@settingsDisplayUseTvInterface": {}, + "settingsLanguageSectionTitle": "Език и формати", + "@settingsLanguageSectionTitle": {}, + "settingsCoordinateFormatTile": "Формат на координатите", + "@settingsCoordinateFormatTile": {}, + "settingsCoordinateFormatDialogTitle": "Формат на координатите", + "@settingsCoordinateFormatDialogTitle": {}, + "settingsUnitSystemTile": "Единици за измерване", + "@settingsUnitSystemTile": {}, + "settingsUnitSystemDialogTitle": "Единици за измерване", + "@settingsUnitSystemDialogTitle": {}, + "settingsForceWesternArabicNumeralsTile": "Принудителни арабски цифри", + "@settingsForceWesternArabicNumeralsTile": {}, + "settingsWidgetShowOutline": "Контур", + "@settingsWidgetShowOutline": {}, + "settingsWidgetOpenPage": "При докосване на джаджата", + "@settingsWidgetOpenPage": {}, + "settingsWidgetDisplayedItem": "Показан артикул", + "@settingsWidgetDisplayedItem": {}, + "statsWithGps": "{count, plural, =1{1 елемент с местоположение} other{{count} елемента с местоположение}}", + "@statsWithGps": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "statsTopCountriesSectionTitle": "Топ страни", + "@statsTopCountriesSectionTitle": {}, + "statsTopTagsSectionTitle": "Топ тагове", + "@statsTopTagsSectionTitle": {}, + "viewerOpenPanoramaButtonLabel": "ОТВОРИ ПАНОРАМА", + "@viewerOpenPanoramaButtonLabel": {}, + "viewerSetWallpaperButtonLabel": "ЗАДАЙ КАТО ТАПЕТ", + "@viewerSetWallpaperButtonLabel": {}, + "viewerErrorUnknown": "Опс!", + "@viewerErrorUnknown": {}, + "viewerInfoPageTitle": "Информация", + "@viewerInfoPageTitle": {}, + "viewerInfoBackToViewerTooltip": "Обратно към viewer", + "@viewerInfoBackToViewerTooltip": {}, + "viewerInfoUnknown": "Неизвестен", + "@viewerInfoUnknown": {}, + "viewerInfoLabelDate": "Дата", + "@viewerInfoLabelDate": {}, + "viewerInfoLabelResolution": "Резолюция", + "@viewerInfoLabelResolution": {}, + "viewerInfoLabelSize": "Размер", + "@viewerInfoLabelSize": {}, + "viewerInfoLabelUri": "Идентификатор", + "@viewerInfoLabelUri": {}, + "viewerInfoLabelPath": "Път", + "@viewerInfoLabelPath": {}, + "viewerInfoLabelDuration": "Продължителност", + "@viewerInfoLabelDuration": {}, + "viewerInfoLabelCoordinates": "Координати", + "@viewerInfoLabelCoordinates": {}, + "viewerInfoLabelAddress": "Адрес", + "@viewerInfoLabelAddress": {}, + "mapStyleDialogTitle": "Стиль карта", + "@mapStyleDialogTitle": {}, + "mapStyleTooltip": "Избирате стил на карта", + "@mapStyleTooltip": {}, + "mapZoomInTooltip": "Увеличете мащаба", + "@mapZoomInTooltip": {}, + "mapZoomOutTooltip": "Намаляване", + "@mapZoomOutTooltip": {}, + "mapPointNorthUpTooltip": "Север нагоре", + "@mapPointNorthUpTooltip": {}, + "mapAttributionOsmHot": "Tiles by [HOT](https://www.hotosm.org/) • Hosted by [OSM France](https://openstreetmap.fr/)", + "@mapAttributionOsmHot": {}, + "mapAttributionStamen": "Tiles by [Stamen Design](https://stamen.com), [CC BY 3.0](https://creativecommons.org/licenses/by/3.0)", + "@mapAttributionStamen": {}, + "openMapPageTooltip": "Преглед на страницата с карта", + "@openMapPageTooltip": {}, + "viewerInfoSearchFieldLabel": "Търсене на метаданни", + "@viewerInfoSearchFieldLabel": {}, + "viewerInfoSearchEmpty": "Няма съответстващи ключове", + "@viewerInfoSearchEmpty": {}, + "viewerInfoSearchSuggestionDate": "Дата и час", + "@viewerInfoSearchSuggestionDate": {}, + "viewerInfoSearchSuggestionDescription": "Описание", + "@viewerInfoSearchSuggestionDescription": {}, + "viewerInfoSearchSuggestionDimensions": "Размери", + "@viewerInfoSearchSuggestionDimensions": {}, + "viewerInfoSearchSuggestionResolution": "Резолюция", + "@viewerInfoSearchSuggestionResolution": {}, + "viewerInfoSearchSuggestionRights": "Права", + "@viewerInfoSearchSuggestionRights": {}, + "wallpaperUseScrollEffect": "Използвайте ефекта на превъртане на началния екран", + "@wallpaperUseScrollEffect": {}, + "tagEditorPageTitle": "Променете тагове", + "@tagEditorPageTitle": {}, + "tagEditorSectionPlaceholders": "Заместители", + "@tagEditorSectionPlaceholders": {}, + "tagPlaceholderCountry": "Държава", + "@tagPlaceholderCountry": {}, + "filePickerDoNotShowHiddenFiles": "Не показвай скритите файлове", + "@filePickerDoNotShowHiddenFiles": {}, + "filePickerOpenFrom": "Отворете от", + "@filePickerOpenFrom": {}, + "settingsVideoGestureSideDoubleTapSeek": "Докоснете два пъти краищата на екрана, за да търсите назад/напред", + "@settingsVideoGestureSideDoubleTapSeek": {}, + "settingsSaveSearchHistory": "Запазване на историята на търсенето", + "@settingsSaveSearchHistory": {}, + "settingsStorageAccessBanner": "Някои директории изискват изрично разрешение за достъп за модифициране на файлове в тях. Тук можете да прегледате директории, до които преди това сте дали достъп.", + "@settingsStorageAccessBanner": {}, + "settingsViewerShowDescription": "Показване описание", + "@settingsViewerShowDescription": {}, + "dynamicAlbumAlreadyExists": "Динамични албум вече съществува", + "@dynamicAlbumAlreadyExists": {}, + "settingsSlideshowFillScreen": "Цял екран", + "@settingsSlideshowFillScreen": {}, + "appExportDynamicAlbums": "Динамични албуми", + "@appExportDynamicAlbums": {}, + "settingsViewerShowOverlayThumbnails": "Показване на миниатюри", + "@settingsViewerShowOverlayThumbnails": {}, + "mapAttributionOsmLiberty": "Tiles by [OpenMapTiles](https://www.openmaptiles.org/), [CC BY](http://creativecommons.org/licenses/by/4.0) • Hosted by [OSM Americana](https://tile.ourmap.us)", + "@mapAttributionOsmLiberty": {}, + "settingsDisablingBinWarningDialogMessage": "Елементите в кошчето ще бъдат изтрити завинаги.", + "@settingsDisablingBinWarningDialogMessage": {}, + "settingsLanguagePageTitle": "Език", + "@settingsLanguagePageTitle": {}, + "settingsSlideshowIntervalTile": "Интервал", + "@settingsSlideshowIntervalTile": {}, + "settingsVideoLoopModeDialogTitle": "Цикличен режим", + "@settingsVideoLoopModeDialogTitle": {}, + "settingsVideoResumptionModeDialogTitle": "Възобновяване на възпроизвеждането", + "@settingsVideoResumptionModeDialogTitle": {}, + "settingsSubtitleThemeShowOutline": "Покажете контур и сянка", + "@settingsSubtitleThemeShowOutline": {}, + "settingsSubtitleThemeTextColor": "Цвят на текст", + "@settingsSubtitleThemeTextColor": {}, + "settingsSubtitleThemeTextOpacity": "Непрозрачност на текста", + "@settingsSubtitleThemeTextOpacity": {}, + "settingsHiddenItemsPageTitle": "Скрити елементи", + "@settingsHiddenItemsPageTitle": {}, + "settingsLanguageTile": "Език", + "@settingsLanguageTile": {}, + "viewerErrorDoesNotExist": "Файлът не съществува.", + "@viewerErrorDoesNotExist": {}, + "mapAttributionOsmData": "Map data © [OpenStreetMap](https://www.openstreetmap.org/copyright) contributors", + "@mapAttributionOsmData": {}, + "filePickerNoItems": "Не откривам нищо", + "@filePickerNoItems": {}, + "newDynamicAlbumDialogTitle": "Нов динамичен албум", + "@newDynamicAlbumDialogTitle": {}, + "tagEditorDiscardDialogMessage": "Искате ли да отхвърлите промените?", + "@tagEditorDiscardDialogMessage": {}, + "filePickerUseThisFolder": "Използвай тази папка", + "@filePickerUseThisFolder": {} +} diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 781a00af8..a04876369 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -85,6 +85,7 @@ "sourceStateLocatingPlaces": "Locating places", "chipActionDelete": "Delete", + "chipActionRemove": "Remove", "chipActionShowCollection": "Show in Collection", "chipActionGoToAlbumPage": "Show in Albums", "chipActionGoToCountryPage": "Show in Countries", @@ -204,6 +205,7 @@ "albumTierSpecial": "Common", "albumTierApps": "Apps", "albumTierVaults": "Vaults", + "albumTierDynamic": "Dynamic", "albumTierRegular": "Others", "coordinateFormatDms": "DMS", @@ -427,6 +429,9 @@ "newAlbumDialogNameLabelAlreadyExistsHelper": "Directory already exists", "newAlbumDialogStorageLabel": "Storage:", + "newDynamicAlbumDialogTitle": "New Dynamic Album", + "dynamicAlbumAlreadyExists": "Dynamic album already exists", + "newVaultWarningDialogMessage": "Items in vaults are only available to this app and no others.\n\nIf you uninstall this app, or clear this app data, you will lose all these items.", "newVaultDialogTitle": "New Vault", "configureVaultDialogTitle": "Configure Vault", @@ -595,6 +600,7 @@ "collectionActionShowTitleSearch": "Show title filter", "collectionActionHideTitleSearch": "Hide title filter", + "collectionActionAddDynamicAlbum": "Add dynamic album", "collectionActionAddShortcut": "Add shortcut", "collectionActionSetHome": "Set as home", "collectionActionEmptyBin": "Empty bin", @@ -806,6 +812,7 @@ "settingsActionImportDialogTitle": "Import", "appExportCovers": "Covers", + "appExportDynamicAlbums": "Dynamic albums", "appExportFavourites": "Favorites", "appExportSettings": "Settings", diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index 1e24e6b2b..d85013c46 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -1392,5 +1392,17 @@ "videoActionShowNextFrame": "Mostrar fotograma siguiente", "@videoActionShowNextFrame": {}, "newAlbumDialogAlbumAlreadyExistsHelper": "El álbum ya existe", - "@newAlbumDialogAlbumAlreadyExistsHelper": {} + "@newAlbumDialogAlbumAlreadyExistsHelper": {}, + "chipActionRemove": "Remover", + "@chipActionRemove": {}, + "dynamicAlbumAlreadyExists": "El álbum dinámico ya existe", + "@dynamicAlbumAlreadyExists": {}, + "collectionActionAddDynamicAlbum": "Añadir álbum dinámico", + "@collectionActionAddDynamicAlbum": {}, + "appExportDynamicAlbums": "Álbumes dinámicos", + "@appExportDynamicAlbums": {}, + "albumTierDynamic": "Dinámico", + "@albumTierDynamic": {}, + "newDynamicAlbumDialogTitle": "Nuevo álbum dinámico", + "@newDynamicAlbumDialogTitle": {} } diff --git a/lib/l10n/app_et.arb b/lib/l10n/app_et.arb index af5471797..847939a35 100644 --- a/lib/l10n/app_et.arb +++ b/lib/l10n/app_et.arb @@ -637,5 +637,622 @@ "format": "decimalPattern" } } - } + }, + "newDynamicAlbumDialogTitle": "Uus dünaamiline album", + "@newDynamicAlbumDialogTitle": {}, + "dynamicAlbumAlreadyExists": "Selline dünaamiline album on juba olemas", + "@dynamicAlbumAlreadyExists": {}, + "editEntryDialogCopyFromItem": "Kopi teisest objektist", + "@editEntryDialogCopyFromItem": {}, + "editEntryDialogTargetFieldsHeader": "Muudetavad väljad", + "@editEntryDialogTargetFieldsHeader": {}, + "editEntryDateDialogTitle": "Kuupäev ja kellaaeg", + "@editEntryDateDialogTitle": {}, + "editEntryDateDialogSetCustom": "Määra soovitud kuupäev", + "@editEntryDateDialogSetCustom": {}, + "unsupportedTypeDialogMessage": "{count, plural, =1{See tegevus pole toetatud antud tüüpi objekti puhul: {types}.} other{See tegevus pole toetatud antud tüüpi objektide puhul: {types}.}}", + "@unsupportedTypeDialogMessage": { + "placeholders": { + "count": {}, + "types": { + "type": "String", + "example": "GIF, TIFF, MP4", + "description": "a list of unsupported types" + } + } + }, + "newAlbumDialogTitle": "Uus album", + "@newAlbumDialogTitle": {}, + "newAlbumDialogNameLabel": "Albumi nimi", + "@newAlbumDialogNameLabel": {}, + "newAlbumDialogAlbumAlreadyExistsHelper": "Sellise nimega album on juba olemas", + "@newAlbumDialogAlbumAlreadyExistsHelper": {}, + "newAlbumDialogNameLabelAlreadyExistsHelper": "Selline kaust on juba olemas", + "@newAlbumDialogNameLabelAlreadyExistsHelper": {}, + "newAlbumDialogStorageLabel": "Andmeruum:", + "@newAlbumDialogStorageLabel": {}, + "deleteMultiAlbumConfirmationDialogMessage": "{count, plural, =1{Kas kustutame need albumis koos seal leiduva ühe objektiga?} other{Kas kustutame need albumis koos seal leiduva {count} objektiga?}}", + "@deleteMultiAlbumConfirmationDialogMessage": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "exportEntryDialogWriteMetadata": "Salvesta metainfo", + "@exportEntryDialogWriteMetadata": {}, + "renameEntryDialogLabel": "Uus nimi", + "@renameEntryDialogLabel": {}, + "editEntryDateDialogCopyField": "Kopeeri muust kuupäevast", + "@editEntryDateDialogCopyField": {}, + "exportEntryDialogHeight": "Kõrgus", + "@exportEntryDialogHeight": {}, + "exportEntryDialogQuality": "Kvaliteet", + "@exportEntryDialogQuality": {}, + "deleteSingleAlbumConfirmationDialogMessage": "{count, plural, =1{Kas kustutame selle albumi koos seal leiduva ühe objektiga?} other{Kas kustutame selle albumi koos seal leiduva {count} objektiga?}}", + "@deleteSingleAlbumConfirmationDialogMessage": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "exportEntryDialogFormat": "Vorming:", + "@exportEntryDialogFormat": {}, + "chipActionRemove": "Eemalda", + "@chipActionRemove": {}, + "exportEntryDialogWidth": "Laius", + "@exportEntryDialogWidth": {}, + "albumTierDynamic": "Dünaamiline", + "@albumTierDynamic": {}, + "durationDialogSeconds": "Sekundid", + "@durationDialogSeconds": {}, + "editEntryLocationDialogTitle": "Asukoht", + "@editEntryLocationDialogTitle": {}, + "editEntryLocationDialogSetCustom": "Määra asukoht ise", + "@editEntryLocationDialogSetCustom": {}, + "editEntryLocationDialogChooseOnMap": "Vali kaardilt", + "@editEntryLocationDialogChooseOnMap": {}, + "editEntryLocationDialogLatitude": "Laiuskraad", + "@editEntryLocationDialogLatitude": {}, + "editEntryLocationDialogLongitude": "Pikkuskraad", + "@editEntryLocationDialogLongitude": {}, + "locationPickerUseThisLocationButton": "Kasuta seda asukohta", + "@locationPickerUseThisLocationButton": {}, + "editEntryRatingDialogTitle": "Hinnang", + "@editEntryRatingDialogTitle": {}, + "removeEntryMetadataDialogTitle": "Metainfo eemaldamine", + "@removeEntryMetadataDialogTitle": {}, + "removeEntryMetadataMotionPhotoXmpWarningDialogMessage": "XMP on vajalik video esitamisel foto sees.\n\nKas oled kindel, et soovid selle metainfo eemaldada?", + "@removeEntryMetadataMotionPhotoXmpWarningDialogMessage": {}, + "videoStreamSelectionDialogAudio": "Heliriba", + "@videoStreamSelectionDialogAudio": {}, + "videoStreamSelectionDialogText": "Subtiitrid", + "@videoStreamSelectionDialogText": {}, + "videoStreamSelectionDialogOff": "Välja lülitatud", + "@videoStreamSelectionDialogOff": {}, + "videoStreamSelectionDialogTrack": "Videoriba", + "@videoStreamSelectionDialogTrack": {}, + "videoStreamSelectionDialogNoSelection": "Rohkem meediaribasid failis pole.", + "@videoStreamSelectionDialogNoSelection": {}, + "viewDialogSortSectionTitle": "Järjesta", + "@viewDialogSortSectionTitle": {}, + "viewDialogGroupSectionTitle": "Rühmita", + "@viewDialogGroupSectionTitle": {}, + "viewDialogLayoutSectionTitle": "Paiguta", + "@viewDialogLayoutSectionTitle": {}, + "tileLayoutMosaic": "Mosaiik", + "@tileLayoutMosaic": {}, + "castDialogTitle": "Seadmed peegeldamiseks", + "@castDialogTitle": {}, + "coverDialogTabCover": "Kaanepilt", + "@coverDialogTabCover": {}, + "coverDialogTabApp": "Rakenduse", + "@coverDialogTabApp": {}, + "aboutPageTitle": "Rakenduse teave", + "@aboutPageTitle": {}, + "aboutLinkLicense": "Litsents", + "@aboutLinkLicense": {}, + "aboutLinkPolicy": "Privaatsuspoliitika", + "@aboutLinkPolicy": {}, + "aboutBugSectionTitle": "Veateated", + "@aboutBugSectionTitle": {}, + "aboutBugCopyInfoInstruction": "Kopeeri süsteemiteave", + "@aboutBugCopyInfoInstruction": {}, + "aboutBugReportInstruction": "Teata GitHubis koos logide ja süsteemiteabega", + "@aboutBugReportInstruction": {}, + "aboutDataUsageSectionTitle": "Andmekasutus", + "@aboutDataUsageSectionTitle": {}, + "aboutDataUsageCache": "Vahemälu", + "@aboutDataUsageCache": {}, + "aboutDataUsageClearCache": "Tühjenda vahemälu", + "@aboutDataUsageClearCache": {}, + "aboutCreditsWorldAtlas1": "See rakendus kasutab TopoJSONi faili, mille on koostanud", + "@aboutCreditsWorldAtlas1": {}, + "aboutCreditsWorldAtlas2": ", avaldatud ISC Litsentsi alusel.", + "@aboutCreditsWorldAtlas2": {}, + "aboutLicensesSectionTitle": "Avatud lähtekoodiga tarkvara litsentsid", + "@aboutLicensesSectionTitle": {}, + "aboutLicensesFlutterPackagesSectionTitle": "Flutteri paketid", + "@aboutLicensesFlutterPackagesSectionTitle": {}, + "aboutLicensesDartPackagesSectionTitle": "Darti paketid", + "@aboutLicensesDartPackagesSectionTitle": {}, + "aboutLicensesShowAllButtonLabel": "Näita kõiki litsentse", + "@aboutLicensesShowAllButtonLabel": {}, + "policyPageTitle": "Privaatsuspoliitika", + "@policyPageTitle": {}, + "collectionPageTitle": "Meediakogu", + "@collectionPageTitle": {}, + "collectionPickPageTitle": "Vali", + "@collectionPickPageTitle": {}, + "collectionSelectPageTitle": "Vali objektid", + "@collectionSelectPageTitle": {}, + "collectionActionShowTitleSearch": "Näita pealkirjade filtrit", + "@collectionActionShowTitleSearch": {}, + "collectionActionHideTitleSearch": "Peida pealkirjade filter", + "@collectionActionHideTitleSearch": {}, + "collectionActionCopy": "Kopeeri albumisse", + "@collectionActionCopy": {}, + "dateThisMonth": "Sel kuul", + "@dateThisMonth": {}, + "entryActionCast": "Peegelda välisseadmesse", + "@entryActionCast": {}, + "hideFilterConfirmationDialogMessage": "Filtrile vastavad fotod ja videod on sinu meediakogust peidetud. Kui soovid neid uuesti kuvada, siis seadistuste alajaotusest „Privaatsus“ saad seda lubada.\n\nKas sa oled kindel, et soovid neid peita?", + "@hideFilterConfirmationDialogMessage": {}, + "editEntryDateDialogExtractFromTitle": "Tuvasta pealkirjast", + "@editEntryDateDialogExtractFromTitle": {}, + "editEntryDateDialogShift": "Nihuta ajatemplit", + "@editEntryDateDialogShift": {}, + "editEntryDateDialogSourceFileModifiedDate": "Faili muutmisaeg", + "@editEntryDateDialogSourceFileModifiedDate": {}, + "durationDialogHours": "Tunnid", + "@durationDialogHours": {}, + "durationDialogMinutes": "Minutid", + "@durationDialogMinutes": {}, + "genericSuccessFeedback": "Valmis!", + "@genericSuccessFeedback": {}, + "genericFailureFeedback": "Ei õnnestunud", + "@genericFailureFeedback": {}, + "genericDangerWarningDialogMessage": "Kas sa oled kindel?", + "@genericDangerWarningDialogMessage": {}, + "tooManyItemsErrorDialogMessage": "Proovi uuesti, aga väiksema arvu objektidega.", + "@tooManyItemsErrorDialogMessage": {}, + "menuActionConfigureView": "Vaata", + "@menuActionConfigureView": {}, + "menuActionSelect": "Vali", + "@menuActionSelect": {}, + "menuActionSelectAll": "Vali kõik", + "@menuActionSelectAll": {}, + "menuActionSelectNone": "Ära vali midagi", + "@menuActionSelectNone": {}, + "menuActionMap": "Kaart", + "@menuActionMap": {}, + "menuActionSlideshow": "Slaidiesitlus", + "@menuActionSlideshow": {}, + "menuActionStats": "Statistika", + "@menuActionStats": {}, + "aboutDataUsageInternal": "Sisemine", + "@aboutDataUsageInternal": {}, + "aboutDataUsageExternal": "Väline", + "@aboutDataUsageExternal": {}, + "aboutLicensesBanner": "See rakendus kasutab järgmiseid avatud lähtekoodiga pakette ja teeke.", + "@aboutLicensesBanner": {}, + "aboutLicensesAndroidLibrariesSectionTitle": "Androidi teegid", + "@aboutLicensesAndroidLibrariesSectionTitle": {}, + "aboutLicensesFlutterPluginsSectionTitle": "Flutteri lisamoodulid", + "@aboutLicensesFlutterPluginsSectionTitle": {}, + "collectionActionMove": "Teisalda albumisse", + "@collectionActionMove": {}, + "collectionActionRescan": "Skaneeri uuesti", + "@collectionActionRescan": {}, + "collectionActionEdit": "Muuda", + "@collectionActionEdit": {}, + "collectionSearchTitlesHintText": "Otsi pealkirju", + "@collectionSearchTitlesHintText": {}, + "collectionGroupAlbum": "Albumi alusel", + "@collectionGroupAlbum": {}, + "collectionGroupMonth": "Kuude kaupa", + "@collectionGroupMonth": {}, + "collectionGroupDay": "Päevade kaupa", + "@collectionGroupDay": {}, + "collectionGroupNone": "Ära rühmita", + "@collectionGroupNone": {}, + "sectionUnknown": "Teadmata", + "@sectionUnknown": {}, + "dateToday": "Täna", + "@dateToday": {}, + "dateYesterday": "Eile", + "@dateYesterday": {}, + "videoSpeedDialogLabel": "Taasesituse kiirus", + "@videoSpeedDialogLabel": {}, + "videoStreamSelectionDialogVideo": "Video", + "@videoStreamSelectionDialogVideo": {}, + "viewDialogReverseSortOrder": "Tagurpidi järjestus", + "@viewDialogReverseSortOrder": {}, + "removeEntryMetadataDialogMore": "Veel", + "@removeEntryMetadataDialogMore": {}, + "tileLayoutList": "Loend", + "@tileLayoutList": {}, + "appPickDialogTitle": "Vali rakendus", + "@appPickDialogTitle": {}, + "coverDialogTabColor": "Värv", + "@coverDialogTabColor": {}, + "tileLayoutGrid": "Ruudustik", + "@tileLayoutGrid": {}, + "appPickDialogNone": "Määratlemata", + "@appPickDialogNone": {}, + "aboutBugSaveLogInstruction": "Salvesta rakenduse logid faili", + "@aboutBugSaveLogInstruction": {}, + "aboutDataUsageMisc": "Varia", + "@aboutDataUsageMisc": {}, + "aboutDataUsageDatabase": "Andmebaas", + "@aboutDataUsageDatabase": {}, + "aboutCreditsSectionTitle": "Tänuavaldused", + "@aboutCreditsSectionTitle": {}, + "aboutBugCopyInfoButton": "Kopeeri", + "@aboutBugCopyInfoButton": {}, + "aboutBugReportButton": "Teata veast", + "@aboutBugReportButton": {}, + "collectionActionEmptyBin": "Tühjenda prügikast", + "@collectionActionEmptyBin": {}, + "aboutTranslatorsSectionTitle": "Tõlkijad", + "@aboutTranslatorsSectionTitle": {}, + "aboutDataUsageData": "Andmed", + "@aboutDataUsageData": {}, + "collectionActionAddShortcut": "Lisa viide", + "@collectionActionAddShortcut": {}, + "collectionActionAddDynamicAlbum": "Lisa dünaamiline album", + "@collectionActionAddDynamicAlbum": {}, + "collectionActionSetHome": "Märgi avaleheks", + "@collectionActionSetHome": {}, + "collectionDeleteFailureFeedback": "{count, plural, =1{1 objekti kustutamine ei õnnestunud} other{{count} objekti kustutamine ei õnnestunud}}", + "@collectionDeleteFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionMoveFailureFeedback": "{count, plural, =1{1 objekti teisaldamine ei õnnestunud} other{{count} objekti teisaldamine ei õnnestunud}}", + "@collectionMoveFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionCopySuccessFeedback": "{count, plural, =1{Kopeerisime 1 objekti} other{Kopeerisime {count} objekti}}", + "@collectionCopySuccessFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionRenameSuccessFeedback": "{count, plural, =1{Muutsime 1 objekti nime} other{Muutsime {count} objekti nime}}", + "@collectionRenameSuccessFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionEmptyImages": "Pilte pole", + "@collectionEmptyImages": {}, + "collectionEmptyGrantAccessButtonLabel": "Luba juurdepääs", + "@collectionEmptyGrantAccessButtonLabel": {}, + "collectionSelectSectionTooltip": "Vali lõik", + "@collectionSelectSectionTooltip": {}, + "collectionDeselectSectionTooltip": "Eemalda lõigu valik", + "@collectionDeselectSectionTooltip": {}, + "drawerAboutButton": "Teave", + "@drawerAboutButton": {}, + "drawerSettingsButton": "Seadistused", + "@drawerSettingsButton": {}, + "drawerCollectionAll": "Kõik kogumikud", + "@drawerCollectionAll": {}, + "drawerCollectionFavourites": "Lemmikud", + "@drawerCollectionFavourites": {}, + "drawerCollectionImages": "Pildid", + "@drawerCollectionImages": {}, + "drawerCollectionVideos": "Videod", + "@drawerCollectionVideos": {}, + "drawerCollectionAnimated": "Animeeritud", + "@drawerCollectionAnimated": {}, + "drawerCollectionMotionPhotos": "Liikuvad fotod", + "@drawerCollectionMotionPhotos": {}, + "drawerCollectionPanoramas": "Panoraamfotod", + "@drawerCollectionPanoramas": {}, + "drawerCollectionRaws": "Töötlemata fotod", + "@drawerCollectionRaws": {}, + "drawerCollectionSphericalVideos": "360° videod", + "@drawerCollectionSphericalVideos": {}, + "drawerAlbumPage": "Albumid", + "@drawerAlbumPage": {}, + "drawerCountryPage": "Riigid", + "@drawerCountryPage": {}, + "drawerPlacePage": "Kohad", + "@drawerPlacePage": {}, + "drawerTagPage": "Sildid", + "@drawerTagPage": {}, + "sortOrderHighestFirst": "Esmalt kõrgemad", + "@sortOrderHighestFirst": {}, + "sortOrderLowestFirst": "Esmalt madalamad", + "@sortOrderLowestFirst": {}, + "sortOrderLargestFirst": "Esmalt suuremad", + "@sortOrderLargestFirst": {}, + "sortOrderSmallestFirst": "Esmalt väiksemad", + "@sortOrderSmallestFirst": {}, + "sortOrderShortestFirst": "Esmalt lühemad", + "@sortOrderShortestFirst": {}, + "albumPickPageTitlePick": "Vali album", + "@albumPickPageTitlePick": {}, + "albumPickPageTitleExport": "Ekspordi albumisse", + "@albumPickPageTitleExport": {}, + "albumPickPageTitleMove": "Teisalda albumisse", + "@albumPickPageTitleMove": {}, + "albumCamera": "Kaamera", + "@albumCamera": {}, + "collectionRenameFailureFeedback": "{count, plural, =1{1 objekti nime muutmine ei õnnestunud} other{{count} objekti nime muutmine ei õnnestunud}}", + "@collectionRenameFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionEditFailureFeedback": "{count, plural, =1{1 objekti muutmine ei õnnestunud} other{{count} objekti muutmine ei õnnestunud}}", + "@collectionEditFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "sortOrderAtoZ": "A kuni Z", + "@sortOrderAtoZ": {}, + "sortOrderZtoA": "Z kuni A", + "@sortOrderZtoA": {}, + "sortByDate": "Kuupäeva alusel", + "@sortByDate": {}, + "sortByItemCount": "Objektide arvu järgi", + "@sortByItemCount": {}, + "sortBySize": "Suuruse alusel", + "@sortBySize": {}, + "sortByName": "Nime alusel", + "@sortByName": {}, + "sortByAlbumFileName": "Albumi ja failinime alusel", + "@sortByAlbumFileName": {}, + "sortByRating": "Hinnangu alusel", + "@sortByRating": {}, + "sortByDuration": "Kestuse järgi", + "@sortByDuration": {}, + "sortOrderNewestFirst": "Esmalt uuemad", + "@sortOrderNewestFirst": {}, + "sortOrderOldestFirst": "Esmalt vanemad", + "@sortOrderOldestFirst": {}, + "sortOrderLongestFirst": "Esmalt pikemad", + "@sortOrderLongestFirst": {}, + "albumGroupTier": "Taseme järgi", + "@albumGroupTier": {}, + "albumGroupType": "Tüübi järgi", + "@albumGroupType": {}, + "albumGroupVolume": "Andmemahu alusel", + "@albumGroupVolume": {}, + "albumGroupNone": "Ära rühmita", + "@albumGroupNone": {}, + "albumMimeTypeMixed": "Erinev sisu", + "@albumMimeTypeMixed": {}, + "albumPickPageTitleCopy": "Kopeeri albumisse", + "@albumPickPageTitleCopy": {}, + "collectionCopyFailureFeedback": "{count, plural, =1{1 objekti kopeerimine ei õnnestunud} other{{count} objekti kopeerimine ei õnnestunud}}", + "@collectionCopyFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionEmptyFavourites": "Sul pole veel lemmikud", + "@collectionEmptyFavourites": {}, + "collectionEmptyVideos": "Videoid pole", + "@collectionEmptyVideos": {}, + "collectionExportFailureFeedback": "{count, plural, =1{1 lehe eksportimine ei õnnestunud} other{{count} lehe eksportimine ei õnnestunud}}", + "@collectionExportFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionEditSuccessFeedback": "{count, plural, =1{Muutsime 1 objekti} other{Muutsime {count} objekti}}", + "@collectionEditSuccessFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionMoveSuccessFeedback": "{count, plural, =1{Teisaldasime 1 objekti} other{Teisaldasime {count} objekti}}", + "@collectionMoveSuccessFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "albumDownload": "Laadi alla", + "@albumDownload": {}, + "albumScreenshots": "Ekraanitõmmised", + "@albumScreenshots": {}, + "albumScreenRecordings": "Ekraanisalvestused", + "@albumScreenRecordings": {}, + "albumVideoCaptures": "Videosalvestused", + "@albumVideoCaptures": {}, + "albumPageTitle": "Albumid", + "@albumPageTitle": {}, + "albumEmpty": "Albumeid ei leidu", + "@albumEmpty": {}, + "createAlbumButtonLabel": "LOO", + "@createAlbumButtonLabel": {}, + "newFilterBanner": "uus", + "@newFilterBanner": {}, + "countryPageTitle": "Riigid", + "@countryPageTitle": {}, + "countryEmpty": "Riike pole", + "@countryEmpty": {}, + "statePageTitle": "Osariigid", + "@statePageTitle": {}, + "stateEmpty": "Osariike pole", + "@stateEmpty": {}, + "placePageTitle": "Asukohad", + "@placePageTitle": {}, + "placeEmpty": "Asukohti pole", + "@placeEmpty": {}, + "tagPageTitle": "Sildid", + "@tagPageTitle": {}, + "tagEmpty": "Silte pole", + "@tagEmpty": {}, + "binPageTitle": "Prügikast", + "@binPageTitle": {}, + "explorerPageTitle": "Sirvija", + "@explorerPageTitle": {}, + "explorerActionSelectStorageVolume": "Vali andmeruum", + "@explorerActionSelectStorageVolume": {}, + "selectStorageVolumeDialogTitle": "Vali andmeruum", + "@selectStorageVolumeDialogTitle": {}, + "searchDateSectionTitle": "Kuupäevad", + "@searchDateSectionTitle": {}, + "searchAlbumsSectionTitle": "Albumid", + "@searchAlbumsSectionTitle": {}, + "searchCountriesSectionTitle": "Riigid", + "@searchCountriesSectionTitle": {}, + "searchStatesSectionTitle": "Osariigid", + "@searchStatesSectionTitle": {}, + "searchPlacesSectionTitle": "Asukohad", + "@searchPlacesSectionTitle": {}, + "searchTagsSectionTitle": "Sildid", + "@searchTagsSectionTitle": {}, + "searchRatingSectionTitle": "Hinnagud", + "@searchRatingSectionTitle": {}, + "searchMetadataSectionTitle": "Metainfo", + "@searchMetadataSectionTitle": {}, + "settingsPageTitle": "Seadistused", + "@settingsPageTitle": {}, + "settingsSystemDefault": "Süsteemi vaikeseadistused", + "@settingsSystemDefault": {}, + "settingsDefault": "Vaikimisi", + "@settingsDefault": {}, + "settingsDisabled": "Pole kasutusel", + "@settingsDisabled": {}, + "settingsAskEverytime": "Küsi iga kord", + "@settingsAskEverytime": {}, + "settingsModificationWarningDialogMessage": "Muud seadistused kuuluvad muutmisele.", + "@settingsModificationWarningDialogMessage": {}, + "settingsSearchFieldLabel": "Otsi seadistustest", + "@settingsSearchFieldLabel": {}, + "settingsSearchEmpty": "Sellist seadistust ei leidu", + "@settingsSearchEmpty": {}, + "settingsActionExport": "Ekspordi", + "@settingsActionExport": {}, + "settingsActionExportDialogTitle": "Eksport", + "@settingsActionExportDialogTitle": {}, + "settingsActionImport": "Impordi", + "@settingsActionImport": {}, + "settingsActionImportDialogTitle": "Import", + "@settingsActionImportDialogTitle": {}, + "appExportCovers": "Kaanepildid", + "@appExportCovers": {}, + "appExportDynamicAlbums": "Dünaamilised albumid", + "@appExportDynamicAlbums": {}, + "appExportFavourites": "Lemmikud", + "@appExportFavourites": {}, + "appExportSettings": "Seadistused", + "@appExportSettings": {}, + "settingsHomeTile": "Avavaade", + "@settingsHomeTile": {}, + "settingsHomeDialogTitle": "Avavaade", + "@settingsHomeDialogTitle": {}, + "settingsNavigationSectionTitle": "Põhivaated", + "@settingsNavigationSectionTitle": {}, + "setHomeCustom": "Sinu valitud", + "@setHomeCustom": {}, + "settingsShowBottomNavigationBar": "Näita alumist liikumisriba", + "@settingsShowBottomNavigationBar": {}, + "settingsKeepScreenOnTile": "Hoia ekraan sisselülitatuna", + "@settingsKeepScreenOnTile": {}, + "settingsKeepScreenOnDialogTitle": "Hoia ekraan sisselülitatuna", + "@settingsKeepScreenOnDialogTitle": {}, + "settingsDoubleBackExit": "Väljumiseks klõpsi „Tagasi“ nuppu kaks korda", + "@settingsDoubleBackExit": {}, + "settingsConfirmationTile": "Kinnitused", + "@settingsConfirmationTile": {}, + "settingsConfirmationBeforeMoveToBinItems": "Küsi kinnitust enne objektide viskamist prügikasti", + "@settingsConfirmationBeforeMoveToBinItems": {}, + "settingsConfirmationBeforeMoveUndatedItems": "Küsi kinnitust enne kuupäevadeta objektide teisaldamist", + "@settingsConfirmationBeforeMoveUndatedItems": {}, + "settingsNavigationDrawerTile": "Ikooniriba", + "@settingsNavigationDrawerTile": {}, + "settingsNavigationDrawerBanner": "Menüüobjektide teisaldamiseks ja järjekorra muutmiseks vajuta ja hoia all.", + "@settingsNavigationDrawerBanner": {}, + "settingsThumbnailSectionTitle": "Pisipildid", + "@settingsThumbnailSectionTitle": {}, + "settingsThumbnailOverlayTile": "Ülekate", + "@settingsThumbnailOverlayTile": {}, + "settingsThumbnailShowHdrIcon": "Näita HDR-ikooni", + "@settingsThumbnailShowHdrIcon": {}, + "settingsThumbnailShowFavouriteIcon": "Näita lemmikute ikooni", + "@settingsThumbnailShowFavouriteIcon": {}, + "settingsThumbnailShowTagIcon": "Näita siltide ikooni", + "@settingsThumbnailShowTagIcon": {}, + "settingsThumbnailShowLocationIcon": "Näita asukoha ikooni", + "@settingsThumbnailShowLocationIcon": {}, + "settingsThumbnailShowMotionPhotoIcon": "Näita liikuva foto ikooni", + "@settingsThumbnailShowMotionPhotoIcon": {}, + "settingsThumbnailShowRating": "Näita hinnangute ikooni", + "@settingsThumbnailShowRating": {}, + "settingsThumbnailShowRawIcon": "Näita töötlemata fotode ikooni", + "@settingsThumbnailShowRawIcon": {}, + "settingsThumbnailShowVideoDuration": "Näita videote kestust", + "@settingsThumbnailShowVideoDuration": {}, + "settingsCollectionQuickActionsTile": "Kiirtoimingud", + "@settingsCollectionQuickActionsTile": {}, + "settingsCollectionQuickActionEditorPageTitle": "Kiirtoimingud", + "@settingsCollectionQuickActionEditorPageTitle": {}, + "settingsCollectionQuickActionTabBrowsing": "Sirvimine", + "@settingsCollectionQuickActionTabBrowsing": {}, + "settingsCollectionQuickActionTabSelecting": "Valimine", + "@settingsCollectionQuickActionTabSelecting": {}, + "settingsCollectionBrowsingQuickActionEditorBanner": "Kui soovid valida, mis tegevused on saadaval objektide sirvimisel, siis vajuta ning hoia soovitud ikooni ning lohista ta vajalikku vaatesse.", + "@settingsCollectionBrowsingQuickActionEditorBanner": {}, + "settingsCollectionSelectionQuickActionEditorBanner": "Kui soovid valida, mis tegevused on saadaval objektide valimisel, siis vajuta ning hoia soovitud ikooni ning lohista ta vajalikku vaatesse.", + "@settingsCollectionSelectionQuickActionEditorBanner": {}, + "settingsCollectionBurstPatternsTile": "Laienda mustreid", + "@settingsCollectionBurstPatternsTile": {}, + "settingsCollectionBurstPatternsNone": "Määratlemata", + "@settingsCollectionBurstPatternsNone": {}, + "settingsViewerSectionTitle": "Pildivaataja", + "@settingsViewerSectionTitle": {}, + "searchCollectionFieldHint": "Otsi kogumikku", + "@searchCollectionFieldHint": {}, + "searchRecentSectionTitle": "Hiljutised", + "@searchRecentSectionTitle": {}, + "settingsNavigationDrawerTabTypes": "Tüübid", + "@settingsNavigationDrawerTabTypes": {}, + "settingsNavigationDrawerTabAlbums": "Albumid", + "@settingsNavigationDrawerTabAlbums": {}, + "settingsNavigationDrawerTabPages": "Lehed", + "@settingsNavigationDrawerTabPages": {}, + "settingsNavigationDrawerAddAlbum": "Lisa album", + "@settingsNavigationDrawerAddAlbum": {}, + "settingsConfirmationDialogTitle": "Kinnitused", + "@settingsConfirmationDialogTitle": {}, + "settingsConfirmationAfterMoveToBinItems": "Näita teadet peale objektide viskamist prügikasti", + "@settingsConfirmationAfterMoveToBinItems": {}, + "settingsConfirmationBeforeDeleteItems": "Küsi kinnitust enne objektide lõplikku kustutamist", + "@settingsConfirmationBeforeDeleteItems": {}, + "settingsThumbnailOverlayPageTitle": "Ülekate", + "@settingsThumbnailOverlayPageTitle": {}, + "settingsNavigationDrawerEditorPageTitle": "Ikooniriba", + "@settingsNavigationDrawerEditorPageTitle": {} } diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb index d654c6552..67ff35978 100644 --- a/lib/l10n/app_fr.arb +++ b/lib/l10n/app_fr.arb @@ -355,7 +355,7 @@ "@setCoverDialogCustom": {}, "hideFilterConfirmationDialogMessage": "Les images et vidéos correspondantes n’apparaîtront plus dans votre collection. Vous pouvez les montrer à nouveau via les réglages de « Confidentialité ».\n\nVoulez-vous vraiment les masquer ?", "@hideFilterConfirmationDialogMessage": {}, - "newAlbumDialogTitle": "Nouvel Album", + "newAlbumDialogTitle": "Nouvel album", "@newAlbumDialogTitle": {}, "newAlbumDialogNameLabel": "Nom de l’album", "@newAlbumDialogNameLabel": {}, @@ -1392,5 +1392,17 @@ "videoActionShowPreviousFrame": "Montrer l’image précédente", "@videoActionShowPreviousFrame": {}, "newAlbumDialogAlbumAlreadyExistsHelper": "L’album existe déjà", - "@newAlbumDialogAlbumAlreadyExistsHelper": {} + "@newAlbumDialogAlbumAlreadyExistsHelper": {}, + "chipActionRemove": "Retirer", + "@chipActionRemove": {}, + "albumTierDynamic": "Dynamique", + "@albumTierDynamic": {}, + "newDynamicAlbumDialogTitle": "Nouvel album dynamique", + "@newDynamicAlbumDialogTitle": {}, + "dynamicAlbumAlreadyExists": "L’album dynamique existe déjà", + "@dynamicAlbumAlreadyExists": {}, + "appExportDynamicAlbums": "Albums dynamiques", + "@appExportDynamicAlbums": {}, + "collectionActionAddDynamicAlbum": "Ajouter un album dynamique", + "@collectionActionAddDynamicAlbum": {} } diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index 7883a79f3..4e024279e 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -1367,7 +1367,7 @@ "@selectStorageVolumeDialogTitle": {}, "setHomeCustom": "Kustom", "@setHomeCustom": {}, - "explorerPageTitle": "Explorer", + "explorerPageTitle": "Penjelajah", "@explorerPageTitle": {}, "sortOrderLongestFirst": "Yang terpanjang dulu", "@sortOrderLongestFirst": {}, @@ -1375,7 +1375,7 @@ "@sortByDuration": {}, "sortOrderShortestFirst": "Yang terpendek dulu", "@sortOrderShortestFirst": {}, - "chipActionGoToExplorerPage": "Tampilkan di Explorer", + "chipActionGoToExplorerPage": "Tampilkan di Penjelajah", "@chipActionGoToExplorerPage": {}, "mapAttributionOsmData": "Data peta © [OpenStreetMap](https://www.openstreetmap.org/copyright) kontributor", "@mapAttributionOsmData": {}, @@ -1392,5 +1392,17 @@ "videoActionShowNextFrame": "Tampilkan bingkai berikutnya", "@videoActionShowNextFrame": {}, "newAlbumDialogAlbumAlreadyExistsHelper": "Album sudah ada", - "@newAlbumDialogAlbumAlreadyExistsHelper": {} + "@newAlbumDialogAlbumAlreadyExistsHelper": {}, + "chipActionRemove": "Hapus", + "@chipActionRemove": {}, + "collectionActionAddDynamicAlbum": "Tambahkan album dinamis", + "@collectionActionAddDynamicAlbum": {}, + "appExportDynamicAlbums": "Album dinamis", + "@appExportDynamicAlbums": {}, + "albumTierDynamic": "Dinamis", + "@albumTierDynamic": {}, + "dynamicAlbumAlreadyExists": "Album dinamis sudah ada", + "@dynamicAlbumAlreadyExists": {}, + "newDynamicAlbumDialogTitle": "Album Dinamis Baru", + "@newDynamicAlbumDialogTitle": {} } diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb index 563943a31..0586f62c3 100644 --- a/lib/l10n/app_it.arb +++ b/lib/l10n/app_it.arb @@ -1,7 +1,7 @@ { "appName": "Aves", "@appName": {}, - "welcomeMessage": "Benvenuto in Aves", + "welcomeMessage": "Benvenuto ad Aves", "@welcomeMessage": {}, "welcomeOptional": "Opzionale", "@welcomeOptional": {}, @@ -639,21 +639,21 @@ "@sortByAlbumFileName": {}, "sortByRating": "Per valutazione", "@sortByRating": {}, - "sortOrderNewestFirst": "Prima i più nuovi", + "sortOrderNewestFirst": "Dal più nuovo", "@sortOrderNewestFirst": {}, - "sortOrderOldestFirst": "Prima i più vecchi", + "sortOrderOldestFirst": "Dal più vecchio", "@sortOrderOldestFirst": {}, "sortOrderAtoZ": "Dalla A alla Z", "@sortOrderAtoZ": {}, "sortOrderZtoA": "Dalla Z alla A", "@sortOrderZtoA": {}, - "sortOrderHighestFirst": "Prima le più alte", + "sortOrderHighestFirst": "Dalla più alta", "@sortOrderHighestFirst": {}, "sortOrderLowestFirst": "Prima le più basse", "@sortOrderLowestFirst": {}, - "sortOrderLargestFirst": "Prima i più grandi", + "sortOrderLargestFirst": "Dal più grande", "@sortOrderLargestFirst": {}, - "sortOrderSmallestFirst": "Prima i più piccoli", + "sortOrderSmallestFirst": "Dal più piccolo", "@sortOrderSmallestFirst": {}, "albumGroupTier": "Per importanza", "@albumGroupTier": {}, @@ -1067,7 +1067,7 @@ "@mapZoomOutTooltip": {}, "mapPointNorthUpTooltip": "Punta a nord verso l’alto", "@mapPointNorthUpTooltip": {}, - "mapAttributionOsmHot": "Titoli di [HOT](https://www.hotosm.org/) • Ospitato da [OSM France](https://openstreetmap.fr/)", + "mapAttributionOsmHot": "Tasselli di [HOT](https://www.hotosm.org/) • Ospitato da [OSM France](https://openstreetmap.fr/)", "@mapAttributionOsmHot": {}, "mapAttributionStamen": "Titoli di [Stamen Design](https://stamen.com), [CC BY 3.0](https://creativecommons.org/licenses/by/3.0)", "@mapAttributionStamen": {}, @@ -1363,9 +1363,9 @@ "@settingsForceWesternArabicNumeralsTile": {}, "sortByDuration": "Per durata", "@sortByDuration": {}, - "sortOrderLongestFirst": "Prima i più lunghi", + "sortOrderLongestFirst": "Dal più lungo", "@sortOrderLongestFirst": {}, - "sortOrderShortestFirst": "Prima i più corti", + "sortOrderShortestFirst": "Dal più corto", "@sortOrderShortestFirst": {}, "explorerPageTitle": "Esploratore", "@explorerPageTitle": {}, @@ -1378,5 +1378,31 @@ "chipActionGoToExplorerPage": "Mostra nell'Esploratore", "@chipActionGoToExplorerPage": {}, "mapAttributionOsmData": "Dati della mappa © collaboratori di [OpenStreetMap](https://www.openstreetmap.org/copyright)", - "@mapAttributionOsmData": {} + "@mapAttributionOsmData": {}, + "newAlbumDialogAlbumAlreadyExistsHelper": "Album già esistente", + "@newAlbumDialogAlbumAlreadyExistsHelper": {}, + "chipActionRemove": "Rimuovi", + "@chipActionRemove": {}, + "videoActionShowPreviousFrame": "Mostra fotogramma precedente", + "@videoActionShowPreviousFrame": {}, + "videoActionShowNextFrame": "Mostra fotogramma successivo", + "@videoActionShowNextFrame": {}, + "albumTierDynamic": "Dinamico", + "@albumTierDynamic": {}, + "newDynamicAlbumDialogTitle": "Nuovo Album Dinamico", + "@newDynamicAlbumDialogTitle": {}, + "collectionActionAddDynamicAlbum": "Aggiunti album dinamico", + "@collectionActionAddDynamicAlbum": {}, + "appExportDynamicAlbums": "Album dinamici", + "@appExportDynamicAlbums": {}, + "dynamicAlbumAlreadyExists": "Album dinamico già esistente", + "@dynamicAlbumAlreadyExists": {}, + "mapAttributionOpenTopoMap": "[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | Tasselli di [OpenTopoMap](https://opentopomap.org/), [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)", + "@mapAttributionOpenTopoMap": {}, + "mapStyleOsmLiberty": "OSM Liberty", + "@mapStyleOsmLiberty": {}, + "mapStyleOpenTopoMap": "OpenTopoMap", + "@mapStyleOpenTopoMap": {}, + "mapAttributionOsmLiberty": "Tasselli di [OpenMapTiles](https://www.openmaptiles.org/), [CC BY](http://creativecommons.org/licenses/by/4.0) • Ospitato da [OSM Americana](https://tile.ourmap.us)", + "@mapAttributionOsmLiberty": {} } diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb index 18c0de660..0794fc876 100644 --- a/lib/l10n/app_ko.arb +++ b/lib/l10n/app_ko.arb @@ -1392,5 +1392,17 @@ "videoActionShowPreviousFrame": "이전 프레임 보기", "@videoActionShowPreviousFrame": {}, "newAlbumDialogAlbumAlreadyExistsHelper": "앨범이 이미 있습니다", - "@newAlbumDialogAlbumAlreadyExistsHelper": {} + "@newAlbumDialogAlbumAlreadyExistsHelper": {}, + "chipActionRemove": "제거", + "@chipActionRemove": {}, + "albumTierDynamic": "동적", + "@albumTierDynamic": {}, + "newDynamicAlbumDialogTitle": "새 동적 앨범", + "@newDynamicAlbumDialogTitle": {}, + "dynamicAlbumAlreadyExists": "동적 앨범이 이미 있습니다", + "@dynamicAlbumAlreadyExists": {}, + "collectionActionAddDynamicAlbum": "동적 앨범 추가", + "@collectionActionAddDynamicAlbum": {}, + "appExportDynamicAlbums": "동적 앨범", + "@appExportDynamicAlbums": {} } diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb index 5b16b8702..5f9521db8 100644 --- a/lib/l10n/app_nl.arb +++ b/lib/l10n/app_nl.arb @@ -1394,5 +1394,17 @@ "videoActionShowPreviousFrame": "Vorig frame weergeven", "@videoActionShowPreviousFrame": {}, "newAlbumDialogAlbumAlreadyExistsHelper": "Album bestaat al", - "@newAlbumDialogAlbumAlreadyExistsHelper": {} + "@newAlbumDialogAlbumAlreadyExistsHelper": {}, + "albumTierDynamic": "Dynamisch", + "@albumTierDynamic": {}, + "newDynamicAlbumDialogTitle": "Nieuw dynamisch album", + "@newDynamicAlbumDialogTitle": {}, + "dynamicAlbumAlreadyExists": "Dynamisch album bestaat al", + "@dynamicAlbumAlreadyExists": {}, + "collectionActionAddDynamicAlbum": "Dynamisch album toevoegen", + "@collectionActionAddDynamicAlbum": {}, + "appExportDynamicAlbums": "Dynamische albums", + "@appExportDynamicAlbums": {}, + "chipActionRemove": "Verwijderen", + "@chipActionRemove": {} } diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index a25b65419..bb51f823a 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -1550,5 +1550,17 @@ "videoActionShowNextFrame": "Pokaż kolejną klatkę", "@videoActionShowNextFrame": {}, "newAlbumDialogAlbumAlreadyExistsHelper": "Album już istnieje", - "@newAlbumDialogAlbumAlreadyExistsHelper": {} + "@newAlbumDialogAlbumAlreadyExistsHelper": {}, + "chipActionRemove": "Usuń", + "@chipActionRemove": {}, + "albumTierDynamic": "Dynamiczny", + "@albumTierDynamic": {}, + "dynamicAlbumAlreadyExists": "Dynamiczny album już istnieje", + "@dynamicAlbumAlreadyExists": {}, + "newDynamicAlbumDialogTitle": "Nowy dynamiczny album", + "@newDynamicAlbumDialogTitle": {}, + "collectionActionAddDynamicAlbum": "Dodaj dynamiczny album", + "@collectionActionAddDynamicAlbum": {}, + "appExportDynamicAlbums": "Dynamiczne albumy", + "@appExportDynamicAlbums": {} } diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb index b31d388d4..a85964f7f 100644 --- a/lib/l10n/app_sk.arb +++ b/lib/l10n/app_sk.arb @@ -1548,5 +1548,19 @@ "videoActionShowPreviousFrame": "Zobraziť predchádzajúci rám", "@videoActionShowPreviousFrame": {}, "videoActionShowNextFrame": "Zobraziť ďalší rám", - "@videoActionShowNextFrame": {} + "@videoActionShowNextFrame": {}, + "newAlbumDialogAlbumAlreadyExistsHelper": "Album už existuje", + "@newAlbumDialogAlbumAlreadyExistsHelper": {}, + "appExportDynamicAlbums": "Dynamické albumy", + "@appExportDynamicAlbums": {}, + "collectionActionAddDynamicAlbum": "Pridať dynamický album", + "@collectionActionAddDynamicAlbum": {}, + "chipActionRemove": "Odstrániť", + "@chipActionRemove": {}, + "albumTierDynamic": "Dynamické", + "@albumTierDynamic": {}, + "newDynamicAlbumDialogTitle": "Nový dynamický album", + "@newDynamicAlbumDialogTitle": {}, + "dynamicAlbumAlreadyExists": "Dynamický album už existuje", + "@dynamicAlbumAlreadyExists": {} } diff --git a/lib/l10n/app_sv.arb b/lib/l10n/app_sv.arb index cf8ed13f6..59775feb6 100644 --- a/lib/l10n/app_sv.arb +++ b/lib/l10n/app_sv.arb @@ -1572,5 +1572,17 @@ "videoActionShowNextFrame": "Visa nästa bildruta", "@videoActionShowNextFrame": {}, "newAlbumDialogAlbumAlreadyExistsHelper": "Albumet existerar redan", - "@newAlbumDialogAlbumAlreadyExistsHelper": {} + "@newAlbumDialogAlbumAlreadyExistsHelper": {}, + "chipActionRemove": "Ta bort", + "@chipActionRemove": {}, + "albumTierDynamic": "Dynamisk", + "@albumTierDynamic": {}, + "newDynamicAlbumDialogTitle": "Nytt dynamiskt album", + "@newDynamicAlbumDialogTitle": {}, + "collectionActionAddDynamicAlbum": "Lägg till dynamiskt album", + "@collectionActionAddDynamicAlbum": {}, + "appExportDynamicAlbums": "Dynamiska album", + "@appExportDynamicAlbums": {}, + "dynamicAlbumAlreadyExists": "Dynamiskt album existerar redan", + "@dynamicAlbumAlreadyExists": {} } diff --git a/lib/l10n/app_ta.arb b/lib/l10n/app_ta.arb new file mode 100644 index 000000000..e98faebdc --- /dev/null +++ b/lib/l10n/app_ta.arb @@ -0,0 +1,1606 @@ +{ + "editEntryDateDialogSourceFileModifiedDate": "கோப்பு மாற்றியமைக்கப்பட்ட தேதி", + "@editEntryDateDialogSourceFileModifiedDate": {}, + "durationDialogMinutes": "நிமையங்கள்", + "@durationDialogMinutes": {}, + "durationDialogSeconds": "விநாடிகள்", + "@durationDialogSeconds": {}, + "editEntryLocationDialogTitle": "இடம்", + "@editEntryLocationDialogTitle": {}, + "editEntryLocationDialogSetCustom": "தனிப்பயன் இருப்பிடத்தை அமை", + "@editEntryLocationDialogSetCustom": {}, + "editEntryLocationDialogChooseOnMap": "வரைபடத்தில் தேர்வு செய்", + "@editEntryLocationDialogChooseOnMap": {}, + "editEntryLocationDialogLatitude": "அகலாங்கு", + "@editEntryLocationDialogLatitude": {}, + "editEntryLocationDialogLongitude": "நெட்டாங்கு", + "@editEntryLocationDialogLongitude": {}, + "locationPickerUseThisLocationButton": "இந்த இருப்பிடத்தைப் பயன்படுத்து", + "@locationPickerUseThisLocationButton": {}, + "editEntryRatingDialogTitle": "மதிப்பிடு", + "@editEntryRatingDialogTitle": {}, + "removeEntryMetadataDialogTitle": "மேனிலை தரவு அகற்றுதல்", + "@removeEntryMetadataDialogTitle": {}, + "removeEntryMetadataDialogMore": "மேலும்", + "@removeEntryMetadataDialogMore": {}, + "removeEntryMetadataMotionPhotoXmpWarningDialogMessage": "இயக்கபுகைப்படத்திற்குள் காணொளியை இயக்க எக்ச்எம்பி தேவை.\n\n நீங்கள் அதை அகற்ற விரும்புகிறீர்களா?", + "@removeEntryMetadataMotionPhotoXmpWarningDialogMessage": {}, + "videoSpeedDialogLabel": "பின்னணி விரைவு", + "@videoSpeedDialogLabel": {}, + "videoStreamSelectionDialogVideo": "காணொளி", + "@videoStreamSelectionDialogVideo": {}, + "videoStreamSelectionDialogAudio": "ஒலிதம்", + "@videoStreamSelectionDialogAudio": {}, + "videoStreamSelectionDialogText": "வசன வரிகள்", + "@videoStreamSelectionDialogText": {}, + "videoStreamSelectionDialogOff": "அணை", + "@videoStreamSelectionDialogOff": {}, + "videoStreamSelectionDialogTrack": "தடம்", + "@videoStreamSelectionDialogTrack": {}, + "videoStreamSelectionDialogNoSelection": "வேறு தடங்கள் இல்லை.", + "@videoStreamSelectionDialogNoSelection": {}, + "genericSuccessFeedback": "முடிந்தது!", + "@genericSuccessFeedback": {}, + "genericFailureFeedback": "தோல்வியுற்றது", + "@genericFailureFeedback": {}, + "genericDangerWarningDialogMessage": "நீங்கள் உறுதியாக இருக்கிறீர்களா?", + "@genericDangerWarningDialogMessage": {}, + "tooManyItemsErrorDialogMessage": "குறைவான உருப்படிகளுடன் மீண்டும் முயற்சி.", + "@tooManyItemsErrorDialogMessage": {}, + "menuActionConfigureView": "பார்வை", + "@menuActionConfigureView": {}, + "menuActionSelect": "தேர்ந்தெடு", + "@menuActionSelect": {}, + "menuActionSelectAll": "அனைத்தையும் தெரிவுசெய்", + "@menuActionSelectAll": {}, + "menuActionSelectNone": "எதுவுமில்லை என்பதைத் தேர்ந்தெடு", + "@menuActionSelectNone": {}, + "menuActionMap": "வரைபடம்", + "@menuActionMap": {}, + "menuActionSlideshow": "ச்லைடுசோ", + "@menuActionSlideshow": {}, + "menuActionStats": "புள்ளிவிவரங்கள்", + "@menuActionStats": {}, + "viewDialogSortSectionTitle": "வரிசைப்படுத்து", + "@viewDialogSortSectionTitle": {}, + "viewDialogGroupSectionTitle": "குழு", + "@viewDialogGroupSectionTitle": {}, + "viewDialogLayoutSectionTitle": "மனையமைவு", + "@viewDialogLayoutSectionTitle": {}, + "viewDialogReverseSortOrder": "தலைகீழ் வரிசை முறை", + "@viewDialogReverseSortOrder": {}, + "tileLayoutMosaic": "மொசைக்", + "@tileLayoutMosaic": {}, + "tileLayoutGrid": "வலைவாய்", + "@tileLayoutGrid": {}, + "tileLayoutList": "பட்டியல்", + "@tileLayoutList": {}, + "castDialogTitle": "நடிகர்கள் சாதனங்கள்", + "@castDialogTitle": {}, + "coverDialogTabCover": "கவர்", + "@coverDialogTabCover": {}, + "coverDialogTabApp": "பயன்பாடு", + "@coverDialogTabApp": {}, + "coverDialogTabColor": "நிறம்", + "@coverDialogTabColor": {}, + "appPickDialogTitle": "பயன்பாட்டைத் தேர்ந்தெடுங்கள்", + "@appPickDialogTitle": {}, + "appPickDialogNone": "எதுவுமில்லை", + "@appPickDialogNone": {}, + "aboutPageTitle": "பற்றி", + "@aboutPageTitle": {}, + "aboutLinkLicense": "உரிமம்", + "@aboutLinkLicense": {}, + "aboutLinkPolicy": "தனியுரிமைக் கொள்கை", + "@aboutLinkPolicy": {}, + "aboutBugSectionTitle": "பிழை அறிக்கை", + "@aboutBugSectionTitle": {}, + "aboutBugReportInstruction": "பதிவுகள் மற்றும் கணினி தகவல்களுடன் அறிவிலிமையம் குறித்த அறிக்கை", + "@aboutBugReportInstruction": {}, + "aboutBugReportButton": "அறிக்கை", + "@aboutBugReportButton": {}, + "aboutDataUsageSectionTitle": "தரவு பயன்பாடு", + "@aboutDataUsageSectionTitle": {}, + "aboutDataUsageData": "தகவல்கள்", + "@aboutDataUsageData": {}, + "aboutDataUsageCache": "கேச்", + "@aboutDataUsageCache": {}, + "aboutDataUsageDatabase": "தரவுத்தளம்", + "@aboutDataUsageDatabase": {}, + "aboutDataUsageMisc": "இதர", + "@aboutDataUsageMisc": {}, + "aboutDataUsageInternal": "உள்", + "@aboutDataUsageInternal": {}, + "aboutDataUsageExternal": "வெளிப்புறம்", + "@aboutDataUsageExternal": {}, + "aboutDataUsageClearCache": "தெளிவான தற்காலிக சேமிப்பு", + "@aboutDataUsageClearCache": {}, + "aboutCreditsSectionTitle": "வரவு", + "@aboutCreditsSectionTitle": {}, + "aboutCreditsWorldAtlas1": "இந்த பயன்பாடு ஒரு டோபோச்சன் கோப்பைப் பயன்படுத்துகிறது", + "@aboutCreditsWorldAtlas1": {}, + "aboutCreditsWorldAtlas2": "ஐ.எச்.சி உரிமத்தின் கீழ்.", + "@aboutCreditsWorldAtlas2": {}, + "aboutTranslatorsSectionTitle": "மொழிபெயர்ப்பாளர்கள்", + "@aboutTranslatorsSectionTitle": {}, + "aboutLicensesSectionTitle": "திறந்த மூல உரிமங்கள்", + "@aboutLicensesSectionTitle": {}, + "aboutLicensesBanner": "இந்த பயன்பாடு பின்வரும் திறந்த மூல தொகுப்புகள் மற்றும் நூலகங்களைப் பயன்படுத்துகிறது.", + "@aboutLicensesBanner": {}, + "aboutLicensesAndroidLibrariesSectionTitle": "ஆண்ட்ராய்டு நூலகங்கள்", + "@aboutLicensesAndroidLibrariesSectionTitle": {}, + "aboutLicensesFlutterPluginsSectionTitle": "Flutter செருகுநிரல்கள்", + "@aboutLicensesFlutterPluginsSectionTitle": {}, + "aboutLicensesFlutterPackagesSectionTitle": "Flutter தொகுப்புகள்", + "@aboutLicensesFlutterPackagesSectionTitle": {}, + "aboutLicensesDartPackagesSectionTitle": "டார்ட் தொகுப்புகள்", + "@aboutLicensesDartPackagesSectionTitle": {}, + "aboutLicensesShowAllButtonLabel": "அனைத்து உரிமங்களையும் காட்டு", + "@aboutLicensesShowAllButtonLabel": {}, + "policyPageTitle": "தனியுரிமைக் கொள்கை", + "@policyPageTitle": {}, + "collectionPageTitle": "சேகரிப்பு", + "@collectionPageTitle": {}, + "collectionPickPageTitle": "தேர்ந்தெடு", + "@collectionPickPageTitle": {}, + "collectionSelectPageTitle": "உருப்படிகளைத் தேர்ந்தெடுக்கவும்", + "@collectionSelectPageTitle": {}, + "collectionActionShowTitleSearch": "தலைப்பு வடிகட்டியைக் காட்டு", + "@collectionActionShowTitleSearch": {}, + "collectionActionHideTitleSearch": "தலைப்பு வடிகட்டியை மறைக்கவும்", + "@collectionActionHideTitleSearch": {}, + "collectionActionAddDynamicAlbum": "மாறும் ஆல்பத்தைச் சேர்க்கவும்", + "@collectionActionAddDynamicAlbum": {}, + "collectionActionEmptyBin": "வெற்று பின்", + "@collectionActionEmptyBin": {}, + "collectionActionCopy": "ஆல்பத்திற்கு நகலெடுக்கவும்", + "@collectionActionCopy": {}, + "collectionActionMove": "ஆல்பத்திற்கு நகர்த்தவும்", + "@collectionActionMove": {}, + "collectionActionRescan": "ரெச்கான்", + "@collectionActionRescan": {}, + "collectionActionEdit": "தொகு", + "@collectionActionEdit": {}, + "collectionGroupAlbum": "ஆல்பத்தால்", + "@collectionGroupAlbum": {}, + "collectionGroupMonth": "மாதத்திற்குள்", + "@collectionGroupMonth": {}, + "collectionGroupDay": "நாளுக்குள்", + "@collectionGroupDay": {}, + "collectionGroupNone": "குழு வேண்டாம்", + "@collectionGroupNone": {}, + "sectionUnknown": "தெரியவில்லை", + "@sectionUnknown": {}, + "dateToday": "இன்று", + "@dateToday": {}, + "dateYesterday": "நேற்று", + "@dateYesterday": {}, + "dateThisMonth": "இந்த மாதம்", + "@dateThisMonth": {}, + "appName": "ஏவ்ச்", + "@appName": {}, + "welcomeMessage": "ஏவ்ச்க்கு வருக", + "@welcomeMessage": {}, + "welcomeOptional": "விரும்பினால்", + "@welcomeOptional": {}, + "welcomeTermsToggle": "விதிமுறைகள் மற்றும் நிபந்தனைகளை நான் ஒப்புக்கொள்கிறேன்", + "@welcomeTermsToggle": {}, + "collectionRenameFailureFeedback": "{count, plural, =1{மறுபெயரிடுவதில் தோல்வி 1 உருப்படி} other{மறுபெயரிடுவதில் தோல்வி {count} உருப்படிகள்}}", + "@collectionRenameFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionEditFailureFeedback": "{count, plural, =1{திருத்துவதில் தோல்வியுற்றது 1 உருப்படி} other{திருத்துவதில் தோல்வியுற்றது {count} உருப்படிகள்}}", + "@collectionEditFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionExportFailureFeedback": "{count, plural, =1{ஏற்றுமதி செய்வதில் தோல்வி 1 பக்கம்} other{ஏற்றுமதி செய்வதில் தோல்வி {count} பக்கங்கள்}}", + "@collectionExportFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionCopySuccessFeedback": "{count, plural, =1{நகலெடுக்கப்பட்டது 1 உருப்படி} other{நகலெடுக்கப்பட்டது {count} உருப்படிகள்}}", + "@collectionCopySuccessFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionMoveSuccessFeedback": "{count, plural, =1{நகர்த்தப்பட்ட 1 உருப்படி} other{நகர்த்தப்பட்ட {count} உருப்படிகள்}}", + "@collectionMoveSuccessFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionRenameSuccessFeedback": "{count, plural, =1{மறுபெயரிடப்பட்டது 1 உருப்படி} other{மறுபெயரிடப்பட்டது {count} உருப்படிகள்}}", + "@collectionRenameSuccessFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionEditSuccessFeedback": "{count, plural, =1{திருத்தப்பட்ட 1 உருப்படி} other{திருத்தப்பட்ட {count} உருப்படிகள்}}", + "@collectionEditSuccessFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionEmptyFavourites": "பிடித்தவை இல்லை", + "@collectionEmptyFavourites": {}, + "collectionEmptyVideos": "வீடியோக்கள் இல்லை", + "@collectionEmptyVideos": {}, + "collectionEmptyImages": "படங்கள் இல்லை", + "@collectionEmptyImages": {}, + "collectionEmptyGrantAccessButtonLabel": "அணுகல் வழங்கவும்", + "@collectionEmptyGrantAccessButtonLabel": {}, + "collectionSelectSectionTooltip": "பிரிவைத் தேர்ந்தெடுக்கவும்", + "@collectionSelectSectionTooltip": {}, + "collectionDeselectSectionTooltip": "பிரிவு தேர்வு", + "@collectionDeselectSectionTooltip": {}, + "drawerAboutButton": "பற்றி", + "@drawerAboutButton": {}, + "drawerSettingsButton": "அமைப்புகள்", + "@drawerSettingsButton": {}, + "drawerCollectionAll": "அனைத்து சேகரிப்பு", + "@drawerCollectionAll": {}, + "drawerCollectionFavourites": "பிடித்தவை", + "@drawerCollectionFavourites": {}, + "drawerCollectionImages": "படங்கள்", + "@drawerCollectionImages": {}, + "drawerCollectionVideos": "வீடியோக்கள்", + "@drawerCollectionVideos": {}, + "drawerCollectionPanoramas": "பனோரமாக்கள்", + "@drawerCollectionPanoramas": {}, + "drawerCollectionAnimated": "அனிமேசன்", + "@drawerCollectionAnimated": {}, + "drawerCollectionMotionPhotos": "இயக்க புகைப்படங்கள்", + "@drawerCollectionMotionPhotos": {}, + "drawerCollectionRaws": "மூல புகைப்படங்கள்", + "@drawerCollectionRaws": {}, + "drawerCollectionSphericalVideos": "360 ° வீடியோக்கள்", + "@drawerCollectionSphericalVideos": {}, + "drawerAlbumPage": "ஆல்பம்", + "@drawerAlbumPage": {}, + "drawerCountryPage": "நாடுகள்", + "@drawerCountryPage": {}, + "drawerPlacePage": "இடங்கள்", + "@drawerPlacePage": {}, + "drawerTagPage": "குறிச்சொற்கள்", + "@drawerTagPage": {}, + "sortByDate": "தேதி", + "@sortByDate": {}, + "sortByName": "பெயரால்", + "@sortByName": {}, + "sortByItemCount": "உருப்படி எண்ணிக்கை மூலம்", + "@sortByItemCount": {}, + "sortBySize": "அளவு மூலம்", + "@sortBySize": {}, + "sortByAlbumFileName": "ஆல்பம் & கோப்பு பெயர்", + "@sortByAlbumFileName": {}, + "sortByRating": "மதிப்பீடு மூலம்", + "@sortByRating": {}, + "sortByDuration": "காலப்படி", + "@sortByDuration": {}, + "sortOrderNewestFirst": "புதிய முதல்", + "@sortOrderNewestFirst": {}, + "sortOrderOldestFirst": "முதலில் பழமையானது", + "@sortOrderOldestFirst": {}, + "sortOrderAtoZ": "அ இருந்து ஔ", + "@sortOrderAtoZ": {}, + "sortOrderZtoA": "ஔ இருந்து அ", + "@sortOrderZtoA": {}, + "sortOrderHighestFirst": "முதலில் அதிகபட்சம்", + "@sortOrderHighestFirst": {}, + "sortOrderLowestFirst": "முதலில் மிகக் குறைவு", + "@sortOrderLowestFirst": {}, + "sortOrderLargestFirst": "முதல் பெரியது", + "@sortOrderLargestFirst": {}, + "sortOrderSmallestFirst": "முதலில் சிறியது", + "@sortOrderSmallestFirst": {}, + "sortOrderShortestFirst": "சிறியது முதலில்", + "@sortOrderShortestFirst": {}, + "sortOrderLongestFirst": "முதலில் நீளமானது", + "@sortOrderLongestFirst": {}, + "albumGroupTier": "வழங்கியவர்", + "@albumGroupTier": {}, + "albumGroupType": "வகை", + "@albumGroupType": {}, + "albumGroupVolume": "சேமிப்பக அளவு மூலம்", + "@albumGroupVolume": {}, + "albumGroupNone": "குழு வேண்டாம்", + "@albumGroupNone": {}, + "albumMimeTypeMixed": "கலப்பு", + "@albumMimeTypeMixed": {}, + "albumPickPageTitleCopy": "ஆல்பத்திற்கு நகலெடுக்கவும்", + "@albumPickPageTitleCopy": {}, + "albumPickPageTitleExport": "ஆல்பத்திற்கு ஏற்றுமதி", + "@albumPickPageTitleExport": {}, + "albumPickPageTitleMove": "ஆல்பத்திற்கு நகர்த்தவும்", + "@albumPickPageTitleMove": {}, + "albumPickPageTitlePick": "ஆல்பத்தைத் தேர்ந்தெடுங்கள்", + "@albumPickPageTitlePick": {}, + "albumCamera": "கேமரா", + "@albumCamera": {}, + "albumDownload": "பதிவிறக்கம்", + "@albumDownload": {}, + "albumScreenshots": "திரைக்காட்சிகள்", + "@albumScreenshots": {}, + "albumScreenRecordings": "திரை பதிவுகள்", + "@albumScreenRecordings": {}, + "albumVideoCaptures": "வீடியோ கைப்பற்றுகிறது", + "@albumVideoCaptures": {}, + "albumPageTitle": "ஆல்பம்", + "@albumPageTitle": {}, + "albumEmpty": "ஆல்பங்கள் இல்லை", + "@albumEmpty": {}, + "createAlbumButtonLabel": "உருவாக்கு", + "@createAlbumButtonLabel": {}, + "newFilterBanner": "புதிய", + "@newFilterBanner": {}, + "countryPageTitle": "நாடுகள்", + "@countryPageTitle": {}, + "countryEmpty": "எந்த நாடுகளும் இல்லை", + "@countryEmpty": {}, + "statePageTitle": "மாநிலங்கள்", + "@statePageTitle": {}, + "stateEmpty": "மாநிலங்கள் இல்லை", + "@stateEmpty": {}, + "placePageTitle": "இடங்கள்", + "@placePageTitle": {}, + "placeEmpty": "இடங்கள் இல்லை", + "@placeEmpty": {}, + "tagEmpty": "குறிச்சொற்கள் இல்லை", + "@tagEmpty": {}, + "binPageTitle": "மறுசுழற்சி பின்", + "@binPageTitle": {}, + "explorerPageTitle": "எக்ச்ப்ளோரர்", + "@explorerPageTitle": {}, + "explorerActionSelectStorageVolume": "சேமிப்பிடத்தைத் தேர்ந்தெடுக்கவும்", + "@explorerActionSelectStorageVolume": {}, + "selectStorageVolumeDialogTitle": "சேமிப்பிடத்தைத் தேர்ந்தெடுக்கவும்", + "@selectStorageVolumeDialogTitle": {}, + "searchCollectionFieldHint": "தேடல் சேகரிப்பு", + "@searchCollectionFieldHint": {}, + "searchRecentSectionTitle": "அண்மைக் கால", + "@searchRecentSectionTitle": {}, + "searchDateSectionTitle": "திகதி", + "@searchDateSectionTitle": {}, + "searchAlbumsSectionTitle": "ஆல்பம்", + "@searchAlbumsSectionTitle": {}, + "searchCountriesSectionTitle": "நாடுகள்", + "@searchCountriesSectionTitle": {}, + "searchStatesSectionTitle": "மாநிலங்கள்", + "@searchStatesSectionTitle": {}, + "searchPlacesSectionTitle": "இடங்கள்", + "@searchPlacesSectionTitle": {}, + "searchTagsSectionTitle": "குறிச்சொற்கள்", + "@searchTagsSectionTitle": {}, + "searchRatingSectionTitle": "மதிப்பீடுகள்", + "@searchRatingSectionTitle": {}, + "searchMetadataSectionTitle": "மேனிலை தரவு", + "@searchMetadataSectionTitle": {}, + "settingsPageTitle": "அமைப்புகள்", + "@settingsPageTitle": {}, + "settingsSystemDefault": "கணினி இயல்புநிலை", + "@settingsSystemDefault": {}, + "settingsDefault": "இயல்புநிலை", + "@settingsDefault": {}, + "settingsDisabled": "முடக்கப்பட்டது", + "@settingsDisabled": {}, + "settingsAskEverytime": "ஒவ்வொரு நேரத்திலும் கேளுங்கள்", + "@settingsAskEverytime": {}, + "settingsModificationWarningDialogMessage": "மற்ற அமைப்புகள் மாற்றப்படும்.", + "@settingsModificationWarningDialogMessage": {}, + "settingsSearchFieldLabel": "அமைப்புகளைத் தேடுங்கள்", + "@settingsSearchFieldLabel": {}, + "settingsSearchEmpty": "பொருந்தக்கூடிய அமைப்பு இல்லை", + "@settingsSearchEmpty": {}, + "settingsActionExport": "ஏற்றுமதி", + "@settingsActionExport": {}, + "settingsActionExportDialogTitle": "ஏற்றுமதி", + "@settingsActionExportDialogTitle": {}, + "settingsActionImport": "இறக்குமதி", + "@settingsActionImport": {}, + "settingsActionImportDialogTitle": "இறக்குமதி", + "@settingsActionImportDialogTitle": {}, + "appExportCovers": "மறையம்", + "@appExportCovers": {}, + "appExportDynamicAlbums": "மாறும் ஆல்பங்கள்", + "@appExportDynamicAlbums": {}, + "appExportFavourites": "பிடித்தவை", + "@appExportFavourites": {}, + "appExportSettings": "அமைப்புகள்", + "@appExportSettings": {}, + "settingsNavigationSectionTitle": "வானோடல்", + "@settingsNavigationSectionTitle": {}, + "settingsHomeTile": "வீடு", + "@settingsHomeTile": {}, + "settingsHomeDialogTitle": "வீடு", + "@settingsHomeDialogTitle": {}, + "setHomeCustom": "தனிப்பயன்", + "@setHomeCustom": {}, + "settingsShowBottomNavigationBar": "கீழே உள்ள வழிசெலுத்தல் பட்டியைக் காட்டு", + "@settingsShowBottomNavigationBar": {}, + "settingsKeepScreenOnTile": "திரையை தொடர்ந்து வைத்திருங்கள்", + "@settingsKeepScreenOnTile": {}, + "settingsKeepScreenOnDialogTitle": "திரையை தொடர்ந்து வைத்திருங்கள்", + "@settingsKeepScreenOnDialogTitle": {}, + "settingsConfirmationTile": "உறுதிப்படுத்தல் உரையாடல்கள்", + "@settingsConfirmationTile": {}, + "settingsConfirmationDialogTitle": "உறுதிப்படுத்தல் உரையாடல்கள்", + "@settingsConfirmationDialogTitle": {}, + "settingsConfirmationBeforeDeleteItems": "பொருட்களை எப்போதும் நீக்குவதற்கு முன் கேளுங்கள்", + "@settingsConfirmationBeforeDeleteItems": {}, + "settingsConfirmationBeforeMoveToBinItems": "மறுசுழற்சி தொட்டியில் பொருட்களை நகர்த்துவதற்கு முன் கேளுங்கள்", + "@settingsConfirmationBeforeMoveToBinItems": {}, + "settingsConfirmationBeforeMoveUndatedItems": "மதிப்பிடப்படாத பொருட்களை நகர்த்துவதற்கு முன் கேளுங்கள்", + "@settingsConfirmationBeforeMoveUndatedItems": {}, + "settingsConfirmationAfterMoveToBinItems": "மறுசுழற்சி தொட்டியில் உருப்படிகளை நகர்த்திய பின் செய்தியைக் காட்டு", + "@settingsConfirmationAfterMoveToBinItems": {}, + "settingsConfirmationVaultDataLoss": "வால்ட் தரவு இழப்பு எச்சரிக்கையைக் காட்டு", + "@settingsConfirmationVaultDataLoss": {}, + "settingsNavigationDrawerTile": "வழிசெலுத்தல் பட்டியல்", + "@settingsNavigationDrawerTile": {}, + "settingsNavigationDrawerEditorPageTitle": "வழிசெலுத்தல் பட்டியல்", + "@settingsNavigationDrawerEditorPageTitle": {}, + "settingsNavigationDrawerBanner": "பட்டியல் உருப்படிகளை நகர்த்தவும் மறுவரிசைப்படுத்தவும் தொட்டுப் பிடிக்கவும்.", + "@settingsNavigationDrawerBanner": {}, + "settingsNavigationDrawerTabTypes": "வகைகள்", + "@settingsNavigationDrawerTabTypes": {}, + "settingsNavigationDrawerTabAlbums": "ஆல்பம்", + "@settingsNavigationDrawerTabAlbums": {}, + "settingsNavigationDrawerTabPages": "பக்கங்கள்", + "@settingsNavigationDrawerTabPages": {}, + "settingsNavigationDrawerAddAlbum": "ஆல்பத்தைச் சேர்க்கவும்", + "@settingsNavigationDrawerAddAlbum": {}, + "settingsThumbnailSectionTitle": "சிறு உருவங்கள்", + "@settingsThumbnailSectionTitle": {}, + "settingsThumbnailOverlayTile": "மேலடுக்கு", + "@settingsThumbnailOverlayTile": {}, + "settingsThumbnailOverlayPageTitle": "மேலடுக்கு", + "@settingsThumbnailOverlayPageTitle": {}, + "settingsThumbnailShowHdrIcon": "எச்டிஆர் ஐகானைக் காட்டு", + "@settingsThumbnailShowHdrIcon": {}, + "settingsThumbnailShowFavouriteIcon": "பிடித்த ஐகானைக் காட்டு", + "@settingsThumbnailShowFavouriteIcon": {}, + "settingsThumbnailShowTagIcon": "டேக் ஐகானைக் காட்டு", + "@settingsThumbnailShowTagIcon": {}, + "settingsThumbnailShowLocationIcon": "இருப்பிட ஐகானைக் காட்டு", + "@settingsThumbnailShowLocationIcon": {}, + "settingsThumbnailShowMotionPhotoIcon": "மோசன் ஃபோட்டோ ஐகானைக் காட்டு", + "@settingsThumbnailShowMotionPhotoIcon": {}, + "settingsThumbnailShowRating": "மதிப்பீட்டைக் காட்டு", + "@settingsThumbnailShowRating": {}, + "settingsThumbnailShowRawIcon": "மூல ஐகானைக் காட்டு", + "@settingsThumbnailShowRawIcon": {}, + "settingsThumbnailShowVideoDuration": "காட்டு ஒளிதோற்றம் duration", + "@settingsThumbnailShowVideoDuration": {}, + "settingsCollectionQuickActionsTile": "விரைவான செயல்கள்", + "@settingsCollectionQuickActionsTile": {}, + "settingsCollectionQuickActionEditorPageTitle": "விரைவான செயல்கள்", + "@settingsCollectionQuickActionEditorPageTitle": {}, + "settingsCollectionQuickActionTabBrowsing": "உலாவுதல்", + "@settingsCollectionQuickActionTabBrowsing": {}, + "settingsCollectionQuickActionTabSelecting": "தேர்ந்தெடுப்பது", + "@settingsCollectionQuickActionTabSelecting": {}, + "settingsCollectionBrowsingQuickActionEditorBanner": "பொத்தான்களை நகர்த்தவும், உருப்படிகளை உலாவும்போது எந்த நடவடிக்கைகள் காண்பிக்கப்படுகின்றன என்பதைத் தேர்ந்தெடுக்கவும்.", + "@settingsCollectionBrowsingQuickActionEditorBanner": {}, + "settingsCollectionBurstPatternsNone": "எதுவுமில்லை", + "@settingsCollectionBurstPatternsNone": {}, + "settingsViewerSectionTitle": "பார்வையாளர்", + "@settingsViewerSectionTitle": {}, + "settingsCollectionSelectionQuickActionEditorBanner": "பொத்தான்களை நகர்த்தவும், உருப்படிகளைத் தேர்ந்தெடுக்கும்போது எந்த நடவடிக்கைகள் காண்பிக்கப்படுகின்றன என்பதைத் தேர்ந்தெடுக்கவும்.", + "@settingsCollectionSelectionQuickActionEditorBanner": {}, + "settingsViewerGestureSideTapNext": "முந்தைய/அடுத்த உருப்படியைக் காட்ட திரை விளிம்புகளில் தட்டவும்", + "@settingsViewerGestureSideTapNext": {}, + "settingsViewerUseCutout": "கட்அவுட் பகுதியைப் பயன்படுத்தவும்", + "@settingsViewerUseCutout": {}, + "settingsViewerMaximumBrightness": "அதிகபட்ச ஒளி", + "@settingsViewerMaximumBrightness": {}, + "settingsMotionPhotoAutoPlay": "ஆட்டோ பிளே மோசன் புகைப்படங்கள்", + "@settingsMotionPhotoAutoPlay": {}, + "settingsImageBackground": "பட பின்னணி", + "@settingsImageBackground": {}, + "settingsViewerQuickActionsTile": "விரைவான செயல்கள்", + "@settingsViewerQuickActionsTile": {}, + "settingsViewerQuickActionEditorDisplayedButtonsSectionTitle": "காட்டப்பட்ட பொத்தான்கள்", + "@settingsViewerQuickActionEditorDisplayedButtonsSectionTitle": {}, + "settingsViewerQuickActionEditorPageTitle": "விரைவான செயல்கள்", + "@settingsViewerQuickActionEditorPageTitle": {}, + "settingsViewerQuickActionEditorBanner": "பொத்தான்களை நகர்த்தவும், பார்வையாளரில் எந்த நடவடிக்கைகள் காண்பிக்கப்படுகின்றன என்பதைத் தேர்ந்தெடுக்கவும்.", + "@settingsViewerQuickActionEditorBanner": {}, + "settingsViewerQuickActionEditorAvailableButtonsSectionTitle": "கிடைக்கும் பொத்தான்கள்", + "@settingsViewerQuickActionEditorAvailableButtonsSectionTitle": {}, + "settingsViewerOverlayTile": "மேலடுக்கு", + "@settingsViewerOverlayTile": {}, + "settingsViewerOverlayPageTitle": "மேலடுக்கு", + "@settingsViewerOverlayPageTitle": {}, + "settingsViewerShowOverlayOnOpening": "திறப்பதில் காட்டு", + "@settingsViewerShowOverlayOnOpening": {}, + "settingsViewerShowHistogram": "இச்டோகிராம் காட்டு", + "@settingsViewerShowHistogram": {}, + "settingsViewerShowMinimap": "மினிமேப்பைக் காட்டு", + "@settingsViewerShowMinimap": {}, + "settingsViewerShowInformation": "தகவலைக் காட்டு", + "@settingsViewerShowInformation": {}, + "settingsViewerShowInformationSubtitle": "தலைப்பு, தேதி, இடம் போன்றவற்றைக் காட்டு.", + "@settingsViewerShowInformationSubtitle": {}, + "settingsViewerShowRatingTags": "மதிப்பீடு மற்றும் குறிச்சொற்களைக் காட்டு", + "@settingsViewerShowRatingTags": {}, + "settingsViewerShowShootingDetails": "படப்பிடிப்பு விவரங்களைக் காட்டு", + "@settingsViewerShowShootingDetails": {}, + "settingsViewerShowDescription": "விளக்கத்தைக் காட்டு", + "@settingsViewerShowDescription": {}, + "settingsViewerShowOverlayThumbnails": "சிறு உருவங்களைக் காட்டு", + "@settingsViewerShowOverlayThumbnails": {}, + "settingsViewerEnableOverlayBlurEffect": "மங்கலான விளைவு", + "@settingsViewerEnableOverlayBlurEffect": {}, + "settingsSlideshowShuffle": "கலக்கு", + "@settingsSlideshowShuffle": {}, + "settingsSlideshowFillScreen": "திரை நிரப்பவும்", + "@settingsSlideshowFillScreen": {}, + "settingsSlideshowAnimatedZoomEffect": "அனிமேசன் சூம் விளைவு", + "@settingsSlideshowAnimatedZoomEffect": {}, + "settingsSlideshowTransitionTile": "மாற்றம்", + "@settingsSlideshowTransitionTile": {}, + "settingsSlideshowIntervalTile": "இடைவேளை", + "@settingsSlideshowIntervalTile": {}, + "settingsSlideshowVideoPlaybackTile": "வீடியோ பிளேபேக்", + "@settingsSlideshowVideoPlaybackTile": {}, + "settingsSlideshowVideoPlaybackDialogTitle": "வீடியோ பிளேபேக்", + "@settingsSlideshowVideoPlaybackDialogTitle": {}, + "settingsVideoPageTitle": "வீடியோ அமைப்புகள்", + "@settingsVideoPageTitle": {}, + "settingsVideoSectionTitle": "ஒளிதோற்றம்", + "@settingsVideoSectionTitle": {}, + "settingsVideoShowVideos": "வீடியோக்களைக் காட்டு", + "@settingsVideoShowVideos": {}, + "settingsVideoPlaybackTile": "பின்னணி", + "@settingsVideoPlaybackTile": {}, + "focalLength": "{length} மிமீ", + "@focalLength": { + "placeholders": { + "length": { + "type": "String", + "example": "5.4" + } + } + }, + "entryActionRename": "மறுபெயரிடு", + "@entryActionRename": {}, + "entryActionRestore": "மீட்டெடு", + "@entryActionRestore": {}, + "entryActionRotateCCW": "எதிரெதிர் திசையில் சுழற்று", + "@entryActionRotateCCW": {}, + "entryActionViewSource": "மூலத்தைக் காண்க", + "@entryActionViewSource": {}, + "entryActionShowGeoTiffOnMap": "வரைபட மேலடுக்கு எனக் காட்டு", + "@entryActionShowGeoTiffOnMap": {}, + "entryActionConvertMotionPhotoToStillImage": "நிலை படத்திற்கு மாற்று", + "@entryActionConvertMotionPhotoToStillImage": {}, + "entryActionViewMotionPhotoVideo": "திறந்த காணொளி", + "@entryActionViewMotionPhotoVideo": {}, + "entryActionEdit": "தொகு", + "@entryActionEdit": {}, + "videoActionReplay10": "10 வினாடிகள் பின்தங்கியபடி நாடிச்செல்", + "@videoActionReplay10": {}, + "videoActionSkip10": "10 வினாடிகள் முன்னோக்கி நாடிச்செல்", + "@videoActionSkip10": {}, + "filterNoTagLabel": "குறிச்சொல்இடப்படாதது", + "@filterNoTagLabel": {}, + "filterNoTitleLabel": "தலைப்பில்லாத", + "@filterNoTitleLabel": {}, + "filterOnThisDayLabel": "இந்த நாளில்", + "@filterOnThisDayLabel": {}, + "filterRecentlyAddedLabel": "அண்மைக் காலத்தில் சேர்க்கப்பட்டது", + "@filterRecentlyAddedLabel": {}, + "albumTierPinned": "பின்செய்த்து", + "@albumTierPinned": {}, + "overlayHistogramNone": "எதுவுமில்லை", + "@overlayHistogramNone": {}, + "videoPlaybackSkip": "தவிர்", + "@videoPlaybackSkip": {}, + "videoPlaybackMuted": "ஒலிமுடக்கி இயக்கு", + "@videoPlaybackMuted": {}, + "videoResumptionModeNever": "ஒருபோதும்", + "@videoResumptionModeNever": {}, + "viewerTransitionZoomIn": "பெரிதாக்கு", + "@viewerTransitionZoomIn": {}, + "viewerTransitionNone": "எதுவுமில்லை", + "@viewerTransitionNone": {}, + "wallpaperTargetHome": "முகப்புத் திரை", + "@wallpaperTargetHome": {}, + "otherDirectoryDescription": "“{name}” அடைவு", + "@otherDirectoryDescription": { + "placeholders": { + "name": { + "type": "String", + "example": "Pictures", + "description": "the name of a specific directory" + } + } + }, + "missingSystemFilePickerDialogMessage": "கணினி கோப்பு பிக்கர் காணவில்லை அல்லது முடக்கப்பட்டுள்ளது. தயவுசெய்து அதை இயக்கி மீண்டும் முயற்சிக்கவும்.", + "@missingSystemFilePickerDialogMessage": {}, + "nameConflictDialogMultipleSourceMessage": "சில கோப்புகளுக்கு ஒரே பெயர் உள்ளது.", + "@nameConflictDialogMultipleSourceMessage": {}, + "videoStartOverButtonLabel": "மறுதொடக்கம்", + "@videoStartOverButtonLabel": {}, + "videoResumeButtonLabel": "மீண்டும் தொடங்குங்கள்", + "@videoResumeButtonLabel": {}, + "setCoverDialogLatest": "அண்மைக் கால உருப்படி", + "@setCoverDialogLatest": {}, + "setCoverDialogAuto": "தானி", + "@setCoverDialogAuto": {}, + "newVaultDialogTitle": "புதிய பெட்டகம்", + "@newVaultDialogTitle": {}, + "vaultBinUsageDialogMessage": "சில பெட்டகங்கள் மறுசுழற்சி தொட்டியைப் பயன்படுத்துகின்றன.", + "@vaultBinUsageDialogMessage": {}, + "renameAlbumDialogLabel": "புதிய பெயர்", + "@renameAlbumDialogLabel": {}, + "renameEntrySetPageInsertTooltip": "புலம் செருகவும்", + "@renameEntrySetPageInsertTooltip": {}, + "renameEntrySetPagePreviewSectionTitle": "முன்னோட்டம்", + "@renameEntrySetPagePreviewSectionTitle": {}, + "renameProcessorCounter": "கணக்கிடு", + "@renameProcessorCounter": {}, + "renameProcessorHash": "கொத்து", + "@renameProcessorHash": {}, + "renameProcessorName": "பெயர்", + "@renameProcessorName": {}, + "exportEntryDialogWidth": "அகலம்", + "@exportEntryDialogWidth": {}, + "exportEntryDialogHeight": "உயரம்", + "@exportEntryDialogHeight": {}, + "editEntryDialogCopyFromItem": "பிற உருப்படியிலிருந்து நகலெடு", + "@editEntryDialogCopyFromItem": {}, + "durationDialogHours": "மணிகள்", + "@durationDialogHours": {}, + "aboutBugSaveLogInstruction": "பயன்பாட்டு பதிவுகளை ஒரு கோப்பில் சேமிக்கவும்", + "@aboutBugSaveLogInstruction": {}, + "aboutBugCopyInfoInstruction": "கணினி தகவல்களை நகலெடுக்கவும்", + "@aboutBugCopyInfoInstruction": {}, + "aboutBugCopyInfoButton": "நகலெடு", + "@aboutBugCopyInfoButton": {}, + "collectionActionAddShortcut": "குறுக்குவழியைச் சேர்க்கவும்", + "@collectionActionAddShortcut": {}, + "collectionActionSetHome": "வீட்டாக அமைக்கவும்", + "@collectionActionSetHome": {}, + "collectionSearchTitlesHintText": "தலைப்புகளைத் தேடுங்கள்", + "@collectionSearchTitlesHintText": {}, + "tagPageTitle": "குறிச்சொற்கள்", + "@tagPageTitle": {}, + "settingsDoubleBackExit": "வெளியேற “பின்” என்பதை இரண்டு முறை தட்டவும்", + "@settingsDoubleBackExit": {}, + "settingsCollectionBurstPatternsTile": "வெடிப்பு வடிவங்கள்", + "@settingsCollectionBurstPatternsTile": {}, + "settingsViewerQuickActionEmpty": "பொத்தான்கள் இல்லை", + "@settingsViewerQuickActionEmpty": {}, + "settingsViewerSlideshowTile": "ச்லைடுசோ", + "@settingsViewerSlideshowTile": {}, + "settingsViewerSlideshowPageTitle": "ச்லைடுசோ", + "@settingsViewerSlideshowPageTitle": {}, + "settingsSlideshowRepeat": "மீண்டும்", + "@settingsSlideshowRepeat": {}, + "settingsSubtitleThemeTile": "வசன வரிகள்", + "@settingsSubtitleThemeTile": {}, + "settingsLanguageTile": "மொழி", + "@settingsLanguageTile": {}, + "statsPageTitle": "புள்ளிவிவரங்கள்", + "@statsPageTitle": {}, + "statsWithGps": "{count, plural, =1{1 இடம் கொண்ட உருப்படி} other{{count} இருப்பிடத்துடன் உருப்படிகள்}}", + "@statsWithGps": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "statsTopAlbumsSectionTitle": "சிறந்த ஆல்பங்கள்", + "@statsTopAlbumsSectionTitle": {}, + "viewerOpenPanoramaButtonLabel": "திறந்த பனோரமா", + "@viewerOpenPanoramaButtonLabel": {}, + "viewerErrorUnknown": "அச்சச்சோ!", + "@viewerErrorUnknown": {}, + "mapZoomInTooltip": "பெரிதாக்கு", + "@mapZoomInTooltip": {}, + "openMapPageTooltip": "வரைபட பக்கத்தில் காண்க", + "@openMapPageTooltip": {}, + "viewerInfoSearchSuggestionDate": "தேதி & நேரம்", + "@viewerInfoSearchSuggestionDate": {}, + "viewerInfoSearchSuggestionDescription": "விவரம்", + "@viewerInfoSearchSuggestionDescription": {}, + "viewerInfoSearchSuggestionDimensions": "பரிமாணங்கள்", + "@viewerInfoSearchSuggestionDimensions": {}, + "collectionDeleteFailureFeedback": "{count, plural, =1{நீக்குவதில் தோல்வி 1 உருப்படி} other{நீக்குவதில் தோல்வி {count} உருப்படிகள்}}", + "@collectionDeleteFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionCopyFailureFeedback": "{count, plural, =1{நகலெடுப்பதில் தோல்வி 1 உருப்படி} other{நகலெடுப்பதில் தோல்வி {count} உருப்படிகள்}}", + "@collectionCopyFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "collectionMoveFailureFeedback": "{count, plural, =1{நகர்த்துவதில் தோல்வி 1 உருப்படி} other{நகர்த்துவதில் தோல்வி {count} உருப்படிகள்}}", + "@collectionMoveFailureFeedback": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "columnCount": "{count, plural, =1{{count} நெடுவரிசை} other{{count} நெடுவரிசைகள்}}", + "@columnCount": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "timeSeconds": "{count, plural, =1{{count} இரண்டாவது} other{{count} விநாடிகள்}}", + "@timeSeconds": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "timeMinutes": "{count, plural, =1{{count} நிமிடம்} other{{count} நிமிடங்கள்}}", + "@timeMinutes": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "itemCount": "{count, plural, =1{{count} உருப்படி} other{{count} உருப்படிகள்}}", + "@itemCount": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "applyButtonLabel": "இடு", + "@applyButtonLabel": {}, + "timeDays": "{count, plural, =1{{count} நாள்} other{{count} நாட்கள்}}", + "@timeDays": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "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": {}, + "hideTooltip": "மறை", + "@hideTooltip": {}, + "resetTooltip": "மீட்டமை", + "@resetTooltip": {}, + "saveTooltip": "சேமி", + "@saveTooltip": {}, + "actionRemove": "அகற்று", + "@actionRemove": {}, + "stopTooltip": "நிறுத்து", + "@stopTooltip": {}, + "pickTooltip": "தேர்ந்தெடு", + "@pickTooltip": {}, + "doubleBackExitMessage": "வெளியேற “பின்” என்பதைத் தட்டவும்.", + "@doubleBackExitMessage": {}, + "doNotAskAgain": "மீண்டும் கேட்க வேண்டாம்", + "@doNotAskAgain": {}, + "sourceStateLoading": "ஏற்றுகிறது", + "@sourceStateLoading": {}, + "sourceStateCataloguing": "பட்டியலிடுதல்", + "@sourceStateCataloguing": {}, + "sourceStateLocatingCountries": "நாடுகளைக் கண்டறிதல்", + "@sourceStateLocatingCountries": {}, + "sourceStateLocatingPlaces": "இடங்களைக் கண்டறிதல்", + "@sourceStateLocatingPlaces": {}, + "chipActionDelete": "நீக்கு", + "@chipActionDelete": {}, + "chipActionRemove": "அகற்று", + "@chipActionRemove": {}, + "chipActionShowCollection": "சேகரிப்பில் காட்டு", + "@chipActionShowCollection": {}, + "chipActionGoToAlbumPage": "ஆல்பங்களில் காட்டு", + "@chipActionGoToAlbumPage": {}, + "chipActionGoToCountryPage": "நாடுகளில் காட்டு", + "@chipActionGoToCountryPage": {}, + "chipActionGoToPlacePage": "இடங்களில் காண்பி", + "@chipActionGoToPlacePage": {}, + "chipActionGoToTagPage": "குறிச்சொற்களில் காட்டு", + "@chipActionGoToTagPage": {}, + "chipActionGoToExplorerPage": "உலாவில் காட்டு", + "@chipActionGoToExplorerPage": {}, + "chipActionFilterOut": "வடிகட்டி நீக்கு", + "@chipActionFilterOut": {}, + "chipActionFilterIn": "வடிகட்டி வை", + "@chipActionFilterIn": {}, + "chipActionHide": "மறை", + "@chipActionHide": {}, + "chipActionLock": "பூட்டு", + "@chipActionLock": {}, + "chipActionPin": "மேலே முள்", + "@chipActionPin": {}, + "chipActionUnpin": "மேலே இருந்து அவிழ்த்து விடு", + "@chipActionUnpin": {}, + "chipActionRename": "மறுபெயரிடு", + "@chipActionRename": {}, + "chipActionSetCover": "அட்டை அமை", + "@chipActionSetCover": {}, + "chipActionShowCountryStates": "நிலைகளைக் காட்டு", + "@chipActionShowCountryStates": {}, + "chipActionCreateAlbum": "ஆல்பத்தை உருவாக்கு", + "@chipActionCreateAlbum": {}, + "chipActionCreateVault": "பெட்டகத்தை உருவாக்கு", + "@chipActionCreateVault": {}, + "chipActionConfigureVault": "பெட்டகத்தை உள்ளமை", + "@chipActionConfigureVault": {}, + "entryActionCopyToClipboard": "இடைநிலைப் பலகைக்கு நகலெடு", + "@entryActionCopyToClipboard": {}, + "entryActionDelete": "நீக்கு", + "@entryActionDelete": {}, + "entryActionConvert": "நிலைமாற்று", + "@entryActionConvert": {}, + "entryActionExport": "ஏற்றுமதி", + "@entryActionExport": {}, + "entryActionInfo": "தகவல்", + "@entryActionInfo": {}, + "entryActionRotateCW": "கடிகார திசையில் சுழற்று", + "@entryActionRotateCW": {}, + "entryActionFlip": "கிடைமட்டமாகப் புரட்டு", + "@entryActionFlip": {}, + "entryActionPrint": "அச்சிடு", + "@entryActionPrint": {}, + "entryActionShare": "பங்கு", + "@entryActionShare": {}, + "entryActionShareImageOnly": "படத்தை மட்டும் பகிர்", + "@entryActionShareImageOnly": {}, + "entryActionShareVideoOnly": "காணொளியைப் பகிர்", + "@entryActionShareVideoOnly": {}, + "entryActionOpen": "உடன் திற", + "@entryActionOpen": {}, + "entryActionSetAs": "என அமை", + "@entryActionSetAs": {}, + "entryActionCast": "நடிகர்கள்", + "@entryActionCast": {}, + "entryActionOpenMap": "வரைபட பயன்பாட்டில் காண்பி", + "@entryActionOpenMap": {}, + "entryActionRotateScreen": "திரை சுழற்று", + "@entryActionRotateScreen": {}, + "entryActionAddFavourite": "பிடித்தவைகளில் சேர்", + "@entryActionAddFavourite": {}, + "entryActionRemoveFavourite": "பிடித்தவைகளிலிருந்து அகற்று", + "@entryActionRemoveFavourite": {}, + "videoActionShowPreviousFrame": "முந்தைய சட்டத்தைக் காட்டு", + "@videoActionShowPreviousFrame": {}, + "videoActionShowNextFrame": "அடுத்த சட்டகத்தைக் காட்டு", + "@videoActionShowNextFrame": {}, + "videoActionSelectStreams": "தடங்களைத் தேர்ந்தெடு", + "@videoActionSelectStreams": {}, + "videoActionSetSpeed": "பின்னணி விரைவு", + "@videoActionSetSpeed": {}, + "videoActionCaptureFrame": "பிடிப்பு சட்டகம்", + "@videoActionCaptureFrame": {}, + "videoActionMute": "முடக்கு", + "@videoActionMute": {}, + "videoActionUnmute": "ஒலித்தடையை நீக்கு", + "@videoActionUnmute": {}, + "videoActionPause": "இடைநிறுத்தம்", + "@videoActionPause": {}, + "videoActionPlay": "இயக்கு", + "@videoActionPlay": {}, + "videoActionABRepeat": "அ-ஆ மீண்டும்", + "@videoActionABRepeat": {}, + "videoRepeatActionSetStart": "தொடக்கத்தை அமை", + "@videoRepeatActionSetStart": {}, + "videoRepeatActionSetEnd": "முடிவை அமை", + "@videoRepeatActionSetEnd": {}, + "viewerActionSettings": "அமைப்புகள்", + "@viewerActionSettings": {}, + "viewerActionLock": "பூட்டு பார்வையாளர்", + "@viewerActionLock": {}, + "slideshowActionResume": "மீண்டும் தொடங்குங்கள்", + "@slideshowActionResume": {}, + "viewerActionUnlock": "பார்வையாளர்பூட்டைத் திற", + "@viewerActionUnlock": {}, + "slideshowActionShowInCollection": "சேகரிப்பில் காட்டு", + "@slideshowActionShowInCollection": {}, + "entryInfoActionEditDate": "தேதி & நேரத்தைத் திருத்து", + "@entryInfoActionEditDate": {}, + "entryInfoActionEditLocation": "இருப்பிடத்தைத் திருத்து", + "@entryInfoActionEditLocation": {}, + "entryInfoActionEditTitleDescription": "தலைப்பு & விளக்கத்தைத் திருத்து", + "@entryInfoActionEditTitleDescription": {}, + "entryInfoActionEditRating": "மதிப்பீட்டைத் திருத்து", + "@entryInfoActionEditRating": {}, + "entryInfoActionEditTags": "குறிச்சொற்களைத் திருத்து", + "@entryInfoActionEditTags": {}, + "filterAspectRatioPortraitLabel": "உருவப்படம்", + "@filterAspectRatioPortraitLabel": {}, + "filterBinLabel": "மறுசுழற்சி கூடை", + "@filterBinLabel": {}, + "entryInfoActionRemoveMetadata": "மேனிலை தரவை அகற்று", + "@entryInfoActionRemoveMetadata": {}, + "entryInfoActionExportMetadata": "ஏற்றுமதி மேனிலை தரவு", + "@entryInfoActionExportMetadata": {}, + "entryInfoActionRemoveLocation": "இருப்பிடத்தை அகற்று", + "@entryInfoActionRemoveLocation": {}, + "editorActionTransform": "உருமாற்று", + "@editorActionTransform": {}, + "editorTransformCrop": "வெட்டி எடு", + "@editorTransformCrop": {}, + "editorTransformRotate": "சுழற்று", + "@editorTransformRotate": {}, + "cropAspectRatioFree": "கட்டற்ற", + "@cropAspectRatioFree": {}, + "cropAspectRatioOriginal": "அசல்", + "@cropAspectRatioOriginal": {}, + "cropAspectRatioSquare": "நாற்கை", + "@cropAspectRatioSquare": {}, + "filterAspectRatioLandscapeLabel": "நிலப்பரப்பு", + "@filterAspectRatioLandscapeLabel": {}, + "filterFavouriteLabel": "பிடித்த", + "@filterFavouriteLabel": {}, + "filterNoDateLabel": "மதிப்பிடப்படாதது", + "@filterNoDateLabel": {}, + "filterNoAddressLabel": "முகவரி இல்லை", + "@filterNoAddressLabel": {}, + "filterLocatedLabel": "அமைந்துள்ளது", + "@filterLocatedLabel": {}, + "filterNoLocationLabel": "திறக்கப்படாதது", + "@filterNoLocationLabel": {}, + "filterNoRatingLabel": "மதிப்பிடப்படாதது", + "@filterNoRatingLabel": {}, + "filterTaggedLabel": "குறிச்சொல்இடப்பட்டது", + "@filterTaggedLabel": {}, + "filterRatingRejectedLabel": "நிராகரிக்கப்பட்டது", + "@filterRatingRejectedLabel": {}, + "filterTypeAnimatedLabel": "உயிருள்ளதுபோல", + "@filterTypeAnimatedLabel": {}, + "filterTypeMotionPhotoLabel": "இயக்கப் புகைப்படம்", + "@filterTypeMotionPhotoLabel": {}, + "filterTypePanoramaLabel": "பரந்ததோற்றம்", + "@filterTypePanoramaLabel": {}, + "filterTypeRawLabel": "பச்சையான", + "@filterTypeRawLabel": {}, + "filterTypeGeotiffLabel": "புவிடிஃப்", + "@filterTypeGeotiffLabel": {}, + "filterMimeImageLabel": "படம்", + "@filterMimeImageLabel": {}, + "filterMimeVideoLabel": "காணொளி", + "@filterMimeVideoLabel": {}, + "filterTypeSphericalVideoLabel": "360 ° காணொளி", + "@filterTypeSphericalVideoLabel": {}, + "accessibilityAnimationsRemove": "திரை விளைவுகளைத் தடு", + "@accessibilityAnimationsRemove": {}, + "accessibilityAnimationsKeep": "திரை விளைவுகளை வைத்திரு", + "@accessibilityAnimationsKeep": {}, + "albumTierNew": "புதிய", + "@albumTierNew": {}, + "wallpaperTargetLock": "பூட்டுத் திரை", + "@wallpaperTargetLock": {}, + "wallpaperTargetHomeLock": "முகப்பு மற்றும் பூட்டு திரைகள்", + "@wallpaperTargetHomeLock": {}, + "widgetDisplayedItemRandom": "சீரற்ற", + "@widgetDisplayedItemRandom": {}, + "widgetDisplayedItemMostRecent": "மிக அண்மைக் கால", + "@widgetDisplayedItemMostRecent": {}, + "albumTierSpecial": "பொது", + "@albumTierSpecial": {}, + "albumTierApps": "பயன்பாடுகள்", + "@albumTierApps": {}, + "albumTierVaults": "நிலவறைகள்", + "@albumTierVaults": {}, + "albumTierDynamic": "மாறும்", + "@albumTierDynamic": {}, + "albumTierRegular": "மற்றவைகள்", + "@albumTierRegular": {}, + "coordinateFormatDms": "டிமிவி", + "@coordinateFormatDms": {}, + "coordinateFormatDecimal": "தசம டிகிரி", + "@coordinateFormatDecimal": {}, + "coordinateDms": "{coordinate} {direction}", + "@coordinateDms": { + "placeholders": { + "coordinate": { + "type": "String", + "example": "38° 41′ 47.72″" + }, + "direction": { + "type": "String", + "example": "S" + } + } + }, + "coordinateDmsNorth": "வ", + "@coordinateDmsNorth": {}, + "coordinateDmsSouth": "தெ", + "@coordinateDmsSouth": {}, + "coordinateDmsEast": "கி", + "@coordinateDmsEast": {}, + "coordinateDmsWest": "மே", + "@coordinateDmsWest": {}, + "displayRefreshRatePreferHighest": "அதிக விகிதம்", + "@displayRefreshRatePreferHighest": {}, + "displayRefreshRatePreferLowest": "குறைந்த விகிதம்", + "@displayRefreshRatePreferLowest": {}, + "keepScreenOnNever": "ஒருபோதும்", + "@keepScreenOnNever": {}, + "keepScreenOnVideoPlayback": "காணொளி இயக்கத்தின்போது", + "@keepScreenOnVideoPlayback": {}, + "lengthUnitPixel": "புள்ளி", + "@lengthUnitPixel": {}, + "lengthUnitPercent": "%", + "@lengthUnitPercent": {}, + "keepScreenOnViewerOnly": "பார்வையாளர் பக்கம் மட்டுமே", + "@keepScreenOnViewerOnly": {}, + "keepScreenOnAlways": "எப்போதும்", + "@keepScreenOnAlways": {}, + "mapStyleGoogleNormal": "கூகிள் வரைபடங்கள்", + "@mapStyleGoogleNormal": {}, + "mapStyleGoogleHybrid": "கூகிள் வரைபடங்கள் (கலப்பின)", + "@mapStyleGoogleHybrid": {}, + "mapStyleGoogleTerrain": "கூகிள் வரைபடங்கள் (நிலப்பரப்பு)", + "@mapStyleGoogleTerrain": {}, + "mapStyleOsmLiberty": "திதெவ விடுதலை", + "@mapStyleOsmLiberty": {}, + "mapStyleOpenTopoMap": "திறடோபோவரைபடம்", + "@mapStyleOpenTopoMap": {}, + "maxBrightnessNever": "ஒருபோதும்", + "@maxBrightnessNever": {}, + "mapStyleOsmHot": "மனிதாபிமான திதெவ", + "@mapStyleOsmHot": {}, + "mapStyleStamenWatercolor": "ச்டேமன் நீர்நிறம்", + "@mapStyleStamenWatercolor": {}, + "maxBrightnessAlways": "எப்போதும்", + "@maxBrightnessAlways": {}, + "nameConflictStrategyRename": "மறுபெயரிடு", + "@nameConflictStrategyRename": {}, + "nameConflictStrategyReplace": "மாற்று", + "@nameConflictStrategyReplace": {}, + "nameConflictStrategySkip": "தவிர்", + "@nameConflictStrategySkip": {}, + "overlayHistogramLuminance": "ஒளிர்மை", + "@overlayHistogramLuminance": {}, + "overlayHistogramRGB": "சிபநீ", + "@overlayHistogramRGB": {}, + "subtitlePositionTop": "மேலே", + "@subtitlePositionTop": {}, + "subtitlePositionBottom": "கீழே", + "@subtitlePositionBottom": {}, + "themeBrightnessLight": "ஒளி", + "@themeBrightnessLight": {}, + "themeBrightnessDark": "இருண்ட", + "@themeBrightnessDark": {}, + "themeBrightnessBlack": "கருப்பு", + "@themeBrightnessBlack": {}, + "unitSystemMetric": "மீட்டர் முறை", + "@unitSystemMetric": {}, + "unitSystemImperial": "ஏகாதிபத்திய", + "@unitSystemImperial": {}, + "vaultLockTypePattern": "வரைவடிவம்", + "@vaultLockTypePattern": {}, + "vaultLockTypePin": "தன்எண்", + "@vaultLockTypePin": {}, + "vaultLockTypePassword": "கடவுச்சொல்", + "@vaultLockTypePassword": {}, + "videoLoopModeNever": "ஒருபோதும்", + "@videoLoopModeNever": {}, + "videoLoopModeShortOnly": "குறுகிய காணொளிகள் மட்டுமே", + "@videoLoopModeShortOnly": {}, + "settingsVideoEnablePip": "படத்துள்-படம்", + "@settingsVideoEnablePip": {}, + "videoLoopModeAlways": "எப்போதும்", + "@videoLoopModeAlways": {}, + "videoControlsPlayOutside": "மற்ற இயக்கியுடன் திற", + "@videoControlsPlayOutside": {}, + "videoPlaybackWithSound": "ஒலியுடன் இயக்கு", + "@videoPlaybackWithSound": {}, + "videoResumptionModeAlways": "எப்போதும்", + "@videoResumptionModeAlways": {}, + "viewerTransitionSlide": "படவில்லை", + "@viewerTransitionSlide": {}, + "viewerTransitionParallax": "இடமாறு தோற்றம்", + "@viewerTransitionParallax": {}, + "viewerTransitionFade": "மங்கல்", + "@viewerTransitionFade": {}, + "widgetOpenPageHome": "திறந்த வீடு", + "@widgetOpenPageHome": {}, + "storageVolumeDescriptionFallbackPrimary": "உள் சேமிப்பு", + "@storageVolumeDescriptionFallbackPrimary": {}, + "storageVolumeDescriptionFallbackNonPrimary": "நினைவு அட்டை", + "@storageVolumeDescriptionFallbackNonPrimary": {}, + "rootDirectoryDescription": "வேர் அடைவு", + "@rootDirectoryDescription": {}, + "widgetOpenPageCollection": "திறந்த சேகரிப்பு", + "@widgetOpenPageCollection": {}, + "widgetOpenPageViewer": "திறந்த பார்வையாளர்", + "@widgetOpenPageViewer": {}, + "widgetTapUpdateWidget": "நிரல் பலகையைப் புதுப்பி", + "@widgetTapUpdateWidget": {}, + "storageAccessDialogMessage": "இந்தப் பயன்பாட்டுக்கு அணுகலை வழங்க அடுத்த திரையில் உள்ள “{volume}” இன் {directory} ஐத் தேர்ந்தெடு.", + "@storageAccessDialogMessage": { + "placeholders": { + "directory": { + "type": "String", + "description": "the name of a directory, using the output of `rootDirectoryDescription` or `otherDirectoryDescription`" + }, + "volume": { + "type": "String", + "example": "SD card", + "description": "the name of a storage volume" + } + } + }, + "restrictedAccessDialogMessage": "இந்தப் பயன்பாடு “{volume}” இன் {directory} இல் உள்ள கோப்புகளை மாற்ற அனுமதிக்கப்படவில்லை.\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" + } + } + }, + "notEnoughSpaceDialogMessage": "இந்தச் செயல்பாட்டிற்கு “{volume}” இல் இலவச இடத்தின் {neededSize} தேவை, ஆனால் {freeSize} மட்டுமே உள்ளது.", + "@notEnoughSpaceDialogMessage": { + "placeholders": { + "neededSize": { + "type": "String", + "example": "314 MB" + }, + "freeSize": { + "type": "String", + "example": "123 MB" + }, + "volume": { + "type": "String", + "example": "SD card", + "description": "the name of a storage volume" + } + } + }, + "nameConflictDialogSingleSourceMessage": "இலக்கு கோப்புறையில் உள்ள சில கோப்புகளுக்கு ஒரே பெயர் உள்ளது.", + "@nameConflictDialogSingleSourceMessage": {}, + "addShortcutDialogLabel": "குறுக்குவழி சிட்டை", + "@addShortcutDialogLabel": {}, + "unsupportedTypeDialogMessage": "{count, plural, =1{இந்தச் செயல்பாடு பின்வரும் வகையின் உருப்படிகளுக்கு ஆதரிக்கப்படவில்லை: {types}.} other{இந்தச் செயல்பாடு பின்வரும் வகைகளின் உருப்படிகளுக்கு ஆதரிக்கப்படவில்லை: {types}.}}", + "@unsupportedTypeDialogMessage": { + "placeholders": { + "count": {}, + "types": { + "type": "String", + "example": "GIF, TIFF, MP4", + "description": "a list of unsupported types" + } + } + }, + "addShortcutButtonLabel": "கூட்டு", + "@addShortcutButtonLabel": {}, + "noMatchingAppDialogMessage": "இதைக் கையாளக்கூடிய பயன்பாடுகள் எதுவும் இல்லை.", + "@noMatchingAppDialogMessage": {}, + "binEntriesConfirmationDialogMessage": "{count, plural, =1{இந்த உருப்படியை மறுசுழற்சி தொட்டியில் நகர்த்தவா?} other{இந்த {count} உருப்படிகளை மறுசுழற்சி தொட்டியில் நகர்த்தவா?}}", + "@binEntriesConfirmationDialogMessage": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "deleteEntriesConfirmationDialogMessage": "{count, plural, =1{இந்த உருப்படியை நீக்கு?} other{இந்த {count} உருப்படிகளை நீக்கு?}}", + "@deleteEntriesConfirmationDialogMessage": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "moveUndatedConfirmationDialogMessage": "தொடர்வதற்கு முன் உருப்படி தேதிகளைச் சேமிக்கவா?", + "@moveUndatedConfirmationDialogMessage": {}, + "moveUndatedConfirmationDialogSetDate": "தேதிகளைச் சேமி", + "@moveUndatedConfirmationDialogSetDate": {}, + "videoResumeDialogMessage": "{time} இல் மீண்டும் இயக்குவதை விரும்புகிறீர்களா?", + "@videoResumeDialogMessage": { + "placeholders": { + "time": { + "type": "String", + "example": "13:37" + } + } + }, + "setCoverDialogCustom": "தனிப்பயன்", + "@setCoverDialogCustom": {}, + "hideFilterConfirmationDialogMessage": "பொருந்தக்கூடிய புகைப்படங்கள் மற்றும் காணொளிகள் உங்கள் சேகரிப்பிலிருந்து மறைக்கப்படும். “தனியுரிமை” அமைப்புகளிலிருந்து அவற்றை மீண்டும் காட்டலாம்.\n\n அவற்றை மறைக்க விரும்புகிறீர்களா?", + "@hideFilterConfirmationDialogMessage": {}, + "newAlbumDialogTitle": "புதிய தொகுப்பு", + "@newAlbumDialogTitle": {}, + "newAlbumDialogNameLabel": "தொகுப்பின் பெயர்", + "@newAlbumDialogNameLabel": {}, + "newAlbumDialogAlbumAlreadyExistsHelper": "தொகுப்பு ஏற்கனவே உள்ளது", + "@newAlbumDialogAlbumAlreadyExistsHelper": {}, + "newAlbumDialogNameLabelAlreadyExistsHelper": "அடைவு ஏற்கனவே உள்ளது", + "@newAlbumDialogNameLabelAlreadyExistsHelper": {}, + "newAlbumDialogStorageLabel": "சேமிப்பு:", + "@newAlbumDialogStorageLabel": {}, + "newDynamicAlbumDialogTitle": "புதிய மாறும் தொகுப்பு", + "@newDynamicAlbumDialogTitle": {}, + "dynamicAlbumAlreadyExists": "மாறும் தொகுப்பு ஏற்கனவே உள்ளது", + "@dynamicAlbumAlreadyExists": {}, + "newVaultWarningDialogMessage": "பெட்டகங்களில் உள்ள உருப்படிகள் இந்தப் பயன்பாட்டிற்கு மட்டுமே கிடைக்கின்றன, மற்றவைகளுக்கு இல்லை.\n\n இந்தப் பயன்பாட்டை நிறுவல் நீக்கினால் அல்லது இந்தப் பயன்பாட்டு தரவை அழித்தால், இந்த உருப்படிகள் அனைத்தையும் இழப்பீர்கள்.", + "@newVaultWarningDialogMessage": {}, + "configureVaultDialogTitle": "பெட்டகத்தை உள்ளமை", + "@configureVaultDialogTitle": {}, + "vaultDialogLockModeWhenScreenOff": "திரை அணைக்கப்படும்போது பூட்டு", + "@vaultDialogLockModeWhenScreenOff": {}, + "vaultDialogLockTypeLabel": "பூட்டு வகை", + "@vaultDialogLockTypeLabel": {}, + "patternDialogEnter": "வடிவத்தை உள்ளிடு", + "@patternDialogEnter": {}, + "patternDialogConfirm": "முறை உறுதிப்படுத்து", + "@patternDialogConfirm": {}, + "pinDialogEnter": "தன்எண் உள்ளிடவும்", + "@pinDialogEnter": {}, + "pinDialogConfirm": "தன்எண் உறுதிப்படுத்து", + "@pinDialogConfirm": {}, + "passwordDialogEnter": "கடவுச்சொல்லை உள்ளிடு", + "@passwordDialogEnter": {}, + "passwordDialogConfirm": "கடவுச்சொல்லை உறுதிப்படுத்து", + "@passwordDialogConfirm": {}, + "authenticateToConfigureVault": "பெட்டகத்தை உள்ளமைக்க ஏற்பு", + "@authenticateToConfigureVault": {}, + "authenticateToUnlockVault": "பெட்டகத்தைத் திறக்க ஏற்பு", + "@authenticateToUnlockVault": {}, + "renameAlbumDialogLabelAlreadyExistsHelper": "அடைவு ஏற்கனவே உள்ளது", + "@renameAlbumDialogLabelAlreadyExistsHelper": {}, + "renameEntrySetPageTitle": "மறுபெயரிடு", + "@renameEntrySetPageTitle": {}, + "renameEntrySetPagePatternFieldLabel": "பெயரிடும் முறை", + "@renameEntrySetPagePatternFieldLabel": {}, + "exportEntryDialogFormat": "வடிவம்:", + "@exportEntryDialogFormat": {}, + "exportEntryDialogQuality": "தரம்", + "@exportEntryDialogQuality": {}, + "deleteSingleAlbumConfirmationDialogMessage": "{count, plural, =1{இந்தத் தொகுப்பையும் அதில் உள்ள உருப்படியையும் நீக்கு?} other{இந்தத் தொகுப்பையும் அதில் உள்ள {count} உருப்படிகளையும் நீக்கு?}}", + "@deleteSingleAlbumConfirmationDialogMessage": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "deleteMultiAlbumConfirmationDialogMessage": "{count, plural, =1{இந்தத் தொகுப்பையும் அவற்றில் உள்ள உருப்படியையும் நீக்கு?} other{இந்தத் தொகுப்பையும் அவற்றில் உள்ள {count} உருப்படிகளையும் நீக்கு?}}", + "@deleteMultiAlbumConfirmationDialogMessage": { + "placeholders": { + "count": { + "format": "decimalPattern" + } + } + }, + "exportEntryDialogWriteMetadata": "மேனிலை தரவை எழுதுங்கள்", + "@exportEntryDialogWriteMetadata": {}, + "renameEntryDialogLabel": "புதிய பெயர்", + "@renameEntryDialogLabel": {}, + "editEntryDialogTargetFieldsHeader": "மாற்றுவதற்கான புலங்கள்", + "@editEntryDialogTargetFieldsHeader": {}, + "editEntryDateDialogTitle": "தேதி & நேரம்", + "@editEntryDateDialogTitle": {}, + "editEntryDateDialogSetCustom": "தனிப்பயன் தேதியை அமை", + "@editEntryDateDialogSetCustom": {}, + "editEntryDateDialogCopyField": "மற்ற தேதியிலிருந்து நகலெடு", + "@editEntryDateDialogCopyField": {}, + "editEntryDateDialogExtractFromTitle": "தலைப்பிலிருந்து பிரித்தெடு", + "@editEntryDateDialogExtractFromTitle": {}, + "editEntryDateDialogShift": "பெயர்வு", + "@editEntryDateDialogShift": {}, + "settingsVideoAutoPlay": "ஆட்டோ நாடகம்", + "@settingsVideoAutoPlay": {}, + "settingsVideoLoopModeTile": "லூப் பயன்முறை", + "@settingsVideoLoopModeTile": {}, + "settingsVideoPlaybackPageTitle": "பின்னணி", + "@settingsVideoPlaybackPageTitle": {}, + "settingsVideoEnableHardwareAcceleration": "வன்பொருள் முடுக்கம்", + "@settingsVideoEnableHardwareAcceleration": {}, + "settingsVideoLoopModeDialogTitle": "லூப் பயன்முறை", + "@settingsVideoLoopModeDialogTitle": {}, + "settingsVideoResumptionModeTile": "பிளேபேக்கை மீண்டும் தொடங்குங்கள்", + "@settingsVideoResumptionModeTile": {}, + "settingsVideoResumptionModeDialogTitle": "பிளேபேக்கை மீண்டும் தொடங்குங்கள்", + "@settingsVideoResumptionModeDialogTitle": {}, + "settingsVideoBackgroundMode": "பின்னணி முறை", + "@settingsVideoBackgroundMode": {}, + "settingsVideoBackgroundModeDialogTitle": "பின்னணி முறை", + "@settingsVideoBackgroundModeDialogTitle": {}, + "settingsVideoControlsTile": "கட்டுப்பாடுகள்", + "@settingsVideoControlsTile": {}, + "settingsVideoControlsPageTitle": "கட்டுப்பாடுகள்", + "@settingsVideoControlsPageTitle": {}, + "settingsVideoButtonsTile": "பொத்தான்கள்", + "@settingsVideoButtonsTile": {}, + "settingsVideoGestureDoubleTapTogglePlay": "விளையாட/இடைநிறுத்த இரட்டை தட்டவும்", + "@settingsVideoGestureDoubleTapTogglePlay": {}, + "settingsVideoGestureSideDoubleTapSeek": "பின்தங்கிய/முன்னோக்கி தேட திரை விளிம்புகளில் இரட்டை தட்டவும்", + "@settingsVideoGestureSideDoubleTapSeek": {}, + "settingsVideoGestureVerticalDragBrightnessVolume": "பிரகாசம்/அளவை சரிசெய்ய மேலே அல்லது கீழே ச்வைப் செய்யவும்", + "@settingsVideoGestureVerticalDragBrightnessVolume": {}, + "settingsSubtitleThemePageTitle": "வசன வரிகள்", + "@settingsSubtitleThemePageTitle": {}, + "settingsSubtitleThemeSample": "இது ஒரு மாதிரி.", + "@settingsSubtitleThemeSample": {}, + "settingsSubtitleThemeTextAlignmentTile": "உரை சீரமைப்பு", + "@settingsSubtitleThemeTextAlignmentTile": {}, + "settingsSubtitleThemeTextAlignmentDialogTitle": "உரை சீரமைப்பு", + "@settingsSubtitleThemeTextAlignmentDialogTitle": {}, + "settingsSubtitleThemeTextPositionTile": "உரை நிலை", + "@settingsSubtitleThemeTextPositionTile": {}, + "settingsSubtitleThemeTextPositionDialogTitle": "உரை நிலை", + "@settingsSubtitleThemeTextPositionDialogTitle": {}, + "settingsSubtitleThemeTextSize": "உரை அளவு", + "@settingsSubtitleThemeTextSize": {}, + "settingsSubtitleThemeShowOutline": "அவுட்லைன் மற்றும் நிழலைக் காட்டுங்கள்", + "@settingsSubtitleThemeShowOutline": {}, + "settingsSubtitleThemeTextColor": "உரை நிறம்", + "@settingsSubtitleThemeTextColor": {}, + "settingsSubtitleThemeTextOpacity": "உரை ஒளிபுகாநிலை", + "@settingsSubtitleThemeTextOpacity": {}, + "settingsSubtitleThemeBackgroundColor": "பின்னணி நிறம்", + "@settingsSubtitleThemeBackgroundColor": {}, + "settingsSubtitleThemeBackgroundOpacity": "பின்னணி ஒளிபுகாநிலை", + "@settingsSubtitleThemeBackgroundOpacity": {}, + "settingsSubtitleThemeTextAlignmentLeft": "இடது", + "@settingsSubtitleThemeTextAlignmentLeft": {}, + "settingsSubtitleThemeTextAlignmentCenter": "நடுவண்", + "@settingsSubtitleThemeTextAlignmentCenter": {}, + "settingsSubtitleThemeTextAlignmentRight": "வலது", + "@settingsSubtitleThemeTextAlignmentRight": {}, + "settingsPrivacySectionTitle": "தனியுரிமை", + "@settingsPrivacySectionTitle": {}, + "settingsAllowInstalledAppAccess": "பயன்பாட்டு சரக்குகளுக்கான அணுகலை அனுமதிக்கவும்", + "@settingsAllowInstalledAppAccess": {}, + "settingsAllowInstalledAppAccessSubtitle": "ஆல்பம் காட்சியை மேம்படுத்த பயன்படுகிறது", + "@settingsAllowInstalledAppAccessSubtitle": {}, + "settingsAllowErrorReporting": "அநாமதேய பிழை அறிக்கையை அனுமதிக்கவும்", + "@settingsAllowErrorReporting": {}, + "settingsSaveSearchHistory": "தேடல் வரலாற்றைச் சேமிக்கவும்", + "@settingsSaveSearchHistory": {}, + "settingsHiddenItemsTile": "மறைக்கப்பட்ட உருப்படிகள்", + "@settingsHiddenItemsTile": {}, + "settingsHiddenItemsPageTitle": "மறைக்கப்பட்ட உருப்படிகள்", + "@settingsHiddenItemsPageTitle": {}, + "settingsHiddenFiltersBanner": "மறைக்கப்பட்ட வடிப்பான்களுடன் பொருந்தக்கூடிய புகைப்படங்கள் மற்றும் வீடியோக்கள் உங்கள் சேகரிப்பில் தோன்றாது.", + "@settingsHiddenFiltersBanner": {}, + "settingsHiddenFiltersEmpty": "மறைக்கப்பட்ட வடிப்பான்கள் இல்லை", + "@settingsHiddenFiltersEmpty": {}, + "settingsStorageAccessTile": "சேமிப்பக அணுகல்", + "@settingsStorageAccessTile": {}, + "settingsEnableBin": "மறுசுழற்சி தொட்டியைப் பயன்படுத்தவும்", + "@settingsEnableBin": {}, + "settingsEnableBinSubtitle": "நீக்கப்பட்ட பொருட்களை 30 நாட்களுக்கு வைத்திருங்கள்", + "@settingsEnableBinSubtitle": {}, + "settingsDisablingBinWarningDialogMessage": "மறுசுழற்சி தொட்டியில் உள்ள உருப்படிகள் என்றென்றும் நீக்கப்படும்.", + "@settingsDisablingBinWarningDialogMessage": {}, + "settingsAllowMediaManagement": "ஊடக நிர்வாகத்தை அனுமதிக்கவும்", + "@settingsAllowMediaManagement": {}, + "settingsStorageAccessPageTitle": "சேமிப்பக அணுகல்", + "@settingsStorageAccessPageTitle": {}, + "settingsStorageAccessBanner": "சில கோப்பகங்களுக்கு அவற்றில் உள்ள கோப்புகளை மாற்ற வெளிப்படையான அணுகல் மானியம் தேவைப்படுகிறது. நீங்கள் முன்பு அணுகலை வழங்கிய கோப்பகங்களை இங்கே மதிப்பாய்வு செய்யலாம்.", + "@settingsStorageAccessBanner": {}, + "settingsStorageAccessEmpty": "அணுகல் மானியங்கள் இல்லை", + "@settingsStorageAccessEmpty": {}, + "settingsStorageAccessRevokeTooltip": "ரத்து செய்யுங்கள்", + "@settingsStorageAccessRevokeTooltip": {}, + "settingsAccessibilitySectionTitle": "அணுகல்", + "@settingsAccessibilitySectionTitle": {}, + "settingsRemoveAnimationsTile": "அனிமேசன்களை அகற்று", + "@settingsRemoveAnimationsTile": {}, + "settingsRemoveAnimationsDialogTitle": "அனிமேசன்களை அகற்று", + "@settingsRemoveAnimationsDialogTitle": {}, + "settingsTimeToTakeActionTile": "நடவடிக்கை எடுக்க வேண்டிய நேரம்", + "@settingsTimeToTakeActionTile": {}, + "settingsAccessibilityShowPinchGestureAlternatives": "மல்டி-டச் சைகை மாற்றுகளைக் காட்டு", + "@settingsAccessibilityShowPinchGestureAlternatives": {}, + "settingsDisplaySectionTitle": "காட்சி", + "@settingsDisplaySectionTitle": {}, + "settingsThemeBrightnessTile": "கருப்பொருள்", + "@settingsThemeBrightnessTile": {}, + "settingsThemeBrightnessDialogTitle": "கருப்பொருள்", + "@settingsThemeBrightnessDialogTitle": {}, + "settingsThemeColorHighlights": "வண்ண சிறப்பம்சங்கள்", + "@settingsThemeColorHighlights": {}, + "settingsThemeEnableDynamicColor": "மாறும் நிறம்", + "@settingsThemeEnableDynamicColor": {}, + "settingsDisplayRefreshRateModeTile": "புதுப்பிப்பு வீதத்தைக் காண்பி", + "@settingsDisplayRefreshRateModeTile": {}, + "settingsDisplayRefreshRateModeDialogTitle": "வீதத்தை புதுப்பிக்கவும்", + "@settingsDisplayRefreshRateModeDialogTitle": {}, + "settingsDisplayUseTvInterface": "ஆண்ட்ராய்டு டிவி இடைமுகம்", + "@settingsDisplayUseTvInterface": {}, + "settingsLanguageSectionTitle": "மொழி & வடிவங்கள்", + "@settingsLanguageSectionTitle": {}, + "settingsLanguagePageTitle": "மொழி", + "@settingsLanguagePageTitle": {}, + "settingsCoordinateFormatTile": "ஒருங்கிணைப்பு வடிவம்", + "@settingsCoordinateFormatTile": {}, + "settingsCoordinateFormatDialogTitle": "ஒருங்கிணைப்பு வடிவம்", + "@settingsCoordinateFormatDialogTitle": {}, + "settingsUnitSystemTile": "அலகுகள்", + "@settingsUnitSystemTile": {}, + "settingsUnitSystemDialogTitle": "அலகுகள்", + "@settingsUnitSystemDialogTitle": {}, + "settingsForceWesternArabicNumeralsTile": "அரபு எண்களை கட்டாயப்படுத்துங்கள்", + "@settingsForceWesternArabicNumeralsTile": {}, + "settingsScreenSaverPageTitle": "திரை சேவர்", + "@settingsScreenSaverPageTitle": {}, + "settingsWidgetPageTitle": "புகைப்பட சட்டகம்", + "@settingsWidgetPageTitle": {}, + "settingsWidgetShowOutline": "அவுட்லைன்", + "@settingsWidgetShowOutline": {}, + "settingsWidgetOpenPage": "விட்செட்டில் தட்டும்போது", + "@settingsWidgetOpenPage": {}, + "settingsWidgetDisplayedItem": "காட்டப்பட்ட உருப்படி", + "@settingsWidgetDisplayedItem": {}, + "settingsCollectionTile": "சேகரிப்பு", + "@settingsCollectionTile": {}, + "statsTopPlacesSectionTitle": "சிறந்த இடங்கள்", + "@statsTopPlacesSectionTitle": {}, + "statsTopCountriesSectionTitle": "சிறந்த நாடுகள்", + "@statsTopCountriesSectionTitle": {}, + "statsTopStatesSectionTitle": "சிறந்த மாநிலங்கள்", + "@statsTopStatesSectionTitle": {}, + "statsTopTagsSectionTitle": "சிறந்த குறிச்சொற்கள்", + "@statsTopTagsSectionTitle": {}, + "viewerSetWallpaperButtonLabel": "வால்பேப்பரை அமைக்கவும்", + "@viewerSetWallpaperButtonLabel": {}, + "viewerErrorDoesNotExist": "கோப்பு இனி இல்லை.", + "@viewerErrorDoesNotExist": {}, + "viewerInfoLabelDescription": "விவரம்", + "@viewerInfoLabelDescription": {}, + "viewerInfoLabelTitle": "தலைப்பு", + "@viewerInfoLabelTitle": {}, + "viewerInfoLabelDate": "திகதி", + "@viewerInfoLabelDate": {}, + "viewerInfoLabelResolution": "பகுத்தல்", + "@viewerInfoLabelResolution": {}, + "viewerInfoLabelSize": "அளவு", + "@viewerInfoLabelSize": {}, + "viewerInfoPageTitle": "தகவல்", + "@viewerInfoPageTitle": {}, + "viewerInfoBackToViewerTooltip": "பார்வையாளருக்குத் திரும்பு", + "@viewerInfoBackToViewerTooltip": {}, + "viewerInfoUnknown": "தெரியவில்லை", + "@viewerInfoUnknown": {}, + "viewerInfoLabelUri": "யூரி", + "@viewerInfoLabelUri": {}, + "viewerInfoLabelPath": "பாதை", + "@viewerInfoLabelPath": {}, + "viewerInfoLabelDuration": "காலம்", + "@viewerInfoLabelDuration": {}, + "viewerInfoLabelOwner": "உரிமையாளர்", + "@viewerInfoLabelOwner": {}, + "viewerInfoLabelCoordinates": "ஒருங்கிணைப்புகள்", + "@viewerInfoLabelCoordinates": {}, + "viewerInfoLabelAddress": "முகவரி", + "@viewerInfoLabelAddress": {}, + "mapStyleDialogTitle": "வரைபட நடை", + "@mapStyleDialogTitle": {}, + "mapStyleTooltip": "வரைபட பாணியைத் தேர்ந்தெடுக்கவும்", + "@mapStyleTooltip": {}, + "mapZoomOutTooltip": "சிறிதாக்கு", + "@mapZoomOutTooltip": {}, + "mapPointNorthUpTooltip": "வடக்கே சுட்டிக்காட்டவும்", + "@mapPointNorthUpTooltip": {}, + "mapAttributionOsmData": "வரைபட தரவு © [OpenStreetMap](https://www.openstreetmap.org/copyright) பங்களிப்பாளர்கள்", + "@mapAttributionOsmData": {}, + "mapAttributionOsmLiberty": "[OpenMapTiles](https://www.openmaptiles.org/) மூலம் ஓடுகள், [CC BY](http://creativecommons.org/licenses/by/4.0) • [OSM Americana](https://tile.ourmap.us) மூலம் புரவலசேவை", + "@mapAttributionOsmLiberty": {}, + "mapAttributionOpenTopoMap": "[SRTM](https://www.earthdata.nasa.gov/sensors/srtm) | [OpenTopoMap](https://opentopomap.org/) மூலம் ஓடுகள், [CC BY-SA](https://creativecommons.org/licenses/by-sa/3.0/)", + "@mapAttributionOpenTopoMap": {}, + "mapAttributionOsmHot": "[HOT](https://www.hotosm.org/) மூலம் ஓடுகள் • [OSM France](https://openstreetmap.fr/) மூலம் புரவலசேவை", + "@mapAttributionOsmHot": {}, + "mapAttributionStamen": "[Stamen Design](https://stamen.com) மூலம் ஓடுகள், [CC BY 3.0](https://creativecommons.org/licenses/by/3.0)", + "@mapAttributionStamen": {}, + "mapEmptyRegion": "இந்த பிராந்தியத்தில் படங்கள் இல்லை", + "@mapEmptyRegion": {}, + "viewerInfoOpenEmbeddedFailureFeedback": "உட்பொதிக்கப்பட்ட தரவைப் பிரித்தெடுப்பதில் தோல்வி", + "@viewerInfoOpenEmbeddedFailureFeedback": {}, + "viewerInfoOpenLinkText": "திற", + "@viewerInfoOpenLinkText": {}, + "viewerInfoViewXmlLinkText": "எக்ச்எம்எல் காண்க", + "@viewerInfoViewXmlLinkText": {}, + "viewerInfoSearchFieldLabel": "மெட்டாடேட்டாவைத் தேடுங்கள்", + "@viewerInfoSearchFieldLabel": {}, + "viewerInfoSearchEmpty": "பொருந்தக்கூடிய விசைகள் இல்லை", + "@viewerInfoSearchEmpty": {}, + "viewerInfoSearchSuggestionResolution": "பகுத்தல்", + "@viewerInfoSearchSuggestionResolution": {}, + "viewerInfoSearchSuggestionRights": "உரிமைகள்", + "@viewerInfoSearchSuggestionRights": {}, + "wallpaperUseScrollEffect": "முகப்புத் திரையில் உருள் விளைவைப் பயன்படுத்தவும்", + "@wallpaperUseScrollEffect": {}, + "tagEditorPageTitle": "குறிச்சொற்களைத் திருத்து", + "@tagEditorPageTitle": {}, + "tagEditorPageNewTagFieldLabel": "புதிய குறிச்சொல்", + "@tagEditorPageNewTagFieldLabel": {}, + "tagEditorPageAddTagTooltip": "குறிச்சொல்லைச் சேர்க்கவும்", + "@tagEditorPageAddTagTooltip": {}, + "tagEditorSectionRecent": "அண்மைக் கால", + "@tagEditorSectionRecent": {}, + "tagEditorSectionPlaceholders": "இடம் வைத்திருப்பவர்கள்", + "@tagEditorSectionPlaceholders": {}, + "tagEditorDiscardDialogMessage": "மாற்றங்களை நிராகரிக்க விரும்புகிறீர்களா?", + "@tagEditorDiscardDialogMessage": {}, + "tagPlaceholderCountry": "நாடு", + "@tagPlaceholderCountry": {}, + "tagPlaceholderState": "மாநிலம்", + "@tagPlaceholderState": {}, + "tagPlaceholderPlace": "இடம்", + "@tagPlaceholderPlace": {}, + "panoramaEnableSensorControl": "சென்சார் கட்டுப்பாட்டை இயக்கவும்", + "@panoramaEnableSensorControl": {}, + "panoramaDisableSensorControl": "சென்சார் கட்டுப்பாட்டை முடக்கு", + "@panoramaDisableSensorControl": {}, + "sourceViewerPageTitle": "மூலம்", + "@sourceViewerPageTitle": {}, + "filePickerShowHiddenFiles": "மறைக்கப்பட்ட கோப்புகளைக் காட்டு", + "@filePickerShowHiddenFiles": {}, + "filePickerDoNotShowHiddenFiles": "மறைக்கப்பட்ட கோப்புகளைக் காட்ட வேண்டாம்", + "@filePickerDoNotShowHiddenFiles": {}, + "filePickerOpenFrom": "இருந்து திறந்திருக்கும்", + "@filePickerOpenFrom": {}, + "filePickerNoItems": "உருப்படிகள் இல்லை", + "@filePickerNoItems": {}, + "filePickerUseThisFolder": "இந்த கோப்புறையைப் பயன்படுத்தவும்", + "@filePickerUseThisFolder": {} +} diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index f7dc3860a..eec66a639 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -1390,5 +1390,19 @@ "newAlbumDialogAlbumAlreadyExistsHelper": "相册已存在", "@newAlbumDialogAlbumAlreadyExistsHelper": {}, "mapAttributionOsmLiberty": "绘制于 [OpenMapTiles](https://www.openmaptiles.org/), [CC BY](http://creativecommons.org/licenses/by/4.0) • 主办方 [OSM Americana](https://tile.ourmap.us)", - "@mapAttributionOsmLiberty": {} + "@mapAttributionOsmLiberty": {}, + "mapStyleOsmLiberty": "OSM Liberty", + "@mapStyleOsmLiberty": {}, + "albumTierDynamic": "动态", + "@albumTierDynamic": {}, + "chipActionRemove": "删除", + "@chipActionRemove": {}, + "newDynamicAlbumDialogTitle": "新动态专辑", + "@newDynamicAlbumDialogTitle": {}, + "collectionActionAddDynamicAlbum": "添加动态专辑", + "@collectionActionAddDynamicAlbum": {}, + "appExportDynamicAlbums": "动态专辑", + "@appExportDynamicAlbums": {}, + "dynamicAlbumAlreadyExists": "动态专辑已存在", + "@dynamicAlbumAlreadyExists": {} } diff --git a/lib/model/app/contributors.dart b/lib/model/app/contributors.dart index 216c02707..a02a10aa7 100644 --- a/lib/model/app/contributors.dart +++ b/lib/model/app/contributors.dart @@ -113,6 +113,9 @@ class Contributors { Contributor('5FeetUnder', '15950507+5FeetUnder@users.noreply.github.com'), Contributor('Cleverson Cândido', 'optimuspraimu@gmail.com'), Contributor('Hasan Kara', 'hasanbeytullahkara@gmail.com'), + Contributor('Thomas Di Cristofaro', 'hostedweblate.8347@tdc.akamail.it'), + Contributor('Petrov', 'i_v_c@mail.ru'), + Contributor('தமிழ்நேரம்', 'anishprabu.t@gmail.com'), // Contributor('Femini', 'nizamismidov4@gmail.com'), // Azerbaijani // Contributor('Alvi Khan', 'aveenalvi@gmail.com'), // Bengali // Contributor('Htet Oo Hlaing', 'htetoh2006@outlook.com'), // Burmese diff --git a/lib/model/covers.dart b/lib/model/covers.dart index 231d8effc..edf189280 100644 --- a/lib/model/covers.dart +++ b/lib/model/covers.dart @@ -2,7 +2,7 @@ import 'dart:async'; import 'package:aves/model/app_inventory.dart'; import 'package:aves/model/entry/entry.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/vaults/vaults.dart'; @@ -16,6 +16,8 @@ import 'package:flutter/painting.dart'; final Covers covers = Covers._private(); +typedef CoverProps = (int? entryId, String? packageName, Color? color); + class Covers { final StreamController?> _entryChangeStreamController = StreamController.broadcast(); final StreamController?> _packageChangeStreamController = StreamController.broadcast(); @@ -39,22 +41,60 @@ class Covers { Set get all => Set.unmodifiable(_rows); - (int? entryId, String? packageName, Color? color)? of(CollectionFilter filter) { - if (filter is AlbumFilter && vaults.isLocked(filter.album)) return null; + CoverProps? of(CollectionFilter filter) { + if (filter is StoredAlbumFilter && vaults.isLocked(filter.album)) return null; final row = _rows.firstWhereOrNull((row) => row.filter == filter); return row != null ? (row.entryId, row.packageName, row.color) : null; } + Future remove(CollectionFilter filter, {bool notify = true}) async { + final props = of(filter); + if (props != null) { + await set(filter: filter, entryId: null, packageName: null, color: null); + + if (notify) { + final (entryId, packageName, color) = props; + if (entryId != null) _entryChangeStreamController.add({filter}); + if (packageName != null) _packageChangeStreamController.add({filter}); + if (color != null) _colorChangeStreamController.add({filter}); + } + } + return props; + } + + Future removeAll(Set filters, {bool notify = true}) async { + final entryIdChanged = {}; + final packageNameChanged = {}; + final colorChanged = {}; + + for (final filter in filters) { + final props = await remove(filter, notify: false); + if (notify && props != null) { + final (entryId, packageName, color) = props; + if (entryId != null) entryIdChanged.add(filter); + if (packageName != null) packageNameChanged.add(filter); + if (color != null) colorChanged.add(filter); + } + } + + if (notify) { + if (entryIdChanged.isNotEmpty) _entryChangeStreamController.add(entryIdChanged); + if (packageNameChanged.isNotEmpty) _packageChangeStreamController.add(packageNameChanged); + if (colorChanged.isNotEmpty) _colorChangeStreamController.add(colorChanged); + } + } + Future set({ required CollectionFilter filter, required int? entryId, required String? packageName, required Color? color, + bool notify = true, }) async { // erase contextual properties from filters before saving them - if (filter is AlbumFilter) { - filter = AlbumFilter(filter.album, null); + if (filter is StoredAlbumFilter) { + filter = StoredAlbumFilter(filter.album, null); } final oldRows = _rows.where((row) => row.filter == filter).toSet(); @@ -77,9 +117,11 @@ class Covers { await localMediaDb.addCovers({row}); } - if (oldEntry != entryId) _entryChangeStreamController.add({filter}); - if (oldPackage != packageName) _packageChangeStreamController.add({filter}); - if (oldColor != color) _colorChangeStreamController.add({filter}); + if (notify) { + if (oldEntry != entryId) _entryChangeStreamController.add({filter}); + if (oldPackage != packageName) _packageChangeStreamController.add({filter}); + if (oldColor != color) _colorChangeStreamController.add({filter}); + } } Future _removeEntryFromRows(Set rows) { @@ -112,7 +154,7 @@ class Covers { } AlbumType effectiveAlbumType(String albumPath) { - final filterPackage = of(AlbumFilter(albumPath, null))?.$2; + final filterPackage = of(StoredAlbumFilter(albumPath, null))?.$2; if (filterPackage != null) { return filterPackage.isEmpty ? AlbumType.regular : AlbumType.app; } else { @@ -121,7 +163,7 @@ class Covers { } String? effectiveAlbumPackage(String albumPath) { - final filterPackage = of(AlbumFilter(albumPath, null))?.$2; + final filterPackage = of(StoredAlbumFilter(albumPath, null))?.$2; return filterPackage ?? appInventory.getAlbumAppPackageName(albumPath); } @@ -129,7 +171,7 @@ class Covers { List>? export(CollectionSource source) { final visibleEntries = source.visibleEntries; - final jsonList = covers.all + final jsonList = all .map((row) { final entryId = row.entryId; final path = visibleEntries.firstWhereOrNull((entry) => entryId == entry.id)?.path; @@ -180,7 +222,7 @@ class Covers { } if (entry != null || packageName != null || colorValue != null) { - covers.set( + set( filter: filter, entryId: entry?.id, packageName: packageName, diff --git a/lib/model/db/db.dart b/lib/model/db/db.dart index 0798e9e6f..d48ad360e 100644 --- a/lib/model/db/db.dart +++ b/lib/model/db/db.dart @@ -1,4 +1,5 @@ import 'package:aves/model/covers.dart'; +import 'package:aves/model/dynamic_albums.dart'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/filters/filters.dart'; @@ -109,6 +110,16 @@ abstract class LocalMediaDb { Future removeCovers(Set filters); + // dynamic albums + + Future clearDynamicAlbums(); + + Future> loadAllDynamicAlbums(); + + Future addDynamicAlbums(Set rows); + + Future removeDynamicAlbums(Set names); + // video playback Future clearVideoPlayback(); diff --git a/lib/model/db/db_sqflite.dart b/lib/model/db/db_sqflite.dart index 5bfb18d3a..cb5e51d9d 100644 --- a/lib/model/db/db_sqflite.dart +++ b/lib/model/db/db_sqflite.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:aves/model/covers.dart'; import 'package:aves/model/db/db.dart'; import 'package:aves/model/db/db_sqflite_upgrade.dart'; +import 'package:aves/model/dynamic_albums.dart'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/filters/filters.dart'; @@ -27,6 +28,7 @@ class SqfliteLocalMediaDb implements LocalMediaDb { static const addressTable = 'address'; static const favouriteTable = 'favourites'; static const coverTable = 'covers'; + static const dynamicAlbumTable = 'dynamicAlbums'; static const vaultTable = 'vaults'; static const trashTable = 'trash'; static const videoPlaybackTable = 'videoPlayback'; @@ -93,6 +95,10 @@ class SqfliteLocalMediaDb implements LocalMediaDb { ', packageName TEXT' ', color INTEGER' ')'); + await db.execute('CREATE TABLE $dynamicAlbumTable(' + 'name TEXT PRIMARY KEY' + ', filter TEXT' + ')'); await db.execute('CREATE TABLE $vaultTable(' 'name TEXT PRIMARY KEY' ', autoLock INTEGER' @@ -110,7 +116,7 @@ class SqfliteLocalMediaDb implements LocalMediaDb { ')'); }, onUpgrade: LocalMediaDbUpgrader.upgradeDb, - version: 11, + version: 12, ); final maxIdRows = await _db.rawQuery('SELECT MAX(id) AS maxId FROM $entryTable'); @@ -137,7 +143,7 @@ class SqfliteLocalMediaDb implements LocalMediaDb { final _dataTypes = dataTypes ?? EntryDataType.values.toSet(); - // using array in `whereArgs` and using it with `where id IN ?` is a pain, so we prefer `batch` instead + // using array in `whereArgs` and using it with `where arg IN ?` is a pain, so we prefer `batch` instead final batch = _db.batch(); const where = 'id = ?'; const coverWhere = 'entryId = ?'; @@ -450,7 +456,7 @@ class SqfliteLocalMediaDb implements LocalMediaDb { Future removeVaults(Set rows) async { if (rows.isEmpty) return; - // using array in `whereArgs` and using it with `where id IN ?` is a pain, so we prefer `batch` instead + // using array in `whereArgs` and using it with `where arg IN ?` is a pain, so we prefer `batch` instead final batch = _db.batch(); rows.map((v) => v.name).forEach((name) => batch.delete(vaultTable, where: 'name = ?', whereArgs: [name])); await batch.commit(noResult: true); @@ -539,7 +545,7 @@ class SqfliteLocalMediaDb implements LocalMediaDb { final ids = rows.map((row) => row.entryId); if (ids.isEmpty) return; - // using array in `whereArgs` and using it with `where id IN ?` is a pain, so we prefer `batch` instead + // using array in `whereArgs` and using it with `where arg IN ?` is a pain, so we prefer `batch` instead final batch = _db.batch(); ids.forEach((id) => batch.delete(favouriteTable, where: 'id = ?', whereArgs: [id])); await batch.commit(noResult: true); @@ -609,12 +615,60 @@ class SqfliteLocalMediaDb implements LocalMediaDb { } }); - // using array in `whereArgs` and using it with `where filter IN ?` is a pain, so we prefer `batch` instead + // using array in `whereArgs` and using it with `where arg IN ?` is a pain, so we prefer `batch` instead final batch = _db.batch(); obsoleteFilterJson.forEach((filterJson) => batch.delete(coverTable, where: 'filter = ?', whereArgs: [filterJson])); await batch.commit(noResult: true); } + // dynamic albums + + @override + Future clearDynamicAlbums() async { + final count = await _db.delete(dynamicAlbumTable, where: '1'); + debugPrint('$runtimeType clearDynamicAlbums deleted $count rows'); + } + + @override + Future> loadAllDynamicAlbums() async { + final result = {}; + final cursor = await _db.queryCursor(dynamicAlbumTable, bufferSize: _queryCursorBufferSize); + while (await cursor.moveNext()) { + final row = DynamicAlbumRow.fromMap(cursor.current); + if (row != null) { + result.add(row); + } + } + return result; + } + + @override + Future addDynamicAlbums(Set rows) async { + if (rows.isEmpty) return; + + final batch = _db.batch(); + rows.forEach((row) => _batchInsertDynamicAlbum(batch, row)); + await batch.commit(noResult: true); + } + + void _batchInsertDynamicAlbum(Batch batch, DynamicAlbumRow row) { + batch.insert( + dynamicAlbumTable, + row.toMap(), + conflictAlgorithm: ConflictAlgorithm.replace, + ); + } + + @override + Future removeDynamicAlbums(Set names) async { + if (names.isEmpty) return; + + // using array in `whereArgs` and using it with `where arg IN ?` is a pain, so we prefer `batch` instead + final batch = _db.batch(); + names.forEach((name) => batch.delete(dynamicAlbumTable, where: 'name = ?', whereArgs: [name])); + await batch.commit(noResult: true); + } + // video playback @override @@ -665,7 +719,7 @@ class SqfliteLocalMediaDb implements LocalMediaDb { Future removeVideoPlayback(Set ids) async { if (ids.isEmpty) return; - // using array in `whereArgs` and using it with `where filter IN ?` is a pain, so we prefer `batch` instead + // using array in `whereArgs` and using it with `where arg IN ?` is a pain, so we prefer `batch` instead final batch = _db.batch(); ids.forEach((id) => batch.delete(videoPlaybackTable, where: 'id = ?', whereArgs: [id])); await batch.commit(noResult: true); diff --git a/lib/model/db/db_sqflite_upgrade.dart b/lib/model/db/db_sqflite_upgrade.dart index d9b902698..ab63f7564 100644 --- a/lib/model/db/db_sqflite_upgrade.dart +++ b/lib/model/db/db_sqflite_upgrade.dart @@ -9,6 +9,7 @@ class LocalMediaDbUpgrader { static const addressTable = SqfliteLocalMediaDb.addressTable; static const favouriteTable = SqfliteLocalMediaDb.favouriteTable; static const coverTable = SqfliteLocalMediaDb.coverTable; + static const dynamicAlbumTable = SqfliteLocalMediaDb.dynamicAlbumTable; static const vaultTable = SqfliteLocalMediaDb.vaultTable; static const trashTable = SqfliteLocalMediaDb.trashTable; static const videoPlaybackTable = SqfliteLocalMediaDb.videoPlaybackTable; @@ -38,6 +39,8 @@ class LocalMediaDbUpgrader { await _upgradeFrom9(db); case 10: await _upgradeFrom10(db); + case 11: + await _upgradeFrom11(db); } oldVersion++; } @@ -376,4 +379,13 @@ class LocalMediaDbUpgrader { ', lockType TEXT' ')'); } + + static Future _upgradeFrom11(Database db) async { + debugPrint('upgrading DB from v11'); + + await db.execute('CREATE TABLE $dynamicAlbumTable(' + 'name TEXT PRIMARY KEY' + ', filter TEXT' + ')'); + } } diff --git a/lib/model/dynamic_albums.dart b/lib/model/dynamic_albums.dart new file mode 100644 index 000000000..bd6e91498 --- /dev/null +++ b/lib/model/dynamic_albums.dart @@ -0,0 +1,109 @@ +import 'package:aves/model/filters/covered/dynamic_album.dart'; +import 'package:aves/model/filters/filters.dart'; +import 'package:aves/services/common/services.dart'; +import 'package:collection/collection.dart'; +import 'package:equatable/equatable.dart'; +import 'package:flutter/foundation.dart'; + +final DynamicAlbums dynamicAlbums = DynamicAlbums._private(); + +class DynamicAlbums with ChangeNotifier { + Set _rows = {}; + + DynamicAlbums._private() { + if (kFlutterMemoryAllocationsEnabled) ChangeNotifier.maybeDispatchObjectCreation(this); + } + + Future init() async { + _rows = (await localMediaDb.loadAllDynamicAlbums()).map((v) => DynamicAlbumFilter(v.name, v.filter)).toSet(); + } + + int get count => _rows.length; + + Set get all => Set.unmodifiable(_rows); + + Future add(DynamicAlbumFilter filter) async { + await localMediaDb.addDynamicAlbums({DynamicAlbumRow(name: filter.name, filter: filter.filter)}); + _rows.add(filter); + + notifyListeners(); + } + + Future remove(Set filters) async { + await localMediaDb.removeDynamicAlbums(filters.map((filter) => filter.name).toSet()); + _rows.removeAll(filters); + + notifyListeners(); + } + + Future clear() async { + await localMediaDb.clearDynamicAlbums(); + _rows.clear(); + + notifyListeners(); + } + + Future rename(DynamicAlbumFilter filter, String newName) async { + await localMediaDb.removeDynamicAlbums({filter.name}); + _rows.remove(filter); + + await add(DynamicAlbumFilter(newName, filter.filter)); + } + + DynamicAlbumFilter? get(String name) => _rows.firstWhereOrNull((row) => row.name == name); + + bool contains(String name) => get(name) != null; + + // import/export + + List? export() { + final jsonList = all.map((row) => row.toJson()).toList(); + return jsonList.isNotEmpty ? jsonList : null; + } + + void import(dynamic jsonList) { + if (jsonList is! List) { + debugPrint('failed to import dynamic albums for jsonMap=$jsonList'); + return; + } + + jsonList.forEach((row) { + final filter = CollectionFilter.fromJson(row); + if (filter == null || filter is! DynamicAlbumFilter) { + debugPrint('failed to import dynamic album for row=$row'); + return; + } + + add(filter); + }); + } +} + +@immutable +class DynamicAlbumRow extends Equatable { + final String name; + final CollectionFilter filter; + + @override + List get props => [name, filter]; + + const DynamicAlbumRow({ + required this.name, + required this.filter, + }); + + static DynamicAlbumRow? fromMap(Map map) { + final filter = CollectionFilter.fromJson(map['filter']); + if (filter == null) return null; + + return DynamicAlbumRow( + name: map['name'] as String, + filter: filter, + ); + } + + Map toMap() => { + 'name': name, + 'filter': filter.toJson(), + }; +} diff --git a/lib/model/favourites.dart b/lib/model/favourites.dart index b54bff4ef..7df6d0281 100644 --- a/lib/model/favourites.dart +++ b/lib/model/favourites.dart @@ -59,7 +59,7 @@ class Favourites with ChangeNotifier { Map>? export(CollectionSource source) { final visibleEntries = source.visibleEntries; - final ids = favourites.all; + final ids = all; final paths = visibleEntries.where((entry) => ids.contains(entry.id)).map((entry) => entry.path).nonNulls.toSet(); final byVolume = groupBy(paths, androidFileUtils.getStorageVolume); final jsonMap = Map.fromEntries(byVolume.entries.map((kv) { @@ -97,7 +97,7 @@ class Favourites with ChangeNotifier { } if (foundEntries.isNotEmpty) { - favourites.add(foundEntries); + add(foundEntries); } if (missedPaths.isNotEmpty) { debugPrint('failed to import favourites with ${missedPaths.length} missed paths'); diff --git a/lib/model/filters/covered/covered.dart b/lib/model/filters/covered/covered.dart new file mode 100644 index 000000000..bea2e8506 --- /dev/null +++ b/lib/model/filters/covered/covered.dart @@ -0,0 +1,17 @@ +import 'package:aves/model/covers.dart'; +import 'package:aves/model/filters/filters.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; + +@immutable +mixin CoveredFilter on CollectionFilter { + @override + Future color(BuildContext context) { + final customColor = covers.of(this)?.$3; + if (customColor != null) { + return SynchronousFuture(customColor); + } + return super.color(context); + } +} + diff --git a/lib/model/filters/covered/dynamic_album.dart b/lib/model/filters/covered/dynamic_album.dart new file mode 100644 index 000000000..d8991ce5f --- /dev/null +++ b/lib/model/filters/covered/dynamic_album.dart @@ -0,0 +1,69 @@ +import 'package:aves/model/filters/covered/covered.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; +import 'package:aves/model/filters/filters.dart'; +import 'package:aves/theme/icons.dart'; +import 'package:aves_model/aves_model.dart'; +import 'package:flutter/widgets.dart'; + +class DynamicAlbumFilter extends AlbumBaseFilter with CoveredFilter { + static const type = 'dynamic_album'; + + final String name; + final CollectionFilter filter; + + @override + List get props => [name, filter, reversed]; + + DynamicAlbumFilter(this.name, this.filter, {super.reversed = false}); + + static DynamicAlbumFilter? fromMap(Map json) { + final filter = CollectionFilter.fromJson(json['filter']); + if (filter == null) return null; + + return DynamicAlbumFilter( + json['name'], + filter, + reversed: json['reversed'] ?? false, + ); + } + + @override + Map toMap() => { + 'type': type, + 'name': name, + 'filter': filter.toJson(), + 'reversed': reversed, + }; + + @override + EntryFilter get positiveTest => filter.test; + + @override + bool get exclusiveProp => false; + + @override + String get universalLabel => name; + + @override + Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) { + return allowGenericIcon ? Icon(AIcons.dynamicAlbum, size: size) : null; + } + + @override + String get category => type; + + @override + String get key => '$type-$reversed-$name'; + + @override + bool match(String query) => name.toUpperCase().contains(query); + + @override + StorageVolume? get storageVolume => null; + + @override + bool get canRename => true; + + @override + bool get isVault => false; +} diff --git a/lib/model/filters/location.dart b/lib/model/filters/covered/location.dart similarity index 96% rename from lib/model/filters/location.dart rename to lib/model/filters/covered/location.dart index 7c29eddf0..701a3f8b4 100644 --- a/lib/model/filters/location.dart +++ b/lib/model/filters/covered/location.dart @@ -1,4 +1,5 @@ import 'package:aves/model/device.dart'; +import 'package:aves/model/filters/covered/covered.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/utils/emoji_utils.dart'; @@ -6,7 +7,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:collection/collection.dart'; import 'package:flutter/widgets.dart'; -class LocationFilter extends CoveredCollectionFilter { +class LocationFilter extends CollectionFilter with CoveredFilter { static const type = 'location'; static const locationSeparator = ';'; diff --git a/lib/model/filters/album.dart b/lib/model/filters/covered/stored_album.dart similarity index 71% rename from lib/model/filters/album.dart rename to lib/model/filters/covered/stored_album.dart index 53121a7ec..91ec8f6ad 100644 --- a/lib/model/filters/album.dart +++ b/lib/model/filters/covered/stored_album.dart @@ -1,15 +1,30 @@ import 'package:aves/model/covers.dart'; +import 'package:aves/model/filters/covered/covered.dart'; import 'package:aves/model/filters/filters.dart'; +import 'package:aves/model/vaults/vaults.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/theme/colors.dart'; import 'package:aves/theme/icons.dart'; +import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/widgets/common/identity/aves_icons.dart'; import 'package:aves_model/aves_model.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; import 'package:provider/provider.dart'; -class AlbumFilter extends CoveredCollectionFilter { +abstract class AlbumBaseFilter extends CollectionFilter { + const AlbumBaseFilter({required super.reversed}); + + bool match(String query); + + StorageVolume? get storageVolume; + + bool get canRename; + + bool get isVault; +} + +class StoredAlbumFilter extends AlbumBaseFilter with CoveredFilter { static const type = 'album'; final String album; @@ -19,12 +34,12 @@ class AlbumFilter extends CoveredCollectionFilter { @override List get props => [album, reversed]; - AlbumFilter(this.album, this.displayName, {super.reversed = false}) { + StoredAlbumFilter(this.album, this.displayName, {super.reversed = false}) { _test = (entry) => entry.directory == album; } - factory AlbumFilter.fromMap(Map json) { - return AlbumFilter( + factory StoredAlbumFilter.fromMap(Map json) { + return StoredAlbumFilter( json['album'], json['uniqueName'], reversed: json['reversed'] ?? false, @@ -95,7 +110,25 @@ class AlbumFilter extends CoveredCollectionFilter { @override String get category => type; - // key `album-{path}` is expected by test driver + // key is expected by test driver @override String get key => '$type-$reversed-$album'; + + @override + bool match(String query) => (displayName ?? album).toUpperCase().contains(query); + + @override + StorageVolume? get storageVolume => androidFileUtils.getStorageVolume(album); + + @override + bool get canRename { + if (isVault) return true; + + // do not allow renaming volume root + final dir = androidFileUtils.relativeDirectoryFromPath(album); + return dir != null && dir.relativeDir.isNotEmpty; + } + + @override + bool get isVault => vaults.isVault(album); } diff --git a/lib/model/filters/tag.dart b/lib/model/filters/covered/tag.dart similarity index 92% rename from lib/model/filters/tag.dart rename to lib/model/filters/covered/tag.dart index 8282ad871..51bffbdaa 100644 --- a/lib/model/filters/tag.dart +++ b/lib/model/filters/covered/tag.dart @@ -1,9 +1,10 @@ +import 'package:aves/model/filters/covered/covered.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:flutter/widgets.dart'; -class TagFilter extends CoveredCollectionFilter { +class TagFilter extends CollectionFilter with CoveredFilter { static const type = 'tag'; final String tag; diff --git a/lib/model/filters/filters.dart b/lib/model/filters/filters.dart index fe52ebbdf..a17eb11bd 100644 --- a/lib/model/filters/filters.dart +++ b/lib/model/filters/filters.dart @@ -1,22 +1,23 @@ import 'dart:convert'; -import 'package:aves/model/covers.dart'; import 'package:aves/model/entry/entry.dart'; -import 'package:aves/model/filters/album.dart'; import 'package:aves/model/filters/aspect_ratio.dart'; import 'package:aves/model/filters/coordinate.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; +import 'package:aves/model/filters/covered/dynamic_album.dart'; +import 'package:aves/model/filters/covered/location.dart'; +import 'package:aves/model/filters/covered/tag.dart'; import 'package:aves/model/filters/date.dart'; import 'package:aves/model/filters/favourite.dart'; -import 'package:aves/model/filters/location.dart'; import 'package:aves/model/filters/mime.dart'; import 'package:aves/model/filters/missing.dart'; -import 'package:aves/model/filters/or.dart'; import 'package:aves/model/filters/path.dart'; import 'package:aves/model/filters/placeholder.dart'; import 'package:aves/model/filters/query.dart'; import 'package:aves/model/filters/rating.dart'; import 'package:aves/model/filters/recent.dart'; -import 'package:aves/model/filters/tag.dart'; +import 'package:aves/model/filters/set_and.dart'; +import 'package:aves/model/filters/set_or.dart'; import 'package:aves/model/filters/trash.dart'; import 'package:aves/model/filters/type.dart'; import 'package:aves/theme/colors.dart'; @@ -31,8 +32,11 @@ abstract class CollectionFilter extends Equatable implements Comparable categoryOrder = [ TrashFilter.type, QueryFilter.type, + SetAndFilter.type, + SetOrFilter.type, MimeFilter.type, - AlbumFilter.type, + DynamicAlbumFilter.type, + StoredAlbumFilter.type, TypeFilter.type, RecentlyAddedFilter.type, DateFilter.type, @@ -44,7 +48,6 @@ abstract class CollectionFilter extends Equatable implements Comparable jsonMap) { final type = jsonMap['type']; switch (type) { - case AlbumFilter.type: - return AlbumFilter.fromMap(jsonMap); case AspectRatioFilter.type: return AspectRatioFilter.fromMap(jsonMap); case CoordinateFilter.type: return CoordinateFilter.fromMap(jsonMap); case DateFilter.type: return DateFilter.fromMap(jsonMap); + case DynamicAlbumFilter.type: + return DynamicAlbumFilter.fromMap(jsonMap); case FavouriteFilter.type: return FavouriteFilter.fromMap(jsonMap); case LocationFilter.type: @@ -70,8 +73,10 @@ abstract class CollectionFilter extends Equatable implements Comparable color(BuildContext context) { - final customColor = covers.of(this)?.$3; - if (customColor != null) { - return SynchronousFuture(customColor); - } - return super.color(context); - } -} - @immutable class FilterGridItem with EquatableMixin { final T filter; diff --git a/lib/model/filters/set_and.dart b/lib/model/filters/set_and.dart new file mode 100644 index 000000000..ca4aea296 --- /dev/null +++ b/lib/model/filters/set_and.dart @@ -0,0 +1,75 @@ +import 'package:aves/model/filters/covered/stored_album.dart'; +import 'package:aves/model/filters/filters.dart'; +import 'package:aves/model/filters/covered/location.dart'; +import 'package:aves/theme/icons.dart'; +import 'package:collection/collection.dart'; +import 'package:flutter/widgets.dart'; + +class SetAndFilter extends CollectionFilter { + static const type = 'and'; + + late final List _filters; + + late final EntryFilter _test; + late final IconData? _genericIcon; + + @override + List get props => [_filters, reversed]; + + CollectionFilter get _first => _filters.first; + + SetAndFilter(Set filters, {super.reversed = false}) { + _filters = filters.toList().sorted(); + _test = (entry) => _filters.every((v) => v.test(entry)); + switch (_first) { + case StoredAlbumFilter(): + _genericIcon = AIcons.album; + case LocationFilter(level: LocationLevel.country): + _genericIcon = AIcons.country; + case LocationFilter(level: LocationLevel.state): + _genericIcon = AIcons.state; + default: + _genericIcon = null; + } + } + + static SetAndFilter? fromMap(Map json) { + final filters = (json['filters'] as List).cast().map(CollectionFilter.fromJson).nonNulls.toSet(); + if (filters.isEmpty) return null; + + return SetAndFilter( + filters, + reversed: json['reversed'] ?? false, + ); + } + + @override + Map toMap() => { + 'type': type, + 'filters': _filters.map((v) => v.toJson()).toList(), + 'reversed': reversed, + }; + + @override + EntryFilter get positiveTest => _test; + + @override + bool get exclusiveProp => false; + + @override + String get universalLabel => _filters.map((v) => v.universalLabel).join(', '); + + @override + String getLabel(BuildContext context) => _filters.map((v) => v.getLabel(context)).join(', '); + + @override + Widget? iconBuilder(BuildContext context, double size, {bool allowGenericIcon = true}) { + return _genericIcon != null ? Icon(_genericIcon, size: size) : _first.iconBuilder(context, size, allowGenericIcon: allowGenericIcon); + } + + @override + String get category => _first.category; + + @override + String get key => '$type-$reversed-${_filters.map((v) => v.key)}'; +} diff --git a/lib/model/filters/or.dart b/lib/model/filters/set_or.dart similarity index 77% rename from lib/model/filters/or.dart rename to lib/model/filters/set_or.dart index ea4876e3d..98f392ff3 100644 --- a/lib/model/filters/or.dart +++ b/lib/model/filters/set_or.dart @@ -1,11 +1,11 @@ -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/theme/icons.dart'; import 'package:collection/collection.dart'; import 'package:flutter/widgets.dart'; -class OrFilter extends CollectionFilter { +class SetOrFilter extends CollectionFilter { static const type = 'or'; late final List _filters; @@ -18,11 +18,11 @@ class OrFilter extends CollectionFilter { CollectionFilter get _first => _filters.first; - OrFilter(Set filters, {super.reversed = false}) { + SetOrFilter(Set filters, {super.reversed = false}) { _filters = filters.toList().sorted(); _test = (entry) => _filters.any((v) => v.test(entry)); switch (_first) { - case AlbumFilter(): + case StoredAlbumFilter(): _genericIcon = AIcons.album; case LocationFilter(level: LocationLevel.country): _genericIcon = AIcons.country; @@ -33,9 +33,12 @@ class OrFilter extends CollectionFilter { } } - factory OrFilter.fromMap(Map json) { - return OrFilter( - (json['filters'] as List).cast().map(CollectionFilter.fromJson).nonNulls.toSet(), + static SetOrFilter? fromMap(Map json) { + final filters = (json['filters'] as List).cast().map(CollectionFilter.fromJson).nonNulls.toSet(); + if (filters.isEmpty) return null; + + return SetOrFilter( + filters, reversed: json['reversed'] ?? false, ); } diff --git a/lib/model/settings/modules/navigation.dart b/lib/model/settings/modules/navigation.dart index 2f6eca490..6b24ed152 100644 --- a/lib/model/settings/modules/navigation.dart +++ b/lib/model/settings/modules/navigation.dart @@ -1,3 +1,4 @@ +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/defaults.dart'; import 'package:aves_model/aves_model.dart'; @@ -64,9 +65,9 @@ mixin NavigationSettings on SettingsAccess { set drawerTypeBookmarks(List newValue) => set(SettingKeys.drawerTypeBookmarksKey, newValue.map((filter) => filter?.toJson() ?? '').toList()); - List? get drawerAlbumBookmarks => getStringList(SettingKeys.drawerAlbumBookmarksKey); + List? get drawerAlbumBookmarks => getStringList(SettingKeys.drawerAlbumBookmarksKey)?.map(CollectionFilter.fromJson).whereType().toList(); - set drawerAlbumBookmarks(List? newValue) => set(SettingKeys.drawerAlbumBookmarksKey, newValue); + set drawerAlbumBookmarks(List? newValue) => set(SettingKeys.drawerAlbumBookmarksKey, newValue?.map((filter) => filter.toJson()).toList()); List get drawerPageBookmarks => getStringList(SettingKeys.drawerPageBookmarksKey) ?? SettingsDefaults.drawerPageBookmarks; diff --git a/lib/model/source/album.dart b/lib/model/source/album.dart index b30b693d8..032ba52c4 100644 --- a/lib/model/source/album.dart +++ b/lib/model/source/album.dart @@ -1,5 +1,6 @@ import 'package:aves/model/entry/entry.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/dynamic_album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/vaults/vaults.dart'; @@ -17,13 +18,13 @@ mixin AlbumMixin on SourceBase { List get rawAlbums => List.unmodifiable(_directories); - Set getNewAlbumFilters(BuildContext context) => Set.unmodifiable(_newAlbums.map((v) => AlbumFilter(v, getAlbumDisplayName(context, v)))); + Set getNewAlbumFilters(BuildContext context) => Set.unmodifiable(_newAlbums.map((v) => StoredAlbumFilter(v, getStoredAlbumDisplayName(context, v)))); int compareAlbumsByName(String? a, String? b) { a ??= ''; b ??= ''; - final ua = getAlbumDisplayName(null, a); - final ub = getAlbumDisplayName(null, b); + final ua = getStoredAlbumDisplayName(null, a); + final ub = getStoredAlbumDisplayName(null, b); final c = compareAsciiUpperCaseNatural(ua, ub); if (c != 0) return c; final va = androidFileUtils.getStorageVolume(a)?.path ?? ''; @@ -36,7 +37,7 @@ mixin AlbumMixin on SourceBase { } void _onAlbumChanged({bool notify = true}) { - invalidateAlbumDisplayNames(); + invalidateStoredAlbumDisplayNames(); if (notify) { notifyAlbumsChanged(); } @@ -82,12 +83,6 @@ mixin AlbumMixin on SourceBase { _directories.removeAll(removableAlbums); _onAlbumChanged(); invalidateAlbumFilterSummary(directories: removableAlbums); - - final bookmarks = settings.drawerAlbumBookmarks; - removableAlbums.forEach((album) { - bookmarks?.remove(album); - }); - settings.drawerAlbumBookmarks = bookmarks; } } @@ -95,13 +90,13 @@ mixin AlbumMixin on SourceBase { if (visibleEntries.any((entry) => entry.directory == album)) return false; if (_newAlbums.contains(album)) return false; if (vaults.isVault(album)) return false; - if (settings.pinnedFilters.whereType().map((v) => v.album).contains(album)) return false; + if (settings.pinnedFilters.whereType().map((v) => v.album).contains(album)) return false; return true; } // filter summary - // by directory + // by filter key final Map _filterEntryCountMap = {}, _filterSizeMap = {}; final Map _filterRecentEntryMap = {}; @@ -117,36 +112,53 @@ mixin AlbumMixin on SourceBase { _filterSizeMap.clear(); _filterRecentEntryMap.clear(); } else { + // clear entries only for modified album directories directories ??= {}; if (entries != null) { directories.addAll(entries.map((entry) => entry.directory).nonNulls); } - directories.forEach((directory) { - _filterEntryCountMap.remove(directory); - _filterSizeMap.remove(directory); - _filterRecentEntryMap.remove(directory); + directories.nonNulls.map((v) => StoredAlbumFilter(v, null).key).forEach((key) { + _filterEntryCountMap.remove(key); + _filterSizeMap.remove(key); + _filterRecentEntryMap.remove(key); }); + + // clear entries for all dynamic albums + invalidateDynamicAlbumFilterSummary(notify: false); } if (notify) { - eventBus.fire(AlbumSummaryInvalidatedEvent(directories)); + eventBus.fire(StoredAlbumSummaryInvalidatedEvent(directories)); + eventBus.fire(const DynamicAlbumSummaryInvalidatedEvent()); } } - int albumEntryCount(AlbumFilter filter) { - return _filterEntryCountMap.putIfAbsent(filter.album, () => visibleEntries.where(filter.test).length); + void invalidateDynamicAlbumFilterSummary({bool notify = true}) { + _filterEntryCountMap.removeWhere(_isDynamicAlbumKey); + _filterSizeMap.removeWhere(_isDynamicAlbumKey); + _filterRecentEntryMap.removeWhere(_isDynamicAlbumKey); + + if (notify) { + eventBus.fire(const DynamicAlbumSummaryInvalidatedEvent()); + } } - int albumSize(AlbumFilter filter) { - return _filterSizeMap.putIfAbsent(filter.album, () => visibleEntries.where(filter.test).map((v) => v.sizeBytes).sum); + bool _isDynamicAlbumKey(String key, _) => key.startsWith('${DynamicAlbumFilter.type}-'); + + int albumEntryCount(AlbumBaseFilter filter) { + return _filterEntryCountMap.putIfAbsent(filter.key, () => visibleEntries.where(filter.test).length); + } + + int albumSize(AlbumBaseFilter filter) { + return _filterSizeMap.putIfAbsent(filter.key, () => visibleEntries.where(filter.test).map((v) => v.sizeBytes).sum); } - AvesEntry? albumRecentEntry(AlbumFilter filter) { - return _filterRecentEntryMap.putIfAbsent(filter.album, () => sortedEntriesByDate.firstWhereOrNull(filter.test)); + AvesEntry? albumRecentEntry(AlbumBaseFilter filter) { + return _filterRecentEntryMap.putIfAbsent(filter.key, () => sortedEntriesByDate.firstWhereOrNull(filter.test)); } // new albums - void createAlbum(String directory) { + void createStoredAlbum(String directory) { if (!_directories.contains(directory)) { _newAlbums.add(directory); addDirectories(albums: {directory}); @@ -156,7 +168,7 @@ mixin AlbumMixin on SourceBase { void renameNewAlbum(String source, String destination) { if (_newAlbums.remove(source)) { cleanEmptyAlbums({source}); - createAlbum(destination); + createStoredAlbum(destination); } } @@ -168,12 +180,12 @@ mixin AlbumMixin on SourceBase { final Map _albumDisplayNamesWithContext = {}, _albumDisplayNamesWithoutContext = {}; - void invalidateAlbumDisplayNames() { + void invalidateStoredAlbumDisplayNames() { _albumDisplayNamesWithContext.clear(); _albumDisplayNamesWithoutContext.clear(); } - String _computeDisplayName(BuildContext? context, String dirPath) { + String _computeStoredAlbumDisplayName(BuildContext? context, String dirPath) { final separator = pContext.separator; assert(!dirPath.endsWith(separator)); @@ -222,16 +234,20 @@ mixin AlbumMixin on SourceBase { } } - String getAlbumDisplayName(BuildContext? context, String dirPath) { + String getStoredAlbumDisplayName(BuildContext? context, String dirPath) { final names = (context != null ? _albumDisplayNamesWithContext : _albumDisplayNamesWithoutContext); - return names.putIfAbsent(dirPath, () => _computeDisplayName(context, dirPath)); + return names.putIfAbsent(dirPath, () => _computeStoredAlbumDisplayName(context, dirPath)); } } class AlbumsChangedEvent {} -class AlbumSummaryInvalidatedEvent { +class DynamicAlbumSummaryInvalidatedEvent { + const DynamicAlbumSummaryInvalidatedEvent(); +} + +class StoredAlbumSummaryInvalidatedEvent { final Set? directories; - const AlbumSummaryInvalidatedEvent(this.directories); + const StoredAlbumSummaryInvalidatedEvent(this.directories); } diff --git a/lib/model/source/collection_lens.dart b/lib/model/source/collection_lens.dart index f7976bdca..81d3bed0d 100644 --- a/lib/model/source/collection_lens.dart +++ b/lib/model/source/collection_lens.dart @@ -6,10 +6,10 @@ import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/entry/sort.dart'; import 'package:aves/model/favourites.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/favourite.dart'; import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/filters/mime.dart'; import 'package:aves/model/filters/query.dart'; import 'package:aves/model/filters/rating.dart'; @@ -143,7 +143,7 @@ class CollectionLens with ChangeNotifier { } bool get showHeaders { - bool showAlbumHeaders() => !filters.any((v) => v is AlbumFilter && !v.reversed); + bool showAlbumHeaders() => !filters.any((v) => v is StoredAlbumFilter && !v.reversed); switch (sortFactor) { case EntrySortFactor.date: diff --git a/lib/model/source/collection_source.dart b/lib/model/source/collection_source.dart index b87f599e8..2efcd06ed 100644 --- a/lib/model/source/collection_source.dart +++ b/lib/model/source/collection_source.dart @@ -7,10 +7,10 @@ import 'package:aves/model/entry/extensions/catalog.dart'; import 'package:aves/model/entry/extensions/location.dart'; import 'package:aves/model/entry/sort.dart'; import 'package:aves/model/favourites.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/location.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; +import 'package:aves/model/filters/covered/tag.dart'; import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/location.dart'; -import 'package:aves/model/filters/tag.dart'; import 'package:aves/model/filters/trash.dart'; import 'package:aves/model/metadata/trash.dart'; import 'package:aves/model/settings/settings.dart'; @@ -75,7 +75,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place object: this, ); } - settings.updateStream.where((event) => event.key == SettingKeys.localeKey).listen((_) => invalidateAlbumDisplayNames()); + settings.updateStream.where((event) => event.key == SettingKeys.localeKey).listen((_) => invalidateStoredAlbumDisplayNames()); settings.updateStream.where((event) => event.key == SettingKeys.hiddenFiltersKey).listen((event) { final oldValue = event.oldValue; if (oldValue is List?) { @@ -144,7 +144,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place Set _getAppHiddenFilters() => { ...settings.hiddenFilters, - ...vaults.vaultDirectories.where(vaults.isLocked).map((v) => AlbumFilter(v, null)), + ...vaults.vaultDirectories.where(vaults.isLocked).map((v) => StoredAlbumFilter(v, null)), }; Iterable _applyHiddenFilters(Iterable entries) { @@ -288,11 +288,10 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place } } - Future renameAlbum(String sourceAlbum, String destinationAlbum, Set entries, Set movedOps) async { - final oldFilter = AlbumFilter(sourceAlbum, null); - final newFilter = AlbumFilter(destinationAlbum, null); + Future renameStoredAlbum(String sourceAlbum, String destinationAlbum, Set entries, Set movedOps) async { + final oldFilter = StoredAlbumFilter(sourceAlbum, null); + final newFilter = StoredAlbumFilter(destinationAlbum, null); - final bookmark = settings.drawerAlbumBookmarks?.indexOf(sourceAlbum); final pinned = settings.pinnedFilters.contains(oldFilter); if (vaults.isVault(sourceAlbum)) { @@ -315,10 +314,17 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place movedOps: movedOps, ); - // restore bookmark and pin, as the obsolete album got removed and its associated state cleaned - if (bookmark != null && bookmark != -1) { - settings.drawerAlbumBookmarks = settings.drawerAlbumBookmarks?..insert(bookmark, destinationAlbum); + // update bookmark + final albumBookmarks = settings.drawerAlbumBookmarks; + if (albumBookmarks != null) { + final index = albumBookmarks.indexWhere((v) => v is StoredAlbumFilter && v.album == sourceAlbum); + if (index >= 0) { + albumBookmarks.removeAt(index); + albumBookmarks.insert(index, newFilter); + settings.drawerAlbumBookmarks = albumBookmarks; + } } + // restore pin, as the obsolete album got removed and its associated state cleaned if (pinned) { settings.pinnedFilters = settings.pinnedFilters ..remove(oldFilter) @@ -541,8 +547,9 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place // filter summary int count(CollectionFilter filter) { - if (filter is AlbumFilter) return albumEntryCount(filter); - if (filter is LocationFilter) { + if (filter is AlbumBaseFilter) { + return albumEntryCount(filter); + } else if (filter is LocationFilter) { switch (filter.level) { case LocationLevel.country: return countryEntryCount(filter); @@ -551,14 +558,16 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place case LocationLevel.place: return placeEntryCount(filter); } + } else if (filter is TagFilter) { + return tagEntryCount(filter); } - if (filter is TagFilter) return tagEntryCount(filter); return 0; } int size(CollectionFilter filter) { - if (filter is AlbumFilter) return albumSize(filter); - if (filter is LocationFilter) { + if (filter is AlbumBaseFilter) { + return albumSize(filter); + } else if (filter is LocationFilter) { switch (filter.level) { case LocationLevel.country: return countrySize(filter); @@ -567,14 +576,16 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place case LocationLevel.place: return placeSize(filter); } + } else if (filter is TagFilter) { + return tagSize(filter); } - if (filter is TagFilter) return tagSize(filter); return 0; } AvesEntry? recentEntry(CollectionFilter filter) { - if (filter is AlbumFilter) return albumRecentEntry(filter); - if (filter is LocationFilter) { + if (filter is AlbumBaseFilter) { + return albumRecentEntry(filter); + } else if (filter is LocationFilter) { switch (filter.level) { case LocationLevel.country: return countryRecentEntry(filter); @@ -583,8 +594,9 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place case LocationLevel.place: return placeRecentEntry(filter); } + } else if (filter is TagFilter) { + return tagRecentEntry(filter); } - if (filter is TagFilter) return tagRecentEntry(filter); return null; } @@ -608,7 +620,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place } void _onVaultsChanged() { - final newlyVisibleFilters = vaults.vaultDirectories.whereNot(vaults.isLocked).map((v) => AlbumFilter(v, null)).toSet(); + final newlyVisibleFilters = vaults.vaultDirectories.whereNot(vaults.isLocked).map((v) => StoredAlbumFilter(v, null)).toSet(); _onFilterVisibilityChanged(newlyVisibleFilters); } } diff --git a/lib/model/source/location/country.dart b/lib/model/source/location/country.dart index bf1b7264a..f7994077a 100644 --- a/lib/model/source/location/country.dart +++ b/lib/model/source/location/country.dart @@ -1,5 +1,5 @@ import 'package:aves/model/entry/entry.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/utils/collection_utils.dart'; import 'package:collection/collection.dart'; diff --git a/lib/model/source/location/location.dart b/lib/model/source/location/location.dart index d2af8780c..30ca150cf 100644 --- a/lib/model/source/location/location.dart +++ b/lib/model/source/location/location.dart @@ -3,7 +3,7 @@ import 'dart:math'; import 'package:aves/geo/countries.dart'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/location.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/metadata/address.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/analysis_controller.dart'; diff --git a/lib/model/source/location/place.dart b/lib/model/source/location/place.dart index 3b518a182..42d7cd39c 100644 --- a/lib/model/source/location/place.dart +++ b/lib/model/source/location/place.dart @@ -1,5 +1,5 @@ import 'package:aves/model/entry/entry.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/utils/collection_utils.dart'; import 'package:collection/collection.dart'; diff --git a/lib/model/source/location/state.dart b/lib/model/source/location/state.dart index bfadc9568..8eb4abdfe 100644 --- a/lib/model/source/location/state.dart +++ b/lib/model/source/location/state.dart @@ -1,5 +1,5 @@ import 'package:aves/model/entry/entry.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/utils/collection_utils.dart'; import 'package:collection/collection.dart'; diff --git a/lib/model/source/media_store_source.dart b/lib/model/source/media_store_source.dart index 9f01ef478..eac2270fb 100644 --- a/lib/model/source/media_store_source.dart +++ b/lib/model/source/media_store_source.dart @@ -1,10 +1,11 @@ import 'dart:async'; import 'package:aves/model/covers.dart'; +import 'package:aves/model/dynamic_albums.dart'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/origins.dart'; import 'package:aves/model/favourites.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/analysis_controller.dart'; import 'package:aves/model/source/collection_source.dart'; @@ -44,7 +45,7 @@ class MediaStoreSource extends CollectionSource { await reportService.log('$runtimeType init target scope=$scope'); _essentialLoader ??= _loadEssentials(); await _essentialLoader; - addDirectories(albums: settings.pinnedFilters.whereType().map((v) => v.album).toSet()); + addDirectories(albums: settings.pinnedFilters.whereType().map((v) => v.album).toSet()); await updateGeneration(); unawaited(_loadEntries( analysisController: analysisController, @@ -59,13 +60,15 @@ class MediaStoreSource extends CollectionSource { await vaults.init(); await favourites.init(); await covers.init(); - final currentTimeZoneOffset = DateTime.now().timeZoneOffset.inMilliseconds; - final catalogTimeZoneOffset = settings.catalogTimeZoneOffsetMillis; - if (currentTimeZoneOffset != catalogTimeZoneOffset) { - unawaited(reportService.recordError('Time zone offset change: $currentTimeZoneOffset -> $catalogTimeZoneOffset. Clear catalog metadata to get correct date/times.')); + await dynamicAlbums.init(); + + final deviceOffset = DateTime.now().timeZoneOffset.inMilliseconds; + final catalogOffset = settings.catalogTimeZoneOffsetMillis; + if (deviceOffset != catalogOffset) { + unawaited(reportService.recordError('Time zone offset change: $catalogOffset -> $deviceOffset. Clear catalog metadata to get correct date/times.')); await localMediaDb.clearDates(); await localMediaDb.clearCatalogMetadata(); - settings.catalogTimeZoneOffsetMillis = currentTimeZoneOffset; + settings.catalogTimeZoneOffsetMillis = deviceOffset; } await loadDates(); debugPrint('$runtimeType load essentials complete in ${stopwatch.elapsed.inMilliseconds}ms'); @@ -80,7 +83,7 @@ class MediaStoreSource extends CollectionSource { state = SourceState.loading; clearEntries(); - final scopeAlbumFilters = _targetScope?.whereType(); + final scopeAlbumFilters = _targetScope?.whereType(); final scopeDirectory = scopeAlbumFilters != null && scopeAlbumFilters.length == 1 ? scopeAlbumFilters.first.album : null; final Set topEntries = {}; diff --git a/lib/model/source/tag.dart b/lib/model/source/tag.dart index efd96d26e..d0f0d3f8d 100644 --- a/lib/model/source/tag.dart +++ b/lib/model/source/tag.dart @@ -1,6 +1,6 @@ import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/catalog.dart'; -import 'package:aves/model/filters/tag.dart'; +import 'package:aves/model/filters/covered/tag.dart'; import 'package:aves/model/metadata/catalog.dart'; import 'package:aves/model/source/analysis_controller.dart'; import 'package:aves/model/source/collection_source.dart'; diff --git a/lib/theme/icons.dart b/lib/theme/icons.dart index f947b079a..031788e88 100644 --- a/lib/theme/icons.dart +++ b/lib/theme/icons.dart @@ -126,6 +126,7 @@ class AIcons { static final unpin = MdiIcons.pinOffOutline; static const print = Icons.print_outlined; static const refresh = Icons.refresh_outlined; + static const remove = Icons.remove_outlined; static final resetBounds = MdiIcons.rayStartEnd; static const reverse = Icons.invert_colors_outlined; static const reset = Icons.restart_alt_outlined; @@ -187,6 +188,7 @@ class AIcons { // albums static const album = Icons.photo_album_outlined; + static const dynamicAlbum = Icons.image_search_outlined; static const cameraAlbum = Icons.photo_camera_outlined; static const downloadAlbum = Icons.file_download; static const screenshotAlbum = Icons.screenshot_outlined; diff --git a/lib/utils/collection_utils.dart b/lib/utils/collection_utils.dart index 0da1d0a58..505f487ac 100644 --- a/lib/utils/collection_utils.dart +++ b/lib/utils/collection_utils.dart @@ -1,5 +1,3 @@ - - extension ExtraList on List { bool replace(E old, E newItem) { final index = indexOf(old); @@ -10,6 +8,15 @@ extension ExtraList on List { } } +extension ExtraSet on Set { + bool replace(E old, E newItem) { + if (!remove(old)) return false; + + add(newItem); + return true; + } +} + extension ExtraMapNullableKey on Map { Map whereNotNullKey() => {for (var v in keys.nonNulls) v: this[v] as V}; } diff --git a/lib/view/src/actions/chip_set.dart b/lib/view/src/actions/chip_set.dart index 0e95ccfbc..e41ceb2d3 100644 --- a/lib/view/src/actions/chip_set.dart +++ b/lib/view/src/actions/chip_set.dart @@ -25,6 +25,7 @@ extension ExtraChipSetActionView on ChipSetAction { ChipSetAction.stats => l10n.menuActionStats, // selecting (single/multiple filters) ChipSetAction.delete => l10n.chipActionDelete, + ChipSetAction.remove => l10n.chipActionRemove, ChipSetAction.hide => l10n.chipActionHide, ChipSetAction.pin => l10n.chipActionPin, ChipSetAction.unpin => l10n.chipActionUnpin, @@ -60,6 +61,7 @@ extension ExtraChipSetActionView on ChipSetAction { ChipSetAction.stats => AIcons.stats, // selecting (single/multiple filters) ChipSetAction.delete => AIcons.delete, + ChipSetAction.remove => AIcons.remove, ChipSetAction.hide => AIcons.hide, ChipSetAction.pin => AIcons.pin, ChipSetAction.unpin => AIcons.unpin, diff --git a/lib/view/src/actions/entry_set.dart b/lib/view/src/actions/entry_set.dart index d9105e0e2..393ff839f 100644 --- a/lib/view/src/actions/entry_set.dart +++ b/lib/view/src/actions/entry_set.dart @@ -17,6 +17,7 @@ extension ExtraEntrySetActionView on EntrySetAction { EntrySetAction.toggleTitleSearch => // different data depending on toggle state l10n.collectionActionShowTitleSearch, + EntrySetAction.addDynamicAlbum => l10n.collectionActionAddDynamicAlbum, EntrySetAction.addShortcut => l10n.collectionActionAddShortcut, EntrySetAction.setHome => l10n.collectionActionSetHome, EntrySetAction.emptyBin => l10n.collectionActionEmptyBin, @@ -62,6 +63,7 @@ extension ExtraEntrySetActionView on EntrySetAction { EntrySetAction.toggleTitleSearch => // different data depending on toggle state AIcons.filter, + EntrySetAction.addDynamicAlbum => AIcons.dynamicAlbum, EntrySetAction.addShortcut => AIcons.addShortcut, EntrySetAction.setHome => AIcons.home, EntrySetAction.emptyBin => AIcons.emptyBin, diff --git a/lib/widgets/aves_app.dart b/lib/widgets/aves_app.dart index 39620f813..6c38f9a65 100644 --- a/lib/widgets/aves_app.dart +++ b/lib/widgets/aves_app.dart @@ -559,12 +559,17 @@ class _AvesAppState extends State with WidgetsBindingObserver { void _applyDisplayRefreshRateMode() => settings.displayRefreshRateMode.apply(); void _applyMaxBrightness() { - switch (settings.maxBrightness) { - case MaxBrightness.never: - case MaxBrightness.viewerOnly: - AvesApp.screenBrightness?.resetApplicationScreenBrightness(); - case MaxBrightness.always: - AvesApp.screenBrightness?.setApplicationScreenBrightness(1); + try { + switch (settings.maxBrightness) { + case MaxBrightness.never: + case MaxBrightness.viewerOnly: + AvesApp.screenBrightness?.resetApplicationScreenBrightness(); + case MaxBrightness.always: + AvesApp.screenBrightness?.setApplicationScreenBrightness(1); + } + } on PlatformException catch (e, stack) { + // `screen_brightness` plugin may fail + reportService.recordError(e, stack); } } diff --git a/lib/widgets/collection/app_bar.dart b/lib/widgets/collection/app_bar.dart index e2f47293f..89f921856 100644 --- a/lib/widgets/collection/app_bar.dart +++ b/lib/widgets/collection/app_bar.dart @@ -322,7 +322,7 @@ class _CollectionAppBarState extends State with SingleTickerPr bool canApply(EntrySetAction action) => _actionDelegate.canApply( action, isSelecting: isSelecting, - itemCount: collection.entryCount, + collection: collection, selectedItemCount: selectedItemCount, ); @@ -462,7 +462,7 @@ class _CollectionAppBarState extends State with SingleTickerPr return selection.selectedItems.expand((entry) => entry.stackedEntries ?? {entry}).toSet(); } - // key is expected by test driver (e.g. 'menu-configureView', 'menu-map') + // key is expected by test driver Key _getActionKey(EntrySetAction action) => Key('menu-${action.name}'); Widget _buildButtonIcon( @@ -636,6 +636,7 @@ class _CollectionAppBarState extends State with SingleTickerPr // browsing case EntrySetAction.searchCollection: case EntrySetAction.toggleTitleSearch: + case EntrySetAction.addDynamicAlbum: case EntrySetAction.addShortcut: case EntrySetAction.setHome: // browsing or selecting diff --git a/lib/widgets/collection/collection_grid.dart b/lib/widgets/collection/collection_grid.dart index 7199fd7ad..b6236a80d 100644 --- a/lib/widgets/collection/collection_grid.dart +++ b/lib/widgets/collection/collection_grid.dart @@ -687,7 +687,7 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> with Widge sectionLayouts.forEach((section) { final directory = (section.sectionKey as EntryAlbumSectionKey).directory; if (directory != null) { - final label = source.getAlbumDisplayName(context, directory); + final label = source.getStoredAlbumDisplayName(context, directory); crumbs[section.minOffset / maxOffset] = label; } }); diff --git a/lib/widgets/collection/draggable_thumb_label.dart b/lib/widgets/collection/draggable_thumb_label.dart index 548dad5bd..8337f22a0 100644 --- a/lib/widgets/collection/draggable_thumb_label.dart +++ b/lib/widgets/collection/draggable_thumb_label.dart @@ -70,5 +70,5 @@ class CollectionDraggableThumbLabel extends StatelessWidget { bool _showAlbumName(BuildContext context, AvesEntry entry) => _hasMultipleSections(context) && entry.directory != null; - String _getAlbumName(BuildContext context, AvesEntry entry) => context.read().getAlbumDisplayName(context, entry.directory!); + String _getAlbumName(BuildContext context, AvesEntry entry) => context.read().getStoredAlbumDisplayName(context, entry.directory!); } diff --git a/lib/widgets/collection/entry_set_action_delegate.dart b/lib/widgets/collection/entry_set_action_delegate.dart index 93f1c5618..b9bb0c8b2 100644 --- a/lib/widgets/collection/entry_set_action_delegate.dart +++ b/lib/widgets/collection/entry_set_action_delegate.dart @@ -2,13 +2,17 @@ import 'dart:async'; import 'package:aves/app_mode.dart'; import 'package:aves/model/device.dart'; +import 'package:aves/model/dynamic_albums.dart'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/favourites.dart'; import 'package:aves/model/entry/extensions/metadata_edition.dart'; import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/favourites.dart'; +import 'package:aves/model/filters/covered/dynamic_album.dart'; import 'package:aves/model/filters/filters.dart'; +import 'package:aves/model/filters/set_and.dart'; +import 'package:aves/model/highlight.dart'; import 'package:aves/model/metadata/date_modifier.dart'; import 'package:aves/model/naming_pattern.dart'; import 'package:aves/model/query.dart'; @@ -39,7 +43,9 @@ import 'package:aves/widgets/dialogs/aves_confirmation_dialog.dart'; import 'package:aves/widgets/dialogs/aves_dialog.dart'; import 'package:aves/widgets/dialogs/convert_entry_dialog.dart'; import 'package:aves/widgets/dialogs/entry_editors/rename_entry_set_page.dart'; +import 'package:aves/widgets/dialogs/filter_editors/add_dynamic_album_dialog.dart'; import 'package:aves/widgets/dialogs/pick_dialogs/location_pick_page.dart'; +import 'package:aves/widgets/filter_grids/albums_page.dart'; import 'package:aves/widgets/map/map_page.dart'; import 'package:aves/widgets/search/search_delegate.dart'; import 'package:aves/widgets/stats/stats_page.dart'; @@ -75,28 +81,29 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware return isSelecting && selectedItemCount == itemCount; // browsing case EntrySetAction.searchCollection: - return !useTvLayout && appMode.canNavigate && !isSelecting; + return appMode.canNavigate && !isSelecting && !useTvLayout; case EntrySetAction.toggleTitleSearch: - return !useTvLayout && !isSelecting; + return !isSelecting && !useTvLayout; case EntrySetAction.addShortcut: return isMain && !isSelecting && !isTrash && device.canPinShortcut; + case EntrySetAction.addDynamicAlbum: case EntrySetAction.setHome: return isMain && !isSelecting && !isTrash && !useTvLayout; case EntrySetAction.emptyBin: - return canWrite && isMain && isTrash; + return isMain && isTrash && canWrite; // browsing or selecting case EntrySetAction.map: case EntrySetAction.slideshow: case EntrySetAction.stats: return isMain; case EntrySetAction.rescan: - return !useTvLayout && isMain && !isTrash && isSelecting; + return isMain && isSelecting && !isTrash && !useTvLayout; // selecting case EntrySetAction.share: case EntrySetAction.toggleFavourite: return isMain && isSelecting && !isTrash; case EntrySetAction.delete: - return canWrite && isMain && isSelecting; + return isMain && isSelecting && canWrite; case EntrySetAction.copy: case EntrySetAction.move: case EntrySetAction.rename: @@ -110,18 +117,19 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware case EntrySetAction.editRating: case EntrySetAction.editTags: case EntrySetAction.removeMetadata: - return canWrite && isMain && isSelecting && !isTrash; + return isMain && isSelecting && !isTrash && canWrite; case EntrySetAction.restore: - return canWrite && isMain && isSelecting && isTrash; + return isMain && isSelecting && isTrash && canWrite; } } bool canApply( EntrySetAction action, { required bool isSelecting, - required int itemCount, + required CollectionLens collection, required int selectedItemCount, }) { + final itemCount = collection.entryCount; final hasItems = itemCount > 0; final hasSelection = selectedItemCount > 0; @@ -139,6 +147,8 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware case EntrySetAction.addShortcut: case EntrySetAction.setHome: return true; + case EntrySetAction.addDynamicAlbum: + return collection.filters.isNotEmpty; case EntrySetAction.emptyBin: return !isSelecting && hasItems; case EntrySetAction.map: @@ -184,6 +194,8 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware final routeName = context.currentRouteName!; settings.setShowTitleQuery(routeName, !settings.getShowTitleQuery(routeName)); context.read().toggle(); + case EntrySetAction.addDynamicAlbum: + _addDynamicAlbum(context); case EntrySetAction.addShortcut: _addShortcut(context); case EntrySetAction.setHome: @@ -727,10 +739,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware ); } - Future _addShortcut(BuildContext context) async { - final collection = context.read(); - final filters = collection.filters; - + static String? _getDefaultNameForFilters(BuildContext context, Set filters) { String? defaultName; if (filters.isNotEmpty) { // we compute the default name beforehand @@ -738,6 +747,67 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware final sortedFilters = List.from(filters)..sort(); defaultName = sortedFilters.first.getLabel(context).replaceAll('\n', ' '); } + return defaultName; + } + + Future _addDynamicAlbum(BuildContext context) async { + final l10n = context.l10n; + final collection = context.read(); + final filters = collection.filters; + if (filters.isEmpty) return; + + // get navigator beforehand because + // local context may be deactivated when action is triggered after navigation + final navigator = Navigator.maybeOf(context); + + final name = await showDialog( + context: context, + builder: (context) => const AddDynamicAlbumDialog(), + routeSettings: const RouteSettings(name: AddDynamicAlbumDialog.routeName), + ); + if (name == null) return; + + final existingAlbum = dynamicAlbums.get(name); + if (existingAlbum != null) { + // album already exists, so we just need to highlight it + await _showDynamicAlbum(navigator, existingAlbum); + } else { + final album = DynamicAlbumFilter(name, filters.length == 1 ? filters.first : SetAndFilter(filters)); + await dynamicAlbums.add(album); + + final showAction = SnackBarAction( + label: l10n.showButtonLabel, + onPressed: () => _showDynamicAlbum(navigator, album), + ); + showFeedback(context, FeedbackType.info, l10n.genericSuccessFeedback, showAction); + } + } + + Future _showDynamicAlbum(NavigatorState? navigator, DynamicAlbumFilter album) async { + // local context may be deactivated when action is triggered after navigation + if (navigator != null) { + final context = navigator.context; + final highlightInfo = context.read(); + if (context.currentRouteName == AlbumListPage.routeName) { + highlightInfo.trackItem(FilterGridItem(album, null), highlightItem: album); + } else { + highlightInfo.set(album); + await navigator.pushAndRemoveUntil( + MaterialPageRoute( + settings: const RouteSettings(name: AlbumListPage.routeName), + builder: (_) => const AlbumListPage(), + ), + (route) => false, + ); + } + } + } + + Future _addShortcut(BuildContext context) async { + final collection = context.read(); + final filters = collection.filters; + + String? defaultName = _getDefaultNameForFilters(context, filters); final result = await showDialog<(AvesEntry?, String)>( context: context, builder: (context) => AddShortcutDialog( diff --git a/lib/widgets/collection/grid/headers/album.dart b/lib/widgets/collection/grid/headers/album.dart index 1a9e43d6e..8adb22ffc 100644 --- a/lib/widgets/collection/grid/headers/album.dart +++ b/lib/widgets/collection/grid/headers/album.dart @@ -53,7 +53,7 @@ class AlbumSectionHeader extends StatelessWidget { return SectionHeader.getPreferredHeight( context: context, maxWidth: maxWidth, - title: source.getAlbumDisplayName(context, directory), + title: source.getStoredAlbumDisplayName(context, directory), hasLeading: covers.effectiveAlbumType(directory) != AlbumType.regular, hasTrailing: androidFileUtils.isOnRemovableStorage(directory), ); diff --git a/lib/widgets/collection/grid/headers/any.dart b/lib/widgets/collection/grid/headers/any.dart index 213b7acf8..6b14ab3dc 100644 --- a/lib/widgets/collection/grid/headers/any.dart +++ b/lib/widgets/collection/grid/headers/any.dart @@ -78,7 +78,7 @@ class CollectionSectionHeader extends StatelessWidget { return AlbumSectionHeader( key: ValueKey(sectionKey), directory: directory, - albumName: directory != null ? source.getAlbumDisplayName(context, directory) : null, + albumName: directory != null ? source.getStoredAlbumDisplayName(context, directory) : null, selectable: selectable, ); } diff --git a/lib/widgets/common/action_controls/quick_choosers/album_chooser.dart b/lib/widgets/common/action_controls/quick_choosers/album_chooser.dart index e705d7670..da5a01782 100644 --- a/lib/widgets/common/action_controls/quick_choosers/album_chooser.dart +++ b/lib/widgets/common/action_controls/quick_choosers/album_chooser.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/widgets/common/action_controls/quick_choosers/common/menu.dart'; @@ -46,6 +46,6 @@ class AlbumQuickChooser extends StatelessWidget with FilterQuickChooserMixin(); - return AlbumFilter(option, source.getAlbumDisplayName(context, option)); + return StoredAlbumFilter(option, source.getStoredAlbumDisplayName(context, option)); } } diff --git a/lib/widgets/common/action_controls/quick_choosers/move_button.dart b/lib/widgets/common/action_controls/quick_choosers/move_button.dart index f15cca1c3..a3cc61bda 100644 --- a/lib/widgets/common/action_controls/quick_choosers/move_button.dart +++ b/lib/widgets/common/action_controls/quick_choosers/move_button.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; @@ -44,7 +44,7 @@ class _MoveButtonState extends ChooserQuickButtonState { final options = settings.recentDestinationAlbums.where(rawAlbums.contains).toList(); final takeCount = FilterQuickChooserMixin.maxTotalOptionCount - options.length; if (takeCount > 0) { - final filters = rawAlbums.whereNot(options.contains).map((album) => AlbumFilter(album, null)).toSet(); + final filters = rawAlbums.whereNot(options.contains).map((album) => StoredAlbumFilter(album, null)).toSet(); final allMapEntries = filters.map((filter) => FilterGridItem(filter, source.recentEntry(filter))).toList(); allMapEntries.sort(FilterNavigationPage.compareFiltersByDate); options.addAll(allMapEntries.take(takeCount).map((v) => v.filter.album)); diff --git a/lib/widgets/common/action_controls/quick_choosers/tag_button.dart b/lib/widgets/common/action_controls/quick_choosers/tag_button.dart index 9437c1f24..beb62331e 100644 --- a/lib/widgets/common/action_controls/quick_choosers/tag_button.dart +++ b/lib/widgets/common/action_controls/quick_choosers/tag_button.dart @@ -1,5 +1,5 @@ import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/tag.dart'; +import 'package:aves/model/filters/covered/tag.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/view/view.dart'; diff --git a/lib/widgets/common/action_mixins/entry_editor.dart b/lib/widgets/common/action_mixins/entry_editor.dart index 02cf9e9c8..43319fc3c 100644 --- a/lib/widgets/common/action_mixins/entry_editor.dart +++ b/lib/widgets/common/action_mixins/entry_editor.dart @@ -3,7 +3,7 @@ import 'package:aves/model/entry/extensions/metadata_edition.dart'; import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/placeholder.dart'; -import 'package:aves/model/filters/tag.dart'; +import 'package:aves/model/filters/covered/tag.dart'; import 'package:aves/model/metadata/date_modifier.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/ref/mime_types.dart'; diff --git a/lib/widgets/common/action_mixins/entry_storage.dart b/lib/widgets/common/action_mixins/entry_storage.dart index 3406ee72f..f57a6e656 100644 --- a/lib/widgets/common/action_mixins/entry_storage.dart +++ b/lib/widgets/common/action_mixins/entry_storage.dart @@ -5,7 +5,7 @@ import 'package:aves/app_mode.dart'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/model/entry/extensions/props.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/trash.dart'; import 'package:aves/model/highlight.dart'; import 'package:aves/model/metadata/date_modifier.dart'; @@ -38,8 +38,10 @@ import 'package:provider/provider.dart'; mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { Future doExport(BuildContext context, Set targetEntries, EntryConvertOptions options) async { - final destinationAlbum = await pickAlbum(context: context, moveType: MoveType.export); - if (destinationAlbum == null) return; + final destinationAlbumFilter = await pickAlbum(context: context, moveType: MoveType.export, storedAlbumsOnly: true); + if (destinationAlbumFilter == null || destinationAlbumFilter is! StoredAlbumFilter) return; + + final destinationAlbum = destinationAlbumFilter.album; if (!await checkStoragePermissionForAlbums(context, {destinationAlbum})) return; if (!await checkFreeSpaceForMove(context, targetEntries, destinationAlbum, MoveType.export)) return; @@ -125,7 +127,7 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { settings: const RouteSettings(name: CollectionPage.routeName), builder: (context) => CollectionPage( source: source, - filters: {AlbumFilter(destinationAlbum, source.getAlbumDisplayName(context, destinationAlbum))}, + filters: {StoredAlbumFilter(destinationAlbum, source.getStoredAlbumDisplayName(context, destinationAlbum))}, highlightTest: (entry) => newUris.contains(entry.uri), ), ), @@ -337,9 +339,10 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { case MoveType.copy: case MoveType.move: case MoveType.export: - final destinationAlbum = await pickAlbum(context: context, moveType: moveType); - if (destinationAlbum == null) return; + final destinationAlbumFilter = await pickAlbum(context: context, moveType: moveType, storedAlbumsOnly: true); + if (destinationAlbumFilter == null || destinationAlbumFilter is! StoredAlbumFilter) return; + final destinationAlbum = destinationAlbumFilter.album; settings.recentDestinationAlbums = settings.recentDestinationAlbums ..remove(destinationAlbum) ..insert(0, destinationAlbum); @@ -452,15 +455,15 @@ mixin EntryStorageMixin on FeedbackMixin, PermissionAwareMixin, SizeAwareMixin { bool highlightTest(AvesEntry entry) => newUris.contains(entry.uri); final collection = context.read(); - if (collection == null || collection.filters.any((f) => f is AlbumFilter || f is TrashFilter)) { + if (collection == null || collection.filters.any((f) => f is StoredAlbumFilter || f is TrashFilter)) { final source = context.read(); final targetFilters = collection?.filters.where((f) => f != TrashFilter.instance).toSet() ?? {}; // we could simply add the filter to the current collection // but navigating makes the change less jarring if (destinationAlbums.length == 1) { final destinationAlbum = destinationAlbums.single; - targetFilters.removeWhere((f) => f is AlbumFilter); - targetFilters.add(AlbumFilter(destinationAlbum, source.getAlbumDisplayName(context, destinationAlbum))); + targetFilters.removeWhere((f) => f is StoredAlbumFilter); + targetFilters.add(StoredAlbumFilter(destinationAlbum, source.getStoredAlbumDisplayName(context, destinationAlbum))); } unawaited(Navigator.maybeOf(context)?.pushAndRemoveUntil( MaterialPageRoute( diff --git a/lib/widgets/common/action_mixins/vault_aware.dart b/lib/widgets/common/action_mixins/vault_aware.dart index 22f6f9f74..13553a8da 100644 --- a/lib/widgets/common/action_mixins/vault_aware.dart +++ b/lib/widgets/common/action_mixins/vault_aware.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/vaults/details.dart'; import 'package:aves/model/vaults/vaults.dart'; @@ -80,10 +80,10 @@ mixin VaultAwareMixin on FeedbackMixin { } Future unlockFilter(BuildContext context, CollectionFilter filter) { - return filter is AlbumFilter ? unlockAlbum(context, filter.album) : Future.value(true); + return filter is StoredAlbumFilter ? unlockAlbum(context, filter.album) : Future.value(true); } - Future unlockFilters(BuildContext context, Set filters) async { + Future unlockFilters(BuildContext context, Set filters) async { var unlocked = true; await Future.forEach(filters, (filter) async { if (unlocked) { @@ -93,7 +93,7 @@ mixin VaultAwareMixin on FeedbackMixin { return unlocked; } - void lockFilters(Set filters) => vaults.lock(filters.map((v) => v.album).toSet()); + void lockFilters(Set filters) => vaults.lock(filters.map((v) => v.album).toSet()); Future setVaultPass(BuildContext context, VaultDetails details) async { switch (details.lockType) { diff --git a/lib/widgets/common/expandable_filter_row.dart b/lib/widgets/common/expandable_filter_row.dart index a7111d555..a69652c43 100644 --- a/lib/widgets/common/expandable_filter_row.dart +++ b/lib/widgets/common/expandable_filter_row.dart @@ -167,7 +167,7 @@ class ExpandableFilterRow extends StatelessWidget { Widget _buildChip(CollectionFilter filter) { return AvesFilterChip( - // key `album-{path}` is expected by test driver + // key is expected by test driver key: Key(filter.key), filter: filter, allowGenericIcon: showGenericIcon, diff --git a/lib/widgets/common/identity/aves_filter_chip.dart b/lib/widgets/common/identity/aves_filter_chip.dart index 357a3b759..4e33ce13c 100644 --- a/lib/widgets/common/identity/aves_filter_chip.dart +++ b/lib/widgets/common/identity/aves_filter_chip.dart @@ -3,12 +3,12 @@ import 'dart:math'; import 'package:aves/app_mode.dart'; import 'package:aves/model/covers.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/filters/path.dart'; import 'package:aves/model/filters/rating.dart'; -import 'package:aves/model/filters/tag.dart'; +import 'package:aves/model/filters/covered/tag.dart'; import 'package:aves/model/settings/enums/accessibility_animations.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/theme/colors.dart'; @@ -102,14 +102,11 @@ class AvesFilterChip extends StatefulWidget { static Future showDefaultLongPressMenu(BuildContext context, CollectionFilter filter, Offset tapPosition) async { if (context.read>().value.canNavigate) { final actions = [ - if (filter is AlbumFilter) ...[ - ChipAction.goToAlbumPage, - ChipAction.goToExplorerPage, - ], + if (filter is AlbumBaseFilter) ChipAction.goToAlbumPage, + if (filter is StoredAlbumFilter || filter is PathFilter) ChipAction.goToExplorerPage, if ((filter is LocationFilter && filter.level == LocationLevel.country)) ChipAction.goToCountryPage, if ((filter is LocationFilter && filter.level == LocationLevel.place)) ChipAction.goToPlacePage, if (filter is TagFilter) ChipAction.goToTagPage, - if (filter is PathFilter) ChipAction.goToExplorerPage, if (filter is RatingFilter && 1 < filter.rating && filter.rating < 5) ...[ if (filter.op != RatingFilter.opOrGreater) ChipAction.ratingOrGreater, if (filter.op != RatingFilter.opOrLower) ChipAction.ratingOrLower, diff --git a/lib/widgets/debug/app_debug_page.dart b/lib/widgets/debug/app_debug_page.dart index 0eb919a7a..bc840d801 100644 --- a/lib/widgets/debug/app_debug_page.dart +++ b/lib/widgets/debug/app_debug_page.dart @@ -1,9 +1,9 @@ import 'dart:async'; import 'package:aves/model/favourites.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/filters/path.dart'; -import 'package:aves/model/filters/tag.dart'; +import 'package:aves/model/filters/covered/tag.dart'; import 'package:aves/model/settings/enums/accessibility_animations.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; diff --git a/lib/widgets/debug/database.dart b/lib/widgets/debug/database.dart index 39d81351d..a1120c8fa 100644 --- a/lib/widgets/debug/database.dart +++ b/lib/widgets/debug/database.dart @@ -1,4 +1,5 @@ import 'package:aves/model/covers.dart'; +import 'package:aves/model/dynamic_albums.dart'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/metadata/address.dart'; @@ -31,6 +32,7 @@ class _DebugAppDatabaseSectionState extends State with late Future> _dbVaultsLoader; late Future> _dbFavouritesLoader; late Future> _dbCoversLoader; + late Future> _dbDynamicAlbumsLoader; late Future> _dbVideoPlaybackLoader; @override @@ -247,6 +249,27 @@ class _DebugAppDatabaseSectionState extends State with ); }, ), + FutureBuilder( + future: _dbDynamicAlbumsLoader, + builder: (context, snapshot) { + if (snapshot.hasError) return Text(snapshot.error.toString()); + + if (snapshot.connectionState != ConnectionState.done) return const SizedBox(); + + return Row( + children: [ + Expanded( + child: Text('dynamic album rows: ${snapshot.data!.length} (${dynamicAlbums.count} in memory)'), + ), + const SizedBox(width: 8), + ElevatedButton( + onPressed: () => dynamicAlbums.clear().then((_) => _reload()), + child: const Text('Clear'), + ), + ], + ); + }, + ), FutureBuilder( future: _dbVideoPlaybackLoader, builder: (context, snapshot) { @@ -279,7 +302,7 @@ class _DebugAppDatabaseSectionState extends State with await _disposeLoadedContent(); _startDbReport(); } - + void _startDbReport() { _dbFileSizeLoader = localMediaDb.dbFileSize(); _dbEntryLoader = localMediaDb.loadEntries(); @@ -290,10 +313,11 @@ class _DebugAppDatabaseSectionState extends State with _dbVaultsLoader = localMediaDb.loadAllVaults(); _dbFavouritesLoader = localMediaDb.loadAllFavourites(); _dbCoversLoader = localMediaDb.loadAllCovers(); + _dbDynamicAlbumsLoader = localMediaDb.loadAllDynamicAlbums(); _dbVideoPlaybackLoader = localMediaDb.loadAllVideoPlayback(); setState(() {}); } - + Future _disposeLoadedContent() async { (await _dbEntryLoader).forEach((v) => v.dispose()); } diff --git a/lib/widgets/dialogs/entry_editors/edit_location_dialog.dart b/lib/widgets/dialogs/entry_editors/edit_location_dialog.dart index a15329c15..d9ed88020 100644 --- a/lib/widgets/dialogs/entry_editors/edit_location_dialog.dart +++ b/lib/widgets/dialogs/entry_editors/edit_location_dialog.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/location.dart'; import 'package:aves/model/entry/extensions/metadata_edition.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/settings/enums/coordinate_format.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; diff --git a/lib/widgets/dialogs/entry_editors/tag_editor_page.dart b/lib/widgets/dialogs/entry_editors/tag_editor_page.dart index c0f7b3bc1..f2f88fe24 100644 --- a/lib/widgets/dialogs/entry_editors/tag_editor_page.dart +++ b/lib/widgets/dialogs/entry_editors/tag_editor_page.dart @@ -1,7 +1,7 @@ import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/placeholder.dart'; -import 'package:aves/model/filters/tag.dart'; +import 'package:aves/model/filters/covered/tag.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/theme/durations.dart'; diff --git a/lib/widgets/dialogs/filter_editors/add_dynamic_album_dialog.dart b/lib/widgets/dialogs/filter_editors/add_dynamic_album_dialog.dart new file mode 100644 index 000000000..b673b8d52 --- /dev/null +++ b/lib/widgets/dialogs/filter_editors/add_dynamic_album_dialog.dart @@ -0,0 +1,93 @@ +import 'package:aves/model/dynamic_albums.dart'; +import 'package:aves/widgets/common/extensions/build_context.dart'; +import 'package:aves/widgets/dialogs/aves_dialog.dart'; +import 'package:flutter/material.dart'; + +class AddDynamicAlbumDialog extends StatefulWidget { + static const routeName = '/dialog/add_dynamic_album'; + + const AddDynamicAlbumDialog({super.key}); + + @override + State createState() => _AddDynamicAlbumDialogState(); +} + +class _AddDynamicAlbumDialogState extends State { + final TextEditingController _nameController = TextEditingController(); + final ValueNotifier _existsNotifier = ValueNotifier(false); + final ValueNotifier _isValidNotifier = ValueNotifier(false); + + @override + void initState() { + super.initState(); + _validate(); + } + + @override + void dispose() { + _nameController.dispose(); + _existsNotifier.dispose(); + _isValidNotifier.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + final l10n = context.l10n; + + return AvesDialog( + title: l10n.newDynamicAlbumDialogTitle, + content: ValueListenableBuilder( + valueListenable: _existsNotifier, + builder: (context, exists, child) { + return TextField( + controller: _nameController, + decoration: InputDecoration( + labelText: l10n.newAlbumDialogNameLabel, + helperText: exists ? l10n.dynamicAlbumAlreadyExists : '', + ), + autofocus: true, + onChanged: (_) => _validate(), + onSubmitted: (_) => _submit(context), + ); + }), + actions: [ + const CancelButton(), + ValueListenableBuilder( + valueListenable: _existsNotifier, + builder: (context, albumExists, child) { + return ValueListenableBuilder( + valueListenable: _isValidNotifier, + builder: (context, isValid, child) { + return TextButton( + onPressed: isValid ? () => _submit(context) : null, + child: Text(albumExists ? l10n.showButtonLabel : l10n.createAlbumButtonLabel), + ); + }, + ); + }, + ), + ], + ); + } + + String? _formatAlbumName() { + final name = _nameController.text.trim(); + if (name.isEmpty) return null; + + return name; + } + + void _validate() { + final name = _formatAlbumName(); + final isValid = name != null; + _isValidNotifier.value = isValid; + _existsNotifier.value = isValid && dynamicAlbums.contains(name); + } + + void _submit(BuildContext context) { + if (_isValidNotifier.value) { + Navigator.maybeOf(context)?.pop(_formatAlbumName()); + } + } +} diff --git a/lib/widgets/dialogs/filter_editors/cover_selection_dialog.dart b/lib/widgets/dialogs/filter_editors/cover_selection_dialog.dart index 02f4dd6a7..0d5eb89ca 100644 --- a/lib/widgets/dialogs/filter_editors/cover_selection_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/cover_selection_dialog.dart @@ -2,7 +2,7 @@ import 'dart:math'; import 'package:aves/image_providers/app_icon_image_provider.dart'; import 'package:aves/model/entry/entry.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; @@ -49,7 +49,7 @@ class _CoverSelectionDialogState extends State { CollectionFilter get filter => widget.filter; - bool get showAppTab => filter is AlbumFilter && settings.isInstalledAppAccessAllowed; + bool get showAppTab => filter is StoredAlbumFilter && settings.isInstalledAppAccessAllowed; bool get showColorTab => settings.themeColorMode == AvesThemeColorMode.polychrome; @@ -205,32 +205,35 @@ class _CoverSelectionDialogState extends State { overflow: TextOverflow.fade, maxLines: 1, ); - return RadioListTile( - value: isCustom, - groupValue: _isCustomEntry, - onChanged: (v) { - if (v == null) return; - if (v && _customEntry == null) { - _pickEntry(); - return; - } - _isCustomEntry = v; - setState(() {}); - }, - title: isCustom - ? Row( - children: [ - title, - const Spacer(), - if (_customEntry != null) - ItemPicker( - extent: itemPickerExtent, - entry: _customEntry!, - onTap: _pickEntry, - ), - ], - ) - : title, + return ListTileTheme.merge( + minVerticalPadding: isCustom && _customEntry != null ? 0 : null, + child: RadioListTile( + value: isCustom, + groupValue: _isCustomEntry, + onChanged: (v) { + if (v == null) return; + if (v && _customEntry == null) { + _pickEntry(); + return; + } + _isCustomEntry = v; + setState(() {}); + }, + title: isCustom + ? Row( + children: [ + title, + const Spacer(), + if (_customEntry != null) + ItemPicker( + extent: itemPickerExtent, + entry: _customEntry!, + onTap: _pickEntry, + ), + ], + ) + : title, + ), ); }, ).toList(); diff --git a/lib/widgets/dialogs/filter_editors/create_album_dialog.dart b/lib/widgets/dialogs/filter_editors/create_stored_album_dialog.dart similarity index 95% rename from lib/widgets/dialogs/filter_editors/create_album_dialog.dart rename to lib/widgets/dialogs/filter_editors/create_stored_album_dialog.dart index af47176c0..cd07ca0aa 100644 --- a/lib/widgets/dialogs/filter_editors/create_album_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/create_stored_album_dialog.dart @@ -12,16 +12,16 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; -class CreateAlbumDialog extends StatefulWidget { - static const routeName = '/dialog/create_album'; +class CreateStoredAlbumDialog extends StatefulWidget { + static const routeName = '/dialog/create_stored_album'; - const CreateAlbumDialog({super.key}); + const CreateStoredAlbumDialog({super.key}); @override - State createState() => _CreateAlbumDialogState(); + State createState() => _CreateStoredAlbumDialogState(); } -class _CreateAlbumDialogState extends State { +class _CreateStoredAlbumDialogState extends State { final ScrollController _scrollController = ScrollController(); final TextEditingController _nameController = TextEditingController(); final FocusNode _nameFieldFocusNode = FocusNode(); diff --git a/lib/widgets/dialogs/filter_editors/edit_vault_dialog.dart b/lib/widgets/dialogs/filter_editors/edit_vault_dialog.dart index a3f363bf3..2017d2118 100644 --- a/lib/widgets/dialogs/filter_editors/edit_vault_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/edit_vault_dialog.dart @@ -1,5 +1,5 @@ import 'package:aves/model/device.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/vaults/details.dart'; @@ -127,7 +127,7 @@ class _EditVaultDialogState extends State with FeedbackMixin, V if (!v) { final album = initialDetails?.path; if (album != null) { - final filter = AlbumFilter(album, null); + final filter = StoredAlbumFilter(album, null); final source = context.read(); if (source.trashedEntries.any(filter.test)) { if (!await showConfirmationDialog( diff --git a/lib/widgets/dialogs/filter_editors/rename_dynamic_album_dialog.dart b/lib/widgets/dialogs/filter_editors/rename_dynamic_album_dialog.dart new file mode 100644 index 000000000..8222b6a1f --- /dev/null +++ b/lib/widgets/dialogs/filter_editors/rename_dynamic_album_dialog.dart @@ -0,0 +1,92 @@ +import 'package:aves/model/dynamic_albums.dart'; +import 'package:aves/widgets/common/extensions/build_context.dart'; +import 'package:aves/widgets/dialogs/aves_dialog.dart'; +import 'package:flutter/material.dart'; + +class RenameDynamicAlbumDialog extends StatefulWidget { + static const routeName = '/dialog/rename_dynamic_album'; + + final String name; + + const RenameDynamicAlbumDialog({ + super.key, + required this.name, + }); + + @override + State createState() => _RenameDynamicAlbumDialogState(); +} + +class _RenameDynamicAlbumDialogState extends State { + final TextEditingController _nameController = TextEditingController(); + final ValueNotifier _existsNotifier = ValueNotifier(false); + final ValueNotifier _isValidNotifier = ValueNotifier(false); + + String get initialValue => widget.name; + + @override + void initState() { + super.initState(); + _nameController.text = initialValue; + _validate(); + } + + @override + void dispose() { + _nameController.dispose(); + _existsNotifier.dispose(); + _isValidNotifier.dispose(); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + return AvesDialog( + content: ValueListenableBuilder( + valueListenable: _existsNotifier, + builder: (context, exists, child) { + return TextField( + controller: _nameController, + decoration: InputDecoration( + labelText: context.l10n.renameAlbumDialogLabel, + helperText: exists ? context.l10n.dynamicAlbumAlreadyExists : '', + ), + autofocus: true, + onChanged: (_) => _validate(), + onSubmitted: (_) => _submit(context), + ); + }), + actions: [ + const CancelButton(), + ValueListenableBuilder( + valueListenable: _isValidNotifier, + builder: (context, isValid, child) { + return TextButton( + onPressed: isValid ? () => _submit(context) : null, + child: Text(context.l10n.applyButtonLabel), + ); + }, + ), + ], + ); + } + + String? _formatAlbumName() { + final name = _nameController.text.trim(); + if (name.isEmpty) return null; + + return name; + } + + Future _validate() async { + final newName = _formatAlbumName(); + _isValidNotifier.value = newName != null && !dynamicAlbums.contains(newName); + _existsNotifier.value = newName != null && dynamicAlbums.contains(newName) && newName != initialValue; + } + + void _submit(BuildContext context) { + if (_isValidNotifier.value) { + Navigator.maybeOf(context)?.pop(_formatAlbumName()); + } + } +} diff --git a/lib/widgets/dialogs/filter_editors/rename_album_dialog.dart b/lib/widgets/dialogs/filter_editors/rename_stored_album_dialog.dart similarity index 89% rename from lib/widgets/dialogs/filter_editors/rename_album_dialog.dart rename to lib/widgets/dialogs/filter_editors/rename_stored_album_dialog.dart index 0d254a30b..cee4f750f 100644 --- a/lib/widgets/dialogs/filter_editors/rename_album_dialog.dart +++ b/lib/widgets/dialogs/filter_editors/rename_stored_album_dialog.dart @@ -5,21 +5,21 @@ import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/dialogs/aves_dialog.dart'; import 'package:flutter/material.dart'; -class RenameAlbumDialog extends StatefulWidget { - static const routeName = '/dialog/rename_album'; +class RenameStoredAlbumDialog extends StatefulWidget { + static const routeName = '/dialog/rename_stored_album'; final String album; - const RenameAlbumDialog({ + const RenameStoredAlbumDialog({ super.key, required this.album, }); @override - State createState() => _RenameAlbumDialogState(); + State createState() => _RenameStoredAlbumDialogState(); } -class _RenameAlbumDialogState extends State { +class _RenameStoredAlbumDialogState extends State { final TextEditingController _nameController = TextEditingController(); final ValueNotifier _existsNotifier = ValueNotifier(false); final ValueNotifier _isValidNotifier = ValueNotifier(false); diff --git a/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart b/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart index 107383a5d..ce31a9357 100644 --- a/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart +++ b/lib/widgets/dialogs/pick_dialogs/album_pick_page.dart @@ -1,5 +1,5 @@ import 'package:aves/app_mode.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/selection.dart'; import 'package:aves/model/settings/enums/accessibility_animations.dart'; @@ -19,7 +19,7 @@ import 'package:aves/widgets/common/identity/empty.dart'; import 'package:aves/widgets/common/providers/query_provider.dart'; import 'package:aves/widgets/common/providers/selection_provider.dart'; import 'package:aves/widgets/dialogs/aves_confirmation_dialog.dart'; -import 'package:aves/widgets/dialogs/filter_editors/create_album_dialog.dart'; +import 'package:aves/widgets/dialogs/filter_editors/create_stored_album_dialog.dart'; import 'package:aves/widgets/dialogs/filter_editors/edit_vault_dialog.dart'; import 'package:aves/widgets/filter_grids/albums_page.dart'; import 'package:aves/widgets/filter_grids/common/action_delegates/album_set.dart'; @@ -30,9 +30,10 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:provider/provider.dart'; -Future pickAlbum({ +Future pickAlbum({ required BuildContext context, required MoveType? moveType, + required bool storedAlbumsOnly, }) async { final source = context.read(); if (source.targetScope != CollectionSource.fullScope) { @@ -41,13 +42,12 @@ Future pickAlbum({ source.canAnalyze = true; await source.init(scope: CollectionSource.fullScope); } - final filter = await Navigator.maybeOf(context)?.push( - MaterialPageRoute( + return await Navigator.maybeOf(context)?.push( + MaterialPageRoute( settings: const RouteSettings(name: _AlbumPickPage.routeName), - builder: (context) => _AlbumPickPage(source: source, moveType: moveType), + builder: (context) => _AlbumPickPage(source: source, moveType: moveType, storedAlbumsOnly: storedAlbumsOnly), ), ); - return filter?.album; } class _AlbumPickPage extends StatefulWidget { @@ -55,10 +55,12 @@ class _AlbumPickPage extends StatefulWidget { final CollectionSource source; final MoveType? moveType; + final bool storedAlbumsOnly; const _AlbumPickPage({ required this.source, required this.moveType, + required this.storedAlbumsOnly, }); @override @@ -111,11 +113,11 @@ class _AlbumPickPageState extends State<_AlbumPickPage> { return StreamBuilder( stream: source.eventBus.on(), builder: (context, snapshot) { - final gridItems = AlbumListPage.getAlbumGridItems(context, source); - return SelectionProvider>( + final gridItems = AlbumListPage.getAlbumGridItems(context, source, storedAlbumsOnly: widget.storedAlbumsOnly); + return SelectionProvider>( child: QueryProvider( startEnabled: settings.getShowTitleQuery(context.currentRouteName!), - child: FilterGridPage( + child: FilterGridPage( settingsRouteKey: AlbumListPage.routeName, appBar: FilterGridAppBar( source: source, @@ -150,7 +152,7 @@ class _AlbumPickPageState extends State<_AlbumPickPage> { List _buildActions( BuildContext context, AppMode appMode, - Selection> selection, + Selection> selection, AlbumChipSetActionDelegate actionDelegate, ) { final itemCount = actionDelegate.allItems.length; @@ -245,8 +247,8 @@ class _AlbumPickPageState extends State<_AlbumPickPage> { Future _createAlbum() async { final directory = await showDialog( context: context, - builder: (context) => const CreateAlbumDialog(), - routeSettings: const RouteSettings(name: CreateAlbumDialog.routeName), + builder: (context) => const CreateStoredAlbumDialog(), + routeSettings: const RouteSettings(name: CreateStoredAlbumDialog.routeName), ); if (directory == null) return; @@ -282,8 +284,8 @@ class _AlbumPickPageState extends State<_AlbumPickPage> { } void _pickAlbum(String directory) { - source.createAlbum(directory); - final filter = AlbumFilter(directory, source.getAlbumDisplayName(context, directory)); - Navigator.maybeOf(context)?.pop(filter); + source.createStoredAlbum(directory); + final filter = StoredAlbumFilter(directory, source.getStoredAlbumDisplayName(context, directory)); + Navigator.maybeOf(context)?.pop(filter); } } diff --git a/lib/widgets/explorer/app_bar.dart b/lib/widgets/explorer/app_bar.dart index f4883915a..b182d11c9 100644 --- a/lib/widgets/explorer/app_bar.dart +++ b/lib/widgets/explorer/app_bar.dart @@ -16,9 +16,9 @@ import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/aves_app_bar.dart'; import 'package:aves/widgets/common/search/route.dart'; import 'package:aves/widgets/dialogs/select_storage_dialog.dart'; +import 'package:aves/widgets/explorer/crumb_line.dart'; import 'package:aves/widgets/explorer/explorer_action_delegate.dart'; import 'package:aves/widgets/search/search_delegate.dart'; -import 'package:aves/widgets/settings/privacy/file_picker/crumb_line.dart'; import 'package:aves_model/aves_model.dart'; import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; diff --git a/lib/widgets/settings/privacy/file_picker/crumb_line.dart b/lib/widgets/explorer/crumb_line.dart similarity index 100% rename from lib/widgets/settings/privacy/file_picker/crumb_line.dart rename to lib/widgets/explorer/crumb_line.dart diff --git a/lib/widgets/explorer/explorer_page.dart b/lib/widgets/explorer/explorer_page.dart index 859a70f72..390578e1b 100644 --- a/lib/widgets/explorer/explorer_page.dart +++ b/lib/widgets/explorer/explorer_page.dart @@ -1,7 +1,7 @@ import 'dart:async'; import 'dart:io'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/path.dart'; import 'package:aves/model/source/album.dart'; @@ -194,7 +194,7 @@ class _ExplorerPageState extends State { final album = _getAlbumPath(source, Directory(dirPath)); if (album != null) { bottom = AvesFilterChip( - filter: AlbumFilter(album, source.getAlbumDisplayName(context, album)), + filter: StoredAlbumFilter(album, source.getStoredAlbumDisplayName(context, album)), maxWidth: double.infinity, onTap: (filter) => _goToCollectionPage(context, filter), onLongPress: null, @@ -237,7 +237,7 @@ class _ExplorerPageState extends State { ? IconTheme.merge( data: baseIconTheme, child: AvesFilterChip( - filter: AlbumFilter(album, source.getAlbumDisplayName(context, album)), + filter: StoredAlbumFilter(album, source.getStoredAlbumDisplayName(context, album)), showText: false, maxWidth: leadingDim, onTap: (filter) => _goToCollectionPage(context, filter), diff --git a/lib/widgets/filter_grids/albums_page.dart b/lib/widgets/filter_grids/albums_page.dart index f2b1641e6..2da03354f 100644 --- a/lib/widgets/filter_grids/albums_page.dart +++ b/lib/widgets/filter_grids/albums_page.dart @@ -1,13 +1,14 @@ import 'package:aves/model/app_inventory.dart'; import 'package:aves/model/covers.dart'; +import 'package:aves/model/dynamic_albums.dart'; import 'package:aves/model/entry/extensions/props.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; +import 'package:aves/model/filters/covered/dynamic_album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/album.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/theme/icons.dart'; -import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/empty.dart'; import 'package:aves/widgets/filter_grids/common/action_delegates/album_set.dart'; @@ -37,29 +38,32 @@ class AlbumListPage extends StatelessWidget { return ValueListenableBuilder( valueListenable: appInventory.areAppNamesReadyNotifier, builder: (context, areAppNamesReady, child) { - return StreamBuilder( - stream: source.eventBus.on(), - builder: (context, snapshot) { - final gridItems = getAlbumGridItems(context, source); - return StreamBuilder?>( - // to update sections by tier - stream: covers.packageChangeStream, - builder: (context, snapshot) => FilterNavigationPage( - source: source, - title: context.l10n.albumPageTitle, - sortFactor: settings.albumSortFactor, - showHeaders: settings.albumGroupFactor != AlbumChipGroupFactor.none, - actionDelegate: AlbumChipSetActionDelegate(gridItems), - filterSections: groupToSections(context, source, gridItems), - newFilters: source.getNewAlbumFilters(context), - applyQuery: applyQuery, - emptyBuilder: () => EmptyContent( - icon: AIcons.album, - text: context.l10n.albumEmpty, + return AnimatedBuilder( + animation: dynamicAlbums, + builder: (context, child) => StreamBuilder( + stream: source.eventBus.on(), + builder: (context, snapshot) { + final gridItems = getAlbumGridItems(context, source); + return StreamBuilder?>( + // to update sections by tier + stream: covers.packageChangeStream, + builder: (context, snapshot) => FilterNavigationPage( + source: source, + title: context.l10n.albumPageTitle, + sortFactor: settings.albumSortFactor, + showHeaders: settings.albumGroupFactor != AlbumChipGroupFactor.none, + actionDelegate: AlbumChipSetActionDelegate(gridItems), + filterSections: groupToSections(context, source, gridItems), + newFilters: source.getNewAlbumFilters(context), + applyQuery: applyQuery, + emptyBuilder: () => EmptyContent( + icon: AIcons.album, + text: context.l10n.albumEmpty, + ), ), - ), - ); - }, + ); + }, + ), ); }, ); @@ -69,21 +73,24 @@ class AlbumListPage extends StatelessWidget { // common with album selection page to move/copy entries - static List> applyQuery(BuildContext context, List> filters, String query) { - return filters.where((item) => (item.filter.displayName ?? item.filter.album).toUpperCase().contains(query)).toList(); + static List> applyQuery(BuildContext context, List> filters, String query) { + return filters.where((item) => item.filter.match(query)).toList(); } - static List> getAlbumGridItems(BuildContext context, CollectionSource source) { - final filters = source.rawAlbums.map((album) => AlbumFilter(album, source.getAlbumDisplayName(context, album))).toSet(); + static List> getAlbumGridItems(BuildContext context, CollectionSource source, {bool storedAlbumsOnly = false}) { + final filters = { + ...source.rawAlbums.map((album) => StoredAlbumFilter(album, source.getStoredAlbumDisplayName(context, album))), + if (!storedAlbumsOnly) ...dynamicAlbums.all, + }; return FilterNavigationPage.sort(settings.albumSortFactor, settings.albumSortReverse, source, filters); } - static Map>> groupToSections(BuildContext context, CollectionSource source, Iterable> sortedMapEntries) { + static Map>> groupToSections(BuildContext context, CollectionSource source, Iterable> sortedMapEntries) { final newFilters = source.getNewAlbumFilters(context); - final pinned = settings.pinnedFilters.whereType(); + final pinned = settings.pinnedFilters.whereType(); - final List> newMapEntries = [], pinnedMapEntries = [], unpinnedMapEntries = []; + final List> newMapEntries = [], pinnedMapEntries = [], unpinnedMapEntries = []; for (final item in sortedMapEntries) { final filter = item.filter; if (newFilters.contains(filter)) { @@ -95,24 +102,31 @@ class AlbumListPage extends StatelessWidget { } } - var sections = >>{}; + var sections = >>{}; switch (settings.albumGroupFactor) { case AlbumChipGroupFactor.importance: final specialKey = AlbumImportanceSectionKey.special(context); final appsKey = AlbumImportanceSectionKey.apps(context); final vaultKey = AlbumImportanceSectionKey.vault(context); final regularKey = AlbumImportanceSectionKey.regular(context); - sections = groupBy, ChipSectionKey>(unpinnedMapEntries, (kv) { - switch (covers.effectiveAlbumType(kv.filter.album)) { - case AlbumType.regular: - return regularKey; - case AlbumType.app: - return appsKey; - case AlbumType.vault: - return vaultKey; - default: - return specialKey; + final dynamicKey = AlbumImportanceSectionKey.dynamic(context); + sections = groupBy, ChipSectionKey>(unpinnedMapEntries, (kv) { + final filter = kv.filter; + if (filter is StoredAlbumFilter) { + switch (covers.effectiveAlbumType(filter.album)) { + case AlbumType.regular: + return regularKey; + case AlbumType.app: + return appsKey; + case AlbumType.vault: + return vaultKey; + default: + return specialKey; + } + } else if (filter is DynamicAlbumFilter) { + return dynamicKey; } + return specialKey; }); sections = { @@ -120,11 +134,12 @@ class AlbumListPage extends StatelessWidget { if (sections.containsKey(specialKey)) specialKey: sections[specialKey]!, if (sections.containsKey(appsKey)) appsKey: sections[appsKey]!, if (sections.containsKey(vaultKey)) vaultKey: sections[vaultKey]!, + if (sections.containsKey(dynamicKey)) dynamicKey: sections[dynamicKey]!, if (sections.containsKey(regularKey)) regularKey: sections[regularKey]!, }; case AlbumChipGroupFactor.mimeType: final visibleEntries = source.visibleEntries; - sections = groupBy, ChipSectionKey>(unpinnedMapEntries, (kv) { + sections = groupBy, ChipSectionKey>(unpinnedMapEntries, (kv) { final matches = visibleEntries.where(kv.filter.test); final hasImage = matches.any((v) => v.isImage); final hasVideo = matches.any((v) => v.isVideo); @@ -133,8 +148,8 @@ class AlbumListPage extends StatelessWidget { return MimeTypeSectionKey.mixed(context); }); case AlbumChipGroupFactor.volume: - sections = groupBy, ChipSectionKey>(unpinnedMapEntries, (kv) { - return StorageVolumeSectionKey(context, androidFileUtils.getStorageVolume(kv.filter.album)); + sections = groupBy, ChipSectionKey>(unpinnedMapEntries, (kv) { + return StorageVolumeSectionKey(context, kv.filter.storageVolume); }); case AlbumChipGroupFactor.none: return { diff --git a/lib/widgets/filter_grids/common/action_delegates/album_set.dart b/lib/widgets/filter_grids/common/action_delegates/album_set.dart index 3b49cfb59..8fa81c36c 100644 --- a/lib/widgets/filter_grids/common/action_delegates/album_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/album_set.dart @@ -1,8 +1,11 @@ import 'dart:io'; import 'package:aves/app_mode.dart'; +import 'package:aves/model/covers.dart'; +import 'package:aves/model/dynamic_albums.dart'; import 'package:aves/model/entry/entry.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/dynamic_album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/highlight.dart'; import 'package:aves/model/settings/settings.dart'; @@ -14,6 +17,7 @@ import 'package:aves/services/common/services.dart'; import 'package:aves/services/media/enums.dart'; import 'package:aves/theme/durations.dart'; import 'package:aves/utils/android_file_utils.dart'; +import 'package:aves/utils/collection_utils.dart'; import 'package:aves/view/view.dart'; import 'package:aves/widgets/common/action_mixins/entry_storage.dart'; import 'package:aves/widgets/common/action_mixins/feedback.dart'; @@ -21,9 +25,10 @@ import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/tile_extent_controller.dart'; import 'package:aves/widgets/dialogs/aves_confirmation_dialog.dart'; import 'package:aves/widgets/dialogs/aves_dialog.dart'; -import 'package:aves/widgets/dialogs/filter_editors/create_album_dialog.dart'; +import 'package:aves/widgets/dialogs/filter_editors/create_stored_album_dialog.dart'; import 'package:aves/widgets/dialogs/filter_editors/edit_vault_dialog.dart'; -import 'package:aves/widgets/dialogs/filter_editors/rename_album_dialog.dart'; +import 'package:aves/widgets/dialogs/filter_editors/rename_dynamic_album_dialog.dart'; +import 'package:aves/widgets/dialogs/filter_editors/rename_stored_album_dialog.dart'; import 'package:aves/widgets/dialogs/tile_view_dialog.dart'; import 'package:aves/widgets/filter_grids/albums_page.dart'; import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart'; @@ -33,13 +38,13 @@ import 'package:flutter/material.dart'; import 'package:flutter/scheduler.dart'; import 'package:provider/provider.dart'; -class AlbumChipSetActionDelegate extends ChipSetActionDelegate with EntryStorageMixin { - final Iterable> _items; +class AlbumChipSetActionDelegate extends ChipSetActionDelegate with EntryStorageMixin { + final Iterable> _items; - AlbumChipSetActionDelegate(Iterable> items) : _items = items; + AlbumChipSetActionDelegate(Iterable> items) : _items = items; @override - Iterable> get allItems => _items; + Iterable> get allItems => _items; @override ChipSortFactor get sortFactor => settings.albumSortFactor; @@ -72,7 +77,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with required AppMode appMode, required bool isSelecting, required int itemCount, - required Set selectedFilters, + required Set selectedFilters, }) { final selectedSingleItem = selectedFilters.length == 1; final isMain = appMode == AppMode.main; @@ -82,14 +87,17 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with case ChipSetAction.createVault: return !settings.isReadOnly && appMode.canCreateFilter && !isSelecting; case ChipSetAction.delete: + return isMain && isSelecting && !settings.isReadOnly && !(selectedFilters.whereType().isEmpty && selectedFilters.whereType().isNotEmpty); + case ChipSetAction.remove: + return isMain && isSelecting && !settings.isReadOnly && selectedFilters.whereType().isEmpty && selectedFilters.whereType().isNotEmpty; case ChipSetAction.rename: return isMain && isSelecting && !settings.isReadOnly; case ChipSetAction.hide: - return isMain && selectedFilters.none((v) => vaults.isVault(v.album)); + return isMain && selectedFilters.none((v) => v.isVault); case ChipSetAction.configureVault: - return isMain && selectedSingleItem && vaults.isVault(selectedFilters.first.album); + return isMain && selectedSingleItem && selectedFilters.first.isVault; case ChipSetAction.lockVault: - return isMain && selectedFilters.any((v) => vaults.isVault(v.album)); + return isMain && selectedFilters.any((v) => v.isVault); default: return super.isVisible( action, @@ -106,25 +114,20 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with ChipSetAction action, { required bool isSelecting, required int itemCount, - required Set selectedFilters, + required Set selectedFilters, }) { final selectedItemCount = selectedFilters.length; final hasSelection = selectedItemCount > 0; switch (action) { + case ChipSetAction.delete: + return selectedFilters.whereType().isNotEmpty && selectedFilters.whereType().isEmpty; case ChipSetAction.rename: - if (selectedFilters.length != 1) return false; - - final dirPath = selectedFilters.first.album; - if (vaults.isVault(dirPath)) return true; - - // do not allow renaming volume root - final dir = androidFileUtils.relativeDirectoryFromPath(dirPath); - return dir != null && dir.relativeDir.isNotEmpty; + return selectedFilters.length == 1 && selectedFilters.first.canRename; case ChipSetAction.hide: return hasSelection; case ChipSetAction.lockVault: - return selectedFilters.map((v) => v.album).any((v) => vaults.isVault(v) && !vaults.isLocked(v)); + return selectedFilters.whereType().map((v) => v.album).any((v) => vaults.isVault(v) && !vaults.isLocked(v)); case ChipSetAction.configureVault: return true; default: @@ -143,14 +146,16 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with switch (action) { // general case ChipSetAction.createAlbum: - _createAlbum(context, locked: false); + _createStoredAlbum(context, locked: false); case ChipSetAction.createVault: - _createAlbum(context, locked: true); + _createStoredAlbum(context, locked: true); // single/multiple filters case ChipSetAction.delete: - _delete(context); + _deleteStoredAlbums(context); + case ChipSetAction.remove: + _removeDynamicAlbum(context); case ChipSetAction.lockVault: - lockFilters(getSelectedFilters(context)); + lockFilters(_getSelectedStoredAlbumFilters(context)); browse(context); // single filter case ChipSetAction.rename: @@ -163,6 +168,14 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with super.onActionSelected(context, action); } + Set _getSelectedStoredAlbumFilters(BuildContext context) { + return getSelectedFilters(context).whereType().toSet(); + } + + Set _getSelectedDynamicAlbumFilters(BuildContext context) { + return getSelectedFilters(context).whereType().toSet(); + } + @override Future configureView(BuildContext context) async { final initialValue = ( @@ -196,7 +209,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with } } - void _createAlbum(BuildContext context, {required bool locked}) async { + void _createStoredAlbum(BuildContext context, {required bool locked}) async { final l10n = context.l10n; final source = context.read(); @@ -218,7 +231,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with final details = await showDialog( context: context, builder: (context) => const EditVaultDialog(), - routeSettings: const RouteSettings(name: CreateAlbumDialog.routeName), + routeSettings: const RouteSettings(name: CreateStoredAlbumDialog.routeName), ); if (details == null) return; @@ -227,15 +240,15 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with } else { directory = await showDialog( context: context, - builder: (context) => const CreateAlbumDialog(), - routeSettings: const RouteSettings(name: CreateAlbumDialog.routeName), + builder: (context) => const CreateStoredAlbumDialog(), + routeSettings: const RouteSettings(name: CreateStoredAlbumDialog.routeName), ); if (directory == null) return; // wait for the dialog to hide await Future.delayed(ADurations.dialogTransitionLoose * timeDilation); } - final filter = AlbumFilter(directory, source.getAlbumDisplayName(context, directory)); + final filter = StoredAlbumFilter(directory, source.getStoredAlbumDisplayName(context, directory)); final albumExists = source.rawAlbums.contains(directory); if (albumExists) { @@ -243,7 +256,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with await _showAlbum(navigator, filter); } else { // create the album and mark it as new - source.createAlbum(directory); + source.createStoredAlbum(directory); final showAction = SnackBarAction( label: l10n.showButtonLabel, @@ -253,7 +266,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with } } - Future _showAlbum(NavigatorState? navigator, AlbumFilter filter) async { + Future _showAlbum(NavigatorState? navigator, StoredAlbumFilter filter) async { // local context may be deactivated when action is triggered after navigation if (navigator != null) { final context = navigator.context; @@ -273,9 +286,9 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with } } - Future _delete(BuildContext context) async { - final filters = getSelectedFilters(context); - final byBinUsage = groupBy(filters, (filter) { + Future _deleteStoredAlbums(BuildContext context) async { + final filters = _getSelectedStoredAlbumFilters(context); + final byBinUsage = groupBy(filters, (filter) { final details = vaults.getVault(filter.album); return details?.useBin ?? settings.enableBin; }); @@ -291,7 +304,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with Future _doDelete({ required BuildContext context, - required Set filters, + required Set filters, required bool enableBin, }) async { if (!await unlockFilters(context, filters)) return; @@ -388,44 +401,125 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with ); } + Future _removeDynamicAlbum(BuildContext context) async { + final l10n = context.l10n; + final confirmed = await showDialog( + context: context, + builder: (context) => AvesDialog( + content: Text(l10n.genericDangerWarningDialogMessage), + actions: [ + const CancelButton(), + TextButton( + onPressed: () => Navigator.maybeOf(context)?.pop(true), + child: Text(l10n.applyButtonLabel), + ), + ], + ), + routeSettings: const RouteSettings(name: AvesDialog.warningRouteName), + ); + if (confirmed == null || !confirmed) return; + + final albumFilters = _getSelectedDynamicAlbumFilters(context); + final names = albumFilters.map((v) => v.name).toSet(); + bool isRemoved(CollectionFilter v) => v is DynamicAlbumFilter && names.contains(v.name); + + await dynamicAlbums.remove(albumFilters); + + // cleanup + await covers.removeAll(albumFilters, notify: true); + settings.drawerAlbumBookmarks = settings.drawerAlbumBookmarks?..removeWhere(isRemoved); + settings.pinnedFilters = settings.pinnedFilters..removeWhere(isRemoved); + + browse(context); + } + Future _rename(BuildContext context) async { final filters = getSelectedFilters(context); if (filters.isEmpty) return; final filter = filters.first; - if (!await unlockFilter(context, filter)) return; + if (filter is StoredAlbumFilter) { + if (!await unlockFilter(context, filter)) return; - final album = filter.album; - if (!vaults.isVault(album)) { - final dir = androidFileUtils.relativeDirectoryFromPath(album); - // do not allow renaming volume root - if (dir == null || dir.relativeDir.isEmpty) return; - - // check whether renaming is possible given OS restrictions, - // before asking to input a new name - final restrictedDirsLowerCase = await storageService.getRestrictedDirectoriesLowerCase(); - if (restrictedDirsLowerCase.contains(dir.copyWith(relativeDir: dir.relativeDir.toLowerCase()))) { - await showRestrictedDirectoryDialog(context, dir); - return; + final album = filter.album; + if (!vaults.isVault(album)) { + final dir = androidFileUtils.relativeDirectoryFromPath(album); + // do not allow renaming volume root + if (dir == null || dir.relativeDir.isEmpty) return; + + // check whether renaming is possible given OS restrictions, + // before asking to input a new name + final restrictedDirsLowerCase = await storageService.getRestrictedDirectoriesLowerCase(); + if (restrictedDirsLowerCase.contains(dir.copyWith(relativeDir: dir.relativeDir.toLowerCase()))) { + await showRestrictedDirectoryDialog(context, dir); + return; + } } + + final newName = await showDialog( + context: context, + builder: (context) => RenameStoredAlbumDialog(album: album), + routeSettings: const RouteSettings(name: RenameStoredAlbumDialog.routeName), + ); + if (newName == null || newName.isEmpty) return; + + await _doRenameStoredAlbum(context, filter, newName); + } else if (filter is DynamicAlbumFilter) { + final newName = await showDialog( + context: context, + builder: (context) => RenameDynamicAlbumDialog(name: filter.name), + routeSettings: const RouteSettings(name: RenameStoredAlbumDialog.routeName), + ); + if (newName == null || newName.isEmpty) return; + + await _doRenameDynamicAlbum(context, filter, newName); } + } - final newName = await showDialog( - context: context, - builder: (context) => RenameAlbumDialog(album: album), - routeSettings: const RouteSettings(name: RenameAlbumDialog.routeName), - ); - if (newName == null || newName.isEmpty) return; + Future _doRenameDynamicAlbum(BuildContext context, DynamicAlbumFilter albumFilter, String newName) async { + final oldName = albumFilter.name; + + // save cover and bookmark before renaming + final cover = await covers.remove(albumFilter, notify: false); + final bookmarks = settings.drawerAlbumBookmarks; + final pinnedFilters = settings.pinnedFilters; + + await dynamicAlbums.rename(albumFilter, newName); + final newFilter = DynamicAlbumFilter(newName, albumFilter.filter); + bool isRenamed(CollectionFilter v) => v is DynamicAlbumFilter && v.name == oldName; + + // update cover + if (cover != null) { + await covers.set( + filter: newFilter, + entryId: cover.$1, + packageName: cover.$2, + color: cover.$3, + notify: true, + ); + } + // update drawer bookmark + final bookmark = bookmarks?.firstWhereOrNull(isRenamed); + if (bookmark != null) { + bookmarks?.replace(bookmark, newFilter); + settings.drawerAlbumBookmarks = bookmarks; + } + // update pin + final pin = pinnedFilters.firstWhereOrNull(isRenamed); + if (pin != null) { + pinnedFilters.replace(pin, newFilter); + settings.pinnedFilters = pinnedFilters; + } - await _doRename(context, filter, newName); + browse(context); } - Future _doRename(BuildContext context, AlbumFilter filter, String newName) async { + Future _doRenameStoredAlbum(BuildContext context, StoredAlbumFilter albumFilter, String newName) async { final l10n = context.l10n; final messenger = ScaffoldMessenger.of(context); final source = context.read(); - final album = filter.album; - final todoEntries = source.visibleEntries.where(filter.test).toSet(); + final album = albumFilter.album; + final todoEntries = source.visibleEntries.where(albumFilter.test).toSet(); final todoCount = todoEntries.length; final destinationAlbumParent = pContext.dirname(album); @@ -455,7 +549,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with onDone: (processed) async { final successOps = processed.where((e) => e.success).toSet(); final movedOps = successOps.where((e) => !e.skipped).toSet(); - await source.renameAlbum(album, destinationAlbum, todoEntries, movedOps); + await source.renameStoredAlbum(album, destinationAlbum, todoEntries, movedOps); browse(context); source.resumeMonitoring(); @@ -478,6 +572,8 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with if (filters.isEmpty) return; final filter = filters.first; + if (filter is! StoredAlbumFilter) return; + if (!await unlockFilter(context, filter)) return; final oldDetails = vaults.getVault(filter.album); @@ -491,7 +587,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with if (newDetails == null || oldDetails == newDetails) return; if (oldDetails.useBin && !newDetails.useBin) { - final filter = AlbumFilter(oldDetails.path, null); + final filter = StoredAlbumFilter(oldDetails.path, null); final source = context.read(); await _deleteEntriesForever(context, source.trashedEntries.where(filter.test).toSet()); } @@ -503,7 +599,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate with // wipe the old pass, if any, so that it does not overwrite the new pass // when renaming the vault afterwards await securityService.writeValue(oldDetails.passKey, null); - await _doRename(context, filter, newName); + await _doRenameStoredAlbum(context, filter, newName); } else { await vaults.update(newDetails); browse(context); diff --git a/lib/widgets/filter_grids/common/action_delegates/chip.dart b/lib/widgets/filter_grids/common/action_delegates/chip.dart index 86b23b105..7f05b7fb5 100644 --- a/lib/widgets/filter_grids/common/action_delegates/chip.dart +++ b/lib/widgets/filter_grids/common/action_delegates/chip.dart @@ -1,4 +1,4 @@ -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/path.dart'; import 'package:aves/model/filters/rating.dart'; @@ -35,9 +35,9 @@ class ChipActionDelegate with FeedbackMixin, VaultAwareMixin { case ChipAction.reverse: return true; case ChipAction.hide: - return !(filter is AlbumFilter && vaults.isVault(filter.album)); + return !(filter is StoredAlbumFilter && vaults.isVault(filter.album)); case ChipAction.lockVault: - return (filter is AlbumFilter && vaults.isVault(filter.album) && !vaults.isLocked(filter.album)); + return (filter is StoredAlbumFilter && vaults.isVault(filter.album) && !vaults.isLocked(filter.album)); } } @@ -54,7 +54,7 @@ class ChipActionDelegate with FeedbackMixin, VaultAwareMixin { _goTo(context, filter, TagListPage.routeName, (context) => const TagListPage()); case ChipAction.goToExplorerPage: String? path; - if (filter is AlbumFilter) { + if (filter is StoredAlbumFilter) { path = filter.album; } else if (filter is PathFilter) { path = filter.path; @@ -77,7 +77,7 @@ class ChipActionDelegate with FeedbackMixin, VaultAwareMixin { case ChipAction.hide: _hide(context, filter); case ChipAction.lockVault: - if (filter is AlbumFilter) { + if (filter is StoredAlbumFilter) { lockFilters({filter}); } } diff --git a/lib/widgets/filter_grids/common/action_delegates/chip_set.dart b/lib/widgets/filter_grids/common/action_delegates/chip_set.dart index b6b5537d7..ec4b49597 100644 --- a/lib/widgets/filter_grids/common/action_delegates/chip_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/chip_set.dart @@ -1,9 +1,9 @@ import 'package:aves/app_mode.dart'; import 'package:aves/model/covers.dart'; import 'package:aves/model/entry/entry.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/or.dart'; +import 'package:aves/model/filters/set_or.dart'; import 'package:aves/model/query.dart'; import 'package:aves/model/selection.dart'; import 'package:aves/model/settings/settings.dart'; @@ -107,6 +107,7 @@ abstract class ChipSetActionDelegate with FeedbackMi case ChipSetAction.showCollection: return appMode.canNavigate; case ChipSetAction.delete: + case ChipSetAction.remove: case ChipSetAction.lockVault: case ChipSetAction.showCountryStates: return false; @@ -148,6 +149,7 @@ abstract class ChipSetActionDelegate with FeedbackMi return (!isSelecting && hasItems) || (isSelecting && hasSelection); // selecting (single/multiple filters) case ChipSetAction.delete: + case ChipSetAction.remove: case ChipSetAction.hide: case ChipSetAction.pin: case ChipSetAction.unpin: @@ -204,6 +206,7 @@ abstract class ChipSetActionDelegate with FeedbackMi case ChipSetAction.showCollection: _goToCollection(context); case ChipSetAction.delete: + case ChipSetAction.remove: case ChipSetAction.lockVault: case ChipSetAction.showCountryStates: break; @@ -264,7 +267,7 @@ abstract class ChipSetActionDelegate with FeedbackMi final filters = getSelectedFilters(context); if (filters.isEmpty) return; - final filter = filters.length > 1 ? OrFilter(filters) : filters.first; + final filter = filters.length > 1 ? SetOrFilter(filters) : filters.first; await Navigator.maybeOf(context)?.push( MaterialPageRoute( settings: const RouteSettings(name: CollectionPage.routeName), @@ -378,7 +381,7 @@ abstract class ChipSetActionDelegate with FeedbackMi ); if (selectedCover == null) return; - if (filter is AlbumFilter) { + if (filter is StoredAlbumFilter) { context.read().clearAppColor(filter.album); } diff --git a/lib/widgets/filter_grids/common/action_delegates/country_set.dart b/lib/widgets/filter_grids/common/action_delegates/country_set.dart index 418fe1640..85aaa3b87 100644 --- a/lib/widgets/filter_grids/common/action_delegates/country_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/country_set.dart @@ -1,7 +1,7 @@ import 'package:aves/app_mode.dart'; import 'package:aves/geo/states.dart'; import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart'; diff --git a/lib/widgets/filter_grids/common/action_delegates/place_set.dart b/lib/widgets/filter_grids/common/action_delegates/place_set.dart index be9549e60..53ebf9a61 100644 --- a/lib/widgets/filter_grids/common/action_delegates/place_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/place_set.dart @@ -1,5 +1,5 @@ import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart'; import 'package:aves/widgets/filter_grids/places_page.dart'; diff --git a/lib/widgets/filter_grids/common/action_delegates/state_set.dart b/lib/widgets/filter_grids/common/action_delegates/state_set.dart index 8fb3d5cf9..4cd2e5cbc 100644 --- a/lib/widgets/filter_grids/common/action_delegates/state_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/state_set.dart @@ -1,5 +1,5 @@ import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/widgets/filter_grids/common/action_delegates/chip_set.dart'; import 'package:aves/widgets/filter_grids/states_page.dart'; diff --git a/lib/widgets/filter_grids/common/action_delegates/tag_set.dart b/lib/widgets/filter_grids/common/action_delegates/tag_set.dart index 251c855a4..0ee0e31b6 100644 --- a/lib/widgets/filter_grids/common/action_delegates/tag_set.dart +++ b/lib/widgets/filter_grids/common/action_delegates/tag_set.dart @@ -1,6 +1,6 @@ import 'package:aves/app_mode.dart'; import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/tag.dart'; +import 'package:aves/model/filters/covered/tag.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/services/common/services.dart'; @@ -50,7 +50,7 @@ class TagChipSetActionDelegate extends ChipSetActionDelegate { final isMain = appMode == AppMode.main; switch (action) { - case ChipSetAction.delete: + case ChipSetAction.remove: return isMain && isSelecting && !settings.isReadOnly; default: return super.isVisible( @@ -68,30 +68,31 @@ class TagChipSetActionDelegate extends ChipSetActionDelegate { reportService.log('$runtimeType handles $action'); switch (action) { // single/multiple filters - case ChipSetAction.delete: - _delete(context); + case ChipSetAction.remove: + _remove(context); default: break; } super.onActionSelected(context, action); } - Future _delete(BuildContext context) async { + Future _remove(BuildContext context) async { final filters = getSelectedFilters(context); final source = context.read(); final todoEntries = source.visibleEntries.where((entry) => filters.any((f) => f.test(entry))).toSet(); final todoTags = filters.map((v) => v.tag).toSet(); + final l10n = context.l10n; final confirmed = await showDialog( context: context, builder: (context) => AvesDialog( - content: Text(context.l10n.genericDangerWarningDialogMessage), + content: Text(l10n.genericDangerWarningDialogMessage), actions: [ const CancelButton(), TextButton( onPressed: () => Navigator.maybeOf(context)?.pop(true), - child: Text(context.l10n.applyButtonLabel), + child: Text(l10n.applyButtonLabel), ), ], ), diff --git a/lib/widgets/filter_grids/common/covered_filter_chip.dart b/lib/widgets/filter_grids/common/covered_filter_chip.dart index bf6d318aa..18b6822e7 100644 --- a/lib/widgets/filter_grids/common/covered_filter_chip.dart +++ b/lib/widgets/filter_grids/common/covered_filter_chip.dart @@ -2,10 +2,11 @@ import 'dart:math'; import 'package:aves/model/app_inventory.dart'; import 'package:aves/model/covers.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/dynamic_album.dart'; +import 'package:aves/model/filters/covered/location.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; +import 'package:aves/model/filters/covered/tag.dart'; import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/location.dart'; -import 'package:aves/model/filters/tag.dart'; import 'package:aves/model/source/album.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/location/country.dart'; @@ -69,11 +70,18 @@ class CoveredFilterChip extends StatelessWidget { builder: (context, snapshot) => Consumer( builder: (context, source, child) { switch (filter) { - case AlbumFilter filter: + case StoredAlbumFilter filter: { final album = filter.album; - return StreamBuilder( - stream: source.eventBus.on().where((event) => event.directories == null || event.directories!.contains(album)), + return StreamBuilder( + stream: source.eventBus.on().where((event) => event.directories == null || event.directories!.contains(album)), + builder: (context, snapshot) => _buildChip(context, source), + ); + } + case DynamicAlbumFilter _: + { + return StreamBuilder( + stream: source.eventBus.on(), builder: (context, snapshot) => _buildChip(context, source), ); } @@ -103,10 +111,10 @@ class CoveredFilterChip extends StatelessWidget { Widget _buildChip(BuildContext context, CollectionSource source) { final _filter = filter; - final entry = _filter is AlbumFilter && vaults.isLocked(_filter.album) ? null : source.coverEntry(_filter); + final entry = _filter is StoredAlbumFilter && vaults.isLocked(_filter.album) ? null : source.coverEntry(_filter); final titlePadding = min(4.0, extent / 32); Key? chipKey; - if (_filter is AlbumFilter) { + if (_filter is StoredAlbumFilter) { // when we asynchronously fetch installed app names, // album filters themselves do not change, but decoration derived from it does chipKey = ValueKey(appInventory.areAppNamesReadyNotifier.value); @@ -172,52 +180,35 @@ class CoveredFilterChip extends StatelessWidget { Color _detailColor(BuildContext context) => Theme.of(context).colorScheme.onSurfaceVariant; Widget _buildDetails(BuildContext context, CollectionSource source, T filter) { - final countFormatter = NumberFormat.decimalPattern(context.locale); - - final padding = min(8.0, extent / 16); - final iconSize = detailIconSize(extent); - final fontSize = detailFontSize(extent); return Row( mainAxisSize: MainAxisSize.min, children: [ - if (pinned) - AnimatedPadding( - padding: EdgeInsetsDirectional.only(end: padding), - duration: ADurations.chipDecorationAnimation, - child: Icon( - AIcons.pin, - color: _detailColor(context), - size: iconSize, - ), - ), - if (filter is AlbumFilter && androidFileUtils.isOnRemovableStorage(filter.album)) - AnimatedPadding( - padding: EdgeInsetsDirectional.only(end: padding), - duration: ADurations.chipDecorationAnimation, - child: Icon( - AIcons.storageCard, - color: _detailColor(context), - size: iconSize, - ), - ), - if (filter is AlbumFilter && vaults.isVault(filter.album)) - AnimatedPadding( - padding: EdgeInsetsDirectional.only(end: padding), - duration: ADurations.chipDecorationAnimation, - child: Icon( - AIcons.locked, - color: _detailColor(context), - size: iconSize, - ), - ), + if (pinned) _buildDetailIcon(context, AIcons.pin), + if (filter is StoredAlbumFilter && androidFileUtils.isOnRemovableStorage(filter.album)) _buildDetailIcon(context, AIcons.storageCard), + if (filter is StoredAlbumFilter && vaults.isVault(filter.album)) _buildDetailIcon(context, AIcons.locked), + if (filter is DynamicAlbumFilter) _buildDetailIcon(context, AIcons.dynamicAlbum), Text( - locked ? AText.valueNotAvailable : countFormatter.format(source.count(filter)), + locked ? AText.valueNotAvailable : NumberFormat.decimalPattern(context.locale).format(source.count(filter)), style: TextStyle( color: _detailColor(context), - fontSize: fontSize, + fontSize: detailFontSize(extent), ), ), ], ); } + + Widget _buildDetailIcon(BuildContext context, IconData icon) { + final padding = min(8.0, extent / 16); + final iconSize = detailIconSize(extent); + return AnimatedPadding( + padding: EdgeInsetsDirectional.only(end: padding), + duration: ADurations.chipDecorationAnimation, + child: Icon( + icon, + color: _detailColor(context), + size: iconSize, + ), + ); + } } diff --git a/lib/widgets/filter_grids/common/enums.dart b/lib/widgets/filter_grids/common/enums.dart index e132044ad..f84d77168 100644 --- a/lib/widgets/filter_grids/common/enums.dart +++ b/lib/widgets/filter_grids/common/enums.dart @@ -2,7 +2,7 @@ import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:flutter/material.dart'; -enum AlbumImportance { newAlbum, pinned, special, apps, vaults, regular } +enum AlbumImportance { newAlbum, pinned, special, apps, vaults, dynamic, regular } extension ExtraAlbumImportance on AlbumImportance { String getText(BuildContext context) { @@ -13,6 +13,7 @@ extension ExtraAlbumImportance on AlbumImportance { AlbumImportance.special => l10n.albumTierSpecial, AlbumImportance.apps => l10n.albumTierApps, AlbumImportance.vaults => l10n.albumTierVaults, + AlbumImportance.dynamic => l10n.albumTierDynamic, AlbumImportance.regular => l10n.albumTierRegular, }; } @@ -24,6 +25,7 @@ extension ExtraAlbumImportance on AlbumImportance { AlbumImportance.special => AIcons.important, AlbumImportance.apps => AIcons.app, AlbumImportance.vaults => AIcons.locked, + AlbumImportance.dynamic => AIcons.dynamicAlbum, AlbumImportance.regular => AIcons.album, }; } diff --git a/lib/widgets/filter_grids/common/filter_tile.dart b/lib/widgets/filter_grids/common/filter_tile.dart index f4c1cdf06..d78e155e2 100644 --- a/lib/widgets/filter_grids/common/filter_tile.dart +++ b/lib/widgets/filter_grids/common/filter_tile.dart @@ -1,5 +1,5 @@ import 'package:aves/app_mode.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/selection.dart'; import 'package:aves/model/settings/settings.dart'; @@ -134,7 +134,7 @@ class FilterTile extends StatelessWidget { Widget build(BuildContext context) { final filter = gridItem.filter; final pinned = settings.pinnedFilters.contains(filter); - final locked = filter is AlbumFilter && vaults.isLocked(filter.album); + final locked = filter is StoredAlbumFilter && vaults.isLocked(filter.album); final onChipTap = onTap != null ? (filter) => onTap?.call() : null; switch (tileLayout) { diff --git a/lib/widgets/filter_grids/common/list_details.dart b/lib/widgets/filter_grids/common/list_details.dart index 3f974cdcb..d8788c80f 100644 --- a/lib/widgets/filter_grids/common/list_details.dart +++ b/lib/widgets/filter_grids/common/list_details.dart @@ -1,5 +1,6 @@ import 'package:aves/model/entry/entry.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/dynamic_album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/theme/format.dart'; @@ -116,11 +117,12 @@ class FilterListDetails extends StatelessWidget { Widget _buildCountRow(BuildContext context, FilterListDetailsThemeData detailsTheme, bool hasTitleLeading) { final _filter = filter; - final removableStorage = _filter is AlbumFilter && androidFileUtils.isOnRemovableStorage(_filter.album); + final removableStorage = _filter is StoredAlbumFilter && androidFileUtils.isOnRemovableStorage(_filter.album); List leadingIcons = [ if (pinned) const Icon(AIcons.pin), if (removableStorage) const Icon(AIcons.storageCard), + if (_filter is DynamicAlbumFilter) const Icon(AIcons.dynamicAlbum), ]; Widget? leading; diff --git a/lib/widgets/filter_grids/common/section_keys.dart b/lib/widgets/filter_grids/common/section_keys.dart index 507dcc57c..26efbe654 100644 --- a/lib/widgets/filter_grids/common/section_keys.dart +++ b/lib/widgets/filter_grids/common/section_keys.dart @@ -35,6 +35,8 @@ class AlbumImportanceSectionKey extends ChipSectionKey { factory AlbumImportanceSectionKey.vault(BuildContext context) => AlbumImportanceSectionKey._private(context, AlbumImportance.vaults); + factory AlbumImportanceSectionKey.dynamic(BuildContext context) => AlbumImportanceSectionKey._private(context, AlbumImportance.dynamic); + factory AlbumImportanceSectionKey.regular(BuildContext context) => AlbumImportanceSectionKey._private(context, AlbumImportance.regular); @override diff --git a/lib/widgets/filter_grids/countries_page.dart b/lib/widgets/filter_grids/countries_page.dart index a26858939..c02f418a9 100644 --- a/lib/widgets/filter_grids/countries_page.dart +++ b/lib/widgets/filter_grids/countries_page.dart @@ -1,5 +1,5 @@ import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/location/country.dart'; diff --git a/lib/widgets/filter_grids/places_page.dart b/lib/widgets/filter_grids/places_page.dart index c0b456a75..a03702341 100644 --- a/lib/widgets/filter_grids/places_page.dart +++ b/lib/widgets/filter_grids/places_page.dart @@ -1,5 +1,5 @@ import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/location/place.dart'; diff --git a/lib/widgets/filter_grids/states_page.dart b/lib/widgets/filter_grids/states_page.dart index b905bf794..3fe194840 100644 --- a/lib/widgets/filter_grids/states_page.dart +++ b/lib/widgets/filter_grids/states_page.dart @@ -1,6 +1,6 @@ import 'package:aves/geo/states.dart'; import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/location/place.dart'; diff --git a/lib/widgets/filter_grids/tags_page.dart b/lib/widgets/filter_grids/tags_page.dart index 70deab605..7012ca3ff 100644 --- a/lib/widgets/filter_grids/tags_page.dart +++ b/lib/widgets/filter_grids/tags_page.dart @@ -1,5 +1,5 @@ import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/tag.dart'; +import 'package:aves/model/filters/covered/tag.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/model/source/tag.dart'; diff --git a/lib/widgets/home_page.dart b/lib/widgets/home_page.dart index ec6478290..03a75d7ca 100644 --- a/lib/widgets/home_page.dart +++ b/lib/widgets/home_page.dart @@ -7,9 +7,9 @@ import 'package:aves/model/app/permissions.dart'; import 'package:aves/model/app_inventory.dart'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/catalog.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/settings/enums/home_page.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; @@ -241,7 +241,7 @@ class _HomePageState extends State { await reportService.log('Initialize source to view item in directory $directory'); final source = context.read(); source.canAnalyze = false; - await source.init(scope: {AlbumFilter(directory, null)}); + await source.init(scope: {StoredAlbumFilter(directory, null)}); } } else { await _initViewerEssentials(); @@ -324,7 +324,7 @@ class _HomePageState extends State { collection = CollectionLens( source: source, - filters: {AlbumFilter(album, source.getAlbumDisplayName(context, album))}, + filters: {StoredAlbumFilter(album, source.getStoredAlbumDisplayName(context, album))}, listenToSource: false, // if we group bursts, opening a burst sub-entry should: // - identify and select the containing main entry, diff --git a/lib/widgets/map/map_page.dart b/lib/widgets/map/map_page.dart index 0639e3739..4d4743f49 100644 --- a/lib/widgets/map/map_page.dart +++ b/lib/widgets/map/map_page.dart @@ -5,7 +5,7 @@ import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/location.dart'; import 'package:aves/model/filters/coordinate.dart'; import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/highlight.dart'; import 'package:aves/model/media/geotiff.dart'; import 'package:aves/model/settings/enums/accessibility_animations.dart'; diff --git a/lib/widgets/navigation/drawer/app_drawer.dart b/lib/widgets/navigation/drawer/app_drawer.dart index 565694312..c05b8d09f 100644 --- a/lib/widgets/navigation/drawer/app_drawer.dart +++ b/lib/widgets/navigation/drawer/app_drawer.dart @@ -1,4 +1,5 @@ -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/dynamic_album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/trash.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/album.dart'; @@ -46,14 +47,29 @@ class AppDrawer extends StatefulWidget { @override State createState() => _AppDrawerState(); - static List getDefaultAlbums(BuildContext context) { + static List _getDefaultAlbums(BuildContext context) { final source = context.read(); final specialAlbums = source.rawAlbums.where((album) { final type = androidFileUtils.getAlbumType(album); return [AlbumType.camera, AlbumType.download, AlbumType.screenshots].contains(type); }).toList() ..sort(source.compareAlbumsByName); - return specialAlbums; + return specialAlbums.map((v) => StoredAlbumFilter(v, source.getStoredAlbumDisplayName(context, v))).toList(); + } + + static List? _getCustomAlbums(BuildContext context) { + final source = context.read(); + return settings.drawerAlbumBookmarks?.map((v) { + if (v is StoredAlbumFilter) { + final album = v.album; + return StoredAlbumFilter(album, source.getStoredAlbumDisplayName(context, album)); + } + return v; + }).toList(); + } + + static List effectiveAlbumBookmarks(BuildContext context) { + return _getCustomAlbums(context) ?? _getDefaultAlbums(context); } } @@ -288,17 +304,22 @@ class _AppDrawerState extends State with WidgetsBindingObserver { return StreamBuilder( stream: source.eventBus.on(), builder: (context, snapshot) { - final albums = settings.drawerAlbumBookmarks ?? AppDrawer.getDefaultAlbums(context); + final albums = AppDrawer.effectiveAlbumBookmarks(context); if (albums.isEmpty) return const SizedBox(); return Column( children: [ const Divider(), - ...albums.map((album) => AlbumNavTile( - album: album, + ...albums.map((filter) => AlbumNavTile( + filter: filter, isSelected: () { if (currentFilters == null || currentFilters.length > 1) return false; final currentFilter = currentFilters.firstOrNull; - return currentFilter is AlbumFilter && currentFilter.album == album; + if (currentFilter is StoredAlbumFilter && filter is StoredAlbumFilter) { + return currentFilter.album == filter.album; + } else if (currentFilter is DynamicAlbumFilter && filter is DynamicAlbumFilter) { + return currentFilter.name == filter.name; + } + return false; }, )), ], diff --git a/lib/widgets/navigation/drawer/collection_nav_tile.dart b/lib/widgets/navigation/drawer/collection_nav_tile.dart index f29f6b2c4..88a1b5b5a 100644 --- a/lib/widgets/navigation/drawer/collection_nav_tile.dart +++ b/lib/widgets/navigation/drawer/collection_nav_tile.dart @@ -1,8 +1,7 @@ -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/theme/icons.dart'; -import 'package:aves/utils/android_file_utils.dart'; import 'package:aves/widgets/collection/collection_page.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/navigation/drawer/tile.dart'; @@ -69,23 +68,21 @@ class CollectionNavTile extends StatelessWidget { } class AlbumNavTile extends StatelessWidget { - final String album; + final AlbumBaseFilter filter; final bool Function() isSelected; const AlbumNavTile({ super.key, - required this.album, + required this.filter, required this.isSelected, }); @override Widget build(BuildContext context) { - final source = context.read(); - final filter = AlbumFilter(album, source.getAlbumDisplayName(context, album)); return CollectionNavTile( leading: DrawerFilterIcon(filter: filter), title: DrawerFilterTitle(filter: filter), - trailing: androidFileUtils.isOnRemovableStorage(album) + trailing: filter.storageVolume?.isRemovable ?? false ? const Icon( AIcons.storageCard, size: 16, diff --git a/lib/widgets/navigation/tv_rail.dart b/lib/widgets/navigation/tv_rail.dart index a55c2cb84..4cc3ddfea 100644 --- a/lib/widgets/navigation/tv_rail.dart +++ b/lib/widgets/navigation/tv_rail.dart @@ -1,6 +1,7 @@ import 'dart:math'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/dynamic_album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/settings/enums/home_page.dart'; import 'package:aves/model/settings/settings.dart'; @@ -218,15 +219,18 @@ class _TvRailState extends State { } List<_NavEntry> _buildAlbumLinks(BuildContext context) { - final source = context.read(); final currentFilters = currentCollection?.filters; - final albums = settings.drawerAlbumBookmarks ?? AppDrawer.getDefaultAlbums(context); - return albums.map((album) { - final filter = AlbumFilter(album, source.getAlbumDisplayName(context, album)); + final albums = AppDrawer.effectiveAlbumBookmarks(context); + return albums.map((filter) { bool isSelected() { if (currentFilters == null || currentFilters.length > 1) return false; final currentFilter = currentFilters.firstOrNull; - return currentFilter is AlbumFilter && currentFilter.album == album; + if (currentFilter is StoredAlbumFilter && filter is StoredAlbumFilter) { + return currentFilter.album == filter.album; + } else if (currentFilter is DynamicAlbumFilter && filter is DynamicAlbumFilter) { + return currentFilter.name == filter.name; + } + return false; } return _NavEntry( diff --git a/lib/widgets/search/search_delegate.dart b/lib/widgets/search/search_delegate.dart index 3c92ebf23..d648f4b57 100644 --- a/lib/widgets/search/search_delegate.dart +++ b/lib/widgets/search/search_delegate.dart @@ -1,15 +1,16 @@ -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/dynamic_albums.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/aspect_ratio.dart'; import 'package:aves/model/filters/date.dart'; import 'package:aves/model/filters/favourite.dart'; import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/filters/mime.dart'; import 'package:aves/model/filters/missing.dart'; import 'package:aves/model/filters/query.dart'; import 'package:aves/model/filters/rating.dart'; import 'package:aves/model/filters/recent.dart'; -import 'package:aves/model/filters/tag.dart'; +import 'package:aves/model/filters/covered/tag.dart'; import 'package:aves/model/filters/type.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/album.dart'; @@ -192,23 +193,27 @@ class CollectionSearchDelegate extends AvesSearchDelegate with FeedbackMixin, Va } Widget _buildAlbumFilters(_ContainQuery containQuery) { - return StreamBuilder( - stream: source.eventBus.on(), - builder: (context, snapshot) { - final filters = source.rawAlbums - .map((album) => AlbumFilter( - album, - source.getAlbumDisplayName(context, album), - )) - .where((filter) => containQuery(filter.displayName ?? filter.album)) - .toList() - ..sort(); - return _buildFilterRow( - context: context, - title: context.l10n.searchAlbumsSectionTitle, - filters: filters, - ); - }, + return AnimatedBuilder( + animation: dynamicAlbums, + builder: (context, child) => StreamBuilder( + stream: source.eventBus.on(), + builder: (context, snapshot) { + final filters = [ + ...source.rawAlbums + .map((album) => StoredAlbumFilter( + album, + source.getStoredAlbumDisplayName(context, album), + )) + .where((filter) => containQuery(filter.displayName ?? filter.album)), + ...dynamicAlbums.all, + ]..sort(); + return _buildFilterRow( + context: context, + title: context.l10n.searchAlbumsSectionTitle, + filters: filters, + ); + }, + ), ); } diff --git a/lib/widgets/settings/app_export/items.dart b/lib/widgets/settings/app_export/items.dart index ac5f20eb3..e8c7c786c 100644 --- a/lib/widgets/settings/app_export/items.dart +++ b/lib/widgets/settings/app_export/items.dart @@ -1,17 +1,19 @@ import 'package:aves/model/covers.dart'; +import 'package:aves/model/dynamic_albums.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_source.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:flutter/widgets.dart'; -enum AppExportItem { covers, favourites, settings } +enum AppExportItem { covers, dynamicAlbums, favourites, settings } extension ExtraAppExportItem on AppExportItem { String getText(BuildContext context) { final l10n = context.l10n; return switch (this) { AppExportItem.covers => l10n.appExportCovers, + AppExportItem.dynamicAlbums => l10n.appExportDynamicAlbums, AppExportItem.favourites => l10n.appExportFavourites, AppExportItem.settings => l10n.appExportSettings, }; @@ -20,6 +22,7 @@ extension ExtraAppExportItem on AppExportItem { dynamic export(CollectionSource source) { return switch (this) { AppExportItem.covers => covers.export(source), + AppExportItem.dynamicAlbums => dynamicAlbums.export(), AppExportItem.favourites => favourites.export(source), AppExportItem.settings => settings.export(), }; @@ -29,6 +32,8 @@ extension ExtraAppExportItem on AppExportItem { switch (this) { case AppExportItem.covers: covers.import(jsonMap, source); + case AppExportItem.dynamicAlbums: + dynamicAlbums.import(jsonMap); case AppExportItem.favourites: favourites.import(jsonMap, source); case AppExportItem.settings: diff --git a/lib/widgets/settings/language/locales.dart b/lib/widgets/settings/language/locales.dart index cb1ec0b10..335b0ffb2 100644 --- a/lib/widgets/settings/language/locales.dart +++ b/lib/widgets/settings/language/locales.dart @@ -5,6 +5,7 @@ class SupportedLocales { static const languagesByLanguageCode = { 'ar': 'العربية', 'be': 'Беларуская мова', + 'bg': 'Български', 'ca': 'Català', 'cs': 'Čeština', 'de': 'Deutsch', @@ -31,6 +32,7 @@ class SupportedLocales { 'ru': 'Русский', 'sk': 'Slovenčina', 'sv': 'Svenska', + 'ta': 'தமிழ்', 'tr': 'Türkçe', 'uk': 'Українська', 'vi': 'Tiếng Việt', diff --git a/lib/widgets/settings/navigation/drawer.dart b/lib/widgets/settings/navigation/drawer.dart index 718374269..417ff86fa 100644 --- a/lib/widgets/settings/navigation/drawer.dart +++ b/lib/widgets/settings/navigation/drawer.dart @@ -1,3 +1,4 @@ +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; import 'package:aves/model/filters/recent.dart'; import 'package:aves/model/settings/settings.dart'; @@ -28,7 +29,7 @@ class NavigationDrawerEditorPage extends StatefulWidget { class _NavigationDrawerEditorPageState extends State { final List _typeItems = []; final Set _visibleTypes = {}; - final List _albumItems = []; + final List _albumItems = []; final List _pageItems = []; final Set _visiblePages = {}; @@ -54,7 +55,7 @@ class _NavigationDrawerEditorPageState extends State _typeItems.addAll(userTypeLinks); _typeItems.addAll(_typeOptions.where((v) => !userTypeLinks.contains(v))); - _albumItems.addAll(settings.drawerAlbumBookmarks ?? AppDrawer.getDefaultAlbums(context)); + _albumItems.addAll(AppDrawer.effectiveAlbumBookmarks(context)); final userPageLinks = settings.drawerPageBookmarks; _visiblePages.addAll(userPageLinks); diff --git a/lib/widgets/settings/navigation/drawer_tab_albums.dart b/lib/widgets/settings/navigation/drawer_tab_albums.dart index 5ee754397..d3d088a1b 100644 --- a/lib/widgets/settings/navigation/drawer_tab_albums.dart +++ b/lib/widgets/settings/navigation/drawer_tab_albums.dart @@ -1,6 +1,5 @@ -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/settings/settings.dart'; -import 'package:aves/model/source/collection_source.dart'; import 'package:aves/theme/icons.dart'; import 'package:aves/widgets/common/extensions/build_context.dart'; import 'package:aves/widgets/common/identity/buttons/outlined_button.dart'; @@ -8,10 +7,9 @@ import 'package:aves/widgets/dialogs/pick_dialogs/album_pick_page.dart'; import 'package:aves/widgets/navigation/drawer/tile.dart'; import 'package:aves/widgets/settings/navigation/drawer_editor_banner.dart'; import 'package:flutter/material.dart'; -import 'package:provider/provider.dart'; class DrawerAlbumTab extends StatefulWidget { - final List items; + final List items; const DrawerAlbumTab({ super.key, @@ -23,11 +21,10 @@ class DrawerAlbumTab extends StatefulWidget { } class _DrawerAlbumTabState extends State { - List get items => widget.items; + List get items => widget.items; @override Widget build(BuildContext context) { - final source = context.read(); return Column( children: [ if (!settings.useTvLayout) ...[ @@ -37,11 +34,10 @@ class _DrawerAlbumTabState extends State { Flexible( child: ReorderableListView.builder( itemBuilder: (context, index) { - final album = items[index]; - final filter = AlbumFilter(album, source.getAlbumDisplayName(context, album)); - void onPressed() => setState(() => items.remove(album)); + final filter = items[index]; + void onPressed() => setState(() => items.remove(filter)); return ListTile( - key: ValueKey(album), + key: ValueKey(filter.key), leading: DrawerFilterIcon(filter: filter), title: DrawerFilterTitle(filter: filter), trailing: IconButton( @@ -68,9 +64,9 @@ class _DrawerAlbumTabState extends State { icon: const Icon(AIcons.add), label: context.l10n.settingsNavigationDrawerAddAlbum, onPressed: () async { - final album = await pickAlbum(context: context, moveType: null); - if (album == null || items.contains(album)) return; - setState(() => items.add(album)); + final albumFilter = await pickAlbum(context: context, moveType: null, storedAlbumsOnly: false); + if (albumFilter == null || items.contains(albumFilter)) return; + setState(() => items.add(albumFilter)); }, ), ], diff --git a/lib/widgets/settings/privacy/file_picker/file_picker_page.dart b/lib/widgets/settings/privacy/file_picker/file_picker_page.dart deleted file mode 100644 index 59a7b56cc..000000000 --- a/lib/widgets/settings/privacy/file_picker/file_picker_page.dart +++ /dev/null @@ -1,224 +0,0 @@ -import 'dart:io'; - -import 'package:aves/model/settings/enums/accessibility_animations.dart'; -import 'package:aves/model/settings/settings.dart'; -import 'package:aves/services/common/services.dart'; -import 'package:aves/theme/durations.dart'; -import 'package:aves/theme/icons.dart'; -import 'package:aves/utils/android_file_utils.dart'; -import 'package:aves/view/view.dart'; -import 'package:aves/widgets/common/basic/font_size_icon_theme.dart'; -import 'package:aves/widgets/common/basic/popup/menu_row.dart'; -import 'package:aves/widgets/common/basic/scaffold.dart'; -import 'package:aves/widgets/common/extensions/build_context.dart'; -import 'package:aves/widgets/common/identity/buttons/outlined_button.dart'; -import 'package:aves/widgets/common/identity/empty.dart'; -import 'package:aves/widgets/settings/privacy/file_picker/crumb_line.dart'; -import 'package:aves_model/aves_model.dart'; -import 'package:collection/collection.dart'; -import 'package:flutter/foundation.dart'; -import 'package:flutter/material.dart'; -import 'package:flutter/scheduler.dart'; -import 'package:provider/provider.dart'; - -class FilePickerPage extends StatefulWidget { - static const routeName = '/file_picker'; - - const FilePickerPage({super.key}); - - @override - State createState() => _FilePickerPageState(); -} - -class _FilePickerPageState extends State { - late VolumeRelativeDirectory _directory; - List? _contents; - - Set get volumes => androidFileUtils.storageVolumes; - - String get currentDirectoryPath => pContext.join(_directory.volumePath, _directory.relativeDir); - - @override - void initState() { - super.initState(); - final primaryVolume = volumes.firstWhereOrNull((v) => v.isPrimary); - if (primaryVolume != null) { - _goTo(primaryVolume.path); - } - } - - @override - Widget build(BuildContext context) { - final l10n = context.l10n; - final showHidden = settings.filePickerShowHiddenFiles; - final visibleContents = _contents?.where((v) { - if (showHidden) { - return true; - } else { - final isHidden = pContext.split(v.path).last.startsWith('.'); - return !isHidden; - } - }).toList(); - final animations = context.select((v) => v.accessibilityAnimations); - return PopScope( - canPop: _directory.relativeDir.isEmpty, - onPopInvokedWithResult: (didPop, result) { - if (didPop) return; - - final parent = pContext.dirname(currentDirectoryPath); - _goTo(parent); - setState(() {}); - }, - child: AvesScaffold( - appBar: AppBar( - title: Text(_getTitle(context)), - actions: [ - FontSizeIconTheme( - child: PopupMenuButton<_PickerAction>( - itemBuilder: (context) { - return [ - PopupMenuItem( - value: _PickerAction.toggleHiddenView, - child: MenuRow(text: showHidden ? l10n.filePickerDoNotShowHiddenFiles : l10n.filePickerShowHiddenFiles), - ), - ]; - }, - onSelected: (action) async { - // wait for the popup menu to hide before proceeding with the action - await Future.delayed(animations.popUpAnimationDelay * timeDilation); - switch (action) { - case _PickerAction.toggleHiddenView: - settings.filePickerShowHiddenFiles = !showHidden; - setState(() {}); - } - }, - popUpAnimationStyle: animations.popUpAnimationStyle, - ), - ), - ], - ), - drawer: _buildDrawer(context), - body: SafeArea( - child: Column( - children: [ - _buildCrumbLine(context), - const Divider(height: 0), - Expanded( - child: visibleContents == null - ? const SizedBox() - : visibleContents.isEmpty - ? Center( - child: EmptyContent( - icon: AIcons.folder, - text: l10n.filePickerNoItems, - ), - ) - : ListView.builder( - itemCount: visibleContents.length, - itemBuilder: (context, index) { - return index < visibleContents.length ? _buildContentLine(context, visibleContents[index]) : const SizedBox(); - }, - ), - ), - const Divider(height: 0), - Padding( - padding: const EdgeInsets.all(8), - child: AvesOutlinedButton( - label: l10n.filePickerUseThisFolder, - onPressed: () => Navigator.maybeOf(context)?.pop(currentDirectoryPath), - ), - ), - ], - ), - ), - ), - ); - } - - Widget _buildCrumbLine(BuildContext context) { - final crumbStyle = Theme.of(context).textTheme.bodyMedium!; - return SizedBox( - height: kMinInteractiveDimension, - child: DefaultTextStyle( - style: crumbStyle.copyWith( - color: crumbStyle.color!.withAlpha((255.0 * .4).round()), - fontWeight: FontWeight.w500, - ), - child: CrumbLine( - directory: _directory, - onTap: (path) { - _goTo(path); - setState(() {}); - }, - ), - ), - ); - } - - String _getTitle(BuildContext context) { - if (_directory.relativeDir.isEmpty) { - return _directory.getVolumeDescription(context); - } - return pContext.split(_directory.relativeDir).last; - } - - Widget _buildDrawer(BuildContext context) { - return Drawer( - child: ListView( - children: [ - SafeArea( - child: Padding( - padding: const EdgeInsets.all(16), - child: Text( - context.l10n.filePickerOpenFrom, - style: Theme.of(context).textTheme.headlineSmall, - ), - ), - ), - ...volumes.map((v) { - final icon = v.isRemovable ? AIcons.storageCard : AIcons.storageMain; - return ListTile( - leading: Icon(icon), - title: Text(v.getDescription(context)), - onTap: () async { - Navigator.maybeOf(context)?.pop(); - await Future.delayed(ADurations.drawerTransitionLoose); - _goTo(v.path); - setState(() {}); - }, - selected: _directory.volumePath == v.path, - ); - }) - ], - ), - ); - } - - Widget _buildContentLine(BuildContext context, FileSystemEntity content) { - return ListTile( - leading: const Icon(AIcons.folder), - title: Text('${Unicode.FSI}${pContext.split(content.path).last}${Unicode.PDI}'), - onTap: () { - _goTo(content.path); - setState(() {}); - }, - ); - } - - void _goTo(String path) { - _directory = androidFileUtils.relativeDirectoryFromPath(path)!; - _contents = null; - final contents = []; - Directory(currentDirectoryPath).list().listen((event) { - final entity = event.absolute; - if (entity is Directory) { - contents.add(entity); - } - }, onDone: () { - _contents = contents..sort((a, b) => compareAsciiUpperCaseNatural(pContext.split(a.path).last, pContext.split(b.path).last)); - setState(() {}); - }); - } -} - -enum _PickerAction { toggleHiddenView } diff --git a/lib/widgets/settings/settings_mobile_page.dart b/lib/widgets/settings/settings_mobile_page.dart index 664e1cfc4..aa7f17fa7 100644 --- a/lib/widgets/settings/settings_mobile_page.dart +++ b/lib/widgets/settings/settings_mobile_page.dart @@ -170,8 +170,8 @@ class _SettingsMobilePageState extends State with FeedbackMi return item.import(importable[item], source); }); showFeedback(context, FeedbackType.info, context.l10n.genericSuccessFeedback); - } catch (error) { - debugPrint('failed to import app json, error=$error'); + } catch (error, stack) { + debugPrint('failed to import app json, error=$error\n$stack'); showFeedback(context, FeedbackType.warn, context.l10n.genericFailureFeedback); } } diff --git a/lib/widgets/stats/stats_page.dart b/lib/widgets/stats/stats_page.dart index 843460915..8100e3ce1 100644 --- a/lib/widgets/stats/stats_page.dart +++ b/lib/widgets/stats/stats_page.dart @@ -1,11 +1,11 @@ import 'dart:async'; import 'package:aves/model/entry/entry.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/filters/rating.dart'; -import 'package:aves/model/filters/tag.dart'; +import 'package:aves/model/filters/covered/tag.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/model/source/collection_source.dart'; @@ -204,7 +204,7 @@ class _StatsPageState extends State with FeedbackMixin, VaultAwareMix ..._buildFilterSection(context, l10n.statsTopStatesSectionTitle, _entryCountPerState, (v) => LocationFilter(LocationLevel.state, v)), ..._buildFilterSection(context, l10n.statsTopPlacesSectionTitle, _entryCountPerPlace, (v) => LocationFilter(LocationLevel.place, v)), ..._buildFilterSection(context, l10n.statsTopTagsSectionTitle, _entryCountPerTag, TagFilter.new), - ..._buildFilterSection(context, l10n.statsTopAlbumsSectionTitle, _entryCountPerAlbum, (v) => AlbumFilter(v, source.getAlbumDisplayName(context, v))), + ..._buildFilterSection(context, l10n.statsTopAlbumsSectionTitle, _entryCountPerAlbum, (v) => StoredAlbumFilter(v, source.getStoredAlbumDisplayName(context, v))), if (showRatings) ..._buildFilterSection(context, l10n.searchRatingSectionTitle, _entryCountPerRating, RatingFilter.new, sortByCount: false, maxRowCount: null), ], ), diff --git a/lib/widgets/viewer/action/entry_info_action_delegate.dart b/lib/widgets/viewer/action/entry_info_action_delegate.dart index 02146ba4c..5d011c49e 100644 --- a/lib/widgets/viewer/action/entry_info_action_delegate.dart +++ b/lib/widgets/viewer/action/entry_info_action_delegate.dart @@ -224,15 +224,16 @@ class EntryInfoActionDelegate with FeedbackMixin, PermissionAwareMixin, EntryEdi } Future _convertMotionPhotoToStillImage(BuildContext context, AvesEntry targetEntry) async { + final l10n = context.l10n; final confirmed = await showDialog( context: context, builder: (context) => AvesDialog( - content: Text(context.l10n.genericDangerWarningDialogMessage), + content: Text(l10n.genericDangerWarningDialogMessage), actions: [ const CancelButton(), TextButton( onPressed: () => Navigator.maybeOf(context)?.pop(true), - child: Text(context.l10n.applyButtonLabel), + child: Text(l10n.applyButtonLabel), ), ], ), diff --git a/lib/widgets/viewer/action/video_action_delegate.dart b/lib/widgets/viewer/action/video_action_delegate.dart index 78375aad4..52662e9ff 100644 --- a/lib/widgets/viewer/action/video_action_delegate.dart +++ b/lib/widgets/viewer/action/video_action_delegate.dart @@ -4,7 +4,7 @@ import 'dart:math'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/location.dart'; import 'package:aves/model/entry/extensions/props.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/source/collection_lens.dart'; import 'package:aves/services/common/services.dart'; import 'package:aves/services/media/enums.dart'; @@ -140,7 +140,7 @@ class VideoActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix settings: const RouteSettings(name: CollectionPage.routeName), builder: (context) => CollectionPage( source: source, - filters: {AlbumFilter(destinationAlbum, source.getAlbumDisplayName(context, destinationAlbum))}, + filters: {StoredAlbumFilter(destinationAlbum, source.getStoredAlbumDisplayName(context, destinationAlbum))}, highlightTest: (entry) => entry.uri == newUri, ), ), diff --git a/lib/widgets/viewer/entry_viewer_stack.dart b/lib/widgets/viewer/entry_viewer_stack.dart index 1ecf6f3c2..752fbff57 100644 --- a/lib/widgets/viewer/entry_viewer_stack.dart +++ b/lib/widgets/viewer/entry_viewer_stack.dart @@ -912,12 +912,17 @@ class _EntryViewerStackState extends State with EntryViewContr await viewerController.stopCast(); - switch (settings.maxBrightness) { - case MaxBrightness.never: - case MaxBrightness.viewerOnly: - await AvesApp.screenBrightness?.resetApplicationScreenBrightness(); - case MaxBrightness.always: - await AvesApp.screenBrightness?.setApplicationScreenBrightness(1); + try { + switch (settings.maxBrightness) { + case MaxBrightness.never: + case MaxBrightness.viewerOnly: + await AvesApp.screenBrightness?.resetApplicationScreenBrightness(); + case MaxBrightness.always: + await AvesApp.screenBrightness?.setApplicationScreenBrightness(1); + } + } on PlatformException catch (e, stack) { + // `screen_brightness` plugin may fail + unawaited(reportService.recordError(e, stack)); } if (settings.keepScreenOn == KeepScreenOn.viewerOnly) { await windowService.keepScreenOn(false); diff --git a/lib/widgets/viewer/info/basic_section.dart b/lib/widgets/viewer/info/basic_section.dart index 13390eab7..99e1662b2 100644 --- a/lib/widgets/viewer/info/basic_section.dart +++ b/lib/widgets/viewer/info/basic_section.dart @@ -6,12 +6,12 @@ import 'package:aves/model/entry/extensions/favourites.dart'; import 'package:aves/model/entry/extensions/multipage.dart'; import 'package:aves/model/entry/extensions/props.dart'; import 'package:aves/model/favourites.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/date.dart'; import 'package:aves/model/filters/favourite.dart'; import 'package:aves/model/filters/mime.dart'; import 'package:aves/model/filters/rating.dart'; -import 'package:aves/model/filters/tag.dart'; +import 'package:aves/model/filters/covered/tag.dart'; import 'package:aves/model/filters/type.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; @@ -131,7 +131,7 @@ class _BasicSectionState extends State { if (entry.isPureVideo && entry.is360) TypeFilter.sphericalVideo, if (entry.isPureVideo && !entry.is360) MimeFilter.video, if (dateTime != null) DateFilter(DateLevel.ymd, dateTime.date), - if (album != null) AlbumFilter(album, collection?.source.getAlbumDisplayName(context, album)), + if (album != null) StoredAlbumFilter(album, collection?.source.getStoredAlbumDisplayName(context, album)), if (entry.rating != 0) RatingFilter(entry.rating), ...tags.map(TagFilter.new), }; diff --git a/lib/widgets/viewer/info/location_section.dart b/lib/widgets/viewer/info/location_section.dart index c3a75c341..2f13b4ff6 100644 --- a/lib/widgets/viewer/info/location_section.dart +++ b/lib/widgets/viewer/info/location_section.dart @@ -1,7 +1,7 @@ import 'package:aves/app_mode.dart'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/entry/extensions/location.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/settings/enums/coordinate_format.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; diff --git a/lib/widgets/viewer/slideshow_page.dart b/lib/widgets/viewer/slideshow_page.dart index a67ca879d..67ce7e9ac 100644 --- a/lib/widgets/viewer/slideshow_page.dart +++ b/lib/widgets/viewer/slideshow_page.dart @@ -1,6 +1,6 @@ import 'package:aves/app_mode.dart'; import 'package:aves/model/entry/entry.dart'; -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/mime.dart'; import 'package:aves/model/settings/settings.dart'; import 'package:aves/model/source/collection_lens.dart'; @@ -147,7 +147,7 @@ class _SlideshowPageState extends State { settings: const RouteSettings(name: CollectionPage.routeName), builder: (context) => CollectionPage( source: source, - filters: album != null ? {AlbumFilter(album, source.getAlbumDisplayName(context, album))} : null, + filters: album != null ? {StoredAlbumFilter(album, source.getStoredAlbumDisplayName(context, album))} : null, highlightTest: (entry) => entry.uri == uri, ), ), diff --git a/plugins/aves_magnifier/pubspec.lock b/plugins/aves_magnifier/pubspec.lock index d0263978c..2a109bdc2 100644 --- a/plugins/aves_magnifier/pubspec.lock +++ b/plugins/aves_magnifier/pubspec.lock @@ -36,10 +36,10 @@ packages: dependency: "direct main" description: name: equatable - sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.0.7" flutter: dependency: "direct main" description: flutter diff --git a/plugins/aves_map/pubspec.lock b/plugins/aves_map/pubspec.lock index 3a7cae9f8..686979457 100644 --- a/plugins/aves_map/pubspec.lock +++ b/plugins/aves_map/pubspec.lock @@ -60,10 +60,10 @@ packages: dependency: "direct main" description: name: equatable - sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.0.7" fluster: dependency: "direct main" description: @@ -113,10 +113,10 @@ packages: dependency: "direct main" description: name: intl - sha256: "99f282cb0e02edcbbf8c6b3bbc7c90b65635156c412e58f3975a7e55284ce685" + sha256: "00f33b908655e606b86d2ade4710a231b802eec6f11e87e4ea3783fd72077a50" url: "https://pub.dev" source: hosted - version: "0.20.0" + version: "0.20.1" latlong2: dependency: "direct main" description: @@ -286,10 +286,10 @@ packages: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "1.1.0" wkt_parser: dependency: transitive description: diff --git a/plugins/aves_model/lib/src/actions/chip_set.dart b/plugins/aves_model/lib/src/actions/chip_set.dart index 491e241e5..ae7a53043 100644 --- a/plugins/aves_model/lib/src/actions/chip_set.dart +++ b/plugins/aves_model/lib/src/actions/chip_set.dart @@ -15,6 +15,7 @@ enum ChipSetAction { stats, // selecting (single/multiple filters) delete, + remove, hide, pin, unpin, @@ -54,6 +55,7 @@ class ChipSetActions { ChipSetAction.pin, ChipSetAction.unpin, ChipSetAction.delete, + ChipSetAction.remove, ChipSetAction.rename, ChipSetAction.showCountryStates, ChipSetAction.hide, diff --git a/plugins/aves_model/lib/src/actions/entry_set.dart b/plugins/aves_model/lib/src/actions/entry_set.dart index 171497d6a..90bc1df4c 100644 --- a/plugins/aves_model/lib/src/actions/entry_set.dart +++ b/plugins/aves_model/lib/src/actions/entry_set.dart @@ -7,6 +7,7 @@ enum EntrySetAction { // browsing searchCollection, toggleTitleSearch, + addDynamicAlbum, addShortcut, setHome, emptyBin, @@ -47,6 +48,7 @@ class EntrySetActions { static const pageBrowsing = [ EntrySetAction.searchCollection, EntrySetAction.toggleTitleSearch, + EntrySetAction.addDynamicAlbum, EntrySetAction.addShortcut, EntrySetAction.setHome, null, diff --git a/plugins/aves_model/pubspec.lock b/plugins/aves_model/pubspec.lock index e7e5e2a72..94e589d98 100644 --- a/plugins/aves_model/pubspec.lock +++ b/plugins/aves_model/pubspec.lock @@ -21,10 +21,10 @@ packages: dependency: "direct main" description: name: equatable - sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.0.7" flutter: dependency: "direct main" description: flutter diff --git a/plugins/aves_report_crashlytics/pubspec.lock b/plugins/aves_report_crashlytics/pubspec.lock index 91fcf4ed0..9af8f7e40 100644 --- a/plugins/aves_report_crashlytics/pubspec.lock +++ b/plugins/aves_report_crashlytics/pubspec.lock @@ -5,10 +5,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: "71c01c1998c40b3af1944ad0a5f374b4e6fef7f3d2df487f3970dbeadaeb25a1" + sha256: eae3133cbb06de9205899b822e3897fc6a8bc278ad4c944b4ce612689369694b url: "https://pub.dev" source: hosted - version: "1.3.46" + version: "1.3.47" async: dependency: transitive description: @@ -68,42 +68,42 @@ packages: dependency: "direct main" description: name: firebase_core - sha256: "2438a75ad803e818ad3bd5df49137ee619c46b6fc7101f4dbc23da07305ce553" + sha256: fef81a53ba1ca618def1f8bef4361df07968434e62cb204c1fb90bb880a03da2 url: "https://pub.dev" source: hosted - version: "3.8.0" + version: "3.8.1" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: e30da58198a6d4b49d5bce4e852f985c32cb10db329ebef9473db2b9f09ce810 + sha256: b94b217e3ad745e784960603d33d99471621ecca151c99c670869b76e50ad2a6 url: "https://pub.dev" source: hosted - version: "5.3.0" + version: "5.3.1" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: f967a7138f5d2ffb1ce15950e2a382924239eaa521150a8f144af34e68b3b3e5 + sha256: "9e69806bb3d905aeec3c1242e0e1475de6ea6d48f456af29d598fb229a2b4e5e" url: "https://pub.dev" source: hosted - version: "2.18.1" + version: "2.18.2" firebase_crashlytics: dependency: "direct main" description: name: firebase_crashlytics - sha256: "4e80ef22428dfecf609df8049419c7446c6e1d797d7f307cad3c7ab70e72ddc5" + sha256: e235c8452d5622fc271404592388fde179e4b62c50e777ad3c8c3369296104ed url: "https://pub.dev" source: hosted - version: "4.1.5" + version: "4.2.0" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: "1104f428ec5249fff62016985719bb232ca91c4bde0d1a033af9b7d8b7451d70" + sha256: "4ddadf44ed0a202f3acad053f12c083877940fa8cc1a9f747ae09e1ef4372160" url: "https://pub.dev" source: hosted - version: "3.6.46" + version: "3.7.0" flutter: dependency: "direct main" description: flutter diff --git a/plugins/aves_services/pubspec.lock b/plugins/aves_services/pubspec.lock index 39c23aac5..f297d944b 100644 --- a/plugins/aves_services/pubspec.lock +++ b/plugins/aves_services/pubspec.lock @@ -67,10 +67,10 @@ packages: dependency: transitive description: name: equatable - sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.0.7" fluster: dependency: transitive description: @@ -120,10 +120,10 @@ packages: dependency: transitive description: name: intl - sha256: "99f282cb0e02edcbbf8c6b3bbc7c90b65635156c412e58f3975a7e55284ce685" + sha256: "00f33b908655e606b86d2ade4710a231b802eec6f11e87e4ea3783fd72077a50" url: "https://pub.dev" source: hosted - version: "0.20.0" + version: "0.20.1" latlong2: dependency: "direct main" description: @@ -293,10 +293,10 @@ packages: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "1.1.0" wkt_parser: dependency: transitive description: diff --git a/plugins/aves_services_google/pubspec.lock b/plugins/aves_services_google/pubspec.lock index eada00d60..c4dd07d48 100644 --- a/plugins/aves_services_google/pubspec.lock +++ b/plugins/aves_services_google/pubspec.lock @@ -89,10 +89,10 @@ packages: dependency: "direct main" description: name: device_info_plus - sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074 + sha256: f545ffbadee826f26f2e1a0f0cbd667ae9a6011cc0f77c0f8f00a969655e6e95 url: "https://pub.dev" source: hosted - version: "10.1.2" + version: "11.1.1" device_info_plus_platform_interface: dependency: transitive description: @@ -105,10 +105,10 @@ packages: dependency: transitive description: name: equatable - sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.0.7" ffi: dependency: transitive description: @@ -219,10 +219,10 @@ packages: dependency: transitive description: name: google_maps_flutter_ios - sha256: "753ebf6a2bc24c5eba8e714c901345d858abd9694b1f878c43614fd3f06b8060" + sha256: "6f798adb0aa1db5adf551f2e39e24bd06c8c0fbe4de912fb2d9b5b3f48147b02" url: "https://pub.dev" source: hosted - version: "2.13.1" + version: "2.13.2" google_maps_flutter_platform_interface: dependency: "direct main" description: @@ -267,10 +267,10 @@ packages: dependency: transitive description: name: intl - sha256: "99f282cb0e02edcbbf8c6b3bbc7c90b65635156c412e58f3975a7e55284ce685" + sha256: "00f33b908655e606b86d2ade4710a231b802eec6f11e87e4ea3783fd72077a50" url: "https://pub.dev" source: hosted - version: "0.20.0" + version: "0.20.1" latlong2: dependency: "direct main" description: @@ -464,18 +464,18 @@ packages: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "1.1.0" win32: dependency: transitive description: name: win32 - sha256: "84ba388638ed7a8cb3445a320c8273136ab2631cd5f2c57888335504ddab1bc2" + sha256: "8b338d4486ab3fbc0ba0db9f9b4f5239b6697fcee427939a40e720cbb9ee0a69" url: "https://pub.dev" source: hosted - version: "5.8.0" + version: "5.9.0" win32_registry: dependency: transitive description: diff --git a/plugins/aves_services_none/pubspec.lock b/plugins/aves_services_none/pubspec.lock index 61e7f2bab..cc9b14f69 100644 --- a/plugins/aves_services_none/pubspec.lock +++ b/plugins/aves_services_none/pubspec.lock @@ -74,10 +74,10 @@ packages: dependency: transitive description: name: equatable - sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.0.7" fluster: dependency: transitive description: @@ -127,10 +127,10 @@ packages: dependency: transitive description: name: intl - sha256: "99f282cb0e02edcbbf8c6b3bbc7c90b65635156c412e58f3975a7e55284ce685" + sha256: "00f33b908655e606b86d2ade4710a231b802eec6f11e87e4ea3783fd72077a50" url: "https://pub.dev" source: hosted - version: "0.20.0" + version: "0.20.1" latlong2: dependency: "direct main" description: @@ -300,10 +300,10 @@ packages: dependency: transitive description: name: web - sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27" + sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "1.1.0" wkt_parser: dependency: transitive description: diff --git a/plugins/aves_video/pubspec.lock b/plugins/aves_video/pubspec.lock index 86fad172f..e22e49e0b 100644 --- a/plugins/aves_video/pubspec.lock +++ b/plugins/aves_video/pubspec.lock @@ -43,10 +43,10 @@ packages: dependency: transitive description: name: equatable - sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.0.7" flutter: dependency: "direct main" description: flutter diff --git a/plugins/aves_video_ffmpeg/pubspec.lock b/plugins/aves_video_ffmpeg/pubspec.lock index a3b97d545..a68d20811 100644 --- a/plugins/aves_video_ffmpeg/pubspec.lock +++ b/plugins/aves_video_ffmpeg/pubspec.lock @@ -50,10 +50,10 @@ packages: dependency: transitive description: name: equatable - sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.0.7" ffmpeg_kit_flutter: dependency: "direct main" description: diff --git a/plugins/aves_video_mpv/pubspec.lock b/plugins/aves_video_mpv/pubspec.lock index 6d8cba9a3..dee6d1517 100644 --- a/plugins/aves_video_mpv/pubspec.lock +++ b/plugins/aves_video_mpv/pubspec.lock @@ -90,10 +90,10 @@ packages: dependency: transitive description: name: equatable - sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.0.7" ffi: dependency: transitive description: @@ -270,10 +270,10 @@ packages: dependency: transitive description: name: screen_brightness - sha256: a43fdbccd5b90044f68057412dde7715cd7499a4c24f5d5da7e01ed4cf41e0af + sha256: a9a98666045ad4ea0d82bca09fe5f007b8440e315075dc948c1507a9b72ee41f url: "https://pub.dev" source: hosted - version: "2.0.0+2" + version: "2.0.1" screen_brightness_android: dependency: transitive description: @@ -310,10 +310,10 @@ packages: dependency: transitive description: name: screen_brightness_windows - sha256: fa97ae838c42f762f04d2d70adb3d957350d6a84e3598ec800e269e7c466eedd + sha256: "5edbfb1dcaedf960f6858efac8ca45d6c18faae17df86e2c03137d3a563ea155" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.0.1" sky_engine: dependency: transitive description: flutter @@ -443,10 +443,10 @@ packages: dependency: transitive description: name: win32 - sha256: "84ba388638ed7a8cb3445a320c8273136ab2631cd5f2c57888335504ddab1bc2" + sha256: "8b338d4486ab3fbc0ba0db9f9b4f5239b6697fcee427939a40e720cbb9ee0a69" url: "https://pub.dev" source: hosted - version: "5.8.0" + version: "5.9.0" xml: dependency: transitive description: diff --git a/pubspec.lock b/pubspec.lock index c03b4bffc..a9608bcda 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -13,10 +13,10 @@ packages: dependency: transitive description: name: _flutterfire_internals - sha256: "71c01c1998c40b3af1944ad0a5f374b4e6fef7f3d2df487f3970dbeadaeb25a1" + sha256: eae3133cbb06de9205899b822e3897fc6a8bc278ad4c944b4ce612689369694b url: "https://pub.dev" source: hosted - version: "1.3.46" + version: "1.3.47" _macros: dependency: transitive description: dart @@ -247,10 +247,10 @@ packages: dependency: transitive description: name: coverage - sha256: "4b03e11f6d5b8f6e5bb5e9f7889a56fe6c5cbe942da5378ea4d4d7f73ef9dfe5" + sha256: e3493833ea012784c740e341952298f1cc77f1f01b1bbc3eb4eecf6984fb7f43 url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.11.1" crypto: dependency: transitive description: @@ -335,10 +335,10 @@ packages: dependency: "direct main" description: name: equatable - sha256: c2b87cb7756efdf69892005af546c56c0b5037f54d2a88269b4f347a505e3ca2 + sha256: "567c64b3cb4cf82397aac55f4f0cbd3ca20d77c6c03bedbc4ceaddc08904aef7" url: "https://pub.dev" source: hosted - version: "2.0.5" + version: "2.0.7" event_bus: dependency: "direct main" description: @@ -351,10 +351,10 @@ packages: dependency: transitive description: name: executor_lib - sha256: "544889daa5726462657dab6410b75f2f8e3a77479d85b307a25c346e243bc38e" + sha256: "95ddf2957d9942d9702855b38dd49677f0ee6a8b77d7b16c0e509c7669d17386" url: "https://pub.dev" source: hosted - version: "1.1.1" + version: "1.1.2" expansion_tile_card: dependency: "direct main" description: @@ -409,42 +409,42 @@ packages: dependency: transitive description: name: firebase_core - sha256: "2438a75ad803e818ad3bd5df49137ee619c46b6fc7101f4dbc23da07305ce553" + sha256: fef81a53ba1ca618def1f8bef4361df07968434e62cb204c1fb90bb880a03da2 url: "https://pub.dev" source: hosted - version: "3.8.0" + version: "3.8.1" firebase_core_platform_interface: dependency: transitive description: name: firebase_core_platform_interface - sha256: e30da58198a6d4b49d5bce4e852f985c32cb10db329ebef9473db2b9f09ce810 + sha256: b94b217e3ad745e784960603d33d99471621ecca151c99c670869b76e50ad2a6 url: "https://pub.dev" source: hosted - version: "5.3.0" + version: "5.3.1" firebase_core_web: dependency: transitive description: name: firebase_core_web - sha256: f967a7138f5d2ffb1ce15950e2a382924239eaa521150a8f144af34e68b3b3e5 + sha256: "9e69806bb3d905aeec3c1242e0e1475de6ea6d48f456af29d598fb229a2b4e5e" url: "https://pub.dev" source: hosted - version: "2.18.1" + version: "2.18.2" firebase_crashlytics: dependency: transitive description: name: firebase_crashlytics - sha256: "4e80ef22428dfecf609df8049419c7446c6e1d797d7f307cad3c7ab70e72ddc5" + sha256: e235c8452d5622fc271404592388fde179e4b62c50e777ad3c8c3369296104ed url: "https://pub.dev" source: hosted - version: "4.1.5" + version: "4.2.0" firebase_crashlytics_platform_interface: dependency: transitive description: name: firebase_crashlytics_platform_interface - sha256: "1104f428ec5249fff62016985719bb232ca91c4bde0d1a033af9b7d8b7451d70" + sha256: "4ddadf44ed0a202f3acad053f12c083877940fa8cc1a9f747ae09e1ef4372160" url: "https://pub.dev" source: hosted - version: "3.6.46" + version: "3.7.0" fixnum: dependency: transitive description: @@ -553,10 +553,10 @@ packages: dependency: "direct main" description: name: flutter_markdown - sha256: f0e599ba89c9946c8e051780f0ec99aba4ba15895e0380a7ab68f420046fc44e + sha256: "255b00afa1a7bad19727da6a7780cf3db6c3c12e68d302d85e0ff1fdf173db9e" url: "https://pub.dev" source: hosted - version: "0.7.4+1" + version: "0.7.4+3" flutter_plugin_android_lifecycle: dependency: transitive description: @@ -664,10 +664,10 @@ packages: dependency: transitive description: name: google_maps_flutter_ios - sha256: "753ebf6a2bc24c5eba8e714c901345d858abd9694b1f878c43614fd3f06b8060" + sha256: "6f798adb0aa1db5adf551f2e39e24bd06c8c0fbe4de912fb2d9b5b3f48147b02" url: "https://pub.dev" source: hosted - version: "2.13.1" + version: "2.13.2" google_maps_flutter_platform_interface: dependency: transitive description: @@ -744,10 +744,10 @@ packages: dependency: transitive description: name: io - sha256: "2ec25704aba361659e10e3e5f5d672068d332fc8ac516421d483a11e5cbd061e" + sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b url: "https://pub.dev" source: hosted - version: "1.0.4" + version: "1.0.5" js: dependency: transitive description: @@ -1076,18 +1076,18 @@ packages: dependency: transitive description: name: path_provider_android - sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a + sha256: "4adf4fd5423ec60a29506c76581bc05854c55e3a0b72d35bb28d661c9686edf2" url: "https://pub.dev" source: hosted - version: "2.2.12" + version: "2.2.15" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.1" path_provider_linux: dependency: transitive description: @@ -1140,10 +1140,10 @@ packages: dependency: "direct main" description: name: percent_indicator - sha256: c37099ad833a883c9d71782321cb65c3a848c21b6939b6185f0ff6640d05814c + sha256: "0d77d5c6fa9b7f60202cedf748b568ba9ba38d3f30405d6ceae4da76f5185462" url: "https://pub.dev" source: hosted - version: "4.2.3" + version: "4.2.4" permission_handler: dependency: "direct main" description: @@ -1172,10 +1172,10 @@ packages: dependency: transitive description: name: permission_handler_html - sha256: af26edbbb1f2674af65a8f4b56e1a6f526156bc273d0e65dd8075fab51c78851 + sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" url: "https://pub.dev" source: hosted - version: "0.1.3+2" + version: "0.1.3+5" permission_handler_platform_interface: dependency: transitive description: @@ -1316,10 +1316,10 @@ packages: dependency: "direct main" description: name: screen_brightness - sha256: a43fdbccd5b90044f68057412dde7715cd7499a4c24f5d5da7e01ed4cf41e0af + sha256: a9a98666045ad4ea0d82bca09fe5f007b8440e315075dc948c1507a9b72ee41f url: "https://pub.dev" source: hosted - version: "2.0.0+2" + version: "2.0.1" screen_brightness_android: dependency: transitive description: @@ -1356,10 +1356,10 @@ packages: dependency: transitive description: name: screen_brightness_windows - sha256: fa97ae838c42f762f04d2d70adb3d957350d6a84e3598ec800e269e7c466eedd + sha256: "5edbfb1dcaedf960f6858efac8ca45d6c18faae17df86e2c03137d3a563ea155" url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.0.1" shared_preferences: dependency: "direct main" description: @@ -1372,10 +1372,10 @@ packages: dependency: transitive description: name: shared_preferences_android - sha256: "3b9febd815c9ca29c9e3520d50ec32f49157711e143b7a4ca039eb87e8ade5ab" + sha256: "7f172d1b06de5da47b6264c2692ee2ead20bbbc246690427cdb4fc301cd0c549" url: "https://pub.dev" source: hosted - version: "2.3.3" + version: "2.3.4" shared_preferences_foundation: dependency: transitive description: @@ -1444,10 +1444,10 @@ packages: dependency: transitive description: name: shelf_web_socket - sha256: "073c147238594ecd0d193f3456a5fe91c4b0abbcc68bf5cd95b36c4e194ac611" + sha256: cc36c297b52866d203dbf9332263c94becc2fe0ceaa9681d07b6ef9807023b67 url: "https://pub.dev" source: hosted - version: "2.0.0" + version: "2.0.1" sky_engine: dependency: transitive description: flutter @@ -1513,10 +1513,10 @@ packages: dependency: transitive description: name: sqflite_common - sha256: "4468b24876d673418a7b7147e5a08a715b4998a7ae69227acafaab762e0e5490" + sha256: "761b9740ecbd4d3e66b8916d784e581861fd3c3553eda85e167bc49fdb68f709" url: "https://pub.dev" source: hosted - version: "2.5.4+5" + version: "2.5.4+6" sqflite_darwin: dependency: transitive description: @@ -1682,10 +1682,10 @@ packages: dependency: transitive description: name: url_launcher_ios - sha256: e43b677296fadce447e987a2f519dcf5f6d1e527dc35d01ffab4fff5b8a7063e + sha256: "16a513b6c12bb419304e72ea0ae2ab4fed569920d1c7cb850263fe3acc824626" url: "https://pub.dev" source: hosted - version: "6.3.1" + version: "6.3.2" url_launcher_linux: dependency: transitive description: @@ -1698,10 +1698,10 @@ packages: dependency: transitive description: name: url_launcher_macos - sha256: "769549c999acdb42b8bcfa7c43d72bf79a382ca7441ab18a808e101149daf672" + sha256: "17ba2000b847f334f16626a574c702b196723af2a289e7a93ffcb79acff855c2" url: "https://pub.dev" source: hosted - version: "3.2.1" + version: "3.2.2" url_launcher_platform_interface: dependency: transitive description: @@ -1850,10 +1850,10 @@ packages: dependency: transitive description: name: win32 - sha256: "84ba388638ed7a8cb3445a320c8273136ab2631cd5f2c57888335504ddab1bc2" + sha256: "8b338d4486ab3fbc0ba0db9f9b4f5239b6697fcee427939a40e720cbb9ee0a69" url: "https://pub.dev" source: hosted - version: "5.8.0" + version: "5.9.0" win32_registry: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 2b40001d1..1c4f7ebaf 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -7,7 +7,7 @@ repository: https://github.com/deckerst/aves # - play changelog: /whatsnew/whatsnew-en-US # - izzy changelog: /fastlane/metadata/android/en-US/changelogs/XXX01.txt # - libre changelog: /fastlane/metadata/android/en-US/changelogs/XXX.txt -version: 1.11.19+138 +version: 1.11.20+139 publish_to: none environment: diff --git a/test/fake/db.dart b/test/fake/db.dart index b5503674a..429481c87 100644 --- a/test/fake/db.dart +++ b/test/fake/db.dart @@ -1,5 +1,6 @@ import 'package:aves/model/covers.dart'; import 'package:aves/model/db/db.dart'; +import 'package:aves/model/dynamic_albums.dart'; import 'package:aves/model/entry/entry.dart'; import 'package:aves/model/favourites.dart'; import 'package:aves/model/filters/filters.dart'; @@ -113,6 +114,9 @@ class FakeAvesDb extends Fake implements LocalMediaDb { @override Future removeCovers(Set filters) => SynchronousFuture(null); + @override + Future> loadAllDynamicAlbums() => SynchronousFuture({}); + // video playback @override diff --git a/test/model/collection_source_test.dart b/test/model/collection_source_test.dart index e799a9cae..9e922806f 100644 --- a/test/model/collection_source_test.dart +++ b/test/model/collection_source_test.dart @@ -6,8 +6,8 @@ import 'package:aves/model/covers.dart'; import 'package:aves/model/db/db.dart'; import 'package:aves/model/entry/extensions/favourites.dart'; import 'package:aves/model/favourites.dart'; -import 'package:aves/model/filters/album.dart'; -import 'package:aves/model/filters/tag.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; +import 'package:aves/model/filters/covered/tag.dart'; import 'package:aves/model/metadata/address.dart'; import 'package:aves/model/metadata/catalog.dart'; import 'package:aves/model/settings/settings.dart'; @@ -117,7 +117,7 @@ void main() { }); test('album/country/tag hidden on launch when their items are hidden by entry prop', () async { - settings.hiddenFilters = {AlbumFilter(testAlbum, 'whatever')}; + settings.hiddenFilters = {StoredAlbumFilter(testAlbum, 'whatever')}; final image1 = FakeMediaStoreService.newImage(testAlbum, 'image1'); (mediaStoreService as FakeMediaStoreService).entries = { @@ -195,7 +195,7 @@ void main() { expect(source.rawAlbums.length, 1); expect(covers.count, 0); - final albumFilter = AlbumFilter(testAlbum, 'whatever'); + final albumFilter = StoredAlbumFilter(testAlbum, 'whatever'); expect(albumFilter.test(image1), true); expect(covers.count, 0); expect(covers.of(albumFilter), null); @@ -217,7 +217,7 @@ void main() { final source = await _initSource(); await image1.toggleFavourite(); - final albumFilter = AlbumFilter(testAlbum, 'whatever'); + final albumFilter = StoredAlbumFilter(testAlbum, 'whatever'); await covers.set(filter: albumFilter, entryId: image1.id, packageName: null, color: null); await source.updateAfterRename( todoEntries: {image1}, @@ -241,7 +241,7 @@ void main() { final source = await _initSource(); await image1.toggleFavourite(); - final albumFilter = AlbumFilter(image1.directory!, 'whatever'); + final albumFilter = StoredAlbumFilter(image1.directory!, 'whatever'); await covers.set(filter: albumFilter, entryId: image1.id, packageName: null, color: null); await source.removeEntries({image1.uri}, includeTrash: true); @@ -261,8 +261,8 @@ void main() { expect(source.rawAlbums.contains(sourceAlbum), true); expect(source.rawAlbums.contains(destinationAlbum), false); - final sourceAlbumFilter = AlbumFilter(sourceAlbum, 'whatever'); - final destinationAlbumFilter = AlbumFilter(destinationAlbum, 'whatever'); + final sourceAlbumFilter = StoredAlbumFilter(sourceAlbum, 'whatever'); + final destinationAlbumFilter = StoredAlbumFilter(destinationAlbum, 'whatever'); expect(sourceAlbumFilter.test(image1), true); expect(destinationAlbumFilter.test(image1), false); @@ -312,7 +312,7 @@ void main() { final source = await _initSource(); expect(source.rawAlbums.length, 1); - final sourceAlbumFilter = AlbumFilter(sourceAlbum, 'whatever'); + final sourceAlbumFilter = StoredAlbumFilter(sourceAlbum, 'whatever'); await covers.set(filter: sourceAlbumFilter, entryId: image1.id, packageName: null, color: null); await source.updateAfterMove( @@ -337,14 +337,14 @@ void main() { final source = await _initSource(); await image1.toggleFavourite(); - var albumFilter = AlbumFilter(sourceAlbum, 'whatever'); + var albumFilter = StoredAlbumFilter(sourceAlbum, 'whatever'); await covers.set(filter: albumFilter, entryId: image1.id, packageName: null, color: null); - await source.renameAlbum(sourceAlbum, destinationAlbum, { + await source.renameStoredAlbum(sourceAlbum, destinationAlbum, { image1 }, { FakeMediaStoreService.moveOpEventForMove(image1, sourceAlbum, destinationAlbum), }); - albumFilter = AlbumFilter(destinationAlbum, 'whatever'); + albumFilter = StoredAlbumFilter(destinationAlbum, 'whatever'); expect(favourites.count, 1); expect(image1.isFavourite, true); @@ -377,20 +377,20 @@ void main() { delegates: AppLocalizations.localizationsDelegates, child: Builder( builder: (context) { - expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Elea/Zeno'), 'Elea/Zeno'); - expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Citium/Zeno'), 'Citium/Zeno'); - expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Cleanthes'), 'Cleanthes'); - expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Chrysippus'), 'Chrysippus'); - expect(source.getAlbumDisplayName(context, '${FakeStorageService.removablePath}Pictures/Chrysippus'), 'Chrysippus (${FakeStorageService.removableDescription})'); - expect(source.getAlbumDisplayName(context, FakeStorageService.primaryRootAlbum), FakeStorageService.primaryDescription); - expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Seneca'), 'Pictures/Seneca'); - expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Seneca'), 'Seneca'); - expect(source.getAlbumDisplayName(context, '${FakeStorageService.removablePath}Pictures/Cicero'), 'Cicero'); - expect(source.getAlbumDisplayName(context, '${FakeStorageService.removablePath}Marcus Aurelius'), 'Marcus Aurelius'); - expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Hannah Arendt'), 'Hannah Arendt'); - expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Arendt'), 'Arendt'); - expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Something'), 'Pictures/Something'); - expect(source.getAlbumDisplayName(context, '${FakeStorageService.primaryPath}Movies/SomeThing'), 'Movies/SomeThing'); + expect(source.getStoredAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Elea/Zeno'), 'Elea/Zeno'); + expect(source.getStoredAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Citium/Zeno'), 'Citium/Zeno'); + expect(source.getStoredAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Cleanthes'), 'Cleanthes'); + expect(source.getStoredAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Chrysippus'), 'Chrysippus'); + expect(source.getStoredAlbumDisplayName(context, '${FakeStorageService.removablePath}Pictures/Chrysippus'), 'Chrysippus (${FakeStorageService.removableDescription})'); + expect(source.getStoredAlbumDisplayName(context, FakeStorageService.primaryRootAlbum), FakeStorageService.primaryDescription); + expect(source.getStoredAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Seneca'), 'Pictures/Seneca'); + expect(source.getStoredAlbumDisplayName(context, '${FakeStorageService.primaryPath}Seneca'), 'Seneca'); + expect(source.getStoredAlbumDisplayName(context, '${FakeStorageService.removablePath}Pictures/Cicero'), 'Cicero'); + expect(source.getStoredAlbumDisplayName(context, '${FakeStorageService.removablePath}Marcus Aurelius'), 'Marcus Aurelius'); + expect(source.getStoredAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Hannah Arendt'), 'Hannah Arendt'); + expect(source.getStoredAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Arendt'), 'Arendt'); + expect(source.getStoredAlbumDisplayName(context, '${FakeStorageService.primaryPath}Pictures/Something'), 'Pictures/Something'); + expect(source.getStoredAlbumDisplayName(context, '${FakeStorageService.primaryPath}Movies/SomeThing'), 'Movies/SomeThing'); return const Placeholder(); }, ), diff --git a/test/model/filters_test.dart b/test/model/filters_test.dart index db4cca9a5..39e35ed96 100644 --- a/test/model/filters_test.dart +++ b/test/model/filters_test.dart @@ -1,10 +1,10 @@ -import 'package:aves/model/filters/album.dart'; +import 'package:aves/model/filters/covered/stored_album.dart'; import 'package:aves/model/filters/aspect_ratio.dart'; import 'package:aves/model/filters/coordinate.dart'; import 'package:aves/model/filters/date.dart'; import 'package:aves/model/filters/favourite.dart'; import 'package:aves/model/filters/filters.dart'; -import 'package:aves/model/filters/location.dart'; +import 'package:aves/model/filters/covered/location.dart'; import 'package:aves/model/filters/mime.dart'; import 'package:aves/model/filters/missing.dart'; import 'package:aves/model/filters/path.dart'; @@ -12,7 +12,7 @@ import 'package:aves/model/filters/placeholder.dart'; import 'package:aves/model/filters/query.dart'; import 'package:aves/model/filters/rating.dart'; import 'package:aves/model/filters/recent.dart'; -import 'package:aves/model/filters/tag.dart'; +import 'package:aves/model/filters/covered/tag.dart'; import 'package:aves/model/filters/type.dart'; import 'package:aves/services/common/services.dart'; import 'package:latlong2/latlong.dart'; @@ -35,7 +35,7 @@ void main() { test('Filter serialization', () { CollectionFilter? jsonRoundTrip(filter) => CollectionFilter.fromJson(filter.toJson()); - final album = AlbumFilter('path/to/album', 'album'); + final album = StoredAlbumFilter('path/to/album', 'album'); expect(album, jsonRoundTrip(album)); final aspectRatio = AspectRatioFilter.landscape; diff --git a/test_driver/driver_screenshots.dart b/test_driver/driver_screenshots.dart index 48bdb8e91..bea53bbdf 100644 --- a/test_driver/driver_screenshots.dart +++ b/test_driver/driver_screenshots.dart @@ -33,6 +33,7 @@ Future configureAndLaunch() async { ..setHome(HomePageSetting.collection) ..enableBottomNavigationBar = true ..drawerTypeBookmarks = [null, FavouriteFilter.instance] + ..drawerAlbumBookmarks = null // collection ..collectionSectionFactor = EntryGroupFactor.month ..collectionSortFactor = EntrySortFactor.date diff --git a/test_driver/driver_screenshots_test.dart b/test_driver/driver_screenshots_test.dart index 711bd0c83..a71863f43 100644 --- a/test_driver/driver_screenshots_test.dart +++ b/test_driver/driver_screenshots_test.dart @@ -159,7 +159,7 @@ void info() { final verticalPageView = find.byValueKey('vertical-pageview'); await driver.scrollY(verticalPageView, -600); - await _selectMapStyle('stamenWatercolor'); + await _selectMapStyle('googleNormal'); await _takeScreenshot(driver, '3'); diff --git a/whatsnew/whatsnew-en-US b/whatsnew/whatsnew-en-US index e405b5830..00cd62158 100644 --- a/whatsnew/whatsnew-en-US +++ b/whatsnew/whatsnew-en-US @@ -1,5 +1,4 @@ -In v1.11.19: -- peruse your videos frame by frame -- create map shortcuts to filtered collections -- enjoy the app in Shavian +In v1.11.20: +- save your filtered collection as dynamic albums +- enjoy the app in Tamil and Bulgarian Full changelog available on GitHub \ No newline at end of file