diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml
index 7b9ac7f6f..483894b13 100644
--- a/.github/workflows/check.yml
+++ b/.github/workflows/check.yml
@@ -17,14 +17,15 @@ jobs:
# Available versions may lag behind https://github.com/flutter/flutter.git
- uses: subosito/flutter-action@v2
with:
- flutter-version: '2.10.4'
+ flutter-version: '3.0.1'
channel: 'stable'
- name: Clone the repository.
uses: actions/checkout@v2
- name: Get packages for the Flutter project.
- run: flutter pub get
+ working-directory: ${{ github.workspace }}/scripts
+ run: ./pub_get_all.sh
- name: Update the flutter version file.
working-directory: ${{ github.workspace }}/scripts
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 3b0d4edd1..cae6d2637 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -19,7 +19,7 @@ jobs:
# Available versions may lag behind https://github.com/flutter/flutter.git
- uses: subosito/flutter-action@v2
with:
- flutter-version: '2.10.4'
+ flutter-version: '3.0.1'
channel: 'stable'
# Workaround for this Android Gradle Plugin issue (supposedly fixed in AGP 4.1):
@@ -31,7 +31,8 @@ jobs:
uses: actions/checkout@v2
- name: Get packages for the Flutter project.
- run: flutter pub get
+ working-directory: ${{ github.workspace }}/scripts
+ run: ./pub_get_all.sh
- name: Update the flutter version file.
working-directory: ${{ github.workspace }}/scripts
@@ -55,12 +56,15 @@ jobs:
rm release.keystore.asc
mkdir outputs
(cd scripts/; ./apply_flavor_play.sh)
- flutter build appbundle -t lib/main_play.dart --flavor play --bundle-sksl-path shaders_2.10.4.sksl.json
+ flutter build appbundle -t lib/main_play.dart --flavor play --bundle-sksl-path shaders_3.0.1.sksl.json
cp build/app/outputs/bundle/playRelease/*.aab outputs
- flutter build apk -t lib/main_play.dart --flavor play --bundle-sksl-path shaders_2.10.4.sksl.json
+ flutter build apk -t lib/main_play.dart --flavor play --bundle-sksl-path shaders_3.0.1.sksl.json
cp build/app/outputs/apk/play/release/*.apk outputs
+ (cd scripts/; ./apply_flavor_huawei.sh)
+ flutter build apk -t lib/main_huawei.dart --flavor huawei --bundle-sksl-path shaders_3.0.1.sksl.json
+ cp build/app/outputs/apk/huawei/release/*.apk outputs
(cd scripts/; ./apply_flavor_izzy.sh)
- flutter build apk -t lib/main_izzy.dart --flavor izzy --split-per-abi --bundle-sksl-path shaders_2.10.4.sksl.json
+ flutter build apk -t lib/main_izzy.dart --flavor izzy --split-per-abi --bundle-sksl-path shaders_3.0.1.sksl.json
cp build/app/outputs/apk/izzy/release/*.apk outputs
rm $AVES_STORE_FILE
env:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 595b4ebeb..82610377d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,29 @@ All notable changes to this project will be documented in this file.
## [Unreleased]
+## [v1.6.5] - 2022-05-25
+
+### Added
+
+- Bottom navigation bar
+- Collection: thumbnail overlay tag icon
+- Collection: fast-scrolling shows breadcrumbs from groups
+- Settings: search
+- Pick: allow selecting multiple items according to request intent
+- `huawei` app flavor (Petal Maps, no Crashlytics)
+
+### Changed
+
+- upgraded Flutter to stable v3.0.1
+- stretching overscroll effect
+- disabled Google Maps layer on Android Lollipop
+
+### Fixed
+
+- grey Google Map layer when size changed
+- Android scrolling screenshot support
+- Voice Access scrolling support
+
## [v1.6.4] - 2022-04-19
### Added
diff --git a/README.md b/README.md
index c891c9f2c..271e29520 100644
--- a/README.md
+++ b/README.md
@@ -12,12 +12,17 @@ Aves is a gallery and metadata explorer app. It is built for Android, with Flutt
[
](https://play.google.com/store/apps/details?id=deckers.thibault.aves&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1)
+[
](https://www.amazon.com/dp/B09XQHQQ72)
[
](https://apt.izzysoft.de/fdroid/index/apk/deckers.thibault.aves)
[
](https://github.com/deckerst/aves/releases/latest)
+
+[Compare versions](https://github.com/deckerst/aves/wiki/App-Versions)
@@ -77,7 +82,7 @@ Aves requires a few permissions to do its job:
### Issues
-[Bug reports](https://github.com/deckerst/aves/issues/new?assignees=&labels=type%3Abug&template=bug_report.md&title=) and [feature requests](https://github.com/deckerst/aves/issues/new?assignees=&labels=type%3Afeature&template=feature_request.md&title=) are welcome. Questions too, though you could also ask them in [Discussions](https://github.com/deckerst/aves/discussions).
+[Bug reports](https://github.com/deckerst/aves/issues/new?assignees=&labels=type%3Abug&template=bug_report.md&title=) and [feature requests](https://github.com/deckerst/aves/issues/new?assignees=&labels=type%3Afeature&template=feature_request.md&title=) are welcome, but read the [guidelines](https://github.com/deckerst/aves/issues/234) first. If you have questions, check out the [discussions](https://github.com/deckerst/aves/discussions).
### Code
diff --git a/analysis_options.yaml b/analysis_options.yaml
index bfd8aadb8..d4f4380d2 100644
--- a/analysis_options.yaml
+++ b/analysis_options.yaml
@@ -11,6 +11,12 @@ analyzer:
linter:
rules:
+ # from 'flutter_lints', excluded
+ use_build_context_synchronously: false # no alternative
+
+ # from 'lints / recommended', excluded
+ no_leading_underscores_for_local_identifiers: false # useful for null checked variable variants
+
# from 'effective dart', excluded
avoid_classes_with_only_static_members: false # too strict
avoid_function_literals_in_foreach_calls: false # benefit?
diff --git a/android/app/agconnect-services.json b/android/app/agconnect-services.json
new file mode 100644
index 000000000..876ecb775
--- /dev/null
+++ b/android/app/agconnect-services.json
@@ -0,0 +1,75 @@
+{
+ "agcgw_all":{
+ "CN":"connect-drcn.dbankcloud.cn",
+ "CN_back":"connect-drcn.hispace.hicloud.com",
+ "DE":"connect-dre.dbankcloud.cn",
+ "DE_back":"connect-dre.hispace.hicloud.com",
+ "RU":"connect-drru.hispace.dbankcloud.ru",
+ "RU_back":"connect-drru.hispace.dbankcloud.ru",
+ "SG":"connect-dra.dbankcloud.cn",
+ "SG_back":"connect-dra.hispace.hicloud.com"
+ },
+ "client":{
+ "cp_id":"2640082000020010713",
+ "product_id":"99536292102197525",
+ "client_id":"874325707927340288",
+ "client_secret":"DCAFAE5C0440ABDBD6DDB2B6EBD7D9B0870C10FCA64759CCD63020D168803AB5",
+ "project_id":"99536292102197525",
+ "app_id":"106014023",
+ "api_key":"DAEDAEzScQA5ri36P2NEiVPSFrOJeYZ0DbEJZMGJrBadW+QudBr5BGHD3vO0tsL1VeBy0RPZefPic3hAWUijcBxCv0zRv0iBjQEptQ==",
+ "package_name":"deckers.thibault.aves"
+ },
+ "oauth_client":{
+ "client_id":"106014023",
+ "client_type":1
+ },
+ "app_info":{
+ "app_id":"106014023",
+ "package_name":"deckers.thibault.aves"
+ },
+ "configuration_version":"3.0",
+ "appInfos":[
+ {
+ "package_name":"deckers.thibault.aves.profile",
+ "client":{
+ "app_id":"106031461"
+ },
+ "app_info":{
+ "package_name":"deckers.thibault.aves.profile",
+ "app_id":"106031461"
+ },
+ "oauth_client":{
+ "client_type":1,
+ "client_id":"106031461"
+ }
+ },
+ {
+ "package_name":"deckers.thibault.aves.debug",
+ "client":{
+ "app_id":"106014297"
+ },
+ "app_info":{
+ "package_name":"deckers.thibault.aves.debug",
+ "app_id":"106014297"
+ },
+ "oauth_client":{
+ "client_type":1,
+ "client_id":"106014297"
+ }
+ },
+ {
+ "package_name":"deckers.thibault.aves",
+ "client":{
+ "app_id":"106014023"
+ },
+ "app_info":{
+ "package_name":"deckers.thibault.aves",
+ "app_id":"106014023"
+ },
+ "oauth_client":{
+ "client_type":1,
+ "client_id":"106014023"
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/android/app/build.gradle b/android/app/build.gradle
index 7b9253ca2..a8d4c0b0a 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -81,6 +81,16 @@ android {
// Google Play
dimension "store"
ext.useCrashlytics = true
+ ext.useHMS = false
+ // generate a universal APK without x86 native libs
+ ext.useNdkAbiFilters = true
+ }
+
+ huawei {
+ // Huawei AppGallery
+ dimension "store"
+ ext.useCrashlytics = false
+ ext.useHMS = true
// generate a universal APK without x86 native libs
ext.useNdkAbiFilters = true
}
@@ -91,6 +101,7 @@ android {
// cf https://android.izzysoft.de/articles/named/app-modules-2
dimension "store"
ext.useCrashlytics = false
+ ext.useHMS = false
// generate APK by ABI, but NDK ABI filters are incompatible with split APK generation
ext.useNdkAbiFilters = false
}
@@ -129,6 +140,7 @@ android {
lint {
disable 'InvalidPackage'
}
+ namespace 'deckers.thibault.aves'
}
flutter {
@@ -147,12 +159,15 @@ dependencies {
implementation 'androidx.multidex:multidex:2.0.1'
implementation 'com.caverock:androidsvg-aar:1.4'
implementation 'com.commonsware.cwac:document:0.4.1'
- implementation 'com.drewnoakes:metadata-extractor:2.17.0'
+ implementation 'com.drewnoakes:metadata-extractor:2.18.0'
// forked, built by JitPack, cf https://jitpack.io/p/deckerst/Android-TiffBitmapFactory
implementation 'com.github.deckerst:Android-TiffBitmapFactory:876e53870a'
// forked, built by JitPack, cf https://jitpack.io/p/deckerst/pixymeta-android
implementation 'com.github.deckerst:pixymeta-android:706bd73d6e'
- implementation 'com.github.bumptech.glide:glide:4.13.1'
+ implementation 'com.github.bumptech.glide:glide:4.13.2'
+
+ // huawei flavor only
+ huaweiImplementation 'com.huawei.agconnect:agconnect-core:1.5.2.300'
kapt 'androidx.annotation:annotation:1.3.0'
kapt 'com.github.bumptech.glide:compiler:4.13.0'
@@ -163,8 +178,12 @@ dependencies {
android.productFlavors.each { flavor ->
def tasks = gradle.startParameter.taskRequests.toString().toLowerCase()
if (tasks.contains(flavor.name) && flavor.ext.useCrashlytics) {
- println("Building flavor with Crashlytics [${flavor.name}] - applying plugin")
+ println("Building flavor [${flavor.name}] with Crashlytics plugin")
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.google.firebase.crashlytics'
}
+ if (tasks.contains(flavor.name) && flavor.ext.useHMS) {
+ println("Building flavor [${flavor.name}] with HMS plugin")
+ apply plugin: 'com.huawei.agconnect'
+ }
}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index de4434e63..055de7299 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -1,6 +1,5 @@
+
+
+
+
+
{
@@ -246,10 +247,20 @@ class MainActivity : FlutterActivity() {
}
private fun pick(call: MethodCall) {
- val pickedUri = call.argument("uri")
- if (pickedUri != null) {
+ val pickedUris = call.argument>("uris")
+ if (pickedUris != null && pickedUris.isNotEmpty()) {
+ val toUri = { uriString: String -> AppAdapterHandler.getShareableUri(context, Uri.parse(uriString)) }
val intent = Intent().apply {
- data = Uri.parse(pickedUri)
+ val firstUri = toUri(pickedUris.first())
+ if (pickedUris.size == 1) {
+ data = firstUri
+ } else {
+ clipData = ClipData.newUri(contentResolver, null, firstUri).apply {
+ pickedUris.drop(1).forEach {
+ addItem(ClipData.Item(toUri(it)))
+ }
+ }
+ }
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
}
setResult(RESULT_OK, intent)
@@ -307,6 +318,7 @@ class MainActivity : FlutterActivity() {
const val INTENT_DATA_KEY_ACTION = "action"
const val INTENT_DATA_KEY_FILTERS = "filters"
const val INTENT_DATA_KEY_MIME_TYPE = "mimeType"
+ const val INTENT_DATA_KEY_ALLOW_MULTIPLE = "allowMultiple"
const val INTENT_DATA_KEY_PAGE = "page"
const val INTENT_DATA_KEY_URI = "uri"
const val INTENT_DATA_KEY_QUERY = "query"
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt
index 195e9d4ab..9437745a3 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/AppAdapterHandler.kt
@@ -216,7 +216,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
try {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as? ClipboardManager
if (clipboard != null) {
- val clip = ClipData.newUri(context.contentResolver, label, getShareableUri(uri))
+ val clip = ClipData.newUri(context.contentResolver, label, getShareableUri(context, uri))
clipboard.setPrimaryClip(clip)
result.success(true)
} else {
@@ -239,7 +239,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
val intent = Intent(Intent.ACTION_EDIT)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION)
- .setDataAndType(getShareableUri(uri), mimeType)
+ .setDataAndType(getShareableUri(context, uri), mimeType)
val started = safeStartActivityChooser(title, intent)
result.success(started)
@@ -256,7 +256,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
val intent = Intent(Intent.ACTION_VIEW)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
- .setDataAndType(getShareableUri(uri), mimeType)
+ .setDataAndType(getShareableUri(context, uri), mimeType)
val started = safeStartActivityChooser(title, intent)
result.success(started)
@@ -286,7 +286,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
val intent = Intent(Intent.ACTION_ATTACH_DATA)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
- .setDataAndType(getShareableUri(uri), mimeType)
+ .setDataAndType(getShareableUri(context, uri), mimeType)
val started = safeStartActivityChooser(title, intent)
result.success(started)
@@ -311,7 +311,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
val intent = Intent(Intent.ACTION_SEND)
.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
.setType(mimeType)
- .putExtra(Intent.EXTRA_STREAM, getShareableUri(uri))
+ .putExtra(Intent.EXTRA_STREAM, getShareableUri(context, uri))
safeStartActivityChooser(title, intent)
} else {
var mimeType = "*/*"
@@ -368,18 +368,6 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
return false
}
- private fun getShareableUri(uri: Uri): Uri? {
- return when (uri.scheme?.lowercase(Locale.ROOT)) {
- ContentResolver.SCHEME_FILE -> {
- uri.path?.let { path ->
- val authority = "${context.applicationContext.packageName}.file_provider"
- FileProvider.getUriForFile(context, authority, File(path))
- }
- }
- else -> uri
- }
- }
-
// shortcuts
private fun pinShortcut(call: MethodCall, result: MethodChannel.Result) {
@@ -443,5 +431,17 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
companion object {
private val LOG_TAG = LogUtils.createTag()
const val CHANNEL = "deckers.thibault/aves/app"
+
+ fun getShareableUri(context: Context, uri: Uri): Uri? {
+ return when (uri.scheme?.lowercase(Locale.ROOT)) {
+ ContentResolver.SCHEME_FILE -> {
+ uri.path?.let { path ->
+ val authority = "${context.applicationContext.packageName}.file_provider"
+ FileProvider.getUriForFile(context, authority, File(path))
+ }
+ }
+ else -> uri
+ }
+ }
}
}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt
index 0d2061740..84f6db94f 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DebugHandler.kt
@@ -308,6 +308,8 @@ class DebugHandler(private val context: Context) : MethodCallHandler {
Log.w(LOG_TAG, "failed to get metadata by metadata-extractor for uri=$uri", e)
} catch (e: NoClassDefFoundError) {
Log.w(LOG_TAG, "failed to get metadata by metadata-extractor for uri=$uri", e)
+ } catch (e: AssertionError) {
+ Log.w(LOG_TAG, "failed to get metadata by metadata-extractor for uri=$uri", e)
}
}
result.success(metadataMap)
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt
index be8ee35ee..7bb635ad6 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/DeviceHandler.kt
@@ -32,10 +32,6 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
"canPinShortcut" to ShortcutManagerCompat.isRequestPinShortcutSupported(context),
"canPrint" to (sdkInt >= Build.VERSION_CODES.KITKAT),
"canRenderFlagEmojis" to (sdkInt >= Build.VERSION_CODES.LOLLIPOP),
- // as of google_maps_flutter v2.1.1, minSDK is 20 because of default PlatformView usage,
- // but using hybrid composition would make it usable on API 19 too,
- // cf https://github.com/flutter/flutter/issues/23728
- "canRenderGoogleMaps" to (sdkInt >= Build.VERSION_CODES.KITKAT_WATCH),
"showPinShortcutFeedback" to (sdkInt >= Build.VERSION_CODES.O),
"supportEdgeToEdgeUIMode" to (sdkInt >= Build.VERSION_CODES.Q),
)
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/EmbeddedDataHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/EmbeddedDataHandler.kt
index 3b7d3a897..f09bf8ce1 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/EmbeddedDataHandler.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/EmbeddedDataHandler.kt
@@ -175,6 +175,8 @@ class EmbeddedDataHandler(private val context: Context) : MethodCallHandler {
Log.w(LOG_TAG, "failed to extract file from XMP", e)
} catch (e: NoClassDefFoundError) {
Log.w(LOG_TAG, "failed to extract file from XMP", e)
+ } catch (e: AssertionError) {
+ Log.w(LOG_TAG, "failed to extract file from XMP", e)
}
}
result.error("extractXmpDataProp-empty", "failed to extract file from XMP uri=$uri prop=$dataPropPath", null)
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataFetchHandler.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataFetchHandler.kt
index a6ebfdfae..ce3d27742 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataFetchHandler.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/MetadataFetchHandler.kt
@@ -331,6 +331,8 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
} catch (e: NoClassDefFoundError) {
Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
+ } catch (e: AssertionError) {
+ Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
}
}
@@ -459,14 +461,14 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
// * `context.getContentResolver().getType()` sometimes returns an incorrect value
// * `MediaMetadataRetriever.setDataSource()` sometimes fails with `status = 0x80000000`
// * file extension is unreliable
- // In the end, `metadata-extractor` is the most reliable, except for `tiff`/`dvd`/`mov` (false positives, false negatives),
+ // In the end, `metadata-extractor` is the most reliable, except for `tiff`/`dvd`/`mov`/`zip` (false positives, false negatives),
// in which case we trust the file extension
// cf https://github.com/drewnoakes/metadata-extractor/issues/296
if (path?.matches(TIFF_EXTENSION_PATTERN) == true) {
metadataMap[KEY_MIME_TYPE] = MimeTypes.TIFF
} else {
dir.getSafeString(FileTypeDirectory.TAG_DETECTED_FILE_MIME_TYPE) {
- if (it != MimeTypes.TIFF && it != MimeTypes.DVD && it != MimeTypes.MOV) {
+ if (it != MimeTypes.TIFF && it != MimeTypes.DVD && it != MimeTypes.MOV && it != MimeTypes.ZIP) {
metadataMap[KEY_MIME_TYPE] = it
}
}
@@ -601,6 +603,8 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
} catch (e: NoClassDefFoundError) {
Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
+ } catch (e: AssertionError) {
+ Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
}
}
@@ -727,6 +731,8 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
} catch (e: NoClassDefFoundError) {
Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
+ } catch (e: AssertionError) {
+ Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
}
}
@@ -784,6 +790,8 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
} catch (e: NoClassDefFoundError) {
Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
+ } catch (e: AssertionError) {
+ Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
}
}
result.error("getGeoTiffInfo-empty", "failed to get info for mimeType=$mimeType uri=$uri", null)
@@ -844,6 +852,8 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
} catch (e: NoClassDefFoundError) {
Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
+ } catch (e: AssertionError) {
+ Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
}
}
result.error("getPanoramaInfo-empty", "failed to get info for mimeType=$mimeType uri=$uri", null)
@@ -894,7 +904,10 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
result.error("getXmp-exception", "failed to read XMP for mimeType=$mimeType uri=$uri", e.message)
return
} catch (e: NoClassDefFoundError) {
- result.error("getXmp-error", "failed to read XMP for mimeType=$mimeType uri=$uri", e.message)
+ result.error("getXmp-noclass", "failed to read XMP for mimeType=$mimeType uri=$uri", e.message)
+ return
+ } catch (e: AssertionError) {
+ result.error("getXmp-assert", "failed to read XMP for mimeType=$mimeType uri=$uri", e.message)
return
}
}
@@ -1031,6 +1044,8 @@ class MetadataFetchHandler(private val context: Context) : MethodCallHandler {
Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
} catch (e: NoClassDefFoundError) {
Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
+ } catch (e: AssertionError) {
+ Log.w(LOG_TAG, "failed to read metadata by metadata-extractor for mimeType=$mimeType uri=$uri", e)
}
}
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/RegionFetcher.kt b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/RegionFetcher.kt
index a4796a3c6..a022bfd8d 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/RegionFetcher.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/channel/calls/fetchers/RegionFetcher.kt
@@ -77,7 +77,7 @@ class RegionFetcher internal constructor(
}
}
if (newDecoder == null) {
- result.error("getRegion-read-null", "failed to open file for uri=$uri regionRect=$regionRect", null)
+ result.error("getRegion-read-null", "failed to open file for mimeType=$mimeType uri=$uri regionRect=$regionRect", null)
return
}
currentDecoderRef = LastDecoderRef(uri, newDecoder)
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MultiPage.kt b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MultiPage.kt
index 241e50f02..e9a5dcabf 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MultiPage.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/metadata/MultiPage.kt
@@ -167,6 +167,8 @@ object MultiPage {
Log.w(LOG_TAG, "failed to get motion photo offset from uri=$uri", e)
} catch (e: NoClassDefFoundError) {
Log.w(LOG_TAG, "failed to get motion photo offset from uri=$uri", e)
+ } catch (e: AssertionError) {
+ Log.w(LOG_TAG, "failed to get motion photo offset from uri=$uri", e)
}
return null
}
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt
index a355a8fa9..09406b448 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/SourceEntry.kt
@@ -204,6 +204,8 @@ class SourceEntry {
// ignore
} catch (e: NoClassDefFoundError) {
// ignore
+ } catch (e: AssertionError) {
+ // ignore
}
}
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ContentImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ContentImageProvider.kt
index 13fa8dc2d..1781bba01 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ContentImageProvider.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ContentImageProvider.kt
@@ -33,6 +33,8 @@ internal class ContentImageProvider : ImageProvider() {
Log.w(LOG_TAG, "failed to get MIME type by metadata-extractor for uri=$uri", e)
} catch (e: NoClassDefFoundError) {
Log.w(LOG_TAG, "failed to get MIME type by metadata-extractor for uri=$uri", e)
+ } catch (e: AssertionError) {
+ Log.w(LOG_TAG, "failed to get MIME type by metadata-extractor for uri=$uri", e)
}
val mimeType = extractorMimeType ?: sourceMimeType
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt
index aa7801064..12ce8e4a5 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/ImageProvider.kt
@@ -1045,6 +1045,9 @@ abstract class ImageProvider {
// used when skipping a move/creation op because the target file already exists
val skippedFieldMap: HashMap = hashMapOf("skipped" to true)
+ // used when deleting instead of moving to bin because the target file no longer exists
+ val deletedFieldMap: HashMap = hashMapOf("deleted" to true)
+
fun isMediaUriPermissionGranted(context: Context, uri: Uri, mimeType: String): Boolean {
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
val safeUri = StorageUtils.getMediaStoreScopedStorageSafeUri(uri, mimeType)
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt
index f6eeb0242..c8f38a340 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/model/provider/MediaStoreImageProvider.kt
@@ -443,18 +443,23 @@ class MediaStoreImageProvider : ImageProvider() {
if (effectiveTargetDir != null) {
val newFields = if (isCancelledOp()) skippedFieldMap else {
val sourceFile = File(sourcePath)
- moveSingle(
- activity = activity,
- sourceFile = sourceFile,
- sourceUri = sourceUri,
- targetDir = effectiveTargetDir,
- targetDirDocFile = targetDirDocFile,
- desiredName = desiredName ?: sourceFile.name,
- nameConflictStrategy = nameConflictStrategy,
- mimeType = mimeType,
- copy = copy,
- toBin = toBin,
- )
+ if (!sourceFile.exists() && toBin) {
+ delete(activity, sourceUri, sourcePath, mimeType = mimeType)
+ deletedFieldMap
+ } else {
+ moveSingle(
+ activity = activity,
+ sourceFile = sourceFile,
+ sourceUri = sourceUri,
+ targetDir = effectiveTargetDir,
+ targetDirDocFile = targetDirDocFile,
+ desiredName = desiredName ?: sourceFile.name,
+ nameConflictStrategy = nameConflictStrategy,
+ mimeType = mimeType,
+ copy = copy,
+ toBin = toBin,
+ )
+ }
}
result["newFields"] = newFields
result["success"] = true
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt
index fa810a2b4..c1a41b665 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/MimeTypes.kt
@@ -46,6 +46,7 @@ object MimeTypes {
// vector
const val SVG = "image/svg+xml"
+ // video
private const val AVI = "video/avi"
private const val AVI_VND = "video/vnd.avi"
const val DVD = "video/dvd"
@@ -57,6 +58,9 @@ object MimeTypes {
private const val OGV = "video/ogg"
private const val WEBM = "video/webm"
+ // others
+ const val ZIP = "application/zip"
+
fun isImage(mimeType: String?) = mimeType != null && mimeType.startsWith("image")
fun isVideo(mimeType: String?) = mimeType != null && mimeType.startsWith("video")
diff --git a/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt b/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt
index 9e5b51ced..8a0e3d2a0 100644
--- a/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt
+++ b/android/app/src/main/kotlin/deckers/thibault/aves/utils/StorageUtils.kt
@@ -21,7 +21,6 @@ import deckers.thibault.aves.utils.MimeTypes.isVideo
import deckers.thibault.aves.utils.PermissionManager.getGrantedDirForPath
import deckers.thibault.aves.utils.UriUtils.tryParseId
import java.io.File
-import java.io.FileNotFoundException
import java.io.InputStream
import java.io.OutputStream
import java.util.*
@@ -404,37 +403,37 @@ object StorageUtils {
// returns the directory `DocumentFile` (from tree URI when scoped storage is required, `File` otherwise)
// returns null if directory does not exist and could not be created
fun createDirectoryDocIfAbsent(context: Context, dirPath: String): DocumentFileCompat? {
- val cleanDirPath = ensureTrailingSeparator(dirPath)
- return if (requireAccessPermission(context, cleanDirPath) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
- val grantedDir = getGrantedDirForPath(context, cleanDirPath) ?: return null
- val rootTreeDocumentUri = convertDirPathToTreeDocumentUri(context, grantedDir) ?: return null
- var parentFile: DocumentFileCompat? = DocumentFileCompat.fromTreeUri(context, rootTreeDocumentUri) ?: return null
- val pathIterator = getPathStepIterator(context, cleanDirPath, grantedDir)
- while (pathIterator?.hasNext() == true) {
- val dirName = pathIterator.next()
- var dirFile = findDocumentFileIgnoreCase(parentFile, dirName)
- if (dirFile == null || !dirFile.exists()) {
- try {
+ try {
+ val cleanDirPath = ensureTrailingSeparator(dirPath)
+ return if (requireAccessPermission(context, cleanDirPath) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ val grantedDir = getGrantedDirForPath(context, cleanDirPath) ?: return null
+ val rootTreeDocumentUri = convertDirPathToTreeDocumentUri(context, grantedDir) ?: return null
+ var parentFile: DocumentFileCompat? = DocumentFileCompat.fromTreeUri(context, rootTreeDocumentUri) ?: return null
+ val pathIterator = getPathStepIterator(context, cleanDirPath, grantedDir)
+ while (pathIterator?.hasNext() == true) {
+ val dirName = pathIterator.next()
+ var dirFile = findDocumentFileIgnoreCase(parentFile, dirName)
+ if (dirFile == null || !dirFile.exists()) {
dirFile = parentFile?.createDirectory(dirName)
if (dirFile == null) {
Log.e(LOG_TAG, "failed to create directory with name=$dirName from parent=$parentFile")
return null
}
- } catch (e: FileNotFoundException) {
- Log.e(LOG_TAG, "failed to create directory with name=$dirName from parent=$parentFile", e)
- return null
}
+ parentFile = dirFile
}
- parentFile = dirFile
+ parentFile
+ } else {
+ val directory = File(cleanDirPath)
+ if (!directory.exists() && !directory.mkdirs()) {
+ Log.e(LOG_TAG, "failed to create directories at path=$cleanDirPath")
+ return null
+ }
+ DocumentFileCompat.fromFile(directory)
}
- parentFile
- } else {
- val directory = File(cleanDirPath)
- if (!directory.exists() && !directory.mkdirs()) {
- Log.e(LOG_TAG, "failed to create directories at path=$cleanDirPath")
- return null
- }
- DocumentFileCompat.fromFile(directory)
+ } catch (e: Exception) {
+ Log.e(LOG_TAG, "failed to create directory at path=$dirPath", e)
+ return null
}
}
diff --git a/android/build.gradle b/android/build.gradle
index ec015c9b9..52ec57d77 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,16 +1,19 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
- ext.kotlin_version = '1.6.20'
+ ext.kotlin_version = '1.6.21'
repositories {
google()
mavenCentral()
+ maven { url 'https://developer.huawei.com/repo/' }
}
dependencies {
- classpath 'com.android.tools.build:gradle:7.1.3'
+ classpath 'com.android.tools.build:gradle:7.2.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
- // GMS & Firebase Crashlytics are not actually used by all flavors
+ // GMS & Firebase Crashlytics (used by some flavors only)
classpath 'com.google.gms:google-services:4.3.10'
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
+ // HMS (used by some flavors only)
+ classpath 'com.huawei.agconnect:agcp:1.5.2.300'
}
}
@@ -18,6 +21,7 @@ allprojects {
repositories {
google()
mavenCentral()
+ maven {url 'https://developer.huawei.com/repo/'}
}
// gradle.projectsEvaluated {
// tasks.withType(JavaCompile) {
diff --git a/android/gradle/wrapper/gradle-wrapper.properties b/android/gradle/wrapper/gradle-wrapper.properties
index 6672c658d..080650cd5 100644
--- a/android/gradle/wrapper/gradle-wrapper.properties
+++ b/android/gradle/wrapper/gradle-wrapper.properties
@@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip
diff --git a/fastlane/metadata/android/de/images/phoneScreenshots/1.png b/fastlane/metadata/android/de/images/phoneScreenshots/1.png
index 149abe06e..a4feab64f 100644
Binary files a/fastlane/metadata/android/de/images/phoneScreenshots/1.png and b/fastlane/metadata/android/de/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/de/images/phoneScreenshots/2.png b/fastlane/metadata/android/de/images/phoneScreenshots/2.png
index 1df869df0..bb7b53e99 100644
Binary files a/fastlane/metadata/android/de/images/phoneScreenshots/2.png and b/fastlane/metadata/android/de/images/phoneScreenshots/2.png differ
diff --git a/fastlane/metadata/android/de/images/phoneScreenshots/3.png b/fastlane/metadata/android/de/images/phoneScreenshots/3.png
index b1dca7f8d..eb5b0abf2 100644
Binary files a/fastlane/metadata/android/de/images/phoneScreenshots/3.png and b/fastlane/metadata/android/de/images/phoneScreenshots/3.png differ
diff --git a/fastlane/metadata/android/de/images/phoneScreenshots/4.png b/fastlane/metadata/android/de/images/phoneScreenshots/4.png
index 73b826187..7566991ad 100644
Binary files a/fastlane/metadata/android/de/images/phoneScreenshots/4.png and b/fastlane/metadata/android/de/images/phoneScreenshots/4.png differ
diff --git a/fastlane/metadata/android/de/images/phoneScreenshots/5.png b/fastlane/metadata/android/de/images/phoneScreenshots/5.png
index 74bc802fa..0a6a56bb6 100644
Binary files a/fastlane/metadata/android/de/images/phoneScreenshots/5.png and b/fastlane/metadata/android/de/images/phoneScreenshots/5.png differ
diff --git a/fastlane/metadata/android/de/images/phoneScreenshots/6.png b/fastlane/metadata/android/de/images/phoneScreenshots/6.png
index 56099d117..8d7fec3d3 100644
Binary files a/fastlane/metadata/android/de/images/phoneScreenshots/6.png and b/fastlane/metadata/android/de/images/phoneScreenshots/6.png differ
diff --git a/fastlane/metadata/android/en-US/changelogs/1071.txt b/fastlane/metadata/android/en-US/changelogs/1071.txt
new file mode 100644
index 000000000..38d163888
--- /dev/null
+++ b/fastlane/metadata/android/en-US/changelogs/1071.txt
@@ -0,0 +1,5 @@
+In v1.6.5:
+- bottom navigation bar
+- fast scroll with breadcrumbs
+- settings search
+Full changelog available on GitHub
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png
index bf177de91..986afd0af 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png
index cab24996a..699c677f4 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/2.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png
index 96767e884..5893043b9 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/3.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png
index 5aa80520d..727166076 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/4.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png
index 763fab5f2..f5a0b693b 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/5.png differ
diff --git a/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png b/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png
index f2e09ae08..dc41af368 100644
Binary files a/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png and b/fastlane/metadata/android/en-US/images/phoneScreenshots/6.png differ
diff --git a/fastlane/metadata/android/es-MX/images/phoneScreenshots/1.png b/fastlane/metadata/android/es-MX/images/phoneScreenshots/1.png
index 4480d45e4..691254b62 100644
Binary files a/fastlane/metadata/android/es-MX/images/phoneScreenshots/1.png and b/fastlane/metadata/android/es-MX/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/es-MX/images/phoneScreenshots/2.png b/fastlane/metadata/android/es-MX/images/phoneScreenshots/2.png
index 94170fd16..619af80e6 100644
Binary files a/fastlane/metadata/android/es-MX/images/phoneScreenshots/2.png and b/fastlane/metadata/android/es-MX/images/phoneScreenshots/2.png differ
diff --git a/fastlane/metadata/android/es-MX/images/phoneScreenshots/3.png b/fastlane/metadata/android/es-MX/images/phoneScreenshots/3.png
index 7e6ed547e..a28e9bf7e 100644
Binary files a/fastlane/metadata/android/es-MX/images/phoneScreenshots/3.png and b/fastlane/metadata/android/es-MX/images/phoneScreenshots/3.png differ
diff --git a/fastlane/metadata/android/es-MX/images/phoneScreenshots/4.png b/fastlane/metadata/android/es-MX/images/phoneScreenshots/4.png
index 6d3fdb80f..bdabfd3f4 100644
Binary files a/fastlane/metadata/android/es-MX/images/phoneScreenshots/4.png and b/fastlane/metadata/android/es-MX/images/phoneScreenshots/4.png differ
diff --git a/fastlane/metadata/android/es-MX/images/phoneScreenshots/5.png b/fastlane/metadata/android/es-MX/images/phoneScreenshots/5.png
index 236a70926..303fe809e 100644
Binary files a/fastlane/metadata/android/es-MX/images/phoneScreenshots/5.png and b/fastlane/metadata/android/es-MX/images/phoneScreenshots/5.png differ
diff --git a/fastlane/metadata/android/es-MX/images/phoneScreenshots/6.png b/fastlane/metadata/android/es-MX/images/phoneScreenshots/6.png
index cc9d9e252..7cbfb60ce 100644
Binary files a/fastlane/metadata/android/es-MX/images/phoneScreenshots/6.png and b/fastlane/metadata/android/es-MX/images/phoneScreenshots/6.png differ
diff --git a/fastlane/metadata/android/fr/images/phoneScreenshots/1.png b/fastlane/metadata/android/fr/images/phoneScreenshots/1.png
index c139324b1..0fd4909e9 100644
Binary files a/fastlane/metadata/android/fr/images/phoneScreenshots/1.png and b/fastlane/metadata/android/fr/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/fr/images/phoneScreenshots/2.png b/fastlane/metadata/android/fr/images/phoneScreenshots/2.png
index 47c0852ce..05cd6c007 100644
Binary files a/fastlane/metadata/android/fr/images/phoneScreenshots/2.png and b/fastlane/metadata/android/fr/images/phoneScreenshots/2.png differ
diff --git a/fastlane/metadata/android/fr/images/phoneScreenshots/3.png b/fastlane/metadata/android/fr/images/phoneScreenshots/3.png
index d1d9df6a8..7a42d6be8 100644
Binary files a/fastlane/metadata/android/fr/images/phoneScreenshots/3.png and b/fastlane/metadata/android/fr/images/phoneScreenshots/3.png differ
diff --git a/fastlane/metadata/android/fr/images/phoneScreenshots/4.png b/fastlane/metadata/android/fr/images/phoneScreenshots/4.png
index 792d081af..4d2f5116d 100644
Binary files a/fastlane/metadata/android/fr/images/phoneScreenshots/4.png and b/fastlane/metadata/android/fr/images/phoneScreenshots/4.png differ
diff --git a/fastlane/metadata/android/fr/images/phoneScreenshots/5.png b/fastlane/metadata/android/fr/images/phoneScreenshots/5.png
index 1083c6be4..48dfa2352 100644
Binary files a/fastlane/metadata/android/fr/images/phoneScreenshots/5.png and b/fastlane/metadata/android/fr/images/phoneScreenshots/5.png differ
diff --git a/fastlane/metadata/android/fr/images/phoneScreenshots/6.png b/fastlane/metadata/android/fr/images/phoneScreenshots/6.png
index 077def362..7597244d7 100644
Binary files a/fastlane/metadata/android/fr/images/phoneScreenshots/6.png and b/fastlane/metadata/android/fr/images/phoneScreenshots/6.png differ
diff --git a/fastlane/metadata/android/id/images/phoneScreenshots/1.png b/fastlane/metadata/android/id/images/phoneScreenshots/1.png
index f157c0be5..2a05491b8 100644
Binary files a/fastlane/metadata/android/id/images/phoneScreenshots/1.png and b/fastlane/metadata/android/id/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/id/images/phoneScreenshots/2.png b/fastlane/metadata/android/id/images/phoneScreenshots/2.png
index 25db2f7d4..386bba1a8 100644
Binary files a/fastlane/metadata/android/id/images/phoneScreenshots/2.png and b/fastlane/metadata/android/id/images/phoneScreenshots/2.png differ
diff --git a/fastlane/metadata/android/id/images/phoneScreenshots/3.png b/fastlane/metadata/android/id/images/phoneScreenshots/3.png
index c5f809a92..8ef649c71 100644
Binary files a/fastlane/metadata/android/id/images/phoneScreenshots/3.png and b/fastlane/metadata/android/id/images/phoneScreenshots/3.png differ
diff --git a/fastlane/metadata/android/id/images/phoneScreenshots/4.png b/fastlane/metadata/android/id/images/phoneScreenshots/4.png
index 022e506a5..f7e145bb2 100644
Binary files a/fastlane/metadata/android/id/images/phoneScreenshots/4.png and b/fastlane/metadata/android/id/images/phoneScreenshots/4.png differ
diff --git a/fastlane/metadata/android/id/images/phoneScreenshots/5.png b/fastlane/metadata/android/id/images/phoneScreenshots/5.png
index 78286da77..a3ae51c2c 100644
Binary files a/fastlane/metadata/android/id/images/phoneScreenshots/5.png and b/fastlane/metadata/android/id/images/phoneScreenshots/5.png differ
diff --git a/fastlane/metadata/android/id/images/phoneScreenshots/6.png b/fastlane/metadata/android/id/images/phoneScreenshots/6.png
index 75ba57bca..a973e9bcf 100644
Binary files a/fastlane/metadata/android/id/images/phoneScreenshots/6.png and b/fastlane/metadata/android/id/images/phoneScreenshots/6.png differ
diff --git a/fastlane/metadata/android/it/images/phoneScreenshots/1.png b/fastlane/metadata/android/it/images/phoneScreenshots/1.png
index ceb69cddb..72c0e69d3 100644
Binary files a/fastlane/metadata/android/it/images/phoneScreenshots/1.png and b/fastlane/metadata/android/it/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/it/images/phoneScreenshots/2.png b/fastlane/metadata/android/it/images/phoneScreenshots/2.png
index 408e48e72..9f291bbca 100644
Binary files a/fastlane/metadata/android/it/images/phoneScreenshots/2.png and b/fastlane/metadata/android/it/images/phoneScreenshots/2.png differ
diff --git a/fastlane/metadata/android/it/images/phoneScreenshots/3.png b/fastlane/metadata/android/it/images/phoneScreenshots/3.png
index e88b88ec0..fb089dd6c 100644
Binary files a/fastlane/metadata/android/it/images/phoneScreenshots/3.png and b/fastlane/metadata/android/it/images/phoneScreenshots/3.png differ
diff --git a/fastlane/metadata/android/it/images/phoneScreenshots/4.png b/fastlane/metadata/android/it/images/phoneScreenshots/4.png
index 91ce48feb..e49333640 100644
Binary files a/fastlane/metadata/android/it/images/phoneScreenshots/4.png and b/fastlane/metadata/android/it/images/phoneScreenshots/4.png differ
diff --git a/fastlane/metadata/android/it/images/phoneScreenshots/5.png b/fastlane/metadata/android/it/images/phoneScreenshots/5.png
index bc7f0e12b..e8d6e3691 100644
Binary files a/fastlane/metadata/android/it/images/phoneScreenshots/5.png and b/fastlane/metadata/android/it/images/phoneScreenshots/5.png differ
diff --git a/fastlane/metadata/android/it/images/phoneScreenshots/6.png b/fastlane/metadata/android/it/images/phoneScreenshots/6.png
index 620873843..0780544c6 100644
Binary files a/fastlane/metadata/android/it/images/phoneScreenshots/6.png and b/fastlane/metadata/android/it/images/phoneScreenshots/6.png differ
diff --git a/fastlane/metadata/android/ja/full_description.txt b/fastlane/metadata/android/ja/full_description.txt
index 2b1431c48..f489d7765 100644
--- a/fastlane/metadata/android/ja/full_description.txt
+++ b/fastlane/metadata/android/ja/full_description.txt
@@ -1,7 +1,7 @@
-Avesはあらゆる画像や動画を扱うことができ、一般的なJPEGやMP4はもちろん、 マルチページTIFF、SVG、古いAVIなどの珍しい形式にも対応しています!
+Avesはあらゆる画像や動画を扱うことができ、一般的なJPEGやMP4はもちろん、 マルチページTIFF、SVG、古いAVIなどの珍しい形式にも対応しています!
-メディアコレクションをスキャンして、モーションフォト、パノラマ(Photo Sphere)、360°動画、GeoTIFFファイルなどを識別します。
+メディアコレクションをスキャンして、モーションフォト、パノラマ(Photo Sphere)、360°動画、GeoTIFFファイルなどを識別します。
-ナビゲーションと検索は、Avesの重要な部分です。アルバムから写真、タグ、地図などへ簡単に移動できます。
+ナビゲーションと検索は、Avesの重要な部分です。アルバムから写真、タグ、地図などへ簡単に移動できます。
Avesは、アプリショートカットやグローバル検索などの機能を、Android(API 19から32まで、つまりAndroid 4.4から12 Lまで)と統合しています。また、メディアビューワーやメディアピッカーとしても機能します。
\ No newline at end of file
diff --git a/fastlane/metadata/android/ja/images/phoneScreenshots/1.png b/fastlane/metadata/android/ja/images/phoneScreenshots/1.png
index b7f99f9c7..727e65483 100644
Binary files a/fastlane/metadata/android/ja/images/phoneScreenshots/1.png and b/fastlane/metadata/android/ja/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/ja/images/phoneScreenshots/2.png b/fastlane/metadata/android/ja/images/phoneScreenshots/2.png
index 8b4364e04..225213380 100644
Binary files a/fastlane/metadata/android/ja/images/phoneScreenshots/2.png and b/fastlane/metadata/android/ja/images/phoneScreenshots/2.png differ
diff --git a/fastlane/metadata/android/ja/images/phoneScreenshots/3.png b/fastlane/metadata/android/ja/images/phoneScreenshots/3.png
index e8b879179..4b85e913a 100644
Binary files a/fastlane/metadata/android/ja/images/phoneScreenshots/3.png and b/fastlane/metadata/android/ja/images/phoneScreenshots/3.png differ
diff --git a/fastlane/metadata/android/ja/images/phoneScreenshots/4.png b/fastlane/metadata/android/ja/images/phoneScreenshots/4.png
index fd39d1ef1..b16fcd1f3 100644
Binary files a/fastlane/metadata/android/ja/images/phoneScreenshots/4.png and b/fastlane/metadata/android/ja/images/phoneScreenshots/4.png differ
diff --git a/fastlane/metadata/android/ja/images/phoneScreenshots/5.png b/fastlane/metadata/android/ja/images/phoneScreenshots/5.png
index 39b6769ea..b57fa9ea2 100644
Binary files a/fastlane/metadata/android/ja/images/phoneScreenshots/5.png and b/fastlane/metadata/android/ja/images/phoneScreenshots/5.png differ
diff --git a/fastlane/metadata/android/ja/images/phoneScreenshots/6.png b/fastlane/metadata/android/ja/images/phoneScreenshots/6.png
index d358bb072..682f00a67 100644
Binary files a/fastlane/metadata/android/ja/images/phoneScreenshots/6.png and b/fastlane/metadata/android/ja/images/phoneScreenshots/6.png differ
diff --git a/fastlane/metadata/android/ko/images/phoneScreenshots/1.png b/fastlane/metadata/android/ko/images/phoneScreenshots/1.png
index ba194324a..941382121 100644
Binary files a/fastlane/metadata/android/ko/images/phoneScreenshots/1.png and b/fastlane/metadata/android/ko/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/ko/images/phoneScreenshots/2.png b/fastlane/metadata/android/ko/images/phoneScreenshots/2.png
index a601e74d5..3f1647e3f 100644
Binary files a/fastlane/metadata/android/ko/images/phoneScreenshots/2.png and b/fastlane/metadata/android/ko/images/phoneScreenshots/2.png differ
diff --git a/fastlane/metadata/android/ko/images/phoneScreenshots/3.png b/fastlane/metadata/android/ko/images/phoneScreenshots/3.png
index 8206e30b9..62b7c19be 100644
Binary files a/fastlane/metadata/android/ko/images/phoneScreenshots/3.png and b/fastlane/metadata/android/ko/images/phoneScreenshots/3.png differ
diff --git a/fastlane/metadata/android/ko/images/phoneScreenshots/4.png b/fastlane/metadata/android/ko/images/phoneScreenshots/4.png
index a089a9252..c80b35c0f 100644
Binary files a/fastlane/metadata/android/ko/images/phoneScreenshots/4.png and b/fastlane/metadata/android/ko/images/phoneScreenshots/4.png differ
diff --git a/fastlane/metadata/android/ko/images/phoneScreenshots/5.png b/fastlane/metadata/android/ko/images/phoneScreenshots/5.png
index 206888b33..f61817ea3 100644
Binary files a/fastlane/metadata/android/ko/images/phoneScreenshots/5.png and b/fastlane/metadata/android/ko/images/phoneScreenshots/5.png differ
diff --git a/fastlane/metadata/android/ko/images/phoneScreenshots/6.png b/fastlane/metadata/android/ko/images/phoneScreenshots/6.png
index 5ad1622f3..e179b2c05 100644
Binary files a/fastlane/metadata/android/ko/images/phoneScreenshots/6.png and b/fastlane/metadata/android/ko/images/phoneScreenshots/6.png differ
diff --git a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/1.png b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/1.png
index 560bcfa3a..d86705a78 100644
Binary files a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/1.png and b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/2.png b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/2.png
index e8abe4129..e2fe95143 100644
Binary files a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/2.png and b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/2.png differ
diff --git a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/3.png b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/3.png
index b28935bfc..cd473cb8f 100644
Binary files a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/3.png and b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/3.png differ
diff --git a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/4.png b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/4.png
index 0e8c26458..833120355 100644
Binary files a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/4.png and b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/4.png differ
diff --git a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/5.png b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/5.png
index 172a48660..bebe66d01 100644
Binary files a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/5.png and b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/5.png differ
diff --git a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/6.png b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/6.png
index 372ddca85..6e25bb7af 100644
Binary files a/fastlane/metadata/android/pt-BR/images/phoneScreenshots/6.png and b/fastlane/metadata/android/pt-BR/images/phoneScreenshots/6.png differ
diff --git a/fastlane/metadata/android/ru/images/phoneScreenshots/1.png b/fastlane/metadata/android/ru/images/phoneScreenshots/1.png
index 2facdbc61..e3aba1e4e 100644
Binary files a/fastlane/metadata/android/ru/images/phoneScreenshots/1.png and b/fastlane/metadata/android/ru/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/ru/images/phoneScreenshots/2.png b/fastlane/metadata/android/ru/images/phoneScreenshots/2.png
index ca1770023..2758b8b4a 100644
Binary files a/fastlane/metadata/android/ru/images/phoneScreenshots/2.png and b/fastlane/metadata/android/ru/images/phoneScreenshots/2.png differ
diff --git a/fastlane/metadata/android/ru/images/phoneScreenshots/3.png b/fastlane/metadata/android/ru/images/phoneScreenshots/3.png
index ac3b92614..f576406e9 100644
Binary files a/fastlane/metadata/android/ru/images/phoneScreenshots/3.png and b/fastlane/metadata/android/ru/images/phoneScreenshots/3.png differ
diff --git a/fastlane/metadata/android/ru/images/phoneScreenshots/4.png b/fastlane/metadata/android/ru/images/phoneScreenshots/4.png
index 4a54f9f2e..b1a478dda 100644
Binary files a/fastlane/metadata/android/ru/images/phoneScreenshots/4.png and b/fastlane/metadata/android/ru/images/phoneScreenshots/4.png differ
diff --git a/fastlane/metadata/android/ru/images/phoneScreenshots/5.png b/fastlane/metadata/android/ru/images/phoneScreenshots/5.png
index 38cb374c4..df6368d83 100644
Binary files a/fastlane/metadata/android/ru/images/phoneScreenshots/5.png and b/fastlane/metadata/android/ru/images/phoneScreenshots/5.png differ
diff --git a/fastlane/metadata/android/ru/images/phoneScreenshots/6.png b/fastlane/metadata/android/ru/images/phoneScreenshots/6.png
index f14b4f9bc..e3454036d 100644
Binary files a/fastlane/metadata/android/ru/images/phoneScreenshots/6.png and b/fastlane/metadata/android/ru/images/phoneScreenshots/6.png differ
diff --git a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/1.png b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/1.png
index 3553a0465..f762e1bbb 100644
Binary files a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/1.png and b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/1.png differ
diff --git a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/2.png b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/2.png
index 82ce94936..297d5e4af 100644
Binary files a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/2.png and b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/2.png differ
diff --git a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/3.png b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/3.png
index c9c49f3ee..b367d768f 100644
Binary files a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/3.png and b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/3.png differ
diff --git a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/4.png b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/4.png
index a5b897d83..ef4ce2758 100644
Binary files a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/4.png and b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/4.png differ
diff --git a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/5.png b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/5.png
index e1942c08e..a29c7d376 100644
Binary files a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/5.png and b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/5.png differ
diff --git a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/6.png b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/6.png
index 7e684f597..9dd4e80de 100644
Binary files a/fastlane/metadata/android/zh-CN/images/phoneScreenshots/6.png and b/fastlane/metadata/android/zh-CN/images/phoneScreenshots/6.png differ
diff --git a/lib/app_flavor.dart b/lib/app_flavor.dart
index 4dfcd54d6..6cd5520ae 100644
--- a/lib/app_flavor.dart
+++ b/lib/app_flavor.dart
@@ -1,5 +1,13 @@
-enum AppFlavor { play, izzy }
+enum AppFlavor { play, huawei, izzy }
extension ExtraAppFlavor on AppFlavor {
- bool get canEnableErrorReporting => this == AppFlavor.play;
+ bool get canEnableErrorReporting {
+ switch (this) {
+ case AppFlavor.play:
+ return true;
+ case AppFlavor.huawei:
+ case AppFlavor.izzy:
+ return false;
+ }
+ }
}
diff --git a/lib/app_mode.dart b/lib/app_mode.dart
index 34f38a48b..70ed305da 100644
--- a/lib/app_mode.dart
+++ b/lib/app_mode.dart
@@ -1,11 +1,13 @@
-enum AppMode { main, pickMediaExternal, pickMediaInternal, pickFilterInternal, view }
+enum AppMode { main, pickSingleMediaExternal, pickMultipleMediaExternal, pickMediaInternal, pickFilterInternal, view }
extension ExtraAppMode on AppMode {
- bool get canSearch => this == AppMode.main || this == AppMode.pickMediaExternal;
+ bool get canSearch => this == AppMode.main || this == AppMode.pickSingleMediaExternal || this == AppMode.pickMultipleMediaExternal;
- bool get canSelect => this == AppMode.main;
+ bool get canSelectMedia => this == AppMode.main || this == AppMode.pickMultipleMediaExternal;
- bool get hasDrawer => this == AppMode.main || this == AppMode.pickMediaExternal;
+ bool get canSelectFilter => this == AppMode.main;
- bool get isPickingMedia => this == AppMode.pickMediaExternal || this == AppMode.pickMediaInternal;
+ bool get hasDrawer => this == AppMode.main || this == AppMode.pickSingleMediaExternal || this == AppMode.pickMultipleMediaExternal;
+
+ bool get isPickingMedia => this == AppMode.pickSingleMediaExternal || this == AppMode.pickMultipleMediaExternal || this == AppMode.pickMediaInternal;
}
diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb
index c6977369b..f2acda38c 100644
--- a/lib/l10n/app_de.arb
+++ b/lib/l10n/app_de.arb
@@ -124,6 +124,8 @@
"mapStyleGoogleNormal": "Google Maps",
"mapStyleGoogleHybrid": "Google Maps (Hybrid)",
"mapStyleGoogleTerrain": "Google Maps (Gelände)",
+ "mapStyleHuaweiNormal": "Petal Maps",
+ "mapStyleHuaweiTerrain": "Petal Maps (Gelände)",
"mapStyleOsmHot": "Humanitäres OSM",
"mapStyleStamenToner": "Stamen Toner (SchwarzWeiß)",
"mapStyleStamenWatercolor": "Stamen Watercolor (Aquarell)",
@@ -406,6 +408,8 @@
"settingsSystemDefault": "System",
"settingsDefault": "Standard",
+ "settingsSearchFieldLabel": "Einstellungen durchsuchen",
+ "settingsSearchEmpty": "Keine passende Einstellung",
"settingsActionExport": "Exportieren",
"settingsActionImport": "Importieren",
@@ -415,6 +419,7 @@
"settingsSectionNavigation": "Navigation",
"settingsHome": "Startseite",
+ "settingsShowBottomNavigationBar": "Untere Navigationsleiste anzeigen",
"settingsKeepScreenOnTile": "Bildschirm eingeschaltet lassen",
"settingsKeepScreenOnTitle": "Bildschirm eingeschaltet lassen",
"settingsDoubleBackExit": "Zum Verlassen zweimal „zurück“ tippen",
@@ -434,7 +439,10 @@
"settingsNavigationDrawerAddAlbum": "Album hinzufügen",
"settingsSectionThumbnails": "Vorschaubilder",
+ "settingsThumbnailOverlayTile": "Überlagerung",
+ "settingsThumbnailOverlayTitle": "Überlagerung",
"settingsThumbnailShowFavouriteIcon": "Favoriten-Symbol anzeigen",
+ "settingsThumbnailShowTagIcon": "Tag-Symbol anzeigen",
"settingsThumbnailShowLocationIcon": "Standort-Symbol anzeigen",
"settingsThumbnailShowMotionPhotoIcon": "Bewegungsfoto-Symbol anzeigen",
"settingsThumbnailShowRating": "Bewertung anzeigen",
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb
index 96359ab8d..0dc847cfa 100644
--- a/lib/l10n/app_en.arb
+++ b/lib/l10n/app_en.arb
@@ -164,6 +164,8 @@
"mapStyleGoogleNormal": "Google Maps",
"mapStyleGoogleHybrid": "Google Maps (Hybrid)",
"mapStyleGoogleTerrain": "Google Maps (Terrain)",
+ "mapStyleHuaweiNormal": "Petal Maps",
+ "mapStyleHuaweiTerrain": "Petal Maps (Terrain)",
"mapStyleOsmHot": "Humanitarian OSM",
"mapStyleStamenToner": "Stamen Toner",
"mapStyleStamenWatercolor": "Stamen Watercolor",
@@ -586,6 +588,8 @@
"settingsSystemDefault": "System",
"settingsDefault": "Default",
+ "settingsSearchFieldLabel": "Search settings",
+ "settingsSearchEmpty": "No matching setting",
"settingsActionExport": "Export",
"settingsActionImport": "Import",
@@ -595,6 +599,7 @@
"settingsSectionNavigation": "Navigation",
"settingsHome": "Home",
+ "settingsShowBottomNavigationBar": "Show bottom navigation bar",
"settingsKeepScreenOnTile": "Keep screen on",
"settingsKeepScreenOnTitle": "Keep Screen On",
"settingsDoubleBackExit": "Tap “back” twice to exit",
@@ -614,7 +619,10 @@
"settingsNavigationDrawerAddAlbum": "Add album",
"settingsSectionThumbnails": "Thumbnails",
+ "settingsThumbnailOverlayTile": "Overlay",
+ "settingsThumbnailOverlayTitle": "Overlay",
"settingsThumbnailShowFavouriteIcon": "Show favorite icon",
+ "settingsThumbnailShowTagIcon": "Show tag icon",
"settingsThumbnailShowLocationIcon": "Show location icon",
"settingsThumbnailShowMotionPhotoIcon": "Show motion photo icon",
"settingsThumbnailShowRating": "Show rating",
@@ -751,7 +759,7 @@
"viewerInfoLabelUri": "URI",
"viewerInfoLabelPath": "Path",
"viewerInfoLabelDuration": "Duration",
- "viewerInfoLabelOwner": "Owned by",
+ "viewerInfoLabelOwner": "Owner",
"viewerInfoLabelCoordinates": "Coordinates",
"viewerInfoLabelAddress": "Address",
diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb
index 78ca559fc..d117a025f 100644
--- a/lib/l10n/app_es.arb
+++ b/lib/l10n/app_es.arb
@@ -58,6 +58,9 @@
"entryActionPrint": "Imprimir",
"entryActionShare": "Compartir",
"entryActionViewSource": "Ver fuente",
+ "entryActionShowGeoTiffOnMap": "Mostrar como mapa superpuesto",
+ "entryActionConvertMotionPhotoToStillImage": "Convertir a imagen fija",
+ "entryActionViewMotionPhotoVideo": "Abrir video",
"entryActionEdit": "Editar",
"entryActionOpen": "Abrir con",
"entryActionSetAs": "Establecer como",
@@ -113,14 +116,16 @@
"videoLoopModeShortOnly": "Sólo videos cortos",
"videoLoopModeAlways": "Siempre",
+ "videoControlsNone": "Ninguno",
"videoControlsPlay": "Reproducir",
"videoControlsPlaySeek": "Reproducir y buscar",
"videoControlsPlayOutside": "Reproducir externamente",
- "videoControlsNone": "Ninguno",
- "mapStyleGoogleNormal": "Mapas de Google",
- "mapStyleGoogleHybrid": "Mapas de Google (Híbrido)",
- "mapStyleGoogleTerrain": "Mapas de Google (Superficie)",
+ "mapStyleGoogleNormal": "Google Maps",
+ "mapStyleGoogleHybrid": "Google Maps (Híbrido)",
+ "mapStyleGoogleTerrain": "Google Maps (Relieve)",
+ "mapStyleHuaweiNormal": "Petal Maps",
+ "mapStyleHuaweiTerrain": "Petal Maps (Relieve)",
"mapStyleOsmHot": "OSM Humanitario",
"mapStyleStamenToner": "Stamen Toner (Monocromático)",
"mapStyleStamenWatercolor": "Stamen Watercolor (Acuarela)",
@@ -184,6 +189,7 @@
"videoResumeButtonLabel": "REANUDAR",
"setCoverDialogLatest": "Elemento más reciente",
+ "setCoverDialogAuto": "Automático",
"setCoverDialogCustom": "Personalizado",
"hideFilterConfirmationDialogMessage": "Fotos y videos que concuerden serán ocultados de su colección. Puede volver a mostrarlos desde los ajustes de «Privacidad».\n\n¿Está seguro de que desea ocultarlos?",
@@ -237,6 +243,7 @@
"removeEntryMetadataDialogMore": "Más",
"removeEntryMetadataMotionPhotoXmpWarningDialogMessage": "XMP es necesario para reproducir la animación de una foto en movimiento.\n\n¿Está seguro de que desea removerlo?",
+ "convertMotionPhotoToStillImageWarningDialogMessage": "¿Está seguro?",
"videoSpeedDialogLabel": "Velocidad de reproducción",
@@ -264,6 +271,14 @@
"tileLayoutGrid": "Cuadrícula",
"tileLayoutList": "Lista",
+ "coverDialogTabCover": "Carátula",
+ "coverDialogTabApp": "Aplicación",
+ "coverDialogTabColor": "Color",
+
+ "appPickDialogTitle": "Escoger aplicación",
+ "appPickDialogNone": "Ninguna",
+
+
"aboutPageTitle": "Acerca de",
"aboutLinkSources": "Fuentes",
"aboutLinkLicense": "Licencia",
@@ -280,7 +295,8 @@
"aboutCredits": "Créditos",
"aboutCreditsWorldAtlas1": "Esta aplicación usa un archivo TopoJSON de",
"aboutCreditsWorldAtlas2": "bajo licencia ISC.",
- "aboutCreditsTranslators": "Traductores",
+ "aboutCreditsTranslators": "Traductores:",
+ "aboutCreditsTranslatorLine": "{language}: {names}",
"aboutLicenses": "Licencias de código abierto",
"aboutLicensesBanner": "Esta aplicación usa los siguientes paquetes y librerías de código abierto.",
@@ -394,6 +410,8 @@
"settingsSystemDefault": "Sistema",
"settingsDefault": "Restablecer",
+ "settingsSearchFieldLabel": "Buscar ajustes",
+ "settingsSearchEmpty": "Sin coincidencias",
"settingsActionExport": "Exportar",
"settingsActionImport": "Importar",
@@ -422,6 +440,8 @@
"settingsNavigationDrawerAddAlbum": "Agregar álbum",
"settingsSectionThumbnails": "Miniaturas",
+ "settingsThumbnailOverlayTile": "Incrustaciones",
+ "settingsThumbnailOverlayTitle": "Incrustaciones",
"settingsThumbnailShowFavouriteIcon": "Mostrar icono de favoritos",
"settingsThumbnailShowLocationIcon": "Mostrar icono de ubicación",
"settingsThumbnailShowMotionPhotoIcon": "Mostrar icono de foto en movimiento",
@@ -456,7 +476,7 @@
"settingsViewerShowInformation": "Mostrar información",
"settingsViewerShowInformationSubtitle": "Mostrar título, fecha, ubicación, etc.",
"settingsViewerShowShootingDetails": "Mostrar detalles de toma",
- "settingsViewerShowOverlayThumbnails": "Mostrar miniaturas",
+ "settingsViewerShowOverlayThumbnails": "Mostrar miniaturas",
"settingsViewerEnableOverlayBlurEffect": "Efecto de difuminado",
"settingsVideoPageTitle": "Ajustes de video",
@@ -466,6 +486,8 @@
"settingsVideoEnableAutoPlay": "Reproducción automática",
"settingsVideoLoopModeTile": "Modo bucle",
"settingsVideoLoopModeTitle": "Modo bucle",
+ "settingsVideoQuickActionsTile": "Acciones rápidas para videos",
+ "settingsVideoQuickActionEditorTitle": "Acciones rápidas",
"settingsSubtitleThemeTile": "Subtítulos",
"settingsSubtitleThemeTitle": "Subtítulos",
diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb
index 574a709b1..de2480249 100644
--- a/lib/l10n/app_fr.arb
+++ b/lib/l10n/app_fr.arb
@@ -124,6 +124,8 @@
"mapStyleGoogleNormal": "Google Maps",
"mapStyleGoogleHybrid": "Google Maps (Satellite)",
"mapStyleGoogleTerrain": "Google Maps (Relief)",
+ "mapStyleHuaweiNormal": "Petal Maps",
+ "mapStyleHuaweiTerrain": "Petal Maps (Relief)",
"mapStyleOsmHot": "OSM Humanitaire",
"mapStyleStamenToner": "Stamen Toner (Monochrome)",
"mapStyleStamenWatercolor": "Stamen Watercolor (Aquarelle)",
@@ -406,6 +408,8 @@
"settingsSystemDefault": "Système",
"settingsDefault": "Par défaut",
+ "settingsSearchFieldLabel": "Recherche de réglages",
+ "settingsSearchEmpty": "Aucun réglage correspondant",
"settingsActionExport": "Exporter",
"settingsActionImport": "Importer",
@@ -415,6 +419,7 @@
"settingsSectionNavigation": "Navigation",
"settingsHome": "Page d’accueil",
+ "settingsShowBottomNavigationBar": "Afficher la barre de navigation",
"settingsKeepScreenOnTile": "Maintenir l’écran allumé",
"settingsKeepScreenOnTitle": "Allumage de l’écran",
"settingsDoubleBackExit": "Presser «\u00A0retour\u00A0» 2 fois pour quitter",
@@ -434,7 +439,10 @@
"settingsNavigationDrawerAddAlbum": "Ajouter un album",
"settingsSectionThumbnails": "Vignettes",
+ "settingsThumbnailOverlayTile": "Incrustations",
+ "settingsThumbnailOverlayTitle": "Incrustations",
"settingsThumbnailShowFavouriteIcon": "Afficher l’icône de favori",
+ "settingsThumbnailShowTagIcon": "Afficher l’icône de libellé",
"settingsThumbnailShowLocationIcon": "Afficher l’icône de lieu",
"settingsThumbnailShowMotionPhotoIcon": "Afficher l’icône de photo animée",
"settingsThumbnailShowRating": "Afficher la notation",
diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb
index 0547da17c..b94a56778 100644
--- a/lib/l10n/app_id.arb
+++ b/lib/l10n/app_id.arb
@@ -17,7 +17,7 @@
"hideButtonLabel": "SEMBUNYIKAN",
"continueButtonLabel": "SELANJUTNYA",
- "cancelTooltip": "Batalkan",
+ "cancelTooltip": "Batal",
"changeTooltip": "Ganti",
"clearTooltip": "Hapus",
"previousTooltip": "Sebelumnya",
@@ -82,7 +82,7 @@
"entryInfoActionEditDate": "Ubah tanggal & waktu",
"entryInfoActionEditLocation": "Ubah lokasi",
- "entryInfoActionEditRating": "Ubah peringkat",
+ "entryInfoActionEditRating": "Ubah nilai",
"entryInfoActionEditTags": "Ubah label",
"entryInfoActionRemoveMetadata": "Hapus metadata",
@@ -90,7 +90,7 @@
"filterFavouriteLabel": "Favorit",
"filterLocationEmptyLabel": "Lokasi yang tidak ditemukan",
"filterTagEmptyLabel": "Tidak dilabel",
- "filterRatingUnratedLabel": "Belum diberi peringkat",
+ "filterRatingUnratedLabel": "Belum diberi nilai",
"filterRatingRejectedLabel": "Ditolak",
"filterTypeAnimatedLabel": "Teranimasi",
"filterTypeMotionPhotoLabel": "Foto bergerak",
@@ -124,6 +124,8 @@
"mapStyleGoogleNormal": "Google Maps",
"mapStyleGoogleHybrid": "Google Maps (Hybrid)",
"mapStyleGoogleTerrain": "Google Maps (Terrain)",
+ "mapStyleHuaweiNormal": "Petal Maps",
+ "mapStyleHuaweiTerrain": "Petal Maps (Terrain)",
"mapStyleOsmHot": "Humanitarian OSM",
"mapStyleStamenToner": "Stamen Toner",
"mapStyleStamenWatercolor": "Stamen Watercolor",
@@ -148,7 +150,7 @@
"albumTierNew": "Baru",
"albumTierPinned": "Disemat",
- "albumTierSpecial": "Umum",
+ "albumTierSpecial": "Biasa",
"albumTierApps": "Aplikasi",
"albumTierRegular": "Lainnya",
@@ -235,7 +237,7 @@
"locationPickerUseThisLocationButton": "Gunakan lokasi ini",
- "editEntryRatingDialogTitle": "Peringkat",
+ "editEntryRatingDialogTitle": "Nilai",
"removeEntryMetadataDialogTitle": "Penghapusan Metadata",
"removeEntryMetadataDialogMore": "Lebih Banyak",
@@ -247,7 +249,7 @@
"videoStreamSelectionDialogVideo": "Video",
"videoStreamSelectionDialogAudio": "Audio",
- "videoStreamSelectionDialogText": "Subtitle",
+ "videoStreamSelectionDialogText": "Subjudul",
"videoStreamSelectionDialogOff": "Mati",
"videoStreamSelectionDialogTrack": "Trek",
"videoStreamSelectionDialogNoSelection": "Tidak ada Trek yang lain.",
@@ -322,7 +324,7 @@
"collectionSortDate": "Lewat tanggal",
"collectionSortSize": "Lewat ukuran",
"collectionSortName": "Lewat nama album & file",
- "collectionSortRating": "Lewat peringkat",
+ "collectionSortRating": "Lewat nilai",
"collectionGroupAlbum": "Lewat album",
"collectionGroupMonth": "Lewat bulan",
@@ -400,12 +402,14 @@
"searchSectionCountries": "Negara",
"searchSectionPlaces": "Tempat",
"searchSectionTags": "Label",
- "searchSectionRating": "Peringkat",
+ "searchSectionRating": "Nilai",
"settingsPageTitle": "Pengaturan",
"settingsSystemDefault": "Sistem",
"settingsDefault": "Default",
+ "settingsSearchFieldLabel": "Cari peraturan",
+ "settingsSearchEmpty": "Tidak ada peraturan yang cocok",
"settingsActionExport": "Ekspor",
"settingsActionImport": "Impor",
@@ -415,6 +419,7 @@
"settingsSectionNavigation": "Navigasi",
"settingsHome": "Beranda",
+ "settingsShowBottomNavigationBar": "Tampilkan bilah navigasi bawah",
"settingsKeepScreenOnTile": "Biarkan layarnya menyala",
"settingsKeepScreenOnTitle": "Biarkan Layarnya Menyala",
"settingsDoubleBackExit": "Ketuk “kembali” dua kali untuk keluar",
@@ -434,10 +439,13 @@
"settingsNavigationDrawerAddAlbum": "Tambahkan album",
"settingsSectionThumbnails": "Thumbnail",
+ "settingsThumbnailOverlayTile": "Hamparan",
+ "settingsThumbnailOverlayTitle": "Hamparan",
"settingsThumbnailShowFavouriteIcon": "Tampilkan ikon favorit",
+ "settingsThumbnailShowTagIcon": "Tampilkan ikon label",
"settingsThumbnailShowLocationIcon": "Tampilkan ikon lokasi",
"settingsThumbnailShowMotionPhotoIcon": "Tampilkan ikon Foto bergerak",
- "settingsThumbnailShowRating": "Tampilkan peringkat",
+ "settingsThumbnailShowRating": "Tampilkan nilai",
"settingsThumbnailShowRawIcon": "Tampilkan ikon raw",
"settingsThumbnailShowVideoDuration": "Tampilkan durasi video",
@@ -479,8 +487,8 @@
"settingsVideoLoopModeTile": "Putar ulang",
"settingsVideoLoopModeTitle": "Putar Ulang",
- "settingsSubtitleThemeTile": "Subtitle",
- "settingsSubtitleThemeTitle": "Subtitle",
+ "settingsSubtitleThemeTile": "Subjudul",
+ "settingsSubtitleThemeTitle": "Subjudul",
"settingsSubtitleThemeSample": "Ini adalah sampel.",
"settingsSubtitleThemeTextAlignmentTile": "Perataan teks",
"settingsSubtitleThemeTextAlignmentTitle": "Perataan Teks",
diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb
index 87a7d1eb4..756339ddd 100644
--- a/lib/l10n/app_it.arb
+++ b/lib/l10n/app_it.arb
@@ -41,7 +41,7 @@
"chipActionGoToTagPage": "Mostra nelle etichette",
"chipActionHide": "Nascondi",
"chipActionPin": "Fissa in alto",
- "chipActionUnpin": "Rimuovi dall'alto",
+ "chipActionUnpin": "Rimuovi dall’alto",
"chipActionRename": "Rinomina",
"chipActionSetCover": "Imposta copertina",
"chipActionCreateAlbum": "Crea album",
@@ -124,6 +124,8 @@
"mapStyleGoogleNormal": "Google Maps",
"mapStyleGoogleHybrid": "Google Maps (Ibrido)",
"mapStyleGoogleTerrain": "Google Maps (Terreno)",
+ "mapStyleHuaweiNormal": "Petal Maps",
+ "mapStyleHuaweiTerrain": "Petal Maps (Terreno)",
"mapStyleOsmHot": "OSM umanitario",
"mapStyleStamenToner": "Stamen Toner (Monocromatico)",
"mapStyleStamenWatercolor": "Stamen Watercolor (Acquerello)",
@@ -193,7 +195,7 @@
"hideFilterConfirmationDialogMessage": "Le foto e i video corrispondenti saranno nascosti dalla tua collezione. Puoi mostrarli di nuovo dalle impostazioni della «Privacy». Sei sicuro di volerli nascondere?",
"newAlbumDialogTitle": "Nuovo album",
- "newAlbumDialogNameLabel": "Nome dell'album",
+ "newAlbumDialogNameLabel": "Nome dell’album",
"newAlbumDialogNameLabelAlreadyExistsHelper": "La cartella esiste già",
"newAlbumDialogStorageLabel": "Archiviazione:",
@@ -219,7 +221,7 @@
"editEntryDateDialogTitle": "Data e ora",
"editEntryDateDialogSetCustom": "Imposta data personalizzata",
- "editEntryDateDialogCopyField": "Copia da un'altra data",
+ "editEntryDateDialogCopyField": "Copia da un’altra data",
"editEntryDateDialogCopyItem": "Copia da un altro elemento",
"editEntryDateDialogExtractFromTitle": "Estrai dal titolo",
"editEntryDateDialogShift": "Turno",
@@ -240,7 +242,7 @@
"removeEntryMetadataDialogTitle": "Rimozione dei metadati",
"removeEntryMetadataDialogMore": "Altro",
- "removeEntryMetadataMotionPhotoXmpWarningDialogMessage": "XMP è richiesto per riprodurre il video all'interno di una foto in movimento.\n\nSei sicuro di volerlo rimuovere?",
+ "removeEntryMetadataMotionPhotoXmpWarningDialogMessage": "XMP è richiesto per riprodurre il video all’interno di una foto in movimento.\n\nSei sicuro di volerlo rimuovere?",
"convertMotionPhotoToStillImageWarningDialogMessage": "Sei sicuro?",
"videoSpeedDialogLabel": "Velocità di riproduzione",
@@ -282,7 +284,7 @@
"aboutLinkPolicy": "Informativa sulla privacy",
"aboutBug": "Segnalazione bug",
- "aboutBugSaveLogInstruction": "Salva i log dell'app in un file",
+ "aboutBugSaveLogInstruction": "Salva i log dell’app in un file",
"aboutBugSaveLogButton": "Salva",
"aboutBugCopyInfoInstruction": "Copia le informazioni di sistema",
"aboutBugCopyInfoButton": "Copia",
@@ -312,8 +314,8 @@
"collectionActionHideTitleSearch": "Nascondi filtro",
"collectionActionAddShortcut": "Aggiungi collegamento",
"collectionActionEmptyBin": "Svuota cestino",
- "collectionActionCopy": "Copia nell'album",
- "collectionActionMove": "Sposta nell'album",
+ "collectionActionCopy": "Copia nell’album",
+ "collectionActionMove": "Sposta nell’album",
"collectionActionRescan": "Riscansiona",
"collectionActionEdit": "Modifica",
@@ -374,8 +376,8 @@
"albumPickPageTitleMove": "Sposta",
"albumPickPageTitlePick": "Seleziona",
- "albumCamera": "Camera",
- "albumDownload": "Download",
+ "albumCamera": "Fotocamera",
+ "albumDownload": "Scaricati",
"albumScreenshots": "Screenshot",
"albumScreenRecordings": "Registrazioni schermo",
"albumVideoCaptures": "Scatti nei video",
@@ -406,6 +408,8 @@
"settingsSystemDefault": "Sistema",
"settingsDefault": "Predefinite",
+ "settingsSearchFieldLabel": "Ricerca impostazioni",
+ "settingsSearchEmpty": "Nessuna impostazione corrispondente",
"settingsActionExport": "Esporta",
"settingsActionImport": "Importa",
@@ -415,6 +419,7 @@
"settingsSectionNavigation": "Navigazione",
"settingsHome": "Pagina iniziale",
+ "settingsShowBottomNavigationBar": "Mostra la barra di navigazione in basso",
"settingsKeepScreenOnTile": "Mantieni acceso lo schermo",
"settingsKeepScreenOnTitle": "Illuminazione schermo",
"settingsDoubleBackExit": "Tocca «indietro» due volte per uscire",
@@ -434,11 +439,14 @@
"settingsNavigationDrawerAddAlbum": "Aggiungi album",
"settingsSectionThumbnails": "Miniature",
- "settingsThumbnailShowFavouriteIcon": "Mostra icona preferita",
- "settingsThumbnailShowLocationIcon": "Mostra l'icona della posizione",
- "settingsThumbnailShowMotionPhotoIcon": "Mostra l'icona della foto in movimento",
+ "settingsThumbnailOverlayTile": "Sovrapposizione",
+ "settingsThumbnailOverlayTitle": "Sovrapposizione",
+ "settingsThumbnailShowFavouriteIcon": "Mostra l’icona Preferiti",
+ "settingsThumbnailShowTagIcon": "Mostra l’icona Etichetta",
+ "settingsThumbnailShowLocationIcon": "Mostra l’icona Posizione",
+ "settingsThumbnailShowMotionPhotoIcon": "Mostra l’icona Foto in movimento",
"settingsThumbnailShowRating": "Mostra la valutazione",
- "settingsThumbnailShowRawIcon": "Mostra icona raw",
+ "settingsThumbnailShowRawIcon": "Mostra icona Raw",
"settingsThumbnailShowVideoDuration": "Mostra la durata del video",
"settingsCollectionQuickActionsTile": "Azioni rapide",
@@ -463,7 +471,7 @@
"settingsViewerOverlayTile": "Sovrapposizione",
"settingsViewerOverlayTitle": "Sovrapposizione",
- "settingsViewerShowOverlayOnOpening": "Mostra all'apertura",
+ "settingsViewerShowOverlayOnOpening": "Mostra all’apertura",
"settingsViewerShowMinimap": "Mostra la minimappa",
"settingsViewerShowInformation": "Mostra informazioni",
"settingsViewerShowInformationSubtitle": "Mostra titolo, data, posizione, ecc.",
@@ -502,7 +510,7 @@
"settingsVideoGestureSideDoubleTapSeek": "Doppio tocco sui bordi dello schermo per cercare avanti/indietro",
"settingsSectionPrivacy": "Privacy",
- "settingsAllowInstalledAppAccess": "Consentire l'accesso all'inventario delle app",
+ "settingsAllowInstalledAppAccess": "Consentire l’accesso all’inventario delle app",
"settingsAllowInstalledAppAccessSubtitle": "Utilizzato per migliorare la visualizzazione degli album",
"settingsAllowErrorReporting": "Consenti segnalazione anonima degli errori",
"settingsSaveSearchHistory": "Salva la cronologia delle ricerche",
@@ -572,15 +580,15 @@
"mapStyleTitle": "Stile Mappa",
"mapStyleTooltip": "Seleziona lo stile della mappa",
- "mapZoomInTooltip": "Zoom in",
- "mapZoomOutTooltip": "Zoom out",
- "mapPointNorthUpTooltip": "Punta a nord verso l'alto",
+ "mapZoomInTooltip": "Ingrandisci",
+ "mapZoomOutTooltip": "Riduci",
+ "mapPointNorthUpTooltip": "Punta a nord verso l’alto",
"mapAttributionOsmHot": "Dati della mappa © collaboratori di [OpenStreetMap](https://www.openstreetmap.org/copyright) - Titoli di [HOT](https://www.hotosm.org/) - Ospitato da [OSM France](https://openstreetmap.fr/)",
"mapAttributionStamen": "Dati della mappa © collaboratori di [OpenStreetMap](https://www.openstreetmap.org/copyright) - Titoli di [Stamen Design](http://stamen.com), [CC BY 3.0](http://creativecommons.org/licenses/by/3.0)",
"openMapPageTooltip": "Visualizza sulla pagina della mappa",
"mapEmptyRegion": "Nessuna immagine in questa regione",
- "viewerInfoOpenEmbeddedFailureFeedback": "Fallita l'estrazione dei dati incorporati",
+ "viewerInfoOpenEmbeddedFailureFeedback": "Fallita l’estrazione dei dati incorporati",
"viewerInfoOpenLinkText": "Apri",
"viewerInfoViewXmlLinkText": "Visualizza XML",
diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb
index cebf8e16a..268aa39e9 100644
--- a/lib/l10n/app_ja.arb
+++ b/lib/l10n/app_ja.arb
@@ -58,6 +58,9 @@
"entryActionPrint": "印刷",
"entryActionShare": "共有",
"entryActionViewSource": "ソースを表示",
+ "entryActionShowGeoTiffOnMap": "地図のオーバーレイとして表示",
+ "entryActionConvertMotionPhotoToStillImage": "静止画に変換",
+ "entryActionViewMotionPhotoVideo": "動画を開く",
"entryActionEdit": "編集",
"entryActionOpen": "アプリで開く",
"entryActionSetAs": "登録",
@@ -88,7 +91,7 @@
"filterLocationEmptyLabel": "位置情報なし",
"filterTagEmptyLabel": "タグ情報なし",
"filterRatingUnratedLabel": "評価情報なし",
- "filterRatingRejectedLabel": "拒否されました",
+ "filterRatingRejectedLabel": "拒否",
"filterTypeAnimatedLabel": "アニメーション",
"filterTypeMotionPhotoLabel": "モーションフォト",
"filterTypePanoramaLabel": "パノラマ",
@@ -121,6 +124,8 @@
"mapStyleGoogleNormal": "Google マップ",
"mapStyleGoogleHybrid": "Google マップ(ハイブリッド)",
"mapStyleGoogleTerrain": "Google マップ(地形)",
+ "mapStyleHuaweiNormal": "Petal マップ",
+ "mapStyleHuaweiTerrain": "Petal マップ(地形)",
"mapStyleOsmHot": "Humanitarian OSM",
"mapStyleStamenToner": "Stamen Toner",
"mapStyleStamenWatercolor": "Stamen Watercolor",
@@ -184,6 +189,7 @@
"videoResumeButtonLabel": "再開",
"setCoverDialogLatest": "最新のアイテム",
+ "setCoverDialogAuto": "自動",
"setCoverDialogCustom": "カスタム",
"hideFilterConfirmationDialogMessage": "一致する写真と動画はコレクションに表示されなくなります。「プライバシー」設定からいつでもアイテムを表示できます。\n\nこれらのアイテムを非表示にしますか?",
@@ -237,6 +243,7 @@
"removeEntryMetadataDialogMore": "もっと見る",
"removeEntryMetadataMotionPhotoXmpWarningDialogMessage": "モーションフォト内の動画を再生するには XMP が必要です。\n\n削除しますか?",
+ "convertMotionPhotoToStillImageWarningDialogMessage": "適用しますか?",
"videoSpeedDialogLabel": "再生速度",
@@ -264,6 +271,13 @@
"tileLayoutGrid": "グリッド表示",
"tileLayoutList": "リスト表示",
+ "coverDialogTabCover": "カバー",
+ "coverDialogTabApp": "アプリ",
+ "coverDialogTabColor": "カラー",
+
+ "appPickDialogTitle": "アプリを選ぶ",
+ "appPickDialogNone": "なし",
+
"aboutPageTitle": "アプリについて",
"aboutLinkSources": "ソース",
"aboutLinkLicense": "ライセンス",
@@ -394,6 +408,8 @@
"settingsSystemDefault": "システム",
"settingsDefault": "デフォルト",
+ "settingsSearchFieldLabel": "検索設定",
+ "settingsSearchEmpty": "一致する設定なし",
"settingsActionExport": "エクスポート",
"settingsActionImport": "インポート",
@@ -403,6 +419,7 @@
"settingsSectionNavigation": "ナビゲーション",
"settingsHome": "ホーム",
+ "settingsShowBottomNavigationBar": "下部のナビゲーションバーを表示",
"settingsKeepScreenOnTile": "画面をオンのままにする",
"settingsKeepScreenOnTitle": "画面をオンのままにする",
"settingsDoubleBackExit": "「戻る」を2回タップして終了",
@@ -422,7 +439,10 @@
"settingsNavigationDrawerAddAlbum": "アルバムを追加",
"settingsSectionThumbnails": "サムネイル",
+ "settingsThumbnailOverlayTile": "オーバーレイ",
+ "settingsThumbnailOverlayTitle": "オーバーレイ",
"settingsThumbnailShowFavouriteIcon": "お気に入りアイコンを表示",
+ "settingsThumbnailShowTagIcon": "タグアイコンを表示",
"settingsThumbnailShowLocationIcon": "位置情報アイコンを表示",
"settingsThumbnailShowMotionPhotoIcon": "モーションフォトアイコンを表示",
"settingsThumbnailShowRating": "評価情報を表示",
diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb
index 6487b8352..57658eff1 100644
--- a/lib/l10n/app_ko.arb
+++ b/lib/l10n/app_ko.arb
@@ -121,9 +121,11 @@
"videoControlsPlayOutside": "다른 앱에서 열기",
"videoControlsNone": "없음",
- "mapStyleGoogleNormal": "구글 지도",
- "mapStyleGoogleHybrid": "구글 지도 (위성)",
- "mapStyleGoogleTerrain": "구글 지도 (지형)",
+ "mapStyleGoogleNormal": "Google 지도",
+ "mapStyleGoogleHybrid": "Google 지도 (위성)",
+ "mapStyleGoogleTerrain": "Google 지도 (지형)",
+ "mapStyleHuaweiNormal": "Petal 지도",
+ "mapStyleHuaweiTerrain": "Petal 지도 (지형)",
"mapStyleOsmHot": "Humanitarian OSM",
"mapStyleStamenToner": "Stamen Toner (토너)",
"mapStyleStamenWatercolor": "Stamen Watercolor (수채화)",
@@ -406,6 +408,8 @@
"settingsSystemDefault": "시스템",
"settingsDefault": "기본",
+ "settingsSearchFieldLabel": "설정 검색",
+ "settingsSearchEmpty": "결과가 없습니다",
"settingsActionExport": "내보내기",
"settingsActionImport": "가져오기",
@@ -415,6 +419,7 @@
"settingsSectionNavigation": "탐색",
"settingsHome": "홈",
+ "settingsShowBottomNavigationBar": "하단 탐색 모음 표시",
"settingsKeepScreenOnTile": "화면 자동 꺼짐 방지",
"settingsKeepScreenOnTitle": "화면 자동 꺼짐 방지",
"settingsDoubleBackExit": "뒤로가기 두번 눌러 앱 종료하기",
@@ -434,7 +439,10 @@
"settingsNavigationDrawerAddAlbum": "앨범 추가",
"settingsSectionThumbnails": "섬네일",
+ "settingsThumbnailOverlayTile": "오버레이",
+ "settingsThumbnailOverlayTitle": "오버레이",
"settingsThumbnailShowFavouriteIcon": "즐겨찾기 아이콘 표시",
+ "settingsThumbnailShowTagIcon": "태그 아이콘 표시",
"settingsThumbnailShowLocationIcon": "위치 아이콘 표시",
"settingsThumbnailShowMotionPhotoIcon": "모션 사진 아이콘 표시",
"settingsThumbnailShowRating": "별점 표시",
diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb
index abe1d5345..0bae2a926 100644
--- a/lib/l10n/app_pt.arb
+++ b/lib/l10n/app_pt.arb
@@ -124,6 +124,8 @@
"mapStyleGoogleNormal": "Google Maps",
"mapStyleGoogleHybrid": "Google Maps (Híbrido)",
"mapStyleGoogleTerrain": "Google Maps (Terreno)",
+ "mapStyleHuaweiNormal": "Petal Maps",
+ "mapStyleHuaweiTerrain": "Petal Maps (Terreno)",
"mapStyleOsmHot": "OSM Humanitário",
"mapStyleStamenToner": "Stamen Toner (Monocromático)",
"mapStyleStamenWatercolor": "Stamen Watercolor (Aquarela)",
@@ -406,6 +408,8 @@
"settingsSystemDefault": "Sistema",
"settingsDefault": "Padrão",
+ "settingsSearchFieldLabel": "Pesquisar configuração",
+ "settingsSearchEmpty": "Nenhuma configuração correspondente",
"settingsActionExport": "Exportar",
"settingsActionImport": "Importar",
@@ -415,6 +419,7 @@
"settingsSectionNavigation": "Navegação",
"settingsHome": "Início",
+ "settingsShowBottomNavigationBar": "Mostrar barra de navegação inferior",
"settingsKeepScreenOnTile": "Manter a tela ligada",
"settingsKeepScreenOnTitle": "Manter a tela ligada",
"settingsDoubleBackExit": "Toque em “voltar” duas vezes para sair",
@@ -434,7 +439,10 @@
"settingsNavigationDrawerAddAlbum": "Adicionar álbum",
"settingsSectionThumbnails": "Miniaturas",
+ "settingsThumbnailOverlayTile": "Sobreposição",
+ "settingsThumbnailOverlayTitle": "Sobreposição",
"settingsThumbnailShowFavouriteIcon": "Mostrar ícone favorito",
+ "settingsThumbnailShowTagIcon": "Mostrar ícone de etiqueta",
"settingsThumbnailShowLocationIcon": "Mostrar ícone de localização",
"settingsThumbnailShowMotionPhotoIcon": "Mostrar ícone de foto em movimento",
"settingsThumbnailShowRating": "Mostrar classificação",
diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb
index 5f3e9aee7..024d7b1ea 100644
--- a/lib/l10n/app_ru.arb
+++ b/lib/l10n/app_ru.arb
@@ -124,6 +124,8 @@
"mapStyleGoogleNormal": "Google Карты",
"mapStyleGoogleHybrid": "Google Карты (Гибридный)",
"mapStyleGoogleTerrain": "Google Карты (Местность)",
+ "mapStyleHuaweiNormal": "Petal Карты",
+ "mapStyleHuaweiTerrain": "Petal Карты (Местность)",
"mapStyleOsmHot": "Команда гуманитарной картопомощи",
"mapStyleStamenToner": "Stamen Toner",
"mapStyleStamenWatercolor": "Stamen Watercolor",
@@ -434,6 +436,8 @@
"settingsNavigationDrawerAddAlbum": "Добавить альбом",
"settingsSectionThumbnails": "Эскизы",
+ "settingsThumbnailOverlayTile": "Наложение",
+ "settingsThumbnailOverlayTitle": "Наложение",
"settingsThumbnailShowFavouriteIcon": "Показать значок избранного",
"settingsThumbnailShowLocationIcon": "Показать значок местоположения",
"settingsThumbnailShowMotionPhotoIcon": "Показать значок «живого фото»",
diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb
index f08d3ad1f..68503af3f 100644
--- a/lib/l10n/app_zh.arb
+++ b/lib/l10n/app_zh.arb
@@ -121,9 +121,11 @@
"videoControlsPlayOutside": "用其他播放器打开",
"videoControlsNone": "无",
- "mapStyleGoogleNormal": "Google Maps",
- "mapStyleGoogleHybrid": "Google Maps (Hybrid)",
- "mapStyleGoogleTerrain": "Google Maps (Terrain)",
+ "mapStyleGoogleNormal": "Google 地图",
+ "mapStyleGoogleHybrid": "Google 地图 (卫星图像)",
+ "mapStyleGoogleTerrain": "Google 地图 (地形)",
+ "mapStyleHuaweiNormal": "Petal 地图",
+ "mapStyleHuaweiTerrain": "Petal 地图 (地形)",
"mapStyleOsmHot": "Humanitarian OSM",
"mapStyleStamenToner": "Stamen Toner",
"mapStyleStamenWatercolor": "Stamen Watercolor",
@@ -406,6 +408,8 @@
"settingsSystemDefault": "系统",
"settingsDefault": "默认",
+ "settingsSearchFieldLabel": "搜索设置",
+ "settingsSearchEmpty": "无匹配设置项",
"settingsActionExport": "导出",
"settingsActionImport": "导入",
@@ -415,6 +419,7 @@
"settingsSectionNavigation": "导航",
"settingsHome": "主页",
+ "settingsShowBottomNavigationBar": "显示底部导航栏",
"settingsKeepScreenOnTile": "保持亮屏",
"settingsKeepScreenOnTitle": "保持亮屏",
"settingsDoubleBackExit": "按两次返回键退出",
@@ -434,7 +439,10 @@
"settingsNavigationDrawerAddAlbum": "添加相册",
"settingsSectionThumbnails": "缩略图",
+ "settingsThumbnailOverlayTile": "叠加层",
+ "settingsThumbnailOverlayTitle": "叠加层",
"settingsThumbnailShowFavouriteIcon": "显示收藏图标",
+ "settingsThumbnailShowTagIcon": "显示标签图标",
"settingsThumbnailShowLocationIcon": "显示位置图标",
"settingsThumbnailShowMotionPhotoIcon": "显示动态照片图标",
"settingsThumbnailShowRating": "显示评分",
diff --git a/lib/main_huawei.dart b/lib/main_huawei.dart
new file mode 100644
index 000000000..6475eb310
--- /dev/null
+++ b/lib/main_huawei.dart
@@ -0,0 +1,6 @@
+import 'package:aves/app_flavor.dart';
+import 'package:aves/main_common.dart';
+
+void main() {
+ mainCommon(AppFlavor.huawei);
+}
diff --git a/lib/model/availability.dart b/lib/model/availability.dart
index bdbb691fd..fb5446d6e 100644
--- a/lib/model/availability.dart
+++ b/lib/model/availability.dart
@@ -1,22 +1,21 @@
-import 'package:aves/model/device.dart';
+import 'package:aves/model/settings/enums/map_style.dart';
+import 'package:aves/services/common/services.dart';
+import 'package:aves_map/aves_map.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:flutter/foundation.dart';
-import 'package:google_api_availability/google_api_availability.dart';
abstract class AvesAvailability {
void onResume();
Future get isConnected;
- Future get hasPlayServices;
-
Future get canLocatePlaces;
- Future get canUseGoogleMaps;
+ List get mapStyles;
}
class LiveAvesAvailability implements AvesAvailability {
- bool? _isConnected, _hasPlayServices;
+ bool? _isConnected;
LiveAvesAvailability() {
Connectivity().onConnectivityChanged.listen(_updateConnectivityFromResult);
@@ -41,19 +40,14 @@ class LiveAvesAvailability implements AvesAvailability {
}
}
+ // local geocoding with `geocoder` seems to require Google Play Services
+ // what about devices with Huawei Mobile Services?
@override
- Future get hasPlayServices async {
- if (_hasPlayServices != null) return SynchronousFuture(_hasPlayServices!);
- final result = await GoogleApiAvailability.instance.checkGooglePlayServicesAvailability();
- _hasPlayServices = result == GooglePlayServicesAvailability.success;
- debugPrint('Device has Play Services=$_hasPlayServices');
- return _hasPlayServices!;
- }
-
- // local geocoding with `geocoder` requires Play Services
- @override
- Future get canLocatePlaces => Future.wait([isConnected, hasPlayServices]).then((results) => results.every((result) => result));
+ Future get canLocatePlaces async => mobileServices.isServiceAvailable && await isConnected;
@override
- Future get canUseGoogleMaps async => device.canRenderGoogleMaps && await hasPlayServices;
+ List get mapStyles => [
+ ...mobileServices.mapStyles,
+ ...EntryMapStyle.values.where((v) => !v.needMobileService),
+ ];
}
diff --git a/lib/model/db/db_metadata_sqflite.dart b/lib/model/db/db_metadata_sqflite.dart
index 854be034d..1acb8b5b1 100644
--- a/lib/model/db/db_metadata_sqflite.dart
+++ b/lib/model/db/db_metadata_sqflite.dart
@@ -220,8 +220,8 @@ class SqfliteMetadataDb implements MetadataDb {
Future> searchLiveEntries(String query, {int? limit}) async {
final rows = await _db.query(
entryTable,
- where: 'title LIKE ? AND trashed = ?',
- whereArgs: ['%$query%', 0],
+ where: '(title LIKE ? OR path LIKE ?) AND trashed = ?',
+ whereArgs: ['%$query%', '%$query%', 0],
orderBy: 'sourceDateTakenMillis DESC',
limit: limit,
);
diff --git a/lib/model/device.dart b/lib/model/device.dart
index e26583f61..ab288f972 100644
--- a/lib/model/device.dart
+++ b/lib/model/device.dart
@@ -5,7 +5,7 @@ final Device device = Device._private();
class Device {
late final String _userAgent;
- late final bool _canGrantDirectoryAccess, _canPinShortcut, _canPrint, _canRenderFlagEmojis, _canRenderGoogleMaps;
+ late final bool _canGrantDirectoryAccess, _canPinShortcut, _canPrint, _canRenderFlagEmojis;
late final bool _showPinShortcutFeedback, _supportEdgeToEdgeUIMode;
String get userAgent => _userAgent;
@@ -18,8 +18,6 @@ class Device {
bool get canRenderFlagEmojis => _canRenderFlagEmojis;
- bool get canRenderGoogleMaps => _canRenderGoogleMaps;
-
bool get showPinShortcutFeedback => _showPinShortcutFeedback;
bool get supportEdgeToEdgeUIMode => _supportEdgeToEdgeUIMode;
@@ -35,7 +33,6 @@ class Device {
_canPinShortcut = capabilities['canPinShortcut'] ?? false;
_canPrint = capabilities['canPrint'] ?? false;
_canRenderFlagEmojis = capabilities['canRenderFlagEmojis'] ?? false;
- _canRenderGoogleMaps = capabilities['canRenderGoogleMaps'] ?? false;
_showPinShortcutFeedback = capabilities['showPinShortcutFeedback'] ?? false;
_supportEdgeToEdgeUIMode = capabilities['supportEdgeToEdgeUIMode'] ?? false;
}
diff --git a/lib/model/entry.dart b/lib/model/entry.dart
index 262d8d24c..7e9d89805 100644
--- a/lib/model/entry.dart
+++ b/lib/model/entry.dart
@@ -584,7 +584,7 @@ class AvesEntry {
: call());
if (addresses.isNotEmpty) {
final address = addresses.first;
- final cc = address.countryCode;
+ final cc = address.countryCode?.toUpperCase();
final cn = address.countryName;
final aa = address.adminArea;
addressDetails = AddressDetails(
diff --git a/lib/model/entry_images.dart b/lib/model/entry_images.dart
index c10467f77..2edd131ce 100644
--- a/lib/model/entry_images.dart
+++ b/lib/model/entry_images.dart
@@ -55,7 +55,7 @@ extension ExtraAvesEntryImages on AvesEntry {
expectedContentLength: sizeBytes,
);
- bool _isReady(Object providerKey) => imageCache!.statusForKey(providerKey).keepAlive;
+ bool _isReady(Object providerKey) => imageCache.statusForKey(providerKey).keepAlive;
List get cachedThumbnails => EntryCache.thumbnailRequestExtents.map(_getThumbnailProviderKey).where(_isReady).map(ThumbnailProvider.new).toList();
diff --git a/lib/model/entry_metadata_edition.dart b/lib/model/entry_metadata_edition.dart
index 33ae00229..a1f8bf829 100644
--- a/lib/model/entry_metadata_edition.dart
+++ b/lib/model/entry_metadata_edition.dart
@@ -22,7 +22,9 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
final appliedModifier = await _applyDateModifierToEntry(userModifier);
if (appliedModifier == null) {
- await reportService.recordError('failed to get date for modifier=$userModifier, entry=$this', null);
+ if (!isMissingAtPath) {
+ await reportService.recordError('failed to get date for modifier=$userModifier, entry=$this', null);
+ }
return {};
}
@@ -374,7 +376,12 @@ extension ExtraAvesEntryMetadataEdition on AvesEntry {
switch (source) {
case DateFieldSource.fileModifiedDate:
try {
- date = path != null ? await File(path!).lastModified() : null;
+ if (path != null) {
+ final file = File(path!);
+ if (await file.exists()) {
+ date = await file.lastModified();
+ }
+ }
} on FileSystemException catch (_) {}
break;
default:
diff --git a/lib/model/filters/coordinate.dart b/lib/model/filters/coordinate.dart
index 90664b8c0..5b42c7e41 100644
--- a/lib/model/filters/coordinate.dart
+++ b/lib/model/filters/coordinate.dart
@@ -4,8 +4,8 @@ import 'package:aves/model/settings/enums/coordinate_format.dart';
import 'package:aves/model/settings/enums/enums.dart';
import 'package:aves/model/settings/settings.dart';
import 'package:aves/theme/icons.dart';
-import 'package:aves/utils/geo_utils.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
+import 'package:aves_map/aves_map.dart';
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:latlong2/latlong.dart';
diff --git a/lib/model/filters/location.dart b/lib/model/filters/location.dart
index 9bdca8654..17a94a947 100644
--- a/lib/model/filters/location.dart
+++ b/lib/model/filters/location.dart
@@ -84,7 +84,7 @@ class LocationFilter extends CoveredCollectionFilter {
static String? countryCodeToFlag(String? code) {
if (code == null || code.length != 2) return null;
- return String.fromCharCodes(code.codeUnits.map((letter) => letter += _countryCodeToFlagDiff));
+ return String.fromCharCodes(code.toUpperCase().codeUnits.map((letter) => letter += _countryCodeToFlagDiff));
}
}
diff --git a/lib/model/geotiff.dart b/lib/model/geotiff.dart
index f86c2a909..dc3ab8deb 100644
--- a/lib/model/geotiff.dart
+++ b/lib/model/geotiff.dart
@@ -5,9 +5,8 @@ import 'dart:ui' as ui;
import 'package:aves/model/entry.dart';
import 'package:aves/model/entry_images.dart';
import 'package:aves/ref/geotiff.dart';
-import 'package:aves/utils/geo_utils.dart';
import 'package:aves/utils/math_utils.dart';
-import 'package:aves/widgets/common/map/tile.dart';
+import 'package:aves_map/aves_map.dart';
import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:latlong2/latlong.dart';
@@ -40,7 +39,7 @@ class GeoTiffInfo extends Equatable {
}
}
-class MappedGeoTiff {
+class MappedGeoTiff with MapOverlay {
final AvesEntry entry;
late LatLng? Function(Point pixel) pointToLatLng;
late Point? Function(Point smPoint) epsg3857ToPoint;
@@ -129,6 +128,7 @@ class MappedGeoTiff {
};
}
+ @override
Future getTile(int tx, int ty, int? zoomLevel) async {
zoomLevel ??= 0;
@@ -217,15 +217,24 @@ class MappedGeoTiff {
);
}
+ @override
+ String get id => entry.uri;
+
+ @override
+ ImageProvider get imageProvider => entry.uriImage;
+
int get width => entry.width;
int get height => entry.height;
+ @override
bool get canOverlay => center != null;
LatLng? get center => pointToLatLng(Point((width / 2).round(), (height / 2).round()));
+ @override
LatLng? get topLeft => pointToLatLng(const Point(0, 0));
+ @override
LatLng? get bottomRight => pointToLatLng(Point(width, height));
}
diff --git a/lib/model/selection.dart b/lib/model/selection.dart
index 2d19227ed..f26d0c4d9 100644
--- a/lib/model/selection.dart
+++ b/lib/model/selection.dart
@@ -11,13 +11,15 @@ class Selection extends ChangeNotifier {
void browse() {
if (!_isSelecting) return;
- clearSelection();
_isSelecting = false;
notifyListeners();
}
void select() {
if (_isSelecting) return;
+ // clear selection on `select`, not on `browse`, so that
+ // the selection count is stable when transitioning to browse
+ clearSelection();
_isSelecting = true;
notifyListeners();
}
@@ -42,7 +44,7 @@ class Selection extends ChangeNotifier {
}
void toggleSelection(T item) {
- if (_selectedItems.isEmpty) select();
+ if (!_isSelecting) select();
if (!_selectedItems.remove(item)) _selectedItems.add(item);
notifyListeners();
}
diff --git a/lib/model/settings/defaults.dart b/lib/model/settings/defaults.dart
index 21d5baddd..ac2fdfc35 100644
--- a/lib/model/settings/defaults.dart
+++ b/lib/model/settings/defaults.dart
@@ -8,6 +8,7 @@ import 'package:aves/model/source/enums.dart';
import 'package:aves/widgets/filter_grids/albums_page.dart';
import 'package:aves/widgets/filter_grids/countries_page.dart';
import 'package:aves/widgets/filter_grids/tags_page.dart';
+import 'package:aves_map/aves_map.dart';
import 'package:flutter/material.dart';
class SettingsDefaults {
@@ -26,6 +27,7 @@ class SettingsDefaults {
static const mustBackTwiceToExit = true;
static const keepScreenOn = KeepScreenOn.viewerOnly;
static const homePage = HomePageSetting.collection;
+ static const showBottomNavigationBar = true;
static const confirmDeleteForever = true;
static const confirmMoveToBin = true;
static const confirmMoveUndatedItems = true;
@@ -52,6 +54,7 @@ class SettingsDefaults {
EntrySetAction.delete,
];
static const showThumbnailFavourite = true;
+ static const showThumbnailTag = false;
static const showThumbnailLocation = true;
static const showThumbnailMotionPhoto = true;
static const showThumbnailRating = true;
diff --git a/lib/model/settings/enums/accessibility_timeout.dart b/lib/model/settings/enums/accessibility_timeout.dart
index b5ef55d78..d9b9f1ebd 100644
--- a/lib/model/settings/enums/accessibility_timeout.dart
+++ b/lib/model/settings/enums/accessibility_timeout.dart
@@ -10,6 +10,8 @@ extension ExtraAccessibilityTimeout on AccessibilityTimeout {
return context.l10n.settingsSystemDefault;
case AccessibilityTimeout.appDefault:
return context.l10n.settingsDefault;
+ case AccessibilityTimeout.s3:
+ return context.l10n.timeSeconds(3);
case AccessibilityTimeout.s10:
return context.l10n.timeSeconds(10);
case AccessibilityTimeout.s30:
diff --git a/lib/model/settings/enums/coordinate_format.dart b/lib/model/settings/enums/coordinate_format.dart
index 1ff17855f..4b3b2ffd9 100644
--- a/lib/model/settings/enums/coordinate_format.dart
+++ b/lib/model/settings/enums/coordinate_format.dart
@@ -1,5 +1,4 @@
import 'package:aves/l10n/l10n.dart';
-import 'package:aves/utils/geo_utils.dart';
import 'package:aves/widgets/common/extensions/build_context.dart';
import 'package:flutter/widgets.dart';
import 'package:intl/intl.dart';
@@ -33,14 +32,32 @@ extension ExtraCoordinateFormat on CoordinateFormat {
final locale = l10n.localeName;
final lat = latLng.latitude;
final lng = latLng.longitude;
- final latSexa = GeoUtils.decimal2sexagesimal(lat, minuteSecondPadding, secondDecimals, locale);
- final lngSexa = GeoUtils.decimal2sexagesimal(lng, minuteSecondPadding, secondDecimals, locale);
+ final latSexa = _decimal2sexagesimal(lat, minuteSecondPadding, secondDecimals, locale);
+ final lngSexa = _decimal2sexagesimal(lng, minuteSecondPadding, secondDecimals, locale);
return [
l10n.coordinateDms(latSexa, lat < 0 ? l10n.coordinateDmsSouth : l10n.coordinateDmsNorth),
l10n.coordinateDms(lngSexa, lng < 0 ? l10n.coordinateDmsWest : l10n.coordinateDmsEast),
];
}
+ static String _decimal2sexagesimal(
+ double degDecimal,
+ bool minuteSecondPadding,
+ int secondDecimals,
+ String locale,
+ ) {
+ final degAbs = degDecimal.abs();
+ final deg = degAbs.toInt();
+ final minDecimal = (degAbs - deg) * 60;
+ final min = minDecimal.toInt();
+ final sec = (minDecimal - min) * 60;
+
+ var minText = NumberFormat('0' * (minuteSecondPadding ? 2 : 1), locale).format(min);
+ var secText = NumberFormat('${'0' * (minuteSecondPadding ? 2 : 1)}${secondDecimals > 0 ? '.${'0' * secondDecimals}' : ''}', locale).format(sec);
+
+ return '$deg° $minText′ $secText″';
+ }
+
static List _toDecimal(AppLocalizations l10n, LatLng latLng) {
final locale = l10n.localeName;
final formatter = NumberFormat('0.000000°', locale);
diff --git a/lib/model/settings/enums/enums.dart b/lib/model/settings/enums/enums.dart
index 461541d2c..6ec4c8714 100644
--- a/lib/model/settings/enums/enums.dart
+++ b/lib/model/settings/enums/enums.dart
@@ -1,6 +1,6 @@
enum AccessibilityAnimations { system, disabled, enabled }
-enum AccessibilityTimeout { system, appDefault, s10, s30, s60, s120 }
+enum AccessibilityTimeout { system, appDefault, s3, s10, s30, s60, s120 }
enum AvesThemeColorMode { monochrome, polychrome }
@@ -12,9 +12,6 @@ enum CoordinateFormat { dms, decimal }
enum EntryBackground { black, white, checkered }
-// browse providers at https://leaflet-extras.github.io/leaflet-providers/preview/
-enum EntryMapStyle { googleNormal, googleHybrid, googleTerrain, osmHot, stamenToner, stamenWatercolor }
-
enum HomePageSetting { collection, albums }
enum KeepScreenOn { never, viewerOnly, always }
diff --git a/lib/model/settings/enums/map_style.dart b/lib/model/settings/enums/map_style.dart
index fa5f8eedb..3bed08e31 100644
--- a/lib/model/settings/enums/map_style.dart
+++ b/lib/model/settings/enums/map_style.dart
@@ -1,8 +1,7 @@
import 'package:aves/widgets/common/extensions/build_context.dart';
+import 'package:aves_map/aves_map.dart';
import 'package:flutter/widgets.dart';
-import 'enums.dart';
-
extension ExtraEntryMapStyle on EntryMapStyle {
String getName(BuildContext context) {
switch (this) {
@@ -12,6 +11,10 @@ extension ExtraEntryMapStyle on EntryMapStyle {
return context.l10n.mapStyleGoogleHybrid;
case EntryMapStyle.googleTerrain:
return context.l10n.mapStyleGoogleTerrain;
+ case EntryMapStyle.hmsNormal:
+ return context.l10n.mapStyleHuaweiNormal;
+ case EntryMapStyle.hmsTerrain:
+ return context.l10n.mapStyleHuaweiTerrain;
case EntryMapStyle.osmHot:
return context.l10n.mapStyleOsmHot;
case EntryMapStyle.stamenToner:
@@ -21,14 +24,27 @@ extension ExtraEntryMapStyle on EntryMapStyle {
}
}
- bool get isGoogleMaps {
+ bool get isHeavy {
switch (this) {
case EntryMapStyle.googleNormal:
case EntryMapStyle.googleHybrid:
case EntryMapStyle.googleTerrain:
+ case EntryMapStyle.hmsNormal:
+ case EntryMapStyle.hmsTerrain:
return true;
default:
return false;
}
}
+
+ bool get needMobileService {
+ switch (this) {
+ case EntryMapStyle.osmHot:
+ case EntryMapStyle.stamenToner:
+ case EntryMapStyle.stamenWatercolor:
+ return false;
+ default:
+ return true;
+ }
+ }
}
diff --git a/lib/model/settings/settings.dart b/lib/model/settings/settings.dart
index f3f4df159..6c0617863 100644
--- a/lib/model/settings/settings.dart
+++ b/lib/model/settings/settings.dart
@@ -11,6 +11,7 @@ import 'package:aves/model/settings/enums/map_style.dart';
import 'package:aves/model/source/enums.dart';
import 'package:aves/services/accessibility_service.dart';
import 'package:aves/services/common/services.dart';
+import 'package:aves_map/aves_map.dart';
import 'package:collection/collection.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
@@ -54,6 +55,7 @@ class Settings extends ChangeNotifier {
static const mustBackTwiceToExitKey = 'must_back_twice_to_exit';
static const keepScreenOnKey = 'keep_screen_on';
static const homePageKey = 'home_page';
+ static const showBottomNavigationBarKey = 'show_bottom_navigation_bar';
static const confirmDeleteForeverKey = 'confirm_delete_forever';
static const confirmMoveToBinKey = 'confirm_move_to_bin';
static const confirmMoveUndatedItemsKey = 'confirm_move_undated_items';
@@ -68,6 +70,7 @@ class Settings extends ChangeNotifier {
static const collectionBrowsingQuickActionsKey = 'collection_browsing_quick_actions';
static const collectionSelectionQuickActionsKey = 'collection_selection_quick_actions';
static const showThumbnailFavouriteKey = 'show_thumbnail_favourite';
+ static const showThumbnailTagKey = 'show_thumbnail_tag';
static const showThumbnailLocationKey = 'show_thumbnail_location';
static const showThumbnailMotionPhotoKey = 'show_thumbnail_motion_photo';
static const showThumbnailRatingKey = 'show_thumbnail_rating';
@@ -161,11 +164,11 @@ class Settings extends ChangeNotifier {
enableOverlayBlurEffect = performanceClass >= 29;
// availability
- final canUseGoogleMaps = await availability.canUseGoogleMaps;
- if (canUseGoogleMaps) {
- infoMapStyle = EntryMapStyle.googleNormal;
+ final defaultMapStyle = mobileServices.defaultMapStyle;
+ if (mobileServices.mapStyles.contains(defaultMapStyle)) {
+ infoMapStyle = defaultMapStyle;
} else {
- final styles = EntryMapStyle.values.whereNot((v) => v.isGoogleMaps).toList();
+ final styles = EntryMapStyle.values.whereNot((v) => v.needMobileService).toList();
infoMapStyle = styles[Random().nextInt(styles.length)];
}
@@ -235,7 +238,7 @@ class Settings extends ChangeNotifier {
if (_locale != null) {
preferredLocales.add(_locale);
} else {
- preferredLocales.addAll(WidgetsBinding.instance!.window.locales);
+ preferredLocales.addAll(WidgetsBinding.instance.window.locales);
if (preferredLocales.isEmpty) {
// the `window` locales may be empty in a window-less service context
preferredLocales.addAll(_systemLocalesFallback);
@@ -292,6 +295,10 @@ class Settings extends ChangeNotifier {
set homePage(HomePageSetting newValue) => setAndNotify(homePageKey, newValue.toString());
+ bool get showBottomNavigationBar => getBoolOrDefault(showBottomNavigationBarKey, SettingsDefaults.showBottomNavigationBar);
+
+ set showBottomNavigationBar(bool newValue) => setAndNotify(showBottomNavigationBarKey, newValue);
+
bool get confirmDeleteForever => getBoolOrDefault(confirmDeleteForeverKey, SettingsDefaults.confirmDeleteForever);
set confirmDeleteForever(bool newValue) => setAndNotify(confirmDeleteForeverKey, newValue);
@@ -347,6 +354,10 @@ class Settings extends ChangeNotifier {
set showThumbnailFavourite(bool newValue) => setAndNotify(showThumbnailFavouriteKey, newValue);
+ bool get showThumbnailTag => getBoolOrDefault(showThumbnailTagKey, SettingsDefaults.showThumbnailTag);
+
+ set showThumbnailTag(bool newValue) => setAndNotify(showThumbnailTagKey, newValue);
+
bool get showThumbnailLocation => getBoolOrDefault(showThumbnailLocationKey, SettingsDefaults.showThumbnailLocation);
set showThumbnailLocation(bool newValue) => setAndNotify(showThumbnailLocationKey, newValue);
@@ -504,7 +515,11 @@ class Settings extends ChangeNotifier {
// info
- EntryMapStyle get infoMapStyle => getEnumOrDefault(infoMapStyleKey, SettingsDefaults.infoMapStyle, EntryMapStyle.values);
+ EntryMapStyle get infoMapStyle {
+ final preferred = getEnumOrDefault(infoMapStyleKey, SettingsDefaults.infoMapStyle, EntryMapStyle.values);
+ final available = availability.mapStyles;
+ return available.contains(preferred) ? preferred : available.first;
+ }
set infoMapStyle(EntryMapStyle newValue) => setAndNotify(infoMapStyleKey, newValue.toString());
@@ -680,12 +695,14 @@ class Settings extends ChangeNotifier {
break;
case isInstalledAppAccessAllowedKey:
case isErrorReportingAllowedKey:
+ case showBottomNavigationBarKey:
case mustBackTwiceToExitKey:
case confirmDeleteForeverKey:
case confirmMoveToBinKey:
case confirmMoveUndatedItemsKey:
case setMetadataDateBeforeFileOpKey:
case showThumbnailFavouriteKey:
+ case showThumbnailTagKey:
case showThumbnailLocationKey:
case showThumbnailMotionPhotoKey:
case showThumbnailRatingKey:
diff --git a/lib/model/source/album.dart b/lib/model/source/album.dart
index 3808fe9d9..1fd58d204 100644
--- a/lib/model/source/album.dart
+++ b/lib/model/source/album.dart
@@ -26,9 +26,11 @@ mixin AlbumMixin on SourceBase {
return compareAsciiUpperCase(va, vb);
}
- void _onAlbumChanged() {
+ void _onAlbumChanged({bool notify = true}) {
invalidateAlbumDisplayNames();
- eventBus.fire(AlbumsChangedEvent());
+ if (notify) {
+ eventBus.fire(AlbumsChangedEvent());
+ }
}
Map getAlbumEntries() {
@@ -55,14 +57,14 @@ mixin AlbumMixin on SourceBase {
void updateDirectories() {
final visibleDirectories = visibleEntries.map((entry) => entry.directory).toSet();
- addDirectories(visibleDirectories);
+ addDirectories(albums: visibleDirectories);
cleanEmptyAlbums();
}
- void addDirectories(Set albums) {
+ void addDirectories({required Set albums, bool notify = true}) {
if (!_directories.containsAll(albums)) {
_directories.addAll(albums);
- _onAlbumChanged();
+ _onAlbumChanged(notify: notify);
}
}
@@ -92,7 +94,11 @@ mixin AlbumMixin on SourceBase {
final Map _filterEntryCountMap = {};
final Map _filterRecentEntryMap = {};
- void invalidateAlbumFilterSummary({Set? entries, Set? directories}) {
+ void invalidateAlbumFilterSummary({
+ Set? entries,
+ Set? directories,
+ bool notify = true,
+ }) {
if (_filterEntryCountMap.isEmpty && _filterRecentEntryMap.isEmpty) return;
if (entries == null && directories == null) {
@@ -108,7 +114,9 @@ mixin AlbumMixin on SourceBase {
_filterRecentEntryMap.remove(directory);
});
}
- eventBus.fire(AlbumSummaryInvalidatedEvent(directories));
+ if (notify) {
+ eventBus.fire(AlbumSummaryInvalidatedEvent(directories));
+ }
}
int albumEntryCount(AlbumFilter filter) {
@@ -123,7 +131,7 @@ mixin AlbumMixin on SourceBase {
void createAlbum(String directory) {
_newAlbums.add(directory);
- addDirectories({directory});
+ addDirectories(albums: {directory});
}
void renameNewAlbum(String source, String destination) {
diff --git a/lib/model/source/collection_source.dart b/lib/model/source/collection_source.dart
index cd8d69453..2ce5a6f79 100644
--- a/lib/model/source/collection_source.dart
+++ b/lib/model/source/collection_source.dart
@@ -43,6 +43,8 @@ mixin SourceBase {
ValueNotifier progressNotifier = ValueNotifier(const ProgressEvent(done: 0, total: 0));
void setProgress({required int done, required int total}) => progressNotifier.value = ProgressEvent(done: done, total: total);
+
+ void invalidateEntries();
}
abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagMixin, TrashMixin {
@@ -112,17 +114,22 @@ abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagM
return entries.where(TrashFilter.instance.test);
}
- void _invalidate([Set? entries]) {
+ void _invalidate({Set? entries, bool notify = true}) {
+ invalidateEntries();
+ invalidateAlbumFilterSummary(entries: entries, notify: notify);
+ invalidateCountryFilterSummary(entries: entries, notify: notify);
+ invalidateTagFilterSummary(entries: entries, notify: notify);
+ }
+
+ @override
+ void invalidateEntries() {
_visibleEntries = null;
_trashedEntries = null;
_sortedEntriesByDate = null;
- invalidateAlbumFilterSummary(entries: entries);
- invalidateCountryFilterSummary(entries: entries);
- invalidateTagFilterSummary(entries: entries);
}
void updateDerivedFilters([Set? entries]) {
- _invalidate(entries);
+ _invalidate(entries: entries);
// it is possible for entries hidden by a filter type, to have an impact on other types
// e.g. given a sole entry for country C and tag T, hiding T should make C disappear too
updateDirectories();
@@ -130,7 +137,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagM
updateTags();
}
- void addEntries(Set entries) {
+ void addEntries(Set entries, {bool notify = true}) {
if (entries.isEmpty) return;
final newIdMapEntries = Map.fromEntries(entries.map((entry) => MapEntry(entry.id, entry)));
@@ -145,10 +152,12 @@ abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagM
_entryById.addAll(newIdMapEntries);
_rawEntries.addAll(entries);
- _invalidate(entries);
+ _invalidate(entries: entries, notify: notify);
- addDirectories(_applyHiddenFilters(entries).map((entry) => entry.directory).toSet());
- eventBus.fire(EntryAddedEvent(entries));
+ addDirectories(albums: _applyHiddenFilters(entries).map((entry) => entry.directory).toSet(), notify: notify);
+ if (notify) {
+ eventBus.fire(EntryAddedEvent(entries));
+ }
}
Future removeEntries(Set uris, {required bool includeTrash}) async {
@@ -320,7 +329,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagM
case MoveType.move:
case MoveType.export:
cleanEmptyAlbums(fromAlbums);
- addDirectories(destinationAlbums);
+ addDirectories(albums: destinationAlbums);
break;
case MoveType.toBin:
case MoveType.fromBin:
@@ -328,7 +337,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagM
break;
}
invalidateAlbumFilterSummary(directories: fromAlbums);
- _invalidate(movedEntries);
+ _invalidate(entries: movedEntries);
eventBus.fire(EntryMovedEvent(moveType, movedEntries));
}
diff --git a/lib/model/source/location.dart b/lib/model/source/location.dart
index 8eb3f8fc7..144cb683b 100644
--- a/lib/model/source/location.dart
+++ b/lib/model/source/location.dart
@@ -24,6 +24,7 @@ mixin LocationMixin on SourceBase {
final saved = await (ids != null ? metadataDb.loadAddressesById(ids) : metadataDb.loadAddresses());
final idMap = entryById;
saved.forEach((metadata) => idMap[metadata.id]?.addressDetails = metadata);
+ invalidateEntries();
onAddressMetadataChanged();
}
@@ -182,7 +183,11 @@ mixin LocationMixin on SourceBase {
final Map _filterEntryCountMap = {};
final Map _filterRecentEntryMap = {};
- void invalidateCountryFilterSummary({Set? entries, Set? countryCodes}) {
+ void invalidateCountryFilterSummary({
+ Set? entries,
+ Set? countryCodes,
+ bool notify = true,
+ }) {
if (_filterEntryCountMap.isEmpty && _filterRecentEntryMap.isEmpty) return;
if (entries == null && countryCodes == null) {
@@ -198,7 +203,9 @@ mixin LocationMixin on SourceBase {
_filterRecentEntryMap.remove(countryCode);
});
}
- eventBus.fire(CountrySummaryInvalidatedEvent(countryCodes));
+ if (notify) {
+ eventBus.fire(CountrySummaryInvalidatedEvent(countryCodes));
+ }
}
int countryEntryCount(LocationFilter filter) {
diff --git a/lib/model/source/media_store_source.dart b/lib/model/source/media_store_source.dart
index 48af4d718..8bd93063f 100644
--- a/lib/model/source/media_store_source.dart
+++ b/lib/model/source/media_store_source.dart
@@ -96,7 +96,9 @@ class MediaStoreSource extends CollectionSource {
// show known entries
debugPrint('$runtimeType refresh ${stopwatch.elapsed} add known entries');
- addEntries(knownEntries);
+ // add entries without notifying, so that the collection is not refreshed
+ // with items that may be hidden right away because of their metadata
+ addEntries(knownEntries, notify: false);
debugPrint('$runtimeType refresh ${stopwatch.elapsed} load metadata');
if (directory != null) {
diff --git a/lib/model/source/tag.dart b/lib/model/source/tag.dart
index b2fa61907..c092600cf 100644
--- a/lib/model/source/tag.dart
+++ b/lib/model/source/tag.dart
@@ -18,6 +18,7 @@ mixin TagMixin on SourceBase {
final saved = await (ids != null ? metadataDb.loadCatalogMetadataById(ids) : metadataDb.loadCatalogMetadata());
final idMap = entryById;
saved.forEach((metadata) => idMap[metadata.id]?.catalogMetadata = metadata);
+ invalidateEntries();
onCatalogMetadataChanged();
}
@@ -77,7 +78,11 @@ mixin TagMixin on SourceBase {
final Map _filterEntryCountMap = {};
final Map _filterRecentEntryMap = {};
- void invalidateTagFilterSummary({Set? entries, Set? tags}) {
+ void invalidateTagFilterSummary({
+ Set? entries,
+ Set? tags,
+ bool notify = true,
+ }) {
if (_filterEntryCountMap.isEmpty && _filterRecentEntryMap.isEmpty) return;
if (entries == null && tags == null) {
@@ -93,7 +98,9 @@ mixin TagMixin on SourceBase {
_filterRecentEntryMap.remove(tag);
});
}
- eventBus.fire(TagSummaryInvalidatedEvent(tags));
+ if (notify) {
+ eventBus.fire(TagSummaryInvalidatedEvent(tags));
+ }
}
int tagEntryCount(TagFilter filter) {
diff --git a/lib/model/video/metadata.dart b/lib/model/video/metadata.dart
index 1b7564ba3..e5e7f1a00 100644
--- a/lib/model/video/metadata.dart
+++ b/lib/model/video/metadata.dart
@@ -24,6 +24,9 @@ import 'package:flutter/foundation.dart';
class VideoMetadataFormatter {
static final _dateY4M2D2H2m2s2Pattern = RegExp(r'(\d{4})[-/](\d{2})[-/](\d{2}) (\d{2}):(\d{2}):(\d{2})');
static final _dateY4M2D2H2m2s2APmPattern = RegExp(r'(\d{4})[-/](\d{2})[-/](\d{2})T(\d+):(\d+):(\d+) ([ap]m)Z');
+ static final _ambiguousDatePatterns = {
+ RegExp(r'^\d{2}[-/]\d{2}[-/]\d{4}$'),
+ };
static final _durationPattern = RegExp(r'(\d+):(\d+):(\d+)(.\d+)');
static final _locationPattern = RegExp(r'([+-][.0-9]+)');
static final Map _codecNames = {
@@ -93,7 +96,7 @@ class VideoMetadataFormatter {
int? dateMillis;
if (isDefined(dateString)) {
dateMillis = parseVideoDate(dateString);
- if (dateMillis == null) {
+ if (dateMillis == null && !isAmbiguousDate(dateString)) {
await reportService.recordError('getCatalogMetadata failed to parse date=$dateString for mimeType=${entry.mimeType} entry=$entry', null);
}
}
@@ -107,6 +110,10 @@ class VideoMetadataFormatter {
return entry.catalogMetadata;
}
+ static bool isAmbiguousDate(String dateString) {
+ return _ambiguousDatePatterns.any((pattern) => pattern.hasMatch(dateString));
+ }
+
static int? parseVideoDate(String dateString) {
final date = DateTime.tryParse(dateString);
if (date != null) {
diff --git a/lib/ref/mime_types.dart b/lib/ref/mime_types.dart
index 185165bec..cec770cae 100644
--- a/lib/ref/mime_types.dart
+++ b/lib/ref/mime_types.dart
@@ -1,6 +1,7 @@
class MimeTypes {
static const anyImage = 'image/*';
+ static const avif = 'image/avif';
static const bmp = 'image/bmp';
static const bmpX = 'image/x-ms-bmp';
static const gif = 'image/gif';
@@ -14,6 +15,7 @@ class MimeTypes {
static const webp = 'image/webp';
static const art = 'image/x-jg';
+ static const cdr = 'image/x-coreldraw';
static const djvu = 'image/vnd.djvu';
static const jxl = 'image/jxl';
static const psdVnd = 'image/vnd.adobe.photoshop';
@@ -65,23 +67,33 @@ class MimeTypes {
// groups
// formats that support transparency
- static const Set alphaImages = {bmp, bmpX, gif, ico, png, svg, tiff, webp};
+ static const Set alphaImages = {avif, bmp, bmpX, gif, heic, heif, ico, png, svg, tiff, webp};
static const Set rawImages = {arw, cr2, crw, dcr, dng, erf, k25, kdc, mrw, nef, nrw, orf, pef, raf, raw, rw2, sr2, srf, srw, x3f};
// TODO TLAD [codec] make it dynamic if it depends on OS/lib versions
- static const Set undecodableImages = {art, crw, djvu, jxl, psdVnd, psdX, octetStream, zip};
+ static const Set undecodableImages = {art, cdr, crw, djvu, jxl, psdVnd, psdX, octetStream, zip};
- static const Set _knownOpaqueImages = {heic, heif, jpeg};
+ static const Set _knownOpaqueImages = {jpeg};
static const Set _knownVideos = {avi, aviVnd, flv, flvX, mkv, mov, mp2t, mp2ts, mp4, mpeg, ogv, webm};
- static final Set knownMediaTypes = {..._knownOpaqueImages, ...alphaImages, ...rawImages, ...undecodableImages, ..._knownVideos};
+ static final Set knownMediaTypes = {
+ anyImage,
+ ..._knownOpaqueImages,
+ ...alphaImages,
+ ...rawImages,
+ ...undecodableImages,
+ anyVideo,
+ ..._knownVideos,
+ };
static bool isImage(String mimeType) => mimeType.startsWith('image');
static bool isVideo(String mimeType) => mimeType.startsWith('video');
+ static bool isVisual(String mimeType) => isImage(mimeType) || isVideo(mimeType);
+
static bool refersToSameType(String a, b) {
switch (a) {
case avi:
diff --git a/lib/services/analysis_service.dart b/lib/services/analysis_service.dart
index 95b8eb752..7c75aa6e4 100644
--- a/lib/services/analysis_service.dart
+++ b/lib/services/analysis_service.dart
@@ -43,6 +43,7 @@ Future _init() async {
WidgetsFlutterBinding.ensureInitialized();
initPlatformServices();
await metadataDb.init();
+ await mobileServices.init();
await settings.init(monitorPlatformSettings: false);
FijkLog.setLevel(FijkLogLevel.Warn);
await reportService.init();
diff --git a/lib/services/common/image_op_events.dart b/lib/services/common/image_op_events.dart
index 13863ad3f..60f055a1c 100644
--- a/lib/services/common/image_op_events.dart
+++ b/lib/services/common/image_op_events.dart
@@ -28,29 +28,29 @@ class ImageOpEvent extends Equatable {
@immutable
class MoveOpEvent extends ImageOpEvent {
final Map newFields;
+ final bool deleted;
@override
- List