Merge branch 'develop'

This commit is contained in:
Thibault Deckers 2022-05-25 09:39:14 +09:00
commit f6c9d91c6f
466 changed files with 6658 additions and 5334 deletions

View file

@ -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

View file

@ -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:

View file

@ -4,6 +4,29 @@ All notable changes to this project will be documented in this file.
## <a id="unreleased"></a>[Unreleased]
## <a id="v1.6.5"></a>[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
## <a id="v1.6.4"></a>[v1.6.4] - 2022-04-19
### Added

View file

@ -12,12 +12,17 @@ Aves is a gallery and metadata explorer app. It is built for Android, with Flutt
[<img src="https://play.google.com/intl/en_us/badges/static/images/badges/en_badge_web_generic.png"
alt='Get it on Google Play'
height="80">](https://play.google.com/store/apps/details?id=deckers.thibault.aves&pcampaignid=pcampaignidMKT-Other-global-all-co-prtnr-py-PartBadge-Mar2515-1)
[<img src="https://raw.githubusercontent.com/deckerst/common/main/assets/amazon-appstore-badge-english-black.png"
alt='Get it on Amazon Appstore'
height="80">](https://www.amazon.com/dp/B09XQHQQ72)
[<img src="https://gitlab.com/IzzyOnDroid/repo/-/raw/master/assets/IzzyOnDroid.png"
alt='Get it on IzzyOnDroid'
height="80">](https://apt.izzysoft.de/fdroid/index/apk/deckers.thibault.aves)
[<img src="https://raw.githubusercontent.com/deckerst/common/main/assets/get-it-on-github.png"
alt='Get it on GitHub'
height="80">](https://github.com/deckerst/aves/releases/latest)
[Compare versions](https://github.com/deckerst/aves/wiki/App-Versions)
<div align="left">
@ -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

View file

@ -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?

View file

@ -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"
}
}
]
}

View file

@ -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'
}
}

View file

@ -1,6 +1,5 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="deckers.thibault.aves"
android:installLocation="auto">
<!--
@ -35,6 +34,15 @@
<intent>
<action android:name="android.intent.action.MAIN" />
</intent>
<!--
from Android R, `url_launcher` method `canLaunchUrl()` will return false,
if appropriate intents are not declared, cf https://pub.dev/packages/url_launcher#configuration=
-->
<!-- to open https URLs -->
<intent>
<action android:name="android.intent.action.VIEW" />
<data android:scheme="https" />
</intent>
</queries>
<application

View file

@ -2,6 +2,7 @@ package deckers.thibault.aves
import android.annotation.SuppressLint
import android.app.SearchManager
import android.content.ClipData
import android.content.Intent
import android.net.Uri
import android.os.Build
@ -16,7 +17,6 @@ import app.loup.streams_channel.StreamsChannel
import deckers.thibault.aves.channel.calls.*
import deckers.thibault.aves.channel.streams.*
import deckers.thibault.aves.utils.LogUtils
import deckers.thibault.aves.utils.PermissionManager
import io.flutter.embedding.android.FlutterActivity
import io.flutter.plugin.common.EventChannel
import io.flutter.plugin.common.MethodCall
@ -222,6 +222,7 @@ class MainActivity : FlutterActivity() {
return hashMapOf(
INTENT_DATA_KEY_ACTION to INTENT_ACTION_PICK,
INTENT_DATA_KEY_MIME_TYPE to intent.type,
INTENT_DATA_KEY_ALLOW_MULTIPLE to (intent.extras?.getBoolean(Intent.EXTRA_ALLOW_MULTIPLE) ?: false),
)
}
Intent.ACTION_SEARCH -> {
@ -246,10 +247,20 @@ class MainActivity : FlutterActivity() {
}
private fun pick(call: MethodCall) {
val pickedUri = call.argument<String>("uri")
if (pickedUri != null) {
val pickedUris = call.argument<List<String>>("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"

View file

@ -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<AppAdapterHandler>()
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
}
}
}
}

View file

@ -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)

View file

@ -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),
)

View file

@ -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)

View file

@ -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)
}
}

View file

@ -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)

View file

@ -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
}

View file

@ -204,6 +204,8 @@ class SourceEntry {
// ignore
} catch (e: NoClassDefFoundError) {
// ignore
} catch (e: AssertionError) {
// ignore
}
}

View file

@ -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

View file

@ -1045,6 +1045,9 @@ abstract class ImageProvider {
// used when skipping a move/creation op because the target file already exists
val skippedFieldMap: HashMap<String, Any?> = hashMapOf("skipped" to true)
// used when deleting instead of moving to bin because the target file no longer exists
val deletedFieldMap: HashMap<String, Any?> = 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)

View file

@ -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

View file

@ -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")

View file

@ -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
}
}

View file

@ -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) {

View file

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 KiB

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 KiB

After

Width:  |  Height:  |  Size: 494 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 209 KiB

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

After

Width:  |  Height:  |  Size: 338 KiB

View file

@ -0,0 +1,5 @@
In v1.6.5:
- bottom navigation bar
- fast scroll with breadcrumbs
- settings search
Full changelog available on GitHub

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 KiB

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 KiB

After

Width:  |  Height:  |  Size: 494 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 KiB

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 365 KiB

After

Width:  |  Height:  |  Size: 339 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 275 KiB

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 KiB

After

Width:  |  Height:  |  Size: 494 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 212 KiB

After

Width:  |  Height:  |  Size: 212 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

After

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 KiB

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 KiB

After

Width:  |  Height:  |  Size: 494 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

After

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 KiB

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 KiB

After

Width:  |  Height:  |  Size: 494 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 KiB

After

Width:  |  Height:  |  Size: 207 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 91 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

After

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 KiB

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 KiB

After

Width:  |  Height:  |  Size: 494 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 KiB

After

Width:  |  Height:  |  Size: 337 KiB

View file

@ -1,7 +1,7 @@
<i>Aves</i>はあらゆる画像や動画を扱うことができ、一般的なJPEGやMP4はもちろん、 <b>マルチページTIFF、SVG、古いAVIなどの珍しい形式にも対応しています</b>
<i>Aves</i>はあらゆる画像や動画を扱うことができ、一般的なJPEGやMP4はもちろん、 <b>マルチページTIFF、SVG、古いAVIなどの珍しい形式にも対応</b>しています!
メディアコレクションをスキャンして、<b>モーションフォト</b>、<b>パノラマ</b>Photo Sphere、<b>360°動画</b>、<b>GeoTIFF<b>ファイルなどを識別します。
メディアコレクションをスキャンして、<b>モーションフォト</b>、<b>パノラマ</b>Photo Sphere、<b>360°動画</b>、<b>GeoTIFF</b>ファイルなどを識別します。
<b>ナビゲーションと検索<b>は、Avesの重要な部分です。アルバムから写真、タグ、地図などへ簡単に移動できます。
<b>ナビゲーションと検索</b>は、Avesの重要な部分です。アルバムから写真、タグ、地図などへ簡単に移動できます。
<i>Aves</i>は、<b>アプリショートカット</b>や<b>グローバル検索</b>などの機能を、Android<b>API 19から32まで</b>、つまりAndroid 4.4から12 Lまでと統合しています。また、<b>メディアビューワー</b>や<b>メディアピッカー</b>としても機能します。

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 KiB

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 KiB

After

Width:  |  Height:  |  Size: 494 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

After

Width:  |  Height:  |  Size: 208 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 80 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 KiB

After

Width:  |  Height:  |  Size: 336 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 KiB

After

Width:  |  Height:  |  Size: 243 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 496 KiB

After

Width:  |  Height:  |  Size: 496 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 KiB

After

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 363 KiB

After

Width:  |  Height:  |  Size: 337 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 274 KiB

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 495 KiB

After

Width:  |  Height:  |  Size: 495 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 214 KiB

After

Width:  |  Height:  |  Size: 214 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

After

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 80 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

After

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 KiB

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 KiB

After

Width:  |  Height:  |  Size: 494 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 215 KiB

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 364 KiB

After

Width:  |  Height:  |  Size: 338 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 KiB

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 494 KiB

After

Width:  |  Height:  |  Size: 494 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 208 KiB

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 86 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 79 KiB

After

Width:  |  Height:  |  Size: 79 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 KiB

After

Width:  |  Height:  |  Size: 337 KiB

View file

@ -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;
}
}
}

View file

@ -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;
}

View file

@ -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",

View file

@ -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",

View file

@ -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",

View file

@ -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 daccueil",
"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 licône de favori",
"settingsThumbnailShowTagIcon": "Afficher licône de libellé",
"settingsThumbnailShowLocationIcon": "Afficher licône de lieu",
"settingsThumbnailShowMotionPhotoIcon": "Afficher licône de photo animée",
"settingsThumbnailShowRating": "Afficher la notation",

View file

@ -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",

View file

@ -41,7 +41,7 @@
"chipActionGoToTagPage": "Mostra nelle etichette",
"chipActionHide": "Nascondi",
"chipActionPin": "Fissa in alto",
"chipActionUnpin": "Rimuovi dall'alto",
"chipActionUnpin": "Rimuovi dallalto",
"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 dellalbum",
"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 unaltra 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 allinterno 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 dellapp 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 nellalbum",
"collectionActionMove": "Sposta nellalbum",
"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 licona Preferiti",
"settingsThumbnailShowTagIcon": "Mostra licona Etichetta",
"settingsThumbnailShowLocationIcon": "Mostra licona Posizione",
"settingsThumbnailShowMotionPhotoIcon": "Mostra licona 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 allapertura",
"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 laccesso allinventario 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 lalto",
"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 lestrazione dei dati incorporati",
"viewerInfoOpenLinkText": "Apri",
"viewerInfoViewXmlLinkText": "Visualizza XML",

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