Merge branch 'develop'
2
.github/workflows/check.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
|||
# Available versions may lag behind https://github.com/flutter/flutter.git
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: '3.0.1'
|
||||
flutter-version: '3.0.2'
|
||||
channel: 'stable'
|
||||
|
||||
- name: Clone the repository.
|
||||
|
|
10
.github/workflows/release.yml
vendored
|
@ -19,7 +19,7 @@ jobs:
|
|||
# Available versions may lag behind https://github.com/flutter/flutter.git
|
||||
- uses: subosito/flutter-action@v2
|
||||
with:
|
||||
flutter-version: '3.0.1'
|
||||
flutter-version: '3.0.2'
|
||||
channel: 'stable'
|
||||
|
||||
# Workaround for this Android Gradle Plugin issue (supposedly fixed in AGP 4.1):
|
||||
|
@ -56,15 +56,15 @@ jobs:
|
|||
rm release.keystore.asc
|
||||
mkdir outputs
|
||||
(cd scripts/; ./apply_flavor_play.sh)
|
||||
flutter build appbundle -t lib/main_play.dart --flavor play --bundle-sksl-path shaders_3.0.1.sksl.json
|
||||
flutter build appbundle -t lib/main_play.dart --flavor play --bundle-sksl-path shaders_3.0.2.sksl.json
|
||||
cp build/app/outputs/bundle/playRelease/*.aab outputs
|
||||
flutter build apk -t lib/main_play.dart --flavor play --bundle-sksl-path shaders_3.0.1.sksl.json
|
||||
flutter build apk -t lib/main_play.dart --flavor play --bundle-sksl-path shaders_3.0.2.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
|
||||
flutter build apk -t lib/main_huawei.dart --flavor huawei --bundle-sksl-path shaders_3.0.2.sksl.json
|
||||
cp build/app/outputs/apk/huawei/release/*.apk outputs
|
||||
(cd scripts/; ./apply_flavor_izzy.sh)
|
||||
flutter build apk -t lib/main_izzy.dart --flavor izzy --split-per-abi --bundle-sksl-path shaders_3.0.1.sksl.json
|
||||
flutter build apk -t lib/main_izzy.dart --flavor izzy --split-per-abi --bundle-sksl-path shaders_3.0.2.sksl.json
|
||||
cp build/app/outputs/apk/izzy/release/*.apk outputs
|
||||
rm $AVES_STORE_FILE
|
||||
env:
|
||||
|
|
20
CHANGELOG.md
|
@ -4,6 +4,26 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
## <a id="unreleased"></a>[Unreleased]
|
||||
|
||||
## <a id="v1.6.9"></a>[v1.6.9] - 2022-06-18
|
||||
|
||||
### Added
|
||||
|
||||
- slideshow
|
||||
- set wallpaper from any media
|
||||
- optional dynamic accent color on Android 12+
|
||||
- Search: date/dimension/size field equality (undocumented)
|
||||
- support Android 13 (API 33)
|
||||
- Turkish translation (thanks metezd)
|
||||
|
||||
### Changed
|
||||
|
||||
- do not force quit on storage permission denial
|
||||
- upgraded Flutter to stable v3.0.2
|
||||
|
||||
### Fixed
|
||||
|
||||
- merge ambiguously cased directories
|
||||
|
||||
## <a id="v1.6.8"></a>[v1.6.8] - 2022-05-27
|
||||
|
||||
### Fixed
|
||||
|
|
|
@ -12,6 +12,9 @@ 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/huawei-appgallery-badge-english-black.png"
|
||||
alt='Get it on Huawei AppGallery'
|
||||
height="80">](https://appgallery.huawei.com/app/C106014023)
|
||||
[<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)
|
||||
|
@ -90,7 +93,7 @@ At this stage this project does *not* accept PRs, except for translations.
|
|||
|
||||
### Translations
|
||||
|
||||
If you want to translate this app in your language and share the result, [there is a guide](https://github.com/deckerst/aves/wiki/Contributing-to-Translations). English, Korean and French are already handled by me. Russian, German, Spanish, Portuguese, Indonesian, Japanese, Italian & Chinese are handled by generous volunteers.
|
||||
If you want to translate this app in your language and share the result, [there is a guide](https://github.com/deckerst/aves/wiki/Contributing-to-Translations). English, Korean and French are already handled by me. Russian, German, Spanish, Portuguese, Indonesian, Japanese, Italian, Chinese & Turkish are handled by generous volunteers.
|
||||
|
||||
### Donations
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ if (keystorePropertiesFile.exists()) {
|
|||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 32
|
||||
compileSdkVersion 33
|
||||
|
||||
sourceSets {
|
||||
main.java.srcDirs += 'src/main/kotlin'
|
||||
|
@ -57,7 +57,7 @@ android {
|
|||
// which implementation `DocumentBuilderImpl` is provided by the OS and is not customizable on Android,
|
||||
// but the implementation on API <19 is not robust enough and fails to build XMP documents
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 32
|
||||
targetSdkVersion 33
|
||||
versionCode flutterVersionCode.toInteger()
|
||||
versionName flutterVersionName
|
||||
manifestPlaceholders = [googleApiKey: keystoreProperties['googleApiKey']]
|
||||
|
@ -154,7 +154,7 @@ repositories {
|
|||
|
||||
dependencies {
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.5.2'
|
||||
implementation 'androidx.core:core-ktx:1.7.0'
|
||||
implementation 'androidx.core:core-ktx:1.8.0'
|
||||
implementation 'androidx.exifinterface:exifinterface:1.3.3'
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation 'com.caverock:androidsvg-aar:1.4'
|
||||
|
|
|
@ -6,16 +6,21 @@
|
|||
Scoped storage on Android Q is inconvenient because users need to confirm edition on each individual file.
|
||||
So we request `WRITE_EXTERNAL_STORAGE` until Q (29), and enable `requestLegacyExternalStorage`
|
||||
-->
|
||||
|
||||
<!-- TODO TLAD [tiramisu] need notification permission? -->
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<!-- TODO TLAD [tiramisu] READ_MEDIA_IMAGE, READ_MEDIA_VIDEO instead of READ_EXTERNAL_STORAGE? -->
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
|
||||
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
|
||||
<uses-permission
|
||||
android:name="android.permission.READ_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="32" />
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="29"
|
||||
tools:ignore="ScopedStorage" />
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.SET_WALLPAPER" />
|
||||
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
<!-- to show foreground service progress via notification -->
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
|
||||
<!-- to access media with original metadata with scoped storage (Android Q+) -->
|
||||
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION" />
|
||||
|
@ -128,6 +133,26 @@
|
|||
android:name="android.app.searchable"
|
||||
android:resource="@xml/searchable" />
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".WallpaperActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/wallpaper"
|
||||
android:theme="@style/NormalTheme">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.ATTACH_DATA" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="video/*" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SET_WALLPAPER" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".AnalysisService"
|
||||
android:description="@string/analysis_service_description"
|
||||
|
|
|
@ -159,7 +159,7 @@ class AnalysisService : MethodChannel.MethodCallHandler, Service() {
|
|||
COMMAND_START -> {
|
||||
runBlocking {
|
||||
FlutterUtils.runOnUiThread {
|
||||
val entryIds = data.get(KEY_ENTRY_IDS)?.takeIf { it is IntArray }?.let { (it as IntArray).toList() }
|
||||
val entryIds = data.getIntArray(KEY_ENTRY_IDS)?.toList()
|
||||
backgroundChannel?.invokeMethod(
|
||||
"start", hashMapOf(
|
||||
"entryIds" to entryIds,
|
||||
|
|
|
@ -15,6 +15,7 @@ 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.getParcelableExtraCompat
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.plugin.common.EventChannel
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
|
@ -215,7 +216,7 @@ class MainActivity : FlutterActivity() {
|
|||
}
|
||||
}
|
||||
Intent.ACTION_VIEW, Intent.ACTION_SEND, "com.android.camera.action.REVIEW" -> {
|
||||
(intent.data ?: (intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri))?.let { uri ->
|
||||
(intent.data ?: intent.getParcelableExtraCompat<Uri>(Intent.EXTRA_STREAM))?.let { uri ->
|
||||
// MIME type is optional
|
||||
val type = intent.type ?: intent.resolveType(context)
|
||||
return hashMapOf(
|
||||
|
@ -332,6 +333,7 @@ class MainActivity : FlutterActivity() {
|
|||
|
||||
const val INTENT_ACTION_PICK = "pick"
|
||||
const val INTENT_ACTION_SEARCH = "search"
|
||||
const val INTENT_ACTION_SET_WALLPAPER = "set_wallpaper"
|
||||
const val INTENT_ACTION_VIEW = "view"
|
||||
|
||||
const val SHORTCUT_KEY_PAGE = "page"
|
||||
|
|
|
@ -0,0 +1,107 @@
|
|||
package deckers.thibault.aves
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.*
|
||||
import android.util.Log
|
||||
import app.loup.streams_channel.StreamsChannel
|
||||
import deckers.thibault.aves.channel.calls.*
|
||||
import deckers.thibault.aves.channel.streams.ImageByteStreamHandler
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
import deckers.thibault.aves.utils.getParcelableExtraCompat
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
|
||||
class WallpaperActivity : FlutterActivity() {
|
||||
private lateinit var intentDataMap: MutableMap<String, Any?>
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
Log.i(LOG_TAG, "onCreate intent=$intent")
|
||||
intent.extras?.takeUnless { it.isEmpty }?.let {
|
||||
Log.i(LOG_TAG, "onCreate intent extras=$it")
|
||||
}
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
val messenger = flutterEngine!!.dartExecutor.binaryMessenger
|
||||
|
||||
// dart -> platform -> dart
|
||||
// - need Context
|
||||
MethodChannel(messenger, DeviceHandler.CHANNEL).setMethodCallHandler(DeviceHandler(this))
|
||||
MethodChannel(messenger, EmbeddedDataHandler.CHANNEL).setMethodCallHandler(EmbeddedDataHandler(this))
|
||||
MethodChannel(messenger, MetadataFetchHandler.CHANNEL).setMethodCallHandler(MetadataFetchHandler(this))
|
||||
// - need Activity
|
||||
MethodChannel(messenger, AccessibilityHandler.CHANNEL).setMethodCallHandler(AccessibilityHandler(this))
|
||||
MethodChannel(messenger, MediaFileHandler.CHANNEL).setMethodCallHandler(MediaFileHandler(this))
|
||||
MethodChannel(messenger, WallpaperHandler.CHANNEL).setMethodCallHandler(WallpaperHandler(this))
|
||||
MethodChannel(messenger, WindowHandler.CHANNEL).setMethodCallHandler(WindowHandler(this))
|
||||
|
||||
// result streaming: dart -> platform ->->-> dart
|
||||
// - need Context
|
||||
StreamsChannel(messenger, ImageByteStreamHandler.CHANNEL).setStreamHandlerFactory { args -> ImageByteStreamHandler(this, args) }
|
||||
|
||||
// intent handling
|
||||
// detail fetch: dart -> platform
|
||||
intentDataMap = extractIntentData(intent)
|
||||
MethodChannel(messenger, VIEWER_CHANNEL).setMethodCallHandler { call, result ->
|
||||
when (call.method) {
|
||||
"getIntentData" -> {
|
||||
result.success(intentDataMap)
|
||||
intentDataMap.clear()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
Log.i(LOG_TAG, "onStart")
|
||||
super.onStart()
|
||||
|
||||
// as of Flutter v3.0.1, the window `viewInsets` and `viewPadding`
|
||||
// are incorrect on startup in some environments (e.g. API 29 emulator),
|
||||
// so we manually request to apply the insets to update the window metrics
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
|
||||
Handler(Looper.getMainLooper()).postDelayed({
|
||||
window.decorView.requestApplyInsets()
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop() {
|
||||
Log.i(LOG_TAG, "onStop")
|
||||
super.onStop()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.i(LOG_TAG, "onDestroy")
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
private fun extractIntentData(intent: Intent?): MutableMap<String, Any?> {
|
||||
when (intent?.action) {
|
||||
Intent.ACTION_ATTACH_DATA, Intent.ACTION_SET_WALLPAPER -> {
|
||||
(intent.data ?: intent.getParcelableExtraCompat<Uri>(Intent.EXTRA_STREAM))?.let { uri ->
|
||||
// MIME type is optional
|
||||
val type = intent.type ?: intent.resolveType(context)
|
||||
return hashMapOf(
|
||||
MainActivity.INTENT_DATA_KEY_ACTION to MainActivity.INTENT_ACTION_SET_WALLPAPER,
|
||||
MainActivity.INTENT_DATA_KEY_MIME_TYPE to type,
|
||||
MainActivity.INTENT_DATA_KEY_URI to uri.toString(),
|
||||
)
|
||||
}
|
||||
}
|
||||
Intent.ACTION_RUN -> {
|
||||
// flutter run
|
||||
}
|
||||
else -> {
|
||||
Log.w(LOG_TAG, "unhandled intent action=${intent?.action}")
|
||||
}
|
||||
}
|
||||
return HashMap()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val LOG_TAG = LogUtils.createTag<WallpaperActivity>()
|
||||
const val VIEWER_CHANNEL = "deckers.thibault/aves/viewer"
|
||||
}
|
||||
}
|
|
@ -30,6 +30,8 @@ import deckers.thibault.aves.model.FieldMap
|
|||
import deckers.thibault.aves.utils.BitmapUtils
|
||||
import deckers.thibault.aves.utils.BitmapUtils.getBytes
|
||||
import deckers.thibault.aves.utils.LogUtils
|
||||
import deckers.thibault.aves.utils.getApplicationInfoCompat
|
||||
import deckers.thibault.aves.utils.queryIntentActivitiesCompat
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||
|
@ -77,7 +79,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
|
||||
val pm = context.packageManager
|
||||
for (resolveInfo in pm.queryIntentActivities(intent, 0)) {
|
||||
for (resolveInfo in pm.queryIntentActivitiesCompat(intent, 0)) {
|
||||
val appInfo = resolveInfo.activityInfo.applicationInfo
|
||||
val packageName = appInfo.packageName
|
||||
if (!packages.containsKey(packageName)) {
|
||||
|
@ -149,7 +151,7 @@ class AppAdapterHandler(private val context: Context) : MethodCallHandler {
|
|||
val size = (sizeDip * density).roundToInt()
|
||||
var data: ByteArray? = null
|
||||
try {
|
||||
val iconResourceId = context.packageManager.getApplicationInfo(packageName, 0).icon
|
||||
val iconResourceId = context.packageManager.getApplicationInfoCompat(packageName, 0).icon
|
||||
if (iconResourceId != Resources.ID_NULL) {
|
||||
val uri = Uri.Builder()
|
||||
.scheme(ContentResolver.SCHEME_ANDROID_RESOURCE)
|
||||
|
|
|
@ -32,6 +32,8 @@ 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),
|
||||
"canSetLockScreenWallpaper" to (sdkInt >= Build.VERSION_CODES.N),
|
||||
"isDynamicColorAvailable" to (sdkInt >= Build.VERSION_CODES.S),
|
||||
"showPinShortcutFeedback" to (sdkInt >= Build.VERSION_CODES.O),
|
||||
"supportEdgeToEdgeUIMode" to (sdkInt >= Build.VERSION_CODES.Q),
|
||||
)
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
package deckers.thibault.aves.channel.calls
|
||||
|
||||
import android.content.Context
|
||||
import android.location.Address
|
||||
import android.location.Geocoder
|
||||
import android.os.Build
|
||||
import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
import java.io.IOException
|
||||
import java.util.*
|
||||
|
||||
|
@ -48,17 +53,7 @@ class GeocodingHandler(private val context: Context) : MethodCallHandler {
|
|||
Geocoder(context)
|
||||
}
|
||||
|
||||
val addresses = try {
|
||||
geocoder!!.getFromLocation(latitude, longitude, maxResults) ?: ArrayList()
|
||||
} catch (e: IOException) {
|
||||
// `grpc failed`, etc.
|
||||
result.error("getAddress-network", "failed to get address because of network issues", e.message)
|
||||
return
|
||||
} catch (e: Exception) {
|
||||
result.error("getAddress-exception", "failed to get address", e.message)
|
||||
return
|
||||
}
|
||||
|
||||
fun processAddresses(addresses: List<Address>) {
|
||||
if (addresses.isEmpty()) {
|
||||
result.error("getAddress-empty", "failed to find any address for latitude=$latitude, longitude=$longitude", null)
|
||||
} else {
|
||||
|
@ -81,6 +76,28 @@ class GeocodingHandler(private val context: Context) : MethodCallHandler {
|
|||
}
|
||||
}
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
geocoder!!.getFromLocation(latitude, longitude, maxResults, object : Geocoder.GeocodeListener {
|
||||
override fun onGeocode(addresses: List<Address?>) = processAddresses(addresses.filterNotNull())
|
||||
|
||||
override fun onError(errorMessage: String?) {
|
||||
result.error("getAddress-asyncerror", "failed to get address", errorMessage)
|
||||
}
|
||||
})
|
||||
} else {
|
||||
try {
|
||||
@Suppress("deprecation")
|
||||
val addresses = geocoder!!.getFromLocation(latitude, longitude, maxResults) ?: ArrayList()
|
||||
processAddresses(addresses)
|
||||
} catch (e: IOException) {
|
||||
// `grpc failed`, etc.
|
||||
result.error("getAddress-network", "failed to get address because of network issues", e.message)
|
||||
} catch (e: Exception) {
|
||||
result.error("getAddress-exception", "failed to get address", e.message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CHANNEL = "deckers.thibault/aves/geocoding"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package deckers.thibault.aves.channel.calls
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.WallpaperManager
|
||||
import android.app.WallpaperManager.FLAG_LOCK
|
||||
import android.app.WallpaperManager.FLAG_SYSTEM
|
||||
import android.os.Build
|
||||
import deckers.thibault.aves.channel.calls.Coresult.Companion.safe
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class WallpaperHandler(private val activity: Activity) : MethodCallHandler {
|
||||
private val ioScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||
when (call.method) {
|
||||
"setWallpaper" -> ioScope.launch { safe(call, result, ::setWallpaper) }
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setWallpaper(call: MethodCall, result: MethodChannel.Result) {
|
||||
val bytes = call.argument<ByteArray>("bytes")
|
||||
val home = call.argument<Boolean>("home")
|
||||
val lock = call.argument<Boolean>("lock")
|
||||
if (bytes == null || home == null || lock == null) {
|
||||
result.error("setWallpaper-args", "failed because of missing arguments", null)
|
||||
return
|
||||
}
|
||||
|
||||
val manager = WallpaperManager.getInstance(activity)
|
||||
val supported = Build.VERSION.SDK_INT < Build.VERSION_CODES.M || manager.isWallpaperSupported
|
||||
val allowed = Build.VERSION.SDK_INT < Build.VERSION_CODES.N || manager.isSetWallpaperAllowed
|
||||
if (!supported || !allowed) {
|
||||
result.error("setWallpaper-unsupported", "failed because setting wallpaper is not allowed", null)
|
||||
return
|
||||
}
|
||||
|
||||
bytes.inputStream().use { input ->
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
val flags = (if (home) FLAG_SYSTEM else 0) or (if (lock) FLAG_LOCK else 0)
|
||||
manager.setStream(input, null, true, flags)
|
||||
} else {
|
||||
manager.setStream(input)
|
||||
}
|
||||
}
|
||||
result.success(true)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val CHANNEL = "deckers.thibault/aves/wallpaper"
|
||||
}
|
||||
}
|
|
@ -220,7 +220,7 @@ object ExifInterfaceHelper {
|
|||
// initialize metadata-extractor directories that we will fill
|
||||
// by tags converted from the ExifInterface attributes
|
||||
// so that we can rely on metadata-extractor descriptions
|
||||
val dirs = DirType.values().associate { Pair(it, it.createDirectory()) }
|
||||
val dirs = DirType.values().associateWith { it.createDirectory() }
|
||||
|
||||
// exclude Exif directory when it only includes image size
|
||||
val isUselessExif = fun(it: Map<String, String>): Boolean {
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
package deckers.thibault.aves.utils
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ResolveInfo
|
||||
import android.os.Build
|
||||
import android.os.Parcelable
|
||||
|
||||
inline fun <reified T> Intent.getParcelableExtraCompat(name: String): T? {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
getParcelableExtra(name, T::class.java)
|
||||
} else {
|
||||
@Suppress("deprecation")
|
||||
getParcelableExtra<Parcelable>(name) as? T
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
fun PackageManager.getApplicationInfoCompat(packageName: String, flags: Int): ApplicationInfo {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
getApplicationInfo(packageName, PackageManager.ApplicationInfoFlags.of(flags.toLong()))
|
||||
} else {
|
||||
@Suppress("deprecation")
|
||||
getApplicationInfo(packageName, flags)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun PackageManager.queryIntentActivitiesCompat(intent: Intent, flags: Int): List<ResolveInfo> {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
queryIntentActivities(intent, PackageManager.ResolveInfoFlags.of(flags.toLong()))
|
||||
} else {
|
||||
@Suppress("deprecation")
|
||||
queryIntentActivities(intent, flags)
|
||||
}
|
||||
}
|
32
android/app/src/main/res/drawable/ic_launcher_mono.xml
Normal file
|
@ -0,0 +1,32 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="100dp"
|
||||
android:height="100dp"
|
||||
android:viewportWidth="100"
|
||||
android:viewportHeight="100">
|
||||
<group
|
||||
android:scaleX=".44"
|
||||
android:scaleY=".44"
|
||||
android:translateX="28"
|
||||
android:translateY="30">
|
||||
<path
|
||||
android:pathData="M3.925,16.034 L60.825,72.933a2.421,2.421 0.001,0 0,3.423 0l10.604,-10.603a6.789,6.789 90.001,0 0,0 -9.601L34.066,11.942A8.264,8.264 22.5,0 0,28.222 9.522H6.623A3.815,3.815 112.5,0 0,3.925 16.034Z"
|
||||
android:strokeWidth="5"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:pathData="m36.36,65.907v28.743a2.557,2.557 22.5,0 0,4.364 1.808L53.817,83.364a6.172,6.172 90,0 0,0 -8.729L42.532,63.35a3.616,3.616 157.5,0 0,-6.172 2.557z"
|
||||
android:strokeWidth="5"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:pathData="M79.653,40.078V11.335A2.557,2.557 22.5,0 0,75.289 9.527L62.195,22.62a6.172,6.172 90,0 0,0 8.729l11.285,11.285a3.616,3.616 157.5,0 0,6.172 -2.557z"
|
||||
android:strokeWidth="5"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineJoin="round" />
|
||||
<path
|
||||
android:pathData="M96.613,16.867 L89.085,9.339a1.917,1.917 157.5,0 0,-3.273 1.356v6.172a4.629,4.629 45,0 0,4.629 4.629h4.255a2.712,2.712 112.5,0 0,1.917 -4.629z"
|
||||
android:strokeWidth="5"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineJoin="round" />
|
||||
</group>
|
||||
</vector>
|
|
@ -2,4 +2,5 @@
|
|||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_mono" />
|
||||
</adaptive-icon>
|
|
@ -2,4 +2,5 @@
|
|||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||
<monochrome android:drawable="@drawable/ic_launcher_mono" />
|
||||
</adaptive-icon>
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="wallpaper">Hintergrundbild</string>
|
||||
<string name="search_shortcut_short_label">Suche</string>
|
||||
<string name="videos_shortcut_short_label">Videos</string>
|
||||
<string name="analysis_channel_name">Analyse von Medien</string>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="wallpaper">Fondo de pantalla</string>
|
||||
<string name="search_shortcut_short_label">Búsqueda</string>
|
||||
<string name="videos_shortcut_short_label">Videos</string>
|
||||
<string name="analysis_channel_name">Explorar medios</string>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="wallpaper">Fond d’écran</string>
|
||||
<string name="search_shortcut_short_label">Recherche</string>
|
||||
<string name="videos_shortcut_short_label">Vidéos</string>
|
||||
<string name="analysis_channel_name">Analyse des images</string>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="wallpaper">Wallpaper</string>
|
||||
<string name="search_shortcut_short_label">Cari</string>
|
||||
<string name="videos_shortcut_short_label">Video</string>
|
||||
<string name="analysis_channel_name">Pindai media</string>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="wallpaper">Sfondo</string>
|
||||
<string name="search_shortcut_short_label">Ricerca</string>
|
||||
<string name="videos_shortcut_short_label">Video</string>
|
||||
<string name="analysis_channel_name">Scansione media</string>
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="search_shortcut_short_label">検索</string>
|
||||
<string name="videos_shortcut_short_label">動画</string>
|
||||
<string name="analysis_channel_name">メディアスキャン</string>
|
||||
<string name="analysis_service_description">画像と動画をスキャン</string>
|
||||
<string name="analysis_notification_default_title">メディアをスキャン中</string>
|
||||
<string name="analysis_notification_action_stop">停止</string>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="wallpaper">壁紙</string>
|
||||
<string name="search_shortcut_short_label">検索</string>
|
||||
<string name="videos_shortcut_short_label">動画</string>
|
||||
<string name="analysis_channel_name">メディアスキャン</string>
|
||||
<string name="analysis_service_description">画像と動画をスキャン</string>
|
||||
<string name="analysis_notification_default_title">メディアをスキャン中</string>
|
||||
<string name="analysis_notification_action_stop">停止</string>
|
||||
</resources>
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">아베스</string>
|
||||
<string name="wallpaper">배경화면</string>
|
||||
<string name="search_shortcut_short_label">검색</string>
|
||||
<string name="videos_shortcut_short_label">동영상</string>
|
||||
<string name="analysis_channel_name">미디어 분석</string>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="wallpaper">Papel de parede</string>
|
||||
<string name="search_shortcut_short_label">Procurar</string>
|
||||
<string name="videos_shortcut_short_label">Vídeos</string>
|
||||
<string name="analysis_channel_name">Digitalização de mídia</string>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="wallpaper">Обои</string>
|
||||
<string name="search_shortcut_short_label">Поиск</string>
|
||||
<string name="videos_shortcut_short_label">Видео</string>
|
||||
<string name="analysis_channel_name">Сканировать медия</string>
|
||||
|
|
11
android/app/src/main/res/values-tr/strings.xml
Normal file
|
@ -0,0 +1,11 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="wallpaper">Duvar kağıdı</string>
|
||||
<string name="search_shortcut_short_label">Arama</string>
|
||||
<string name="videos_shortcut_short_label">Videolar</string>
|
||||
<string name="analysis_channel_name">Medya tarama</string>
|
||||
<string name="analysis_service_description">Görüntüleri ve videoları tarayın</string>
|
||||
<string name="analysis_notification_default_title">Medya taranıyor</string>
|
||||
<string name="analysis_notification_action_stop">Durdur</string>
|
||||
</resources>
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="wallpaper">壁纸</string>
|
||||
<string name="search_shortcut_short_label">搜索</string>
|
||||
<string name="videos_shortcut_short_label">视频</string>
|
||||
<string name="analysis_channel_name">媒体扫描</string>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="wallpaper">Wallpaper</string>
|
||||
<string name="search_shortcut_short_label">Search</string>
|
||||
<string name="videos_shortcut_short_label">Videos</string>
|
||||
<string name="analysis_channel_name">Media scan</string>
|
||||
|
|
|
@ -1,17 +1,17 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
buildscript {
|
||||
ext.kotlin_version = '1.6.21'
|
||||
ext.kotlin_version = '1.7.0'
|
||||
repositories {
|
||||
google()
|
||||
mavenCentral()
|
||||
maven { url 'https://developer.huawei.com/repo/' }
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.2.0'
|
||||
classpath 'com.android.tools.build:gradle:7.2.1'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
// GMS & Firebase Crashlytics (used by some flavors only)
|
||||
classpath 'com.google.gms:google-services:4.3.10'
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.8.1'
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.0'
|
||||
// HMS (used by some flavors only)
|
||||
classpath 'com.huawei.agconnect:agcp:1.5.2.300'
|
||||
}
|
||||
|
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
<b>Navigation und Suche</b> ist ein wichtiger Bestandteil von <i>Aves</i>. Das Ziel besteht darin, dass Benutzer problemlos von Alben zu Fotos zu Tags zu Karten usw. wechseln können.
|
||||
|
||||
<i>Aves</i> lässt sich mit Android (von <b>API 19 bis 32</b>, d. h. von KitKat bis Android 12L) mit Funktionen wie <b>App-Verknüpfungen</b> und <b>globaler Suche</b> integrieren. Es funktioniert auch als <b>Medienbetrachter und -auswahl</b>.
|
||||
<i>Aves</i> lässt sich mit Android (von <b>API 19 bis 33</b>, d. h. von KitKat bis Android 13) mit Funktionen wie <b>App-Verknüpfungen</b> und <b>globaler Suche</b> integrieren. Es funktioniert auch als <b>Medienbetrachter und -auswahl</b>.
|
5
fastlane/metadata/android/en-US/changelogs/1075.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
In v1.6.9:
|
||||
- start slideshows
|
||||
- change your wallpaper
|
||||
- enjoy the app in Turkish
|
||||
Full changelog available on GitHub
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
<b>Navigation and search</b> is an important part of <i>Aves</i>. The goal is for users to easily flow from albums to photos to tags to maps, etc.
|
||||
|
||||
<i>Aves</i> integrates with Android (from <b>API 19 to 32</b>, i.e. from KitKat to Android 12L) with features such as <b>app shortcuts</b> and <b>global search</b> handling. It also works as a <b>media viewer and picker</b>.
|
||||
<i>Aves</i> integrates with Android (from <b>API 19 to 33</b>, i.e. from KitKat to Android 13) with features such as <b>app shortcuts</b> and <b>global search</b> handling. It also works as a <b>media viewer and picker</b>.
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
La <b>navegación y búsqueda</b> son partes importantes de <i>Aves</i>. Su propósito es que los usuarios puedan fácimente ir de álbumes a fotos, etiquetas, mapas, etc.
|
||||
|
||||
<i>Aves</i> se integra con Android (desde <b>API 19 a 32</b>, por ej. desde KitKat hasta Android 12L) con características como <b>vínculos de aplicación</b> y manejo de <b>búsqueda global</b>. También funciona como un <b>visor y seleccionador multimedia</b>.
|
||||
<i>Aves</i> se integra con Android (desde <b>API 19 a 33</b>, por ej. desde KitKat hasta Android 13) con características como <b>vínculos de aplicación</b> y manejo de <b>búsqueda global</b>. También funciona como un <b>visor y seleccionador multimedia</b>.
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
<b>Navigasi dan pencarian</b> merupakan bagian penting dari <i>Aves</i>. Tujuannya adalah agar pengguna dengan mudah mengalir dari album ke foto ke tag ke peta, dll.
|
||||
|
||||
<i>Aves</i> terintegrasi dengan Android (dari <b>API 19 ke 32</b>, yaitu dari KitKat ke Android 12L) dengan fitur-fitur seperti <b>pintasan aplikasi</b> dan <b>pencarian global</b> penanganan. Ini juga berfungsi sebagai <b>penampil dan pemilih media</b>.
|
||||
<i>Aves</i> terintegrasi dengan Android (dari <b>API 19 ke 33</b>, yaitu dari KitKat ke Android 13) dengan fitur-fitur seperti <b>pintasan aplikasi</b> dan <b>pencarian global</b> penanganan. Ini juga berfungsi sebagai <b>penampil dan pemilih media</b>.
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
<b>Navigazione e ricerca</b> sono una parte importante di <i>Aves</i>. L'obiettivo è che gli utenti passino facilmente dagli album alle foto, ai tag, alle mappe, ecc.
|
||||
|
||||
<i>Aves</i> si integra con Android (da <b>API 19 a 32</b>, cioè da KitKat ad Android 12L) con caratteristiche come <b>collegamenti alle app</b> e la gestione della <b>ricerca globale</b>. Funziona anche come <b>visualizzazione e raccolta di media</b>.
|
||||
<i>Aves</i> si integra con Android (da <b>API 19 a 33</b>, cioè da KitKat ad Android 13) con caratteristiche come <b>collegamenti alle app</b> e la gestione della <b>ricerca globale</b>. Funziona anche come <b>visualizzazione e raccolta di media</b>.
|
||||
|
|
|
@ -4,4 +4,4 @@
|
|||
|
||||
<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>としても機能します。
|
||||
<i>Aves</i>は、<b>アプリショートカット</b>や<b>グローバル検索</b>などの機能を、Android(<b>API 19から33まで</b>、つまりAndroid 4.4から13 Lまで)と統合しています。また、<b>メディアビューワー</b>や<b>メディアピッカー</b>としても機能します。
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
<b>Navegação e pesquisa</b> é uma parte importante do <i>Aves</i>. O objetivo é que os usuários fluam facilmente de álbuns para fotos, etiquetas, mapas, etc.
|
||||
|
||||
<i>Aves</i> integra com Android (de <b>API 19 para 32</b>, i.e. de KitKat para Android 12L) com recursos como <b>atalhos de apps</b> e <b>pesquisa global</b> manipulação. Também funciona como um <b>visualizador e selecionador de mídia</b>.
|
||||
<i>Aves</i> integra com Android (de <b>API 19 para 33</b>, i.e. de KitKat para Android 13) com recursos como <b>atalhos de apps</b> e <b>pesquisa global</b> manipulação. Também funciona como um <b>visualizador e selecionador de mídia</b>.
|
||||
|
|
5
fastlane/metadata/android/tr/full_description.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
<i>Aves</i> tipik JPEG ve MP4'lerin yanı sıra <b>çok sayfalı TIFF'ler, SVG'ler, eski AVI'ler ve daha fazlası</b> gibi daha egzotik şeyler de dahil olmak üzere her türlü görüntü ve videoyu işleyebilir! <b>Hareketli fotoğrafları</b>, <b>panoramaları</b> (fotoğraf küreleri olarak da bilinir), <b>360° videoları</b> ve <b>GeoTIFF</b> dosyalarını tanımlamak için medya koleksiyonunuzu tarar.
|
||||
|
||||
<b>Gezinme ve arama</b> <i>Aves'in</i> önemli bir parçasıdır. Amaç, kullanıcıların albümlerden fotoğraflara, etiketlerden haritalara vb. kolayca geçmesini sağlamaktır.
|
||||
|
||||
<i>Aves</i>, <b>uygulama kısayolları</b> ve <b>global arama<b> işleme gibi özelliklerle Android (<b>API 19'dan 33'ye</b>, yani KitKat'tan Android 13'ye kadar) ile entegre olur. Ayrıca bir <b>medya görüntüleyici ve alıcı</b> olarak da çalışır.
|
BIN
fastlane/metadata/android/tr/images/featureGraphic.png
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
fastlane/metadata/android/tr/images/phoneScreenshots/1.png
Normal file
After Width: | Height: | Size: 245 KiB |
BIN
fastlane/metadata/android/tr/images/phoneScreenshots/2.png
Normal file
After Width: | Height: | Size: 494 KiB |
BIN
fastlane/metadata/android/tr/images/phoneScreenshots/3.png
Normal file
After Width: | Height: | Size: 208 KiB |
BIN
fastlane/metadata/android/tr/images/phoneScreenshots/4.png
Normal file
After Width: | Height: | Size: 92 KiB |
BIN
fastlane/metadata/android/tr/images/phoneScreenshots/5.png
Normal file
After Width: | Height: | Size: 79 KiB |
BIN
fastlane/metadata/android/tr/images/phoneScreenshots/6.png
Normal file
After Width: | Height: | Size: 338 KiB |
1
fastlane/metadata/android/tr/short_description.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Galeri ve meta veri gezgini
|
|
@ -2,4 +2,4 @@
|
|||
|
||||
<b>导航与搜索</b>是 <i>Aves</i> 的核心功能之一,旨在帮助用户在相册、照片、标签、地图等之间轻松切换。
|
||||
|
||||
<i> Aves</i> 与 Android(<b>API 19-32</b>,即从 KitKat 到 Android 12L)集成,具有<b>快捷方式</b>和<b>全局搜索</b>等功能。它还可用作<b>媒体查看器和选择器<b>。
|
||||
<i> Aves</i> 与 Android(<b>API 19-33</b>,即从 KitKat 到 Android 13)集成,具有<b>快捷方式</b>和<b>全局搜索</b>等功能。它还可用作<b>媒体查看器和选择器<b>。
|
||||
|
|
|
@ -1,4 +1,13 @@
|
|||
enum AppMode { main, pickSingleMediaExternal, pickMultipleMediaExternal, pickMediaInternal, pickFilterInternal, view }
|
||||
enum AppMode {
|
||||
main,
|
||||
pickSingleMediaExternal,
|
||||
pickMultipleMediaExternal,
|
||||
pickMediaInternal,
|
||||
pickFilterInternal,
|
||||
setWallpaper,
|
||||
slideshow,
|
||||
view,
|
||||
}
|
||||
|
||||
extension ExtraAppMode on AppMode {
|
||||
bool get canSearch => this == AppMode.main || this == AppMode.pickSingleMediaExternal || this == AppMode.pickMultipleMediaExternal;
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
"entryActionDelete": "Löschen",
|
||||
"entryActionConvert": "Konvertieren",
|
||||
"entryActionExport": "Exportieren",
|
||||
"entryActionInfo": "Info",
|
||||
"entryActionRename": "Umbenennen",
|
||||
"entryActionRestore": "Wiederherstellen",
|
||||
"entryActionRotateCCW": "Drehen gegen den Uhrzeigersinn",
|
||||
|
@ -80,6 +81,9 @@
|
|||
"videoActionSetSpeed": "Wiedergabegeschwindigkeit",
|
||||
"videoActionSettings": "Einstellungen",
|
||||
|
||||
"slideshowActionResume": "Wiedergabe",
|
||||
"slideshowActionShowInCollection": "In Sammlung anzeigen",
|
||||
|
||||
"entryInfoActionEditDate": "Datum & Uhrzeit bearbeiten",
|
||||
"entryInfoActionEditLocation": "Standort bearbeiten",
|
||||
"entryInfoActionEditRating": "Bewertung bearbeiten",
|
||||
|
@ -144,10 +148,23 @@
|
|||
"displayRefreshRatePreferHighest": "Höchste Rate",
|
||||
"displayRefreshRatePreferLowest": "Niedrigste Rate",
|
||||
|
||||
"slideshowVideoPlaybackSkip": "Überspringen",
|
||||
"slideshowVideoPlaybackMuted": "Stumm abspielen",
|
||||
"slideshowVideoPlaybackWithSound": "Mit Ton abspielen",
|
||||
|
||||
"themeBrightnessLight": "Hell",
|
||||
"themeBrightnessDark": "Dunkel",
|
||||
"themeBrightnessBlack": "Schwarz",
|
||||
|
||||
"viewerTransitionSlide": "Dia",
|
||||
"viewerTransitionParallax": "Parallaxe",
|
||||
"viewerTransitionFade": "Ausblenden",
|
||||
"viewerTransitionZoomIn": "Heranzoomen",
|
||||
|
||||
"wallpaperTargetHome": "Startbildschirm",
|
||||
"wallpaperTargetLock": "Sperrbildschirm",
|
||||
"wallpaperTargetHomeLock": "Start- und Sperrbildschirm",
|
||||
|
||||
"albumTierNew": "Neu",
|
||||
"albumTierPinned": "Angeheftet",
|
||||
"albumTierSpecial": "Häufig verwendet",
|
||||
|
@ -262,6 +279,7 @@
|
|||
"menuActionSelectAll": "Alle auswählen",
|
||||
"menuActionSelectNone": "Keine auswählen",
|
||||
"menuActionMap": "Karte",
|
||||
"menuActionSlideshow": "Diashow",
|
||||
"menuActionStats": "Statistiken",
|
||||
|
||||
"viewDialogTabSort": "Sortieren",
|
||||
|
@ -349,6 +367,7 @@
|
|||
"collectionEmptyFavourites": "Keine Favoriten",
|
||||
"collectionEmptyVideos": "Keine Videos",
|
||||
"collectionEmptyImages": "Keine Bilder",
|
||||
"collectionEmptyGrantAccessButtonLabel": "Zugriff gewähren",
|
||||
|
||||
"collectionSelectSectionTooltip": "Bereich auswählen",
|
||||
"collectionDeselectSectionTooltip": "Bereich abwählen",
|
||||
|
@ -479,6 +498,17 @@
|
|||
"settingsViewerShowOverlayThumbnails": "Vorschaubilder anzeigen",
|
||||
"settingsViewerEnableOverlayBlurEffect": "Unschärfe-Effekt",
|
||||
|
||||
"settingsViewerSlideshowTile": "Diashow",
|
||||
"settingsViewerSlideshowTitle": "Diashow",
|
||||
"settingsSlideshowRepeat": "Wiederholung",
|
||||
"settingsSlideshowShuffle": "Mischen",
|
||||
"settingsSlideshowTransitionTile": "Übergang",
|
||||
"settingsSlideshowTransitionTitle": "Übergang",
|
||||
"settingsSlideshowIntervalTile": "Intervall",
|
||||
"settingsSlideshowIntervalTitle": "Intervall",
|
||||
"settingsSlideshowVideoPlaybackTile": "Videowiedergabe",
|
||||
"settingsSlideshowVideoPlaybackTitle": "Videowiedergabe",
|
||||
|
||||
"settingsVideoPageTitle": "Video-Einstellungen",
|
||||
"settingsSectionVideo": "Video",
|
||||
"settingsVideoShowVideos": "Videos anzeigen",
|
||||
|
@ -543,6 +573,7 @@
|
|||
"settingsSectionDisplay": "Anzeige",
|
||||
"settingsThemeBrightness": "Thema",
|
||||
"settingsThemeColorHighlights": "Farbige Highlights",
|
||||
"settingsThemeEnableDynamicColor": "Dynamische Farben",
|
||||
"settingsDisplayRefreshRateModeTile": "Bildwiederholrate der Anzeige",
|
||||
"settingsDisplayRefreshRateModeTitle": "Bildwiederholrate",
|
||||
|
||||
|
@ -560,6 +591,7 @@
|
|||
"statsTopTags": "Top-Tags",
|
||||
|
||||
"viewerOpenPanoramaButtonLabel": "ÖFFNE PANORAMA",
|
||||
"viewerSetWallpaperButtonLabel": "HINTERGRUNDBILD EINSTELLEN",
|
||||
"viewerErrorUnknown": "Ups!",
|
||||
"viewerErrorDoesNotExist": "Die Datei existiert nicht mehr.",
|
||||
|
||||
|
|
|
@ -78,6 +78,7 @@
|
|||
"entryActionDelete": "Delete",
|
||||
"entryActionConvert": "Convert",
|
||||
"entryActionExport": "Export",
|
||||
"entryActionInfo": "Info",
|
||||
"entryActionRename": "Rename",
|
||||
"entryActionRestore": "Restore",
|
||||
"entryActionRotateCCW": "Rotate counterclockwise",
|
||||
|
@ -108,6 +109,9 @@
|
|||
"videoActionSetSpeed": "Playback speed",
|
||||
"videoActionSettings": "Settings",
|
||||
|
||||
"slideshowActionResume": "Resume",
|
||||
"slideshowActionShowInCollection": "Show in Collection",
|
||||
|
||||
"entryInfoActionEditDate": "Edit date & time",
|
||||
"entryInfoActionEditLocation": "Edit location",
|
||||
"entryInfoActionEditRating": "Edit rating",
|
||||
|
@ -184,10 +188,23 @@
|
|||
"displayRefreshRatePreferHighest": "Highest rate",
|
||||
"displayRefreshRatePreferLowest": "Lowest rate",
|
||||
|
||||
"slideshowVideoPlaybackSkip": "Skip",
|
||||
"slideshowVideoPlaybackMuted": "Play muted",
|
||||
"slideshowVideoPlaybackWithSound": "Play with sound",
|
||||
|
||||
"themeBrightnessLight": "Light",
|
||||
"themeBrightnessDark": "Dark",
|
||||
"themeBrightnessBlack": "Black",
|
||||
|
||||
"viewerTransitionSlide": "Slide",
|
||||
"viewerTransitionParallax": "Parallax",
|
||||
"viewerTransitionFade": "Fade",
|
||||
"viewerTransitionZoomIn": "Zoom in",
|
||||
|
||||
"wallpaperTargetHome": "Home screen",
|
||||
"wallpaperTargetLock": "Lock screen",
|
||||
"wallpaperTargetHomeLock": "Home and lock screens",
|
||||
|
||||
"albumTierNew": "New",
|
||||
"albumTierPinned": "Pinned",
|
||||
"albumTierSpecial": "Common",
|
||||
|
@ -392,6 +409,7 @@
|
|||
"menuActionSelectAll": "Select all",
|
||||
"menuActionSelectNone": "Select none",
|
||||
"menuActionMap": "Map",
|
||||
"menuActionSlideshow": "Slideshow",
|
||||
"menuActionStats": "Stats",
|
||||
|
||||
"viewDialogTabSort": "Sort",
|
||||
|
@ -529,6 +547,7 @@
|
|||
"collectionEmptyFavourites": "No favorites",
|
||||
"collectionEmptyVideos": "No videos",
|
||||
"collectionEmptyImages": "No images",
|
||||
"collectionEmptyGrantAccessButtonLabel": "Grant access",
|
||||
|
||||
"collectionSelectSectionTooltip": "Select section",
|
||||
"collectionDeselectSectionTooltip": "Deselect section",
|
||||
|
@ -659,6 +678,17 @@
|
|||
"settingsViewerShowOverlayThumbnails": "Show thumbnails",
|
||||
"settingsViewerEnableOverlayBlurEffect": "Blur effect",
|
||||
|
||||
"settingsViewerSlideshowTile": "Slideshow",
|
||||
"settingsViewerSlideshowTitle": "Slideshow",
|
||||
"settingsSlideshowRepeat": "Repeat",
|
||||
"settingsSlideshowShuffle": "Shuffle",
|
||||
"settingsSlideshowTransitionTile": "Transition",
|
||||
"settingsSlideshowTransitionTitle": "Transition",
|
||||
"settingsSlideshowIntervalTile": "Interval",
|
||||
"settingsSlideshowIntervalTitle": "Interval",
|
||||
"settingsSlideshowVideoPlaybackTile": "Video playback",
|
||||
"settingsSlideshowVideoPlaybackTitle": "Video Playback",
|
||||
|
||||
"settingsVideoPageTitle": "Video Settings",
|
||||
"settingsSectionVideo": "Video",
|
||||
"settingsVideoShowVideos": "Show videos",
|
||||
|
@ -723,6 +753,7 @@
|
|||
"settingsSectionDisplay": "Display",
|
||||
"settingsThemeBrightness": "Theme",
|
||||
"settingsThemeColorHighlights": "Color highlights",
|
||||
"settingsThemeEnableDynamicColor": "Dynamic color",
|
||||
"settingsDisplayRefreshRateModeTile": "Display refresh rate",
|
||||
"settingsDisplayRefreshRateModeTitle": "Refresh Rate",
|
||||
|
||||
|
@ -745,6 +776,7 @@
|
|||
"statsTopTags": "Top Tags",
|
||||
|
||||
"viewerOpenPanoramaButtonLabel": "OPEN PANORAMA",
|
||||
"viewerSetWallpaperButtonLabel": "SET WALLPAPER",
|
||||
"viewerErrorUnknown": "Oops!",
|
||||
"viewerErrorDoesNotExist": "The file no longer exists.",
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
"entryActionDelete": "Borrar",
|
||||
"entryActionConvert": "Convertir",
|
||||
"entryActionExport": "Exportar",
|
||||
"entryActionInfo": "Información",
|
||||
"entryActionRename": "Renombrar",
|
||||
"entryActionRestore": "Restaurar",
|
||||
"entryActionRotateCCW": "Rotar en sentido antihorario",
|
||||
|
@ -80,6 +81,9 @@
|
|||
"videoActionSetSpeed": "Velocidad de reproducción",
|
||||
"videoActionSettings": "Ajustes",
|
||||
|
||||
"slideshowActionResume": "Reanudar",
|
||||
"slideshowActionShowInCollection": "Mostrar en Colección",
|
||||
|
||||
"entryInfoActionEditDate": "Editar fecha y hora",
|
||||
"entryInfoActionEditLocation": "Editar ubicación",
|
||||
"entryInfoActionEditRating": "Editar clasificación",
|
||||
|
@ -144,10 +148,23 @@
|
|||
"displayRefreshRatePreferHighest": "Alta tasa",
|
||||
"displayRefreshRatePreferLowest": "Baja tasa",
|
||||
|
||||
"slideshowVideoPlaybackSkip": "Saltear",
|
||||
"slideshowVideoPlaybackMuted": "Reproducir sin sonido",
|
||||
"slideshowVideoPlaybackWithSound": "Reproducir con sonido",
|
||||
|
||||
"themeBrightnessLight": "Claro",
|
||||
"themeBrightnessDark": "Obscuro",
|
||||
"themeBrightnessBlack": "Negro",
|
||||
|
||||
"viewerTransitionSlide": "Diapositiva",
|
||||
"viewerTransitionParallax": "Paralaje",
|
||||
"viewerTransitionFade": "Desvanecer",
|
||||
"viewerTransitionZoomIn": "Acercar",
|
||||
|
||||
"wallpaperTargetHome": "Pantalla de inicio",
|
||||
"wallpaperTargetLock": "Pantalla de bloqueo",
|
||||
"wallpaperTargetHomeLock": "Pantallas de inicio y bloqueo",
|
||||
|
||||
"albumTierNew": "Nuevo",
|
||||
"albumTierPinned": "Fijado",
|
||||
"albumTierSpecial": "Común",
|
||||
|
@ -262,6 +279,7 @@
|
|||
"menuActionSelectAll": "Seleccionar todo",
|
||||
"menuActionSelectNone": "Deseleccionar",
|
||||
"menuActionMap": "Mapa",
|
||||
"menuActionSlideshow": "Presentación",
|
||||
"menuActionStats": "Estadísticas",
|
||||
|
||||
"viewDialogTabSort": "Ordenar",
|
||||
|
@ -278,7 +296,6 @@
|
|||
"appPickDialogTitle": "Escoger aplicación",
|
||||
"appPickDialogNone": "Ninguna",
|
||||
|
||||
|
||||
"aboutPageTitle": "Acerca de",
|
||||
"aboutLinkSources": "Fuentes",
|
||||
"aboutLinkLicense": "Licencia",
|
||||
|
@ -296,7 +313,6 @@
|
|||
"aboutCreditsWorldAtlas1": "Esta aplicación usa un archivo TopoJSON de",
|
||||
"aboutCreditsWorldAtlas2": "bajo licencia ISC.",
|
||||
"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.",
|
||||
|
@ -351,6 +367,7 @@
|
|||
"collectionEmptyFavourites": "Sin favoritos",
|
||||
"collectionEmptyVideos": "Sin videos",
|
||||
"collectionEmptyImages": "Sin imágenes",
|
||||
"collectionEmptyGrantAccessButtonLabel": "Otorgar accceso",
|
||||
|
||||
"collectionSelectSectionTooltip": "Seleccionar sección",
|
||||
"collectionDeselectSectionTooltip": "Deseleccionar sección",
|
||||
|
@ -421,6 +438,7 @@
|
|||
|
||||
"settingsSectionNavigation": "Navegación",
|
||||
"settingsHome": "Inicio",
|
||||
"settingsShowBottomNavigationBar": "Mostrar barra de navegación inferior",
|
||||
"settingsKeepScreenOnTile": "Mantener pantalla encendida",
|
||||
"settingsKeepScreenOnTitle": "Mantener pantalla encendida",
|
||||
"settingsDoubleBackExit": "Presione «atrás» dos veces para salir",
|
||||
|
@ -443,6 +461,7 @@
|
|||
"settingsThumbnailOverlayTile": "Incrustaciones",
|
||||
"settingsThumbnailOverlayTitle": "Incrustaciones",
|
||||
"settingsThumbnailShowFavouriteIcon": "Mostrar icono de favoritos",
|
||||
"settingsThumbnailShowTagIcon": "Mostrar ícono de etiqueta",
|
||||
"settingsThumbnailShowLocationIcon": "Mostrar icono de ubicación",
|
||||
"settingsThumbnailShowMotionPhotoIcon": "Mostrar icono de foto en movimiento",
|
||||
"settingsThumbnailShowRating": "Mostrar clasificación",
|
||||
|
@ -479,6 +498,17 @@
|
|||
"settingsViewerShowOverlayThumbnails": "Mostrar miniaturas",
|
||||
"settingsViewerEnableOverlayBlurEffect": "Efecto de difuminado",
|
||||
|
||||
"settingsViewerSlideshowTile": "Presentación",
|
||||
"settingsViewerSlideshowTitle": "Presentación",
|
||||
"settingsSlideshowRepeat": "Repetir",
|
||||
"settingsSlideshowShuffle": "Mezclar",
|
||||
"settingsSlideshowTransitionTile": "Transición",
|
||||
"settingsSlideshowTransitionTitle": "Transición",
|
||||
"settingsSlideshowIntervalTile": "Intervalo",
|
||||
"settingsSlideshowIntervalTitle": "Intervalo",
|
||||
"settingsSlideshowVideoPlaybackTile": "Reproducción de video",
|
||||
"settingsSlideshowVideoPlaybackTitle": "Reproducción de video",
|
||||
|
||||
"settingsVideoPageTitle": "Ajustes de video",
|
||||
"settingsSectionVideo": "Video",
|
||||
"settingsVideoShowVideos": "Mostrar videos",
|
||||
|
@ -486,8 +516,6 @@
|
|||
"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",
|
||||
|
@ -545,6 +573,7 @@
|
|||
"settingsSectionDisplay": "Pantalla",
|
||||
"settingsThemeBrightness": "Tema",
|
||||
"settingsThemeColorHighlights": "Acentos de color",
|
||||
"settingsThemeEnableDynamicColor": "Color dinámico",
|
||||
"settingsDisplayRefreshRateModeTile": "Tasa de refresco de la pantalla",
|
||||
"settingsDisplayRefreshRateModeTitle": "Tasa de refresco",
|
||||
|
||||
|
@ -562,6 +591,7 @@
|
|||
"statsTopTags": "Etiquetas principales",
|
||||
|
||||
"viewerOpenPanoramaButtonLabel": "ABRIR PANORÁMICA",
|
||||
"viewerSetWallpaperButtonLabel": "ESTABLECER FONDO",
|
||||
"viewerErrorUnknown": "¡Ups!",
|
||||
"viewerErrorDoesNotExist": "El archivo no existe.",
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
"entryActionDelete": "Supprimer",
|
||||
"entryActionConvert": "Convertir",
|
||||
"entryActionExport": "Exporter",
|
||||
"entryActionInfo": "Détails",
|
||||
"entryActionRename": "Renommer",
|
||||
"entryActionRestore": "Restaurer",
|
||||
"entryActionRotateCCW": "Pivoter à gauche",
|
||||
|
@ -80,6 +81,9 @@
|
|||
"videoActionSetSpeed": "Vitesse de lecture",
|
||||
"videoActionSettings": "Préférences",
|
||||
|
||||
"slideshowActionResume": "Reprendre",
|
||||
"slideshowActionShowInCollection": "Afficher dans Collection",
|
||||
|
||||
"entryInfoActionEditDate": "Modifier la date",
|
||||
"entryInfoActionEditLocation": "Modifier le lieu",
|
||||
"entryInfoActionEditRating": "Modifier la notation",
|
||||
|
@ -144,10 +148,23 @@
|
|||
"displayRefreshRatePreferHighest": "Fréquence maximale",
|
||||
"displayRefreshRatePreferLowest": "Fréquence minimale",
|
||||
|
||||
"slideshowVideoPlaybackSkip": "Passer",
|
||||
"slideshowVideoPlaybackMuted": "Jouer sans son",
|
||||
"slideshowVideoPlaybackWithSound": "Jouer avec son",
|
||||
|
||||
"themeBrightnessLight": "Clair",
|
||||
"themeBrightnessDark": "Sombre",
|
||||
"themeBrightnessBlack": "Noir",
|
||||
|
||||
"viewerTransitionSlide": "Défilement",
|
||||
"viewerTransitionParallax": "Parallaxe",
|
||||
"viewerTransitionFade": "Fondu",
|
||||
"viewerTransitionZoomIn": "Zoom",
|
||||
|
||||
"wallpaperTargetHome": "Écran d’accueil",
|
||||
"wallpaperTargetLock": "Écran de verrouillage",
|
||||
"wallpaperTargetHomeLock": "Écrans accueil et verrouillage",
|
||||
|
||||
"albumTierNew": "Nouveaux",
|
||||
"albumTierPinned": "Épinglés",
|
||||
"albumTierSpecial": "Standards",
|
||||
|
@ -262,6 +279,7 @@
|
|||
"menuActionSelectAll": "Tout sélectionner",
|
||||
"menuActionSelectNone": "Tout désélectionner",
|
||||
"menuActionMap": "Carte",
|
||||
"menuActionSlideshow": "Diaporama",
|
||||
"menuActionStats": "Statistiques",
|
||||
|
||||
"viewDialogTabSort": "Tri",
|
||||
|
@ -349,6 +367,7 @@
|
|||
"collectionEmptyFavourites": "Aucun favori",
|
||||
"collectionEmptyVideos": "Aucune vidéo",
|
||||
"collectionEmptyImages": "Aucune image",
|
||||
"collectionEmptyGrantAccessButtonLabel": "Autoriser l’accès",
|
||||
|
||||
"collectionSelectSectionTooltip": "Sélectionner la section",
|
||||
"collectionDeselectSectionTooltip": "Désélectionner la section",
|
||||
|
@ -479,6 +498,17 @@
|
|||
"settingsViewerShowOverlayThumbnails": "Afficher les vignettes",
|
||||
"settingsViewerEnableOverlayBlurEffect": "Effets de flou",
|
||||
|
||||
"settingsViewerSlideshowTile": "Diaporama",
|
||||
"settingsViewerSlideshowTitle": "Diaporama",
|
||||
"settingsSlideshowRepeat": "Répéter",
|
||||
"settingsSlideshowShuffle": "Aléatoire",
|
||||
"settingsSlideshowTransitionTile": "Transition",
|
||||
"settingsSlideshowTransitionTitle": "Transition",
|
||||
"settingsSlideshowIntervalTile": "Intervalle",
|
||||
"settingsSlideshowIntervalTitle": "Intervalle",
|
||||
"settingsSlideshowVideoPlaybackTile": "Lecture de vidéos",
|
||||
"settingsSlideshowVideoPlaybackTitle": "Lecture de vidéos",
|
||||
|
||||
"settingsVideoPageTitle": "Réglages vidéo",
|
||||
"settingsSectionVideo": "Vidéo",
|
||||
"settingsVideoShowVideos": "Afficher les vidéos",
|
||||
|
@ -543,6 +573,7 @@
|
|||
"settingsSectionDisplay": "Affichage",
|
||||
"settingsThemeBrightness": "Thème",
|
||||
"settingsThemeColorHighlights": "Surlignages colorés",
|
||||
"settingsThemeEnableDynamicColor": "Couleur dynamique",
|
||||
"settingsDisplayRefreshRateModeTile": "Fréquence d’actualisation de l'écran",
|
||||
"settingsDisplayRefreshRateModeTitle": "Fréquence d’actualisation",
|
||||
|
||||
|
@ -560,6 +591,7 @@
|
|||
"statsTopTags": "Top libellés",
|
||||
|
||||
"viewerOpenPanoramaButtonLabel": "OUVRIR LE PANORAMA",
|
||||
"viewerSetWallpaperButtonLabel": "APPLIQUER",
|
||||
"viewerErrorUnknown": "Zut !",
|
||||
"viewerErrorDoesNotExist": "Le fichier n’existe plus.",
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
"entryActionDelete": "Hapus",
|
||||
"entryActionConvert": "Ubah",
|
||||
"entryActionExport": "Ekspor",
|
||||
"entryActionInfo": "Info",
|
||||
"entryActionRename": "Ganti nama",
|
||||
"entryActionRestore": "Pulihkan",
|
||||
"entryActionRotateCCW": "Putar berlawanan arah jarum jam",
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
"entryActionDelete": "Elimina",
|
||||
"entryActionConvert": "Converti",
|
||||
"entryActionExport": "Esportazione",
|
||||
"entryActionInfo": "Info",
|
||||
"entryActionRename": "Rinomina",
|
||||
"entryActionRestore": "Ripristina",
|
||||
"entryActionRotateCCW": "Ruota in senso antiorario",
|
||||
|
@ -80,6 +81,9 @@
|
|||
"videoActionSetSpeed": "Velocità di riproduzione",
|
||||
"videoActionSettings": "Impostazioni",
|
||||
|
||||
"slideshowActionResume": "Riprendi",
|
||||
"slideshowActionShowInCollection": "Mostra nella Collezione",
|
||||
|
||||
"entryInfoActionEditDate": "Modifica data e ora",
|
||||
"entryInfoActionEditLocation": "Modifica posizione",
|
||||
"entryInfoActionEditRating": "Modifica valutazione",
|
||||
|
@ -144,10 +148,23 @@
|
|||
"displayRefreshRatePreferHighest": "Frequenza massima",
|
||||
"displayRefreshRatePreferLowest": "Frequenza minima",
|
||||
|
||||
"slideshowVideoPlaybackSkip": "Salta",
|
||||
"slideshowVideoPlaybackMuted": "Riproduci senza audio",
|
||||
"slideshowVideoPlaybackWithSound": "Riproduci con audio",
|
||||
|
||||
"themeBrightnessLight": "Chiaro",
|
||||
"themeBrightnessDark": "Scuro",
|
||||
"themeBrightnessBlack": "Nero",
|
||||
|
||||
"viewerTransitionSlide": "Diapositiva",
|
||||
"viewerTransitionParallax": "Parallasse",
|
||||
"viewerTransitionFade": "Dissolvenza",
|
||||
"viewerTransitionZoomIn": "Ingrandisci",
|
||||
|
||||
"wallpaperTargetHome": "Schermata iniziale",
|
||||
"wallpaperTargetLock": "Schermata di blocco",
|
||||
"wallpaperTargetHomeLock": "Schermata iniziale e di blocco",
|
||||
|
||||
"albumTierNew": "Nuovi",
|
||||
"albumTierPinned": "Fissati",
|
||||
"albumTierSpecial": "Frequenti",
|
||||
|
@ -262,6 +279,7 @@
|
|||
"menuActionSelectAll": "Seleziona tutto",
|
||||
"menuActionSelectNone": "Deseleziona tutto",
|
||||
"menuActionMap": "Mappa",
|
||||
"menuActionSlideshow": "Presentazione",
|
||||
"menuActionStats": "Statistiche",
|
||||
|
||||
"viewDialogTabSort": "Ordina",
|
||||
|
@ -349,6 +367,7 @@
|
|||
"collectionEmptyFavourites": "Nessun preferito",
|
||||
"collectionEmptyVideos": "Nessun video",
|
||||
"collectionEmptyImages": "Nessuna immagine",
|
||||
"collectionEmptyGrantAccessButtonLabel": "Consenti accesso",
|
||||
|
||||
"collectionSelectSectionTooltip": "Seleziona sezione",
|
||||
"collectionDeselectSectionTooltip": "Deseleziona sezione",
|
||||
|
@ -479,6 +498,17 @@
|
|||
"settingsViewerShowOverlayThumbnails": "Mostra le miniature",
|
||||
"settingsViewerEnableOverlayBlurEffect": "Effetto sfocatura",
|
||||
|
||||
"settingsViewerSlideshowTile": "Presentazione",
|
||||
"settingsViewerSlideshowTitle": "Presentazione",
|
||||
"settingsSlideshowRepeat": "Ripeti",
|
||||
"settingsSlideshowShuffle": "Ordine casuale",
|
||||
"settingsSlideshowTransitionTile": "Transizione",
|
||||
"settingsSlideshowTransitionTitle": "Transizione",
|
||||
"settingsSlideshowIntervalTile": "Intervallo",
|
||||
"settingsSlideshowIntervalTitle": "Intervallo",
|
||||
"settingsSlideshowVideoPlaybackTile": "Riproduzione video",
|
||||
"settingsSlideshowVideoPlaybackTitle": "Riproduzione video",
|
||||
|
||||
"settingsVideoPageTitle": "Impostazioni video",
|
||||
"settingsSectionVideo": "Video",
|
||||
"settingsVideoShowVideos": "Mostra video",
|
||||
|
@ -543,6 +573,7 @@
|
|||
"settingsSectionDisplay": "Schermo",
|
||||
"settingsThemeBrightness": "Tema",
|
||||
"settingsThemeColorHighlights": "Colori evidenziati",
|
||||
"settingsThemeEnableDynamicColor": "Colori dinamici",
|
||||
"settingsDisplayRefreshRateModeTile": "Frequenza di aggiornamento dello schermo",
|
||||
"settingsDisplayRefreshRateModeTitle": "Frequenza di aggiornamento",
|
||||
|
||||
|
@ -560,6 +591,7 @@
|
|||
"statsTopTags": "Etichette più frequenti",
|
||||
|
||||
"viewerOpenPanoramaButtonLabel": "APRI PANORAMA",
|
||||
"viewerSetWallpaperButtonLabel": "IMPOSTA SFONDO",
|
||||
"viewerErrorUnknown": "Ops!",
|
||||
"viewerErrorDoesNotExist": "Il file non esiste più",
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
"entryActionDelete": "削除",
|
||||
"entryActionConvert": "変換",
|
||||
"entryActionExport": "エクスポート",
|
||||
"entryActionInfo": "情報",
|
||||
"entryActionRename": "名前を変更",
|
||||
"entryActionRestore": "元に戻す",
|
||||
"entryActionRotateCCW": "反時計回りに回転",
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
"entryActionDelete": "삭제",
|
||||
"entryActionConvert": "변환",
|
||||
"entryActionExport": "내보내기",
|
||||
"entryActionInfo": "상세정보",
|
||||
"entryActionRename": "이름 변경",
|
||||
"entryActionRestore": "복원",
|
||||
"entryActionRotateCCW": "좌회전",
|
||||
|
@ -80,6 +81,9 @@
|
|||
"videoActionSetSpeed": "재생 배속",
|
||||
"videoActionSettings": "설정",
|
||||
|
||||
"slideshowActionResume": "이어서",
|
||||
"slideshowActionShowInCollection": "미디어 페이지에서 보기",
|
||||
|
||||
"entryInfoActionEditDate": "날짜 및 시간 수정",
|
||||
"entryInfoActionEditLocation": "위치 수정",
|
||||
"entryInfoActionEditRating": "별점 수정",
|
||||
|
@ -144,10 +148,23 @@
|
|||
"displayRefreshRatePreferHighest": "가장 높은 재생률",
|
||||
"displayRefreshRatePreferLowest": "가장 낮은 재생률",
|
||||
|
||||
"slideshowVideoPlaybackSkip": "생략",
|
||||
"slideshowVideoPlaybackMuted": "음소거 재생",
|
||||
"slideshowVideoPlaybackWithSound": "일반 재생",
|
||||
|
||||
"themeBrightnessLight": "라이트",
|
||||
"themeBrightnessDark": "다크",
|
||||
"themeBrightnessBlack": "검은색",
|
||||
|
||||
"viewerTransitionSlide": "좌우",
|
||||
"viewerTransitionParallax": "시차",
|
||||
"viewerTransitionFade": "페이드",
|
||||
"viewerTransitionZoomIn": "확대",
|
||||
|
||||
"wallpaperTargetHome": "홈 화면",
|
||||
"wallpaperTargetLock": "잠금화면",
|
||||
"wallpaperTargetHomeLock": "홈 및 잠금화면",
|
||||
|
||||
"albumTierNew": "신규",
|
||||
"albumTierPinned": "고정",
|
||||
"albumTierSpecial": "기본",
|
||||
|
@ -262,6 +279,7 @@
|
|||
"menuActionSelectAll": "모두 선택",
|
||||
"menuActionSelectNone": "모두 해제",
|
||||
"menuActionMap": "지도",
|
||||
"menuActionSlideshow": "슬라이드쇼",
|
||||
"menuActionStats": "통계",
|
||||
|
||||
"viewDialogTabSort": "정렬",
|
||||
|
@ -349,6 +367,7 @@
|
|||
"collectionEmptyFavourites": "즐겨찾기가 없습니다",
|
||||
"collectionEmptyVideos": "동영상이 없습니다",
|
||||
"collectionEmptyImages": "사진이 없습니다",
|
||||
"collectionEmptyGrantAccessButtonLabel": "접근 허용",
|
||||
|
||||
"collectionSelectSectionTooltip": "묶음 선택",
|
||||
"collectionDeselectSectionTooltip": "묶음 선택 해제",
|
||||
|
@ -479,6 +498,17 @@
|
|||
"settingsViewerShowOverlayThumbnails": "섬네일 표시",
|
||||
"settingsViewerEnableOverlayBlurEffect": "흐림 효과",
|
||||
|
||||
"settingsViewerSlideshowTile": "슬라이드쇼",
|
||||
"settingsViewerSlideshowTitle": "슬라이드쇼",
|
||||
"settingsSlideshowRepeat": "반복",
|
||||
"settingsSlideshowShuffle": "순서섞기",
|
||||
"settingsSlideshowTransitionTile": "전환 효과",
|
||||
"settingsSlideshowTransitionTitle": "전환 효과",
|
||||
"settingsSlideshowIntervalTile": "교체 주기",
|
||||
"settingsSlideshowIntervalTitle": "교체 주기",
|
||||
"settingsSlideshowVideoPlaybackTile": "동영상 재생",
|
||||
"settingsSlideshowVideoPlaybackTitle": "동영상 재생",
|
||||
|
||||
"settingsVideoPageTitle": "동영상 설정",
|
||||
"settingsSectionVideo": "동영상",
|
||||
"settingsVideoShowVideos": "미디어에 동영상 표시",
|
||||
|
@ -543,6 +573,7 @@
|
|||
"settingsSectionDisplay": "디스플레이",
|
||||
"settingsThemeBrightness": "테마",
|
||||
"settingsThemeColorHighlights": "색 강조",
|
||||
"settingsThemeEnableDynamicColor": "동적 색상",
|
||||
"settingsDisplayRefreshRateModeTile": "화면 재생률",
|
||||
"settingsDisplayRefreshRateModeTitle": "화면 재생률",
|
||||
|
||||
|
@ -560,6 +591,7 @@
|
|||
"statsTopTags": "태그 랭킹",
|
||||
|
||||
"viewerOpenPanoramaButtonLabel": "파노라마 열기",
|
||||
"viewerSetWallpaperButtonLabel": "설정",
|
||||
"viewerErrorUnknown": "아이구!",
|
||||
"viewerErrorDoesNotExist": "파일이 존재하지 않습니다.",
|
||||
|
||||
|
|
|
@ -49,6 +49,7 @@
|
|||
"entryActionCopyToClipboard": "Copiar para área de transferência",
|
||||
"entryActionDelete": "Excluir",
|
||||
"entryActionExport": "Exportar",
|
||||
"entryActionInfo": "Informações",
|
||||
"entryActionConvert": "Converter",
|
||||
"entryActionRename": "Renomear",
|
||||
"entryActionRestore": "Restaurar",
|
||||
|
@ -80,6 +81,9 @@
|
|||
"videoActionSetSpeed": "Velocidade de reprodução",
|
||||
"videoActionSettings": "Configurações",
|
||||
|
||||
"slideshowActionResume": "Retomar",
|
||||
"slideshowActionShowInCollection": "Mostrar na Coleção",
|
||||
|
||||
"entryInfoActionEditDate": "Editar data e hora",
|
||||
"entryInfoActionEditLocation": "Editar localização",
|
||||
"entryInfoActionEditRating": "Editar classificação",
|
||||
|
@ -144,10 +148,23 @@
|
|||
"displayRefreshRatePreferHighest": "Taxa mais alta",
|
||||
"displayRefreshRatePreferLowest": "Taxa mais baixa",
|
||||
|
||||
"slideshowVideoPlaybackSkip": "Pular",
|
||||
"slideshowVideoPlaybackMuted": "Reproduzir sem som",
|
||||
"slideshowVideoPlaybackWithSound": "Reproduzir com som",
|
||||
|
||||
"themeBrightnessLight": "Claro",
|
||||
"themeBrightnessDark": "Escuro",
|
||||
"themeBrightnessBlack": "Preto",
|
||||
|
||||
"viewerTransitionSlide": "Deslizar",
|
||||
"viewerTransitionParallax": "Parallax",
|
||||
"viewerTransitionFade": "Desvaneça",
|
||||
"viewerTransitionZoomIn": "Mais zoom",
|
||||
|
||||
"wallpaperTargetHome": "Tela inicial",
|
||||
"wallpaperTargetLock": "Tela de bloqueio",
|
||||
"wallpaperTargetHomeLock": "Telas iniciais e de bloqueio",
|
||||
|
||||
"albumTierNew": "Novo",
|
||||
"albumTierPinned": "Fixada",
|
||||
"albumTierSpecial": "Comum",
|
||||
|
@ -262,6 +279,7 @@
|
|||
"menuActionSelectAll": "Selecionar tudo",
|
||||
"menuActionSelectNone": "Selecione nenhum",
|
||||
"menuActionMap": "Mapa",
|
||||
"menuActionSlideshow": "Apresentação de slides",
|
||||
"menuActionStats": "Estatísticas",
|
||||
|
||||
"viewDialogTabSort": "Organizar",
|
||||
|
@ -349,6 +367,7 @@
|
|||
"collectionEmptyFavourites": "Nenhum favorito",
|
||||
"collectionEmptyVideos": "Nenhum video",
|
||||
"collectionEmptyImages": "Nenhuma image",
|
||||
"collectionEmptyGrantAccessButtonLabel": "Garantir acesso",
|
||||
|
||||
"collectionSelectSectionTooltip": "Selecionar seção",
|
||||
"collectionDeselectSectionTooltip": "Desmarcar seção",
|
||||
|
@ -479,6 +498,17 @@
|
|||
"settingsViewerShowOverlayThumbnails": "Mostrar miniaturas",
|
||||
"settingsViewerEnableOverlayBlurEffect": "Efeito de desfoque",
|
||||
|
||||
"settingsViewerSlideshowTile": "Apresentação de slides",
|
||||
"settingsViewerSlideshowTitle": "Apresentação de slides",
|
||||
"settingsSlideshowRepeat": "Repetir",
|
||||
"settingsSlideshowShuffle": "Embaralhar",
|
||||
"settingsSlideshowTransitionTile": "Transição",
|
||||
"settingsSlideshowTransitionTitle": "Transição",
|
||||
"settingsSlideshowIntervalTile": "Intervalo",
|
||||
"settingsSlideshowIntervalTitle": "Intervalo",
|
||||
"settingsSlideshowVideoPlaybackTile": "Reprodução de vídeo",
|
||||
"settingsSlideshowVideoPlaybackTitle": "Reprodução de vídeo",
|
||||
|
||||
"settingsVideoPageTitle": "Configurações de vídeo",
|
||||
"settingsSectionVideo": "Vídeo",
|
||||
"settingsVideoShowVideos": "Mostrar vídeos",
|
||||
|
@ -543,6 +573,7 @@
|
|||
"settingsSectionDisplay": "Tela",
|
||||
"settingsThemeBrightness": "Tema",
|
||||
"settingsThemeColorHighlights": "Destaques de cores",
|
||||
"settingsThemeEnableDynamicColor": "Cor dinâmica",
|
||||
"settingsDisplayRefreshRateModeTile": "Taxa de atualização de exibição",
|
||||
"settingsDisplayRefreshRateModeTitle": "Taxa de atualização",
|
||||
|
||||
|
@ -560,6 +591,7 @@
|
|||
"statsTopTags": "Principais Etiquetas",
|
||||
|
||||
"viewerOpenPanoramaButtonLabel": "ABRIR PANORAMA",
|
||||
"viewerSetWallpaperButtonLabel": "DEFINIR PAPEL DE PAREDE",
|
||||
"viewerErrorUnknown": "Algo não está certo!",
|
||||
"viewerErrorDoesNotExist": "O arquivo não existe mais.",
|
||||
|
||||
|
|
|
@ -50,6 +50,7 @@
|
|||
"entryActionDelete": "Удалить",
|
||||
"entryActionConvert": "Конвертировать",
|
||||
"entryActionExport": "Экспорт",
|
||||
"entryActionInfo": "Информация",
|
||||
"entryActionRename": "Переименовать",
|
||||
"entryActionRestore": "Восстановить",
|
||||
"entryActionRotateCCW": "Повернуть против часовой стрелки",
|
||||
|
|
639
lib/l10n/app_tr.arb
Normal file
|
@ -0,0 +1,639 @@
|
|||
{
|
||||
"appName": "Aves",
|
||||
"welcomeMessage": "Aves'e Hoş Geldiniz",
|
||||
"welcomeOptional": "İsteğe bağlı",
|
||||
"welcomeTermsToggle": "Hüküm ve koşulları kabul ediyorum",
|
||||
"itemCount": "{count, plural, =1{1 öğe} other{{count} öğe}}",
|
||||
|
||||
"timeSeconds": "{seconds, plural, =1{1 saniye} other{{seconds} saniye}}",
|
||||
"timeMinutes": "{minutes, plural, =1{1 dakika} other{{minutes} dakika}}",
|
||||
"timeDays": "{days, plural, =1{1 gün} other{{days} gün}}",
|
||||
"focalLength": "{length} mm",
|
||||
|
||||
"applyButtonLabel": "UYGULA",
|
||||
"deleteButtonLabel": "SİL",
|
||||
"nextButtonLabel": "SONRAKİ",
|
||||
"showButtonLabel": "GÖSTER",
|
||||
"hideButtonLabel": "GİZLE",
|
||||
"continueButtonLabel": "DEVAM ET",
|
||||
|
||||
"cancelTooltip": "İptal et",
|
||||
"changeTooltip": "Değiştir",
|
||||
"clearTooltip": "Temizle",
|
||||
"previousTooltip": "Önceki",
|
||||
"nextTooltip": "Sonraki",
|
||||
"showTooltip": "Göster",
|
||||
"hideTooltip": "Gizle",
|
||||
"actionRemove": "Kaldır",
|
||||
"resetButtonTooltip": "Sıfırla",
|
||||
|
||||
"doubleBackExitMessage": "Çıkmak için tekrar “geri”, düğmesine dokunun.",
|
||||
"doNotAskAgain": "Bir daha sorma",
|
||||
|
||||
"sourceStateLoading": "Yükleniyor",
|
||||
"sourceStateCataloguing": "Kataloglanıyor",
|
||||
"sourceStateLocatingCountries": "Ülkeler konumlandırılıyor",
|
||||
"sourceStateLocatingPlaces": "Konum belirleniyor",
|
||||
|
||||
"chipActionDelete": "Sil",
|
||||
"chipActionGoToAlbumPage": "Albümlerde göster",
|
||||
"chipActionGoToCountryPage": "Ülkelerde göster",
|
||||
"chipActionGoToTagPage": "Etiketlerde göster",
|
||||
"chipActionHide": "Gizle",
|
||||
"chipActionPin": "Başa sabitle",
|
||||
"chipActionUnpin": "Baştan çıkar",
|
||||
"chipActionRename": "Yeniden adlandır",
|
||||
"chipActionSetCover": "Kapağı ayarla",
|
||||
"chipActionCreateAlbum": "Albüm oluştur",
|
||||
|
||||
"entryActionCopyToClipboard": "Panoya kopyala",
|
||||
"entryActionDelete": "Sil",
|
||||
"entryActionConvert": "Dönüştür",
|
||||
"entryActionExport": "Dışa aktar",
|
||||
"entryActionInfo": "Bilgi",
|
||||
"entryActionRename": "Yeniden adlandır",
|
||||
"entryActionRestore": "Dışa aktar",
|
||||
"entryActionRotateCCW": "Saat yönünün tersine döndür",
|
||||
"entryActionRotateCW": "Saat yönünde döndür",
|
||||
"entryActionFlip": "Yatay olarak çevir",
|
||||
"entryActionPrint": "Yazdır",
|
||||
"entryActionShare": "Paylaş",
|
||||
"entryActionViewSource": "Kaynağı görüntüle",
|
||||
"entryActionShowGeoTiffOnMap": "Harita katmanı olarak göster",
|
||||
"entryActionConvertMotionPhotoToStillImage": "Hareketsiz görüntüye dönüştür",
|
||||
"entryActionViewMotionPhotoVideo": "Videoyu aç",
|
||||
"entryActionEdit": "Düzenle",
|
||||
"entryActionOpen": "Şununla aç",
|
||||
"entryActionSetAs": "Olarak ayarla",
|
||||
"entryActionOpenMap": "Harita uygulamasında göster",
|
||||
"entryActionRotateScreen": "Ekranı döndür",
|
||||
"entryActionAddFavourite": "Favorilere ekle",
|
||||
"entryActionRemoveFavourite": "Favorilerden kaldır",
|
||||
|
||||
"videoActionCaptureFrame": "Çerçeve yakala",
|
||||
"videoActionMute": "Sustur",
|
||||
"videoActionUnmute": "Susturmayı kaldır",
|
||||
"videoActionPause": "Duraklat",
|
||||
"videoActionPlay": "Oynat",
|
||||
"videoActionReplay10": "10 saniye geri git",
|
||||
"videoActionSkip10": "10 saniye ileri git",
|
||||
"videoActionSelectStreams": "Parça seç",
|
||||
"videoActionSetSpeed": "Oynatma hızı",
|
||||
"videoActionSettings": "Ayarlar",
|
||||
|
||||
"entryInfoActionEditDate": "Tarih ve saati düzenle",
|
||||
"entryInfoActionEditLocation": "Konumu düzenle",
|
||||
"entryInfoActionEditRating": "Derecelendirmeyi düzenle",
|
||||
"entryInfoActionEditTags": "Etiketleri düzenle",
|
||||
"entryInfoActionRemoveMetadata": "Meta verileri kaldır",
|
||||
|
||||
"filterBinLabel": "Geri dönüşüm kutusu",
|
||||
"filterFavouriteLabel": "Favori",
|
||||
"filterLocationEmptyLabel": "Konumsuz",
|
||||
"filterTagEmptyLabel": "Etiketsiz",
|
||||
"filterRatingUnratedLabel": "Derecelendirilmemiş",
|
||||
"filterRatingRejectedLabel": "Reddedilmiş",
|
||||
"filterTypeAnimatedLabel": "Hareketli",
|
||||
"filterTypeMotionPhotoLabel": "Hareketli Fotoğraf",
|
||||
"filterTypePanoramaLabel": "Panorama",
|
||||
"filterTypeRawLabel": "Raw",
|
||||
"filterTypeSphericalVideoLabel": "360° Video",
|
||||
"filterTypeGeotiffLabel": "GeoTIFF",
|
||||
"filterMimeImageLabel": "Resim",
|
||||
"filterMimeVideoLabel": "Video",
|
||||
|
||||
"coordinateFormatDms": "DMS",
|
||||
"coordinateFormatDecimal": "Ondalık dereceler",
|
||||
"coordinateDms": "{coordinate} {direction}",
|
||||
|
||||
"coordinateDmsNorth": "K",
|
||||
"coordinateDmsSouth": "G",
|
||||
"coordinateDmsEast": "D",
|
||||
"coordinateDmsWest": "B",
|
||||
|
||||
"unitSystemMetric": "Metrik",
|
||||
"unitSystemImperial": "İngiliz",
|
||||
|
||||
"videoLoopModeNever": "Asla",
|
||||
"videoLoopModeShortOnly": "Yalnızca kısa videolar",
|
||||
"videoLoopModeAlways": "Her zaman",
|
||||
|
||||
"videoControlsPlay": "Oynat",
|
||||
"videoControlsPlaySeek": "Oynat ve ileri/geri git",
|
||||
"videoControlsPlayOutside": "Başka bir oynatıcı ile aç",
|
||||
"videoControlsNone": "Hiçbiri",
|
||||
|
||||
"mapStyleGoogleNormal": "Google Haritalar",
|
||||
"mapStyleGoogleHybrid": "Google Haritalar (Hibrit)",
|
||||
"mapStyleGoogleTerrain": "Google Haritalar (Arazi)",
|
||||
"mapStyleHuaweiNormal": "Petal Haritalar",
|
||||
"mapStyleHuaweiTerrain": "Petal Haritalar (Arazi)",
|
||||
"mapStyleOsmHot": "İnsancıl OSM",
|
||||
"mapStyleStamenToner": "Stamen Tonik",
|
||||
"mapStyleStamenWatercolor": "Stamen Suluboya",
|
||||
|
||||
"nameConflictStrategyRename": "Yeniden adlandır",
|
||||
"nameConflictStrategyReplace": "Değiştir",
|
||||
"nameConflictStrategySkip": "Atla",
|
||||
|
||||
"keepScreenOnNever": "Asla",
|
||||
"keepScreenOnViewerOnly": "Yalnızca görüntüleyici sayfası",
|
||||
"keepScreenOnAlways": "Her zaman",
|
||||
|
||||
"accessibilityAnimationsRemove": "Ekran efektlerini önle",
|
||||
"accessibilityAnimationsKeep": "Ekran efektlerini koru",
|
||||
|
||||
"displayRefreshRatePreferHighest": "En yüksek oran",
|
||||
"displayRefreshRatePreferLowest": "En düşük oran",
|
||||
|
||||
"themeBrightnessLight": "Açık",
|
||||
"themeBrightnessDark": "Koyu",
|
||||
"themeBrightnessBlack": "Siyah",
|
||||
|
||||
"albumTierNew": "Yeni",
|
||||
"albumTierPinned": "Sabitlenmiş",
|
||||
"albumTierSpecial": "Genel",
|
||||
"albumTierApps": "Uygulamalar",
|
||||
"albumTierRegular": "Diğer",
|
||||
|
||||
"storageVolumeDescriptionFallbackPrimary": "Dahili depolama",
|
||||
"storageVolumeDescriptionFallbackNonPrimary": "SD kart",
|
||||
"rootDirectoryDescription": "kök dizin",
|
||||
"otherDirectoryDescription": "“{name}” dizin",
|
||||
|
||||
"storageAccessDialogTitle": "Depolama Erişimi",
|
||||
"storageAccessDialogMessage": "Bu uygulamaya erişim sağlamak için lütfen bir sonraki ekranda “{volume}” öğesinin {directory} dizinini seçin.",
|
||||
|
||||
"restrictedAccessDialogTitle": "Kısıtlı Erişim",
|
||||
"restrictedAccessDialogMessage": "Bu uygulamanın “{volume}” içindeki {directory} dosyaları değiştirmesine izin verilmiyor.\n\nÖğeleri başka bir dizine taşımak için lütfen önceden yüklenmiş bir dosya yöneticisi veya galeri uygulaması kullanın.",
|
||||
|
||||
"notEnoughSpaceDialogTitle": "Yeterli Yer Yok",
|
||||
"notEnoughSpaceDialogMessage": "Bu işlemin tamamlanması için “{volume}” üzerinde {needSize} boş alana ihtiyaç var, ancak yalnızca {freeSize} kaldı.",
|
||||
|
||||
"missingSystemFilePickerDialogTitle": "Eksik Sistem Dosya Seçicisi",
|
||||
"missingSystemFilePickerDialogMessage": "Sistem dosya seçicisi eksik veya devre dışı. Lütfen etkinleştirin ve tekrar deneyin.",
|
||||
|
||||
"unsupportedTypeDialogTitle": "Desteklenmeyen Türler",
|
||||
"unsupportedTypeDialogMessage": "{count, plural, =1{Bu işlem aşağıdaki türdeki öğeler için desteklenmez: {types}.} other{Bu işlem aşağıdaki türlerdeki öğeler için desteklenmez: {types}.}}",
|
||||
|
||||
"nameConflictDialogSingleSourceMessage": "Hedef klasördeki bazı dosyalar aynı ada sahip.",
|
||||
"nameConflictDialogMultipleSourceMessage": "Bazı dosyalar aynı ada sahip.",
|
||||
|
||||
"addShortcutDialogLabel": "Kısayol etiketi",
|
||||
"addShortcutButtonLabel": "EKLE",
|
||||
|
||||
"noMatchingAppDialogTitle": "Eşleşen Uygulama Yok",
|
||||
"noMatchingAppDialogMessage": "Bununla ilgilenebilecek bir uygulama yok.",
|
||||
|
||||
"binEntriesConfirmationDialogMessage": "{count, plural, =1{Bu öğe geri dönüşüm kutusuna taşınsın mı?} other{Bu {count} madde geri dönüşüm kutusuna atılsın mı?}}",
|
||||
|
||||
"deleteEntriesConfirmationDialogMessage": "{count, plural, =1{Bu öğe silinsin mi?} other{Bu {count} öğe silinsin mi?}}",
|
||||
|
||||
"moveUndatedConfirmationDialogMessage": "Devam etmeden önce öğe tarihleri kaydedilsin mi?",
|
||||
"moveUndatedConfirmationDialogSetDate": "Tarihleri kaydet",
|
||||
|
||||
"videoResumeDialogMessage": "{time} itibarıyla oynatmaya devam etmek istiyor musunuz?",
|
||||
|
||||
"videoStartOverButtonLabel": "BAŞTAN BAŞLA",
|
||||
"videoResumeButtonLabel": "SÜRDÜR",
|
||||
|
||||
"setCoverDialogLatest": "Son öğe",
|
||||
"setCoverDialogAuto": "Otomatik",
|
||||
"setCoverDialogCustom": "Özel",
|
||||
|
||||
"hideFilterConfirmationDialogMessage": "Eşleşen fotoğraf ve videolar koleksiyonunuzdan gizlenecektir. Bunları “Gizlilik”, ayarlarından tekrar gösterebilirsiniz.\n\nBunları gizlemek istediğinizden emin misiniz?",
|
||||
|
||||
"newAlbumDialogTitle": "Yeni Albüm",
|
||||
"newAlbumDialogNameLabel": "Albüm adı",
|
||||
"newAlbumDialogNameLabelAlreadyExistsHelper": "Dizin zaten var",
|
||||
"newAlbumDialogStorageLabel": "Depolama:",
|
||||
|
||||
"renameAlbumDialogLabel": "Yeni ad",
|
||||
"renameAlbumDialogLabelAlreadyExistsHelper": "Dizin zaten var",
|
||||
|
||||
"renameEntrySetPageTitle": "Yeniden adlandır",
|
||||
"renameEntrySetPagePatternFieldLabel": "İsimlendirme şekli",
|
||||
"renameEntrySetPageInsertTooltip": "Alan ekle",
|
||||
"renameEntrySetPagePreview": "Önizleme",
|
||||
|
||||
"renameProcessorCounter": "Sayaç",
|
||||
"renameProcessorName": "Ad",
|
||||
|
||||
"deleteSingleAlbumConfirmationDialogMessage": "{count, plural, =1{Bu albüm ve öğesi silinsin mi?} other{Bu albüm ve {count} öğesi silinsin mi?}}",
|
||||
|
||||
"deleteMultiAlbumConfirmationDialogMessage": "{count, plural, =1{Bu albümler ve öğeleri silinsin mi?} other{Bu albümler ve {count} öğesi silinsin mi?}}",
|
||||
|
||||
"exportEntryDialogFormat": "Biçim:",
|
||||
"exportEntryDialogWidth": "Genişlik",
|
||||
"exportEntryDialogHeight": "Yükseklik",
|
||||
|
||||
"renameEntryDialogLabel": "Yeni ad",
|
||||
|
||||
"editEntryDateDialogTitle": "Tarih ve Saat",
|
||||
"editEntryDateDialogSetCustom": "Özel tarih ayarla",
|
||||
"editEntryDateDialogCopyField": "Başka bir tarihten kopyala",
|
||||
"editEntryDateDialogCopyItem": "Başka bir öğeden kopyala",
|
||||
"editEntryDateDialogExtractFromTitle": "Başlıktan ayıkla",
|
||||
"editEntryDateDialogShift": "Değişim",
|
||||
"editEntryDateDialogSourceFileModifiedDate": "Dosya değiştirilme tarihi",
|
||||
"editEntryDateDialogTargetFieldsHeader": "Değiştirilecek alanlar",
|
||||
"editEntryDateDialogHours": "Saat",
|
||||
"editEntryDateDialogMinutes": "Dakika",
|
||||
|
||||
"editEntryLocationDialogTitle": "Konum",
|
||||
"editEntryLocationDialogChooseOnMapTooltip": "Harita üzerinde seç",
|
||||
"editEntryLocationDialogLatitude": "Enlem",
|
||||
"editEntryLocationDialogLongitude": "Boylam",
|
||||
|
||||
"locationPickerUseThisLocationButton": "Bu konumu kullan",
|
||||
|
||||
"editEntryRatingDialogTitle": "Derecelendirme",
|
||||
|
||||
"removeEntryMetadataDialogTitle": "Meta veri kaldırma",
|
||||
"removeEntryMetadataDialogMore": "Daha fazla",
|
||||
|
||||
"removeEntryMetadataMotionPhotoXmpWarningDialogMessage": "Hareketli bir fotoğrafın içindeki videoyu oynatmak için XMP gereklidir.\n\nKaldırmak istediğinizden emin misiniz?",
|
||||
"convertMotionPhotoToStillImageWarningDialogMessage": "Emin misiniz?",
|
||||
|
||||
"videoSpeedDialogLabel": "Oynatma hızı",
|
||||
|
||||
"videoStreamSelectionDialogVideo": "Video",
|
||||
"videoStreamSelectionDialogAudio": "Ses",
|
||||
"videoStreamSelectionDialogText": "Altyazı",
|
||||
"videoStreamSelectionDialogOff": "Kapalı",
|
||||
"videoStreamSelectionDialogTrack": "Parça",
|
||||
"videoStreamSelectionDialogNoSelection": "Başka parça yok.",
|
||||
|
||||
"genericSuccessFeedback": "Başarılı!",
|
||||
"genericFailureFeedback": "Başarısız",
|
||||
|
||||
"menuActionConfigureView": "Görünüm",
|
||||
"menuActionSelect": "Seç",
|
||||
"menuActionSelectAll": "Hepsini seç",
|
||||
"menuActionSelectNone": "Hiçbirini seçme",
|
||||
"menuActionMap": "Harita",
|
||||
"menuActionStats": "İstatistikler",
|
||||
|
||||
"viewDialogTabSort": "Sırala",
|
||||
"viewDialogTabGroup": "Grup",
|
||||
"viewDialogTabLayout": "Düzen",
|
||||
|
||||
"tileLayoutGrid": "Izgara",
|
||||
"tileLayoutList": "Liste",
|
||||
|
||||
"coverDialogTabCover": "Kapak",
|
||||
"coverDialogTabApp": "Uygulama",
|
||||
"coverDialogTabColor": "Renk",
|
||||
|
||||
"appPickDialogTitle": "Uygulama seç",
|
||||
"appPickDialogNone": "Yok",
|
||||
|
||||
"aboutPageTitle": "Hakkında",
|
||||
"aboutLinkSources": "Kaynaklar",
|
||||
"aboutLinkLicense": "Lisans",
|
||||
"aboutLinkPolicy": "Gizlilik Politikası",
|
||||
|
||||
"aboutBug": "Hata Bildirimi",
|
||||
"aboutBugSaveLogInstruction": "Uygulama günlüklerini bir dosyaya kaydet",
|
||||
"aboutBugSaveLogButton": "Kaydet",
|
||||
"aboutBugCopyInfoInstruction": "Sistem bilgilerini kopyala",
|
||||
"aboutBugCopyInfoButton": "Kopyala",
|
||||
"aboutBugReportInstruction": "GitHub'da günlükleri ve sistem bilgilerini içeren bir rapor oluştur",
|
||||
"aboutBugReportButton": "Raporla",
|
||||
|
||||
"aboutCredits": "Kredi",
|
||||
"aboutCreditsWorldAtlas1": "Bu uygulama bir TopoJSON dosyası kullanır",
|
||||
"aboutCreditsWorldAtlas2": "ISC Lisansı kapsamında.",
|
||||
"aboutCreditsTranslators": "Tercümanlar",
|
||||
|
||||
"aboutLicenses": "Açık Kaynak Lisansları",
|
||||
"aboutLicensesBanner": "Bu uygulama aşağıdaki açık kaynaklı paketleri ve kütüphaneleri kullanır.",
|
||||
"aboutLicensesAndroidLibraries": "Android Kütüphaneleri",
|
||||
"aboutLicensesFlutterPlugins": "Flutter Eklentileri",
|
||||
"aboutLicensesFlutterPackages": "Flutter Paketleri",
|
||||
"aboutLicensesDartPackages": "Dart Paketleri",
|
||||
"aboutLicensesShowAllButtonLabel": "Tüm Lisansları Göster",
|
||||
|
||||
"policyPageTitle": "Gizlilik Politikası",
|
||||
|
||||
"collectionPageTitle": "Koleksiyon",
|
||||
"collectionPickPageTitle": "Seç",
|
||||
"collectionSelectPageTitle": "Öğeleri seç",
|
||||
|
||||
"collectionActionShowTitleSearch": "Başlık filtresini göster",
|
||||
"collectionActionHideTitleSearch": "Başlık filtresini gizle",
|
||||
"collectionActionAddShortcut": "Kısayol ekle",
|
||||
"collectionActionEmptyBin": "Boş çöp kutusu",
|
||||
"collectionActionCopy": "Albüme kopyala",
|
||||
"collectionActionMove": "Albüme taşı",
|
||||
"collectionActionRescan": "Yeniden tara",
|
||||
"collectionActionEdit": "Düzenle",
|
||||
|
||||
"collectionSearchTitlesHintText": "Başlıkları ara",
|
||||
|
||||
"collectionSortDate": "Tarihe göre",
|
||||
"collectionSortSize": "Boyuta göre",
|
||||
"collectionSortName": "Albüm ve dosya adına göre",
|
||||
"collectionSortRating": "Derecelendirmeye göre",
|
||||
|
||||
"collectionGroupAlbum": "Albüme göre",
|
||||
"collectionGroupMonth": "Aya göre",
|
||||
"collectionGroupDay": "Güne göre",
|
||||
"collectionGroupNone": "Gruplama",
|
||||
|
||||
"sectionUnknown": "Bilinmeyen",
|
||||
"dateToday": "Bugün",
|
||||
"dateYesterday": "Dün",
|
||||
"dateThisMonth": "Bu ay",
|
||||
"collectionDeleteFailureFeedback": "{count, plural, =1{1 öğe silinemedi} other{{count} öğe silinemedi}}",
|
||||
|
||||
"collectionCopyFailureFeedback": "{count, plural, =1{1 öğe kopyalanamadı} other{{count} öğe kopyalanamadı}}",
|
||||
|
||||
"collectionMoveFailureFeedback": "{count, plural, =1{1 öğe taşınamadı} other{{count} öğe taşınamadı}}",
|
||||
|
||||
"collectionRenameFailureFeedback": "{count, plural, =1{1 öğenin adı değiştirilemedi} other{{count} öğenin adı değiştirilemedi}}",
|
||||
|
||||
"collectionEditFailureFeedback": "{count, plural, =1{1 öğe düzenlenemedi} other{{count} öğe düzenlenemedi}}",
|
||||
|
||||
"collectionExportFailureFeedback": "{count, plural, =1{1 sayfa dışa aktarılamadı} other{{count} sayfa dışa aktarılamadı}",
|
||||
|
||||
"collectionCopySuccessFeedback": "{count, plural, =1{1 öğe kopyalandı} other{{count} öğe kopyalandı}}",
|
||||
|
||||
"collectionMoveSuccessFeedback": "{count, plural, =1{1 öğe taşındı} other{{count} öğe taşındı}}",
|
||||
|
||||
"collectionRenameSuccessFeedback": "{count, plural, =1{1 öğenin adı değiştirildi} other{{count} öğenin adı değiştirildi}}",
|
||||
|
||||
"collectionEditSuccessFeedback": "{count, plural, =1{1 öğe düzenlendi} other{{count} öğe düzenlendi}}",
|
||||
|
||||
"collectionEmptyFavourites": "Favori yok",
|
||||
"collectionEmptyVideos": "Video yok",
|
||||
"collectionEmptyImages": "Resim yok",
|
||||
"collectionEmptyGrantAccessButtonLabel": "Erişim izni",
|
||||
|
||||
"collectionSelectSectionTooltip": "Bölüm seç",
|
||||
"collectionDeselectSectionTooltip": "Bölüm seçimini kaldır",
|
||||
|
||||
"drawerCollectionAll": "Tüm koleksiyon",
|
||||
"drawerCollectionFavourites": "Favoriler",
|
||||
"drawerCollectionImages": "Resimler",
|
||||
"drawerCollectionVideos": "Videolar",
|
||||
"drawerCollectionAnimated": "Hareketli",
|
||||
"drawerCollectionMotionPhotos": "Hareketli fotoğraflar",
|
||||
"drawerCollectionPanoramas": "Panoramalar",
|
||||
"drawerCollectionRaws": "Raw fotoğraflar",
|
||||
"drawerCollectionSphericalVideos": "360° Videolar",
|
||||
|
||||
"chipSortDate": "Tarihe göre",
|
||||
"chipSortName": "Adına göre",
|
||||
"chipSortCount": "Öğe sayısına göre",
|
||||
|
||||
"albumGroupTier": "Kademeye göre",
|
||||
"albumGroupVolume": "Depolama hacmine göre",
|
||||
"albumGroupNone": "Gruplama",
|
||||
|
||||
"albumPickPageTitleCopy": "Albüme kopyala",
|
||||
"albumPickPageTitleExport": "Albüme aktar",
|
||||
"albumPickPageTitleMove": "Albüme taşı",
|
||||
"albumPickPageTitlePick": "Albüm seç",
|
||||
|
||||
"albumCamera": "Kamera",
|
||||
"albumDownload": "İndir",
|
||||
"albumScreenshots": "Ekran görüntüleri",
|
||||
"albumScreenRecordings": "Ekran kayıtları",
|
||||
"albumVideoCaptures": "Video çekimleri",
|
||||
|
||||
"albumPageTitle": "Albümler",
|
||||
"albumEmpty": "Albüm yok",
|
||||
"createAlbumTooltip": "Albüm oluştur",
|
||||
"createAlbumButtonLabel": "OLUŞTUR",
|
||||
"newFilterBanner": "yeni",
|
||||
|
||||
"countryPageTitle": "Ülkeler",
|
||||
"countryEmpty": "Ülke yok",
|
||||
|
||||
"tagPageTitle": "Etiketler",
|
||||
"tagEmpty": "Etiket yok",
|
||||
|
||||
"binPageTitle": "Geri Dönüşüm Kutusu",
|
||||
|
||||
"searchCollectionFieldHint": "Koleksiyonu ara",
|
||||
"searchSectionRecent": "Yakın zamanda",
|
||||
"searchSectionAlbums": "Albümler",
|
||||
"searchSectionCountries": "Ülkeler",
|
||||
"searchSectionPlaces": "Yerler",
|
||||
"searchSectionTags": "Etiketler",
|
||||
"searchSectionRating": "Derecelendirmeler",
|
||||
|
||||
"settingsPageTitle": "Ayarlar",
|
||||
"settingsSystemDefault": "Sistem",
|
||||
"settingsDefault": "Varsayılan",
|
||||
|
||||
"settingsSearchFieldLabel": "Ayarlarda ara",
|
||||
"settingsSearchEmpty": "Eşleşen ayar bulunamadı",
|
||||
"settingsActionExport": "Dışa aktar",
|
||||
"settingsActionImport": "İçe aktar",
|
||||
|
||||
"appExportCovers": "Kapaklar",
|
||||
"appExportFavourites": "Favoriler",
|
||||
"appExportSettings": "Ayarlar",
|
||||
|
||||
"settingsSectionNavigation": "Gezinti",
|
||||
"settingsHome": "Anasayfa",
|
||||
"settingsShowBottomNavigationBar": "Alt gezinti çubuğunu göster",
|
||||
"settingsKeepScreenOnTile": "Ekranı açık tut",
|
||||
"settingsKeepScreenOnTitle": "Ekranı Açık Tut",
|
||||
"settingsDoubleBackExit": "Çıkmak için iki kez “geri” düğmesine dokunun",
|
||||
|
||||
"settingsConfirmationDialogTile": "Onaylama diyalogları",
|
||||
"settingsConfirmationDialogTitle": "Onaylama Diyalogları",
|
||||
"settingsConfirmationDialogDeleteItems": "Öğeleri sonsuza dek silmeden önce sor",
|
||||
"settingsConfirmationDialogMoveToBinItems": "Eşyaları geri dönüşüm kutusuna atmadan önce sor",
|
||||
"settingsConfirmationDialogMoveUndatedItems": "Tarihsiz eşyaları taşımadan önce sor",
|
||||
|
||||
"settingsNavigationDrawerTile": "Gezinti menüsü",
|
||||
"settingsNavigationDrawerEditorTitle": "Gezinti Menüsü",
|
||||
"settingsNavigationDrawerBanner": "Menü öğelerini taşımak ve yeniden sıralamak için dokunun ve basılı tutun.",
|
||||
"settingsNavigationDrawerTabTypes": "Türler",
|
||||
"settingsNavigationDrawerTabAlbums": "Albümler",
|
||||
"settingsNavigationDrawerTabPages": "Sayfalar",
|
||||
"settingsNavigationDrawerAddAlbum": "Albüm ekle",
|
||||
|
||||
"settingsSectionThumbnails": "Küçük resimler",
|
||||
"settingsThumbnailOverlayTile": "Kaplama",
|
||||
"settingsThumbnailOverlayTitle": "Kaplama",
|
||||
"settingsThumbnailShowFavouriteIcon": "Favori simgeyi göster",
|
||||
"settingsThumbnailShowTagIcon": "Etiket simgesini göster",
|
||||
"settingsThumbnailShowLocationIcon": "Konum simgesini göster",
|
||||
"settingsThumbnailShowMotionPhotoIcon": "Hareketli fotoğraf simgesini göster",
|
||||
"settingsThumbnailShowRating": "Derecelendirmeyi göster",
|
||||
"settingsThumbnailShowRawIcon": "Raw simgesini göster",
|
||||
"settingsThumbnailShowVideoDuration": "Video süresini göster",
|
||||
|
||||
"settingsCollectionQuickActionsTile": "Hızlı eylemler",
|
||||
"settingsCollectionQuickActionEditorTitle": "Hızlı Eylemler",
|
||||
"settingsCollectionQuickActionTabBrowsing": "Gözatma",
|
||||
"settingsCollectionQuickActionTabSelecting": "Seçme",
|
||||
"settingsCollectionBrowsingQuickActionEditorBanner": "Düğmeleri hareket ettirmek ve öğelere göz atarken hangi eylemlerin görüntüleneceğini seçmek için dokunun ve basılı tutun.",
|
||||
"settingsCollectionSelectionQuickActionEditorBanner": "Düğmeleri hareket ettirmek ve öğeleri seçerken hangi eylemlerin görüntüleneceğini seçmek için dokunun ve basılı tutun.",
|
||||
|
||||
"settingsSectionViewer": "Görüntüleyici",
|
||||
"settingsViewerUseCutout": "Kesim alanını kullan",
|
||||
"settingsViewerMaximumBrightness": "Maksimum parlaklık",
|
||||
"settingsMotionPhotoAutoPlay": "Hareketli fotoğrafları otomatik oynat",
|
||||
"settingsImageBackground": "Resim arka planı",
|
||||
|
||||
"settingsViewerQuickActionsTile": "Hızlı eylemler",
|
||||
"settingsViewerQuickActionEditorTitle": "Hızlı Eylemler",
|
||||
"settingsViewerQuickActionEditorBanner": "Düğmeleri hareket ettirmek ve görüntüleyicide hangi eylemlerin görüntüleneceğini seçmek için dokunun ve basılı tutun.",
|
||||
"settingsViewerQuickActionEditorDisplayedButtons": "Gösterilen Düğmeler",
|
||||
"settingsViewerQuickActionEditorAvailableButtons": "Mevcut Düğmeler",
|
||||
"settingsViewerQuickActionEmpty": "Düğme yok",
|
||||
|
||||
"settingsViewerOverlayTile": "Kaplama",
|
||||
"settingsViewerOverlayTitle": "Kaplama",
|
||||
"settingsViewerShowOverlayOnOpening": "Açılışta göster",
|
||||
"settingsViewerShowMinimap": "Mini haritayı göster",
|
||||
"settingsViewerShowInformation": "Bilgileri göster",
|
||||
"settingsViewerShowInformationSubtitle": "Başlığı, tarihi, konumu vb. göster.",
|
||||
"settingsViewerShowShootingDetails": "Çekim ayrıntılarını göster",
|
||||
"settingsViewerShowOverlayThumbnails": "Küçük resimleri göster",
|
||||
"settingsViewerEnableOverlayBlurEffect": "Bulanıklık efekti",
|
||||
|
||||
"settingsVideoPageTitle": "Video Ayarları",
|
||||
"settingsSectionVideo": "Video",
|
||||
"settingsVideoShowVideos": "Videoları göster",
|
||||
"settingsVideoEnableHardwareAcceleration": "Donanım hızlandırma",
|
||||
"settingsVideoEnableAutoPlay": "Otomatik oynat",
|
||||
"settingsVideoLoopModeTile": "Döngü modu",
|
||||
"settingsVideoLoopModeTitle": "Döngü Modu",
|
||||
|
||||
"settingsSubtitleThemeTile": "Altyazılar",
|
||||
"settingsSubtitleThemeTitle": "Altyazılar",
|
||||
"settingsSubtitleThemeSample": "Bu bir örnek.",
|
||||
"settingsSubtitleThemeTextAlignmentTile": "Metin hizalama",
|
||||
"settingsSubtitleThemeTextAlignmentTitle": "Metin Hizalama",
|
||||
"settingsSubtitleThemeTextSize": "Metin boyutu",
|
||||
"settingsSubtitleThemeShowOutline": "Dış çizgiyi ve gölgeyi göster",
|
||||
"settingsSubtitleThemeTextColor": "Metin rengi",
|
||||
"settingsSubtitleThemeTextOpacity": "Metin opaklığı",
|
||||
"settingsSubtitleThemeBackgroundColor": "Arka plan rengi",
|
||||
"settingsSubtitleThemeBackgroundOpacity": "Arka plan opaklığı",
|
||||
"settingsSubtitleThemeTextAlignmentLeft": "Sol",
|
||||
"settingsSubtitleThemeTextAlignmentCenter": "Merkez",
|
||||
"settingsSubtitleThemeTextAlignmentRight": "Sağ",
|
||||
|
||||
"settingsVideoControlsTile": "Kontroller",
|
||||
"settingsVideoControlsTitle": "Kontroller",
|
||||
"settingsVideoButtonsTile": "Düğmeler",
|
||||
"settingsVideoButtonsTitle": "Düğmeler",
|
||||
"settingsVideoGestureDoubleTapTogglePlay": "Oynatmak/duraklatmak için çift dokunun",
|
||||
"settingsVideoGestureSideDoubleTapSeek": "Geri/ileri aramak için ekran kenarlarına çift dokunun",
|
||||
|
||||
"settingsSectionPrivacy": "Gizlilik",
|
||||
"settingsAllowInstalledAppAccess": "Uygulama envanterine erişime izin ver",
|
||||
"settingsAllowInstalledAppAccessSubtitle": "Albüm görüntüsünü iyileştirmek için kullanılır",
|
||||
"settingsAllowErrorReporting": "Anonim hata raporlamasına izin ver",
|
||||
"settingsSaveSearchHistory": "Arama geçmişini kaydet",
|
||||
"settingsEnableBin": "Geri dönüşüm kutusunu kullan",
|
||||
"settingsEnableBinSubtitle": "Silinen öğeleri 30 gün boyunca saklar",
|
||||
|
||||
"settingsHiddenItemsTile": "Gizli öğeler",
|
||||
"settingsHiddenItemsTitle": "Gizli Öğeler",
|
||||
|
||||
"settingsHiddenFiltersTitle": "Gizli Filtreler",
|
||||
"settingsHiddenFiltersBanner": "Gizli filtrelerle eşleşen fotoğraflar ve videolar koleksiyonunuzda görünmeyecektir.",
|
||||
"settingsHiddenFiltersEmpty": "Gizli filtre yok",
|
||||
|
||||
"settingsHiddenPathsTitle": "Gizli Yollar",
|
||||
"settingsHiddenPathsBanner": "Bu klasörlerdeki veya alt klasörlerindeki fotoğraflar ve videolar koleksiyonunuzda görünmeyecektir.",
|
||||
"addPathTooltip": "Yol ekle",
|
||||
|
||||
"settingsStorageAccessTile": "Depolama erişimi",
|
||||
"settingsStorageAccessTitle": "Depolama Erişimi",
|
||||
"settingsStorageAccessBanner": "Bazı dizinler, içlerindeki dosyaları değiştirmek için açık bir erişim izni gerektirir. Daha önce erişim izni verdiğiniz dizinleri buradan inceleyebilirsiniz.",
|
||||
"settingsStorageAccessEmpty": "Erişim izni yok",
|
||||
"settingsStorageAccessRevokeTooltip": "Geri al",
|
||||
|
||||
"settingsSectionAccessibility": "Erişilebilirlik",
|
||||
"settingsRemoveAnimationsTile": "Animasyonları kaldır",
|
||||
"settingsRemoveAnimationsTitle": "Animasyonları Kaldır",
|
||||
"settingsTimeToTakeActionTile": "Harekete geçme zamanı",
|
||||
"settingsTimeToTakeActionTitle": "Harekete Geçme Zamanı",
|
||||
|
||||
"settingsSectionDisplay": "Ekran",
|
||||
"settingsThemeBrightness": "Tema",
|
||||
"settingsThemeColorHighlights": "Renk vurguları",
|
||||
"settingsThemeEnableDynamicColor": "Dinamik renk",
|
||||
"settingsDisplayRefreshRateModeTile": "Görüntü yenileme hızı",
|
||||
"settingsDisplayRefreshRateModeTitle": "Yenileme Hızı",
|
||||
|
||||
"settingsSectionLanguage": "Dil ve Biçimler",
|
||||
"settingsLanguage": "Dil",
|
||||
"settingsCoordinateFormatTile": "Koordinat formatı",
|
||||
"settingsCoordinateFormatTitle": "Koordinat Formatı",
|
||||
"settingsUnitSystemTile": "Birimler",
|
||||
"settingsUnitSystemTitle": "Birimler",
|
||||
|
||||
"statsPageTitle": "İstatistikler",
|
||||
"statsWithGps": "{count, plural, =1{1 konuma sahip öğe} other{{count} konuma sahip öğe}}",
|
||||
"statsTopCountries": "Başlıca Ülkeler",
|
||||
"statsTopPlaces": "Başlıca Yerler",
|
||||
"statsTopTags": "Başlıca Etiketler",
|
||||
|
||||
"viewerOpenPanoramaButtonLabel": "PANORAMAYI AÇ",
|
||||
"viewerErrorUnknown": "Tüh!",
|
||||
"viewerErrorDoesNotExist": "Dosya artık mevcut değil.",
|
||||
|
||||
"viewerInfoPageTitle": "Bilgi",
|
||||
"viewerInfoBackToViewerTooltip": "Görüntüleyiciye geri dön",
|
||||
|
||||
"viewerInfoUnknown": "bilinmeyen",
|
||||
"viewerInfoLabelTitle": "Başlık",
|
||||
"viewerInfoLabelDate": "Tarih",
|
||||
"viewerInfoLabelResolution": "Çözünürlük",
|
||||
"viewerInfoLabelSize": "Boyut",
|
||||
"viewerInfoLabelUri": "URI",
|
||||
"viewerInfoLabelPath": "Yol",
|
||||
"viewerInfoLabelDuration": "Süre",
|
||||
"viewerInfoLabelOwner": "Sahibi",
|
||||
"viewerInfoLabelCoordinates": "Koordinatlar",
|
||||
"viewerInfoLabelAddress": "Adres",
|
||||
|
||||
"mapStyleTitle": "Harita Şekli",
|
||||
"mapStyleTooltip": "Harita şeklini seç",
|
||||
"mapZoomInTooltip": "Yakınlaştır",
|
||||
"mapZoomOutTooltip": "Uzaklaştır",
|
||||
"mapPointNorthUpTooltip": "Kuzeyi göster",
|
||||
"mapAttributionOsmHot": "Harita verileri © [OpenStreetMap](https://www.openstreetmap.org/copyright) katkıda bulunanlar - Kutucuklar [HOT](https://www.hotosm.org/) tarafından hazırlanmıştır - [OSM France](https://openstreetmap.fr/) tarafından barındırılmaktadır",
|
||||
"mapAttributionStamen": "Harita verileri © [OpenStreetMap](https://www.openstreetmap.org/copyright) katkıda bulunanlar - Döşemeler [Stamen Design](http://stamen.com), [CC BY 3.0](http://creativecommons.org/licenses/by/3.0)",
|
||||
"openMapPageTooltip": "Harita sayfasında görüntüle",
|
||||
"mapEmptyRegion": "Bu bölgede resim yok",
|
||||
|
||||
"viewerInfoOpenEmbeddedFailureFeedback": "Gömülü veriler ayıklanamadı",
|
||||
"viewerInfoOpenLinkText": "Aç",
|
||||
"viewerInfoViewXmlLinkText": "XML'i Görüntüle",
|
||||
|
||||
"viewerInfoSearchFieldLabel": "Meta verileri ara",
|
||||
"viewerInfoSearchEmpty": "Eşleşen anahtar yok",
|
||||
"viewerInfoSearchSuggestionDate": "Tarih ve saat",
|
||||
"viewerInfoSearchSuggestionDescription": "Açıklama",
|
||||
"viewerInfoSearchSuggestionDimensions": "Boyutlar",
|
||||
"viewerInfoSearchSuggestionResolution": "Çözünürlük",
|
||||
"viewerInfoSearchSuggestionRights": "Haklar",
|
||||
|
||||
"tagEditorPageTitle": "Etiketleri Düzenle",
|
||||
"tagEditorPageNewTagFieldLabel": "Yeni etiket",
|
||||
"tagEditorPageAddTagTooltip": "Etiket ekle",
|
||||
"tagEditorSectionRecent": "Yakın zamanda",
|
||||
|
||||
"panoramaEnableSensorControl": "Sensör kontrolünü etkinleştir",
|
||||
"panoramaDisableSensorControl": "Sensör kontrolünü devre dışı bırak",
|
||||
|
||||
"sourceViewerPageTitle": "Kaynak",
|
||||
|
||||
"filePickerShowHiddenFiles": "Gizli dosyaları göster",
|
||||
"filePickerDoNotShowHiddenFiles": "Gizli dosyaları gösterme",
|
||||
"filePickerOpenFrom": "Şuradan aç",
|
||||
"filePickerNoItems": "Öğe yok",
|
||||
"filePickerUseThisFolder": "Bu klasörü kullan"
|
||||
}
|
|
@ -50,6 +50,7 @@
|
|||
"entryActionDelete": "删除",
|
||||
"entryActionConvert": "转换",
|
||||
"entryActionExport": "导出",
|
||||
"entryActionInfo": "信息",
|
||||
"entryActionRename": "重命名",
|
||||
"entryActionRestore": "恢复",
|
||||
"entryActionRotateCCW": "逆时针旋转",
|
||||
|
@ -80,6 +81,9 @@
|
|||
"videoActionSetSpeed": "播放速度",
|
||||
"videoActionSettings": "设置",
|
||||
|
||||
"slideshowActionResume": "继续",
|
||||
"slideshowActionShowInCollection": "在媒体集中显示",
|
||||
|
||||
"entryInfoActionEditDate": "编辑日期和时间",
|
||||
"entryInfoActionEditLocation": "编辑位置",
|
||||
"entryInfoActionEditRating": "修改评分",
|
||||
|
@ -144,10 +148,23 @@
|
|||
"displayRefreshRatePreferHighest": "最高刷新率",
|
||||
"displayRefreshRatePreferLowest": "最低刷新率",
|
||||
|
||||
"slideshowVideoPlaybackSkip": "跳过",
|
||||
"slideshowVideoPlaybackMuted": "静音播放",
|
||||
"slideshowVideoPlaybackWithSound": "带音播放",
|
||||
|
||||
"themeBrightnessLight": "浅色",
|
||||
"themeBrightnessDark": "深色",
|
||||
"themeBrightnessBlack": "黑色",
|
||||
|
||||
"viewerTransitionSlide": "滑动",
|
||||
"viewerTransitionParallax": "视差滚动",
|
||||
"viewerTransitionFade": "淡入淡出",
|
||||
"viewerTransitionZoomIn": "放大",
|
||||
|
||||
"wallpaperTargetHome": "主屏幕",
|
||||
"wallpaperTargetLock": "锁屏界面",
|
||||
"wallpaperTargetHomeLock": "主屏幕 + 锁屏界面",
|
||||
|
||||
"albumTierNew": "新的",
|
||||
"albumTierPinned": "钉选",
|
||||
"albumTierSpecial": "普通",
|
||||
|
@ -262,6 +279,7 @@
|
|||
"menuActionSelectAll": "全选",
|
||||
"menuActionSelectNone": "全不选",
|
||||
"menuActionMap": "地图",
|
||||
"menuActionSlideshow": "幻灯片",
|
||||
"menuActionStats": "统计",
|
||||
|
||||
"viewDialogTabSort": "排序",
|
||||
|
@ -349,6 +367,7 @@
|
|||
"collectionEmptyFavourites": "无收藏项",
|
||||
"collectionEmptyVideos": "无视频",
|
||||
"collectionEmptyImages": "无图像",
|
||||
"collectionEmptyGrantAccessButtonLabel": "授予访问权限",
|
||||
|
||||
"collectionSelectSectionTooltip": "选择部分",
|
||||
"collectionDeselectSectionTooltip": "取消选择部分",
|
||||
|
@ -479,6 +498,17 @@
|
|||
"settingsViewerShowOverlayThumbnails": "显示缩略图",
|
||||
"settingsViewerEnableOverlayBlurEffect": "模糊特效",
|
||||
|
||||
"settingsViewerSlideshowTile": "幻灯片",
|
||||
"settingsViewerSlideshowTitle": "幻灯片",
|
||||
"settingsSlideshowRepeat": "重复",
|
||||
"settingsSlideshowShuffle": "随机播放",
|
||||
"settingsSlideshowTransitionTile": "过渡动画",
|
||||
"settingsSlideshowTransitionTitle": "过渡动画",
|
||||
"settingsSlideshowIntervalTile": "时间间隔",
|
||||
"settingsSlideshowIntervalTitle": "时间间隔",
|
||||
"settingsSlideshowVideoPlaybackTile": "视频回放",
|
||||
"settingsSlideshowVideoPlaybackTitle": "视频回放",
|
||||
|
||||
"settingsVideoPageTitle": "视频设置",
|
||||
"settingsSectionVideo": "视频",
|
||||
"settingsVideoShowVideos": "显示视频",
|
||||
|
@ -543,6 +573,7 @@
|
|||
"settingsSectionDisplay": "显示",
|
||||
"settingsThemeBrightness": "主题",
|
||||
"settingsThemeColorHighlights": "色彩强调",
|
||||
"settingsThemeEnableDynamicColor": "动态色彩",
|
||||
"settingsDisplayRefreshRateModeTile": "显示刷新率",
|
||||
"settingsDisplayRefreshRateModeTitle": "刷新率",
|
||||
|
||||
|
@ -560,6 +591,7 @@
|
|||
"statsTopTags": "热门标签",
|
||||
|
||||
"viewerOpenPanoramaButtonLabel": "打开全景",
|
||||
"viewerSetWallpaperButtonLabel": "设置壁纸",
|
||||
"viewerErrorUnknown": "糟糕!",
|
||||
"viewerErrorDoesNotExist": "该文件不存在",
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ enum ChipSetAction {
|
|||
createAlbum,
|
||||
// browsing or selecting
|
||||
map,
|
||||
slideshow,
|
||||
stats,
|
||||
// selecting (single/multiple filters)
|
||||
delete,
|
||||
|
@ -36,6 +37,7 @@ class ChipSetActions {
|
|||
ChipSetAction.search,
|
||||
ChipSetAction.createAlbum,
|
||||
ChipSetAction.map,
|
||||
ChipSetAction.slideshow,
|
||||
ChipSetAction.stats,
|
||||
];
|
||||
|
||||
|
@ -47,6 +49,7 @@ class ChipSetActions {
|
|||
ChipSetAction.rename,
|
||||
ChipSetAction.hide,
|
||||
ChipSetAction.map,
|
||||
ChipSetAction.slideshow,
|
||||
ChipSetAction.stats,
|
||||
];
|
||||
}
|
||||
|
@ -71,6 +74,8 @@ extension ExtraChipSetAction on ChipSetAction {
|
|||
// browsing or selecting
|
||||
case ChipSetAction.map:
|
||||
return context.l10n.menuActionMap;
|
||||
case ChipSetAction.slideshow:
|
||||
return context.l10n.menuActionSlideshow;
|
||||
case ChipSetAction.stats:
|
||||
return context.l10n.menuActionStats;
|
||||
// selecting (single/multiple filters)
|
||||
|
@ -111,6 +116,8 @@ extension ExtraChipSetAction on ChipSetAction {
|
|||
// browsing or selecting
|
||||
case ChipSetAction.map:
|
||||
return AIcons.map;
|
||||
case ChipSetAction.slideshow:
|
||||
return AIcons.slideshow;
|
||||
case ChipSetAction.stats:
|
||||
return AIcons.stats;
|
||||
// selecting (single/multiple filters)
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
|
|||
import 'package:flutter/widgets.dart';
|
||||
|
||||
enum EntryAction {
|
||||
info,
|
||||
addShortcut,
|
||||
copyToClipboard,
|
||||
delete,
|
||||
|
@ -43,6 +44,7 @@ enum EntryAction {
|
|||
|
||||
class EntryActions {
|
||||
static const topLevel = [
|
||||
EntryAction.info,
|
||||
EntryAction.share,
|
||||
EntryAction.edit,
|
||||
EntryAction.rename,
|
||||
|
@ -102,6 +104,8 @@ class EntryActions {
|
|||
extension ExtraEntryAction on EntryAction {
|
||||
String getText(BuildContext context) {
|
||||
switch (this) {
|
||||
case EntryAction.info:
|
||||
return context.l10n.entryActionInfo;
|
||||
case EntryAction.addShortcut:
|
||||
return context.l10n.collectionActionAddShortcut;
|
||||
case EntryAction.copyToClipboard:
|
||||
|
@ -188,6 +192,8 @@ extension ExtraEntryAction on EntryAction {
|
|||
|
||||
IconData getIconData() {
|
||||
switch (this) {
|
||||
case EntryAction.info:
|
||||
return AIcons.info;
|
||||
case EntryAction.addShortcut:
|
||||
return AIcons.addShortcut;
|
||||
case EntryAction.copyToClipboard:
|
||||
|
|
|
@ -15,6 +15,7 @@ enum EntrySetAction {
|
|||
emptyBin,
|
||||
// browsing or selecting
|
||||
map,
|
||||
slideshow,
|
||||
stats,
|
||||
rescan,
|
||||
// selecting
|
||||
|
@ -48,6 +49,7 @@ class EntrySetActions {
|
|||
EntrySetAction.toggleTitleSearch,
|
||||
EntrySetAction.addShortcut,
|
||||
EntrySetAction.map,
|
||||
EntrySetAction.slideshow,
|
||||
EntrySetAction.stats,
|
||||
EntrySetAction.rescan,
|
||||
EntrySetAction.emptyBin,
|
||||
|
@ -59,6 +61,7 @@ class EntrySetActions {
|
|||
EntrySetAction.toggleTitleSearch,
|
||||
EntrySetAction.addShortcut,
|
||||
EntrySetAction.map,
|
||||
EntrySetAction.slideshow,
|
||||
EntrySetAction.stats,
|
||||
EntrySetAction.rescan,
|
||||
];
|
||||
|
@ -72,6 +75,7 @@ class EntrySetActions {
|
|||
EntrySetAction.rename,
|
||||
EntrySetAction.toggleFavourite,
|
||||
EntrySetAction.map,
|
||||
EntrySetAction.slideshow,
|
||||
EntrySetAction.stats,
|
||||
EntrySetAction.rescan,
|
||||
// editing actions are in their subsection
|
||||
|
@ -86,6 +90,7 @@ class EntrySetActions {
|
|||
EntrySetAction.rename,
|
||||
EntrySetAction.toggleFavourite,
|
||||
EntrySetAction.map,
|
||||
EntrySetAction.slideshow,
|
||||
EntrySetAction.stats,
|
||||
EntrySetAction.rescan,
|
||||
// editing actions are in their subsection
|
||||
|
@ -125,6 +130,8 @@ extension ExtraEntrySetAction on EntrySetAction {
|
|||
// browsing or selecting
|
||||
case EntrySetAction.map:
|
||||
return context.l10n.menuActionMap;
|
||||
case EntrySetAction.slideshow:
|
||||
return context.l10n.menuActionSlideshow;
|
||||
case EntrySetAction.stats:
|
||||
return context.l10n.menuActionStats;
|
||||
case EntrySetAction.rescan:
|
||||
|
@ -190,6 +197,8 @@ extension ExtraEntrySetAction on EntrySetAction {
|
|||
// browsing or selecting
|
||||
case EntrySetAction.map:
|
||||
return AIcons.map;
|
||||
case EntrySetAction.slideshow:
|
||||
return AIcons.slideshow;
|
||||
case EntrySetAction.stats:
|
||||
return AIcons.stats;
|
||||
case EntrySetAction.rescan:
|
||||
|
|
30
lib/model/actions/slideshow_actions.dart
Normal file
|
@ -0,0 +1,30 @@
|
|||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum SlideshowAction {
|
||||
resume,
|
||||
showInCollection,
|
||||
}
|
||||
|
||||
extension ExtraSlideshowAction on SlideshowAction {
|
||||
String getText(BuildContext context) {
|
||||
switch (this) {
|
||||
case SlideshowAction.resume:
|
||||
return context.l10n.slideshowActionResume;
|
||||
case SlideshowAction.showInCollection:
|
||||
return context.l10n.slideshowActionShowInCollection;
|
||||
}
|
||||
}
|
||||
|
||||
Widget getIcon() => Icon(_getIconData());
|
||||
|
||||
IconData _getIconData() {
|
||||
switch (this) {
|
||||
case SlideshowAction.resume:
|
||||
return AIcons.play;
|
||||
case SlideshowAction.showInCollection:
|
||||
return AIcons.allCollection;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -5,8 +5,8 @@ final Device device = Device._private();
|
|||
|
||||
class Device {
|
||||
late final String _userAgent;
|
||||
late final bool _canGrantDirectoryAccess, _canPinShortcut, _canPrint, _canRenderFlagEmojis;
|
||||
late final bool _showPinShortcutFeedback, _supportEdgeToEdgeUIMode;
|
||||
late final bool _canGrantDirectoryAccess, _canPinShortcut, _canPrint, _canRenderFlagEmojis, _canSetLockScreenWallpaper;
|
||||
late final bool _isDynamicColorAvailable, _showPinShortcutFeedback, _supportEdgeToEdgeUIMode;
|
||||
|
||||
String get userAgent => _userAgent;
|
||||
|
||||
|
@ -18,6 +18,10 @@ class Device {
|
|||
|
||||
bool get canRenderFlagEmojis => _canRenderFlagEmojis;
|
||||
|
||||
bool get canSetLockScreenWallpaper => _canSetLockScreenWallpaper;
|
||||
|
||||
bool get isDynamicColorAvailable => _isDynamicColorAvailable;
|
||||
|
||||
bool get showPinShortcutFeedback => _showPinShortcutFeedback;
|
||||
|
||||
bool get supportEdgeToEdgeUIMode => _supportEdgeToEdgeUIMode;
|
||||
|
@ -33,6 +37,8 @@ class Device {
|
|||
_canPinShortcut = capabilities['canPinShortcut'] ?? false;
|
||||
_canPrint = capabilities['canPrint'] ?? false;
|
||||
_canRenderFlagEmojis = capabilities['canRenderFlagEmojis'] ?? false;
|
||||
_canSetLockScreenWallpaper = capabilities['canSetLockScreenWallpaper'] ?? false;
|
||||
_isDynamicColorAvailable = capabilities['isDynamicColorAvailable'] ?? false;
|
||||
_showPinShortcutFeedback = capabilities['showPinShortcutFeedback'] ?? false;
|
||||
_supportEdgeToEdgeUIMode = capabilities['supportEdgeToEdgeUIMode'] ?? false;
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import 'dart:ui';
|
|||
|
||||
import 'package:aves/geo/countries.dart';
|
||||
import 'package:aves/model/entry_cache.dart';
|
||||
import 'package:aves/model/entry_dirs.dart';
|
||||
import 'package:aves/model/favourites.dart';
|
||||
import 'package:aves/model/geotiff.dart';
|
||||
import 'package:aves/model/metadata/address.dart';
|
||||
|
@ -31,7 +32,8 @@ class AvesEntry {
|
|||
// `sizeBytes`, `dateModifiedSecs` can be missing in viewer mode
|
||||
int id;
|
||||
String uri;
|
||||
String? _path, _directory, _filename, _extension, _sourceTitle;
|
||||
String? _path, _filename, _extension, _sourceTitle;
|
||||
EntryDir? _directory;
|
||||
int? pageId, contentId;
|
||||
final String sourceMimeType;
|
||||
int width, height, sourceRotationDegrees;
|
||||
|
@ -175,8 +177,8 @@ class AvesEntry {
|
|||
|
||||
// directory path, without the trailing separator
|
||||
String? get directory {
|
||||
_directory ??= path != null ? pContext.dirname(path!) : null;
|
||||
return _directory;
|
||||
_directory ??= entryDirRepo.getOrCreate(path != null ? pContext.dirname(path!) : null);
|
||||
return _directory!.resolved;
|
||||
}
|
||||
|
||||
String? get filenameWithoutExtension {
|
||||
|
|
68
lib/model/entry_dirs.dart
Normal file
|
@ -0,0 +1,68 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves/utils/android_file_utils.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
||||
final entryDirRepo = EntryDirRepo._private();
|
||||
|
||||
class EntryDirRepo {
|
||||
EntryDirRepo._private();
|
||||
|
||||
// mapping between the raw entry directory path to a resolvable directory
|
||||
final Map<String?, EntryDir> _dirs = {};
|
||||
final StreamController<EntryDir> _ambiguousDirStreamController = StreamController.broadcast();
|
||||
|
||||
Stream<EntryDir> get ambiguousDirStream => _ambiguousDirStreamController.stream;
|
||||
|
||||
// get a resolvable directory for a raw entry directory path
|
||||
EntryDir getOrCreate(String? asIs) {
|
||||
var entryDir = _dirs[asIs];
|
||||
if (entryDir != null) return entryDir;
|
||||
|
||||
final asIsLower = asIs?.toLowerCase();
|
||||
entryDir = _dirs.values.firstWhereOrNull((dir) => dir.asIsLower == asIsLower);
|
||||
if (entryDir != null && !entryDir.ambiguous) {
|
||||
entryDir.ambiguous = true;
|
||||
_ambiguousDirStreamController.add(entryDir);
|
||||
}
|
||||
|
||||
return _dirs.putIfAbsent(asIs, () => entryDir ?? EntryDir(asIs));
|
||||
}
|
||||
}
|
||||
|
||||
// Some directories are ambiguous because they use different cases,
|
||||
// but the OS merge and present them as one directory.
|
||||
// This class resolves ambiguous directories to get the directory path
|
||||
// with the right case, as presented by the OS.
|
||||
class EntryDir {
|
||||
final String? asIs, asIsLower;
|
||||
bool ambiguous = false;
|
||||
String? _resolved;
|
||||
|
||||
EntryDir(this.asIs) : asIsLower = asIs?.toLowerCase();
|
||||
|
||||
String? get resolved {
|
||||
if (!ambiguous) return asIs;
|
||||
if (asIs == null) return null;
|
||||
|
||||
_resolved ??= _resolve();
|
||||
return _resolved;
|
||||
}
|
||||
|
||||
String? _resolve() {
|
||||
final vrl = VolumeRelativeDirectory.fromPath(asIs!);
|
||||
if (vrl == null || vrl.relativeDir.isEmpty) return asIs;
|
||||
|
||||
var resolved = vrl.volumePath;
|
||||
final parts = pContext.split(vrl.relativeDir);
|
||||
for (final part in parts) {
|
||||
final partLower = part.toLowerCase();
|
||||
final childrenDirs = Directory(resolved).listSync().where((v) => v.absolute is Directory).toSet();
|
||||
final found = childrenDirs.firstWhereOrNull((v) => pContext.basename(v.path).toLowerCase() == partLower);
|
||||
resolved = found?.path ?? '$resolved${pContext.separator}$part';
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
}
|
|
@ -5,6 +5,7 @@ import 'package:aves/image_providers/thumbnail_provider.dart';
|
|||
import 'package:aves/image_providers/uri_image_provider.dart';
|
||||
import 'package:aves/model/entry.dart';
|
||||
import 'package:aves/model/entry_cache.dart';
|
||||
import 'package:aves/utils/math_utils.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
|
@ -63,4 +64,15 @@ extension ExtraAvesEntryImages on AvesEntry {
|
|||
final sizedThumbnailKey = EntryCache.thumbnailRequestExtents.map(_getThumbnailProviderKey).firstWhereOrNull(_isReady);
|
||||
return sizedThumbnailKey != null ? ThumbnailProvider(sizedThumbnailKey) : getThumbnail();
|
||||
}
|
||||
|
||||
// magic number used to derive sample size from scale
|
||||
static const scaleFactor = 2.0;
|
||||
|
||||
static int sampleSizeForScale(double scale) {
|
||||
var sample = 0;
|
||||
if (0 < scale && scale < 1) {
|
||||
sample = highestPowerOf2((1 / scale) / scaleFactor);
|
||||
}
|
||||
return max<int>(1, sample);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import 'package:aves/model/entry.dart';
|
|||
import 'package:aves/model/filters/filters.dart';
|
||||
import 'package:aves/theme/colors.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/utils/file_utils.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
@ -19,11 +20,25 @@ class QueryFilter extends CollectionFilter {
|
|||
@override
|
||||
List<Object?> get props => [query, live];
|
||||
|
||||
static final _fieldPattern = RegExp(r'(.+)([=<>])(.+)');
|
||||
static final _fileSizePattern = RegExp(r'(\d+)([KMG])?');
|
||||
static const keyContentId = 'ID';
|
||||
static const keyContentYear = 'YEAR';
|
||||
static const keyContentMonth = 'MONTH';
|
||||
static const keyContentDay = 'DAY';
|
||||
static const keyContentWidth = 'WIDTH';
|
||||
static const keyContentHeight = 'HEIGHT';
|
||||
static const keyContentSize = 'SIZE';
|
||||
static const opEqual = '=';
|
||||
static const opLower = '<';
|
||||
static const opGreater = '>';
|
||||
|
||||
QueryFilter(this.query, {this.colorful = true, this.live = false}) {
|
||||
var upQuery = query.toUpperCase();
|
||||
if (upQuery.startsWith('ID:')) {
|
||||
final id = int.tryParse(upQuery.substring(3));
|
||||
_test = (entry) => entry.id == id;
|
||||
|
||||
final test = fieldTest(upQuery);
|
||||
if (test != null) {
|
||||
_test = test;
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -82,4 +97,114 @@ class QueryFilter extends CollectionFilter {
|
|||
|
||||
@override
|
||||
String get key => '$type-$query';
|
||||
|
||||
EntryFilter? fieldTest(String upQuery) {
|
||||
var match = _fieldPattern.firstMatch(upQuery);
|
||||
if (match == null) return null;
|
||||
|
||||
final key = match.group(1)?.trim();
|
||||
final op = match.group(2)?.trim();
|
||||
var valueString = match.group(3)?.trim();
|
||||
if (key == null || op == null || valueString == null) return null;
|
||||
|
||||
final valueInt = int.tryParse(valueString);
|
||||
|
||||
switch (key) {
|
||||
case keyContentId:
|
||||
if (valueInt == null) return null;
|
||||
if (op == opEqual) {
|
||||
return (entry) => entry.contentId == valueInt;
|
||||
}
|
||||
break;
|
||||
case keyContentYear:
|
||||
if (valueInt == null) return null;
|
||||
switch (op) {
|
||||
case opEqual:
|
||||
return (entry) => (entry.bestDate?.year ?? 0) == valueInt;
|
||||
case opLower:
|
||||
return (entry) => (entry.bestDate?.year ?? 0) < valueInt;
|
||||
case opGreater:
|
||||
return (entry) => (entry.bestDate?.year ?? 0) > valueInt;
|
||||
}
|
||||
break;
|
||||
case keyContentMonth:
|
||||
if (valueInt == null) return null;
|
||||
switch (op) {
|
||||
case opEqual:
|
||||
return (entry) => (entry.bestDate?.month ?? 0) == valueInt;
|
||||
case opLower:
|
||||
return (entry) => (entry.bestDate?.month ?? 0) < valueInt;
|
||||
case opGreater:
|
||||
return (entry) => (entry.bestDate?.month ?? 0) > valueInt;
|
||||
}
|
||||
break;
|
||||
case keyContentDay:
|
||||
if (valueInt == null) return null;
|
||||
switch (op) {
|
||||
case opEqual:
|
||||
return (entry) => (entry.bestDate?.day ?? 0) == valueInt;
|
||||
case opLower:
|
||||
return (entry) => (entry.bestDate?.day ?? 0) < valueInt;
|
||||
case opGreater:
|
||||
return (entry) => (entry.bestDate?.day ?? 0) > valueInt;
|
||||
}
|
||||
break;
|
||||
case keyContentWidth:
|
||||
if (valueInt == null) return null;
|
||||
switch (op) {
|
||||
case opEqual:
|
||||
return (entry) => entry.displaySize.width == valueInt;
|
||||
case opLower:
|
||||
return (entry) => entry.displaySize.width < valueInt;
|
||||
case opGreater:
|
||||
return (entry) => entry.displaySize.width > valueInt;
|
||||
}
|
||||
break;
|
||||
case keyContentHeight:
|
||||
if (valueInt == null) return null;
|
||||
switch (op) {
|
||||
case opEqual:
|
||||
return (entry) => entry.displaySize.height == valueInt;
|
||||
case opLower:
|
||||
return (entry) => entry.displaySize.height < valueInt;
|
||||
case opGreater:
|
||||
return (entry) => entry.displaySize.height > valueInt;
|
||||
}
|
||||
break;
|
||||
case keyContentSize:
|
||||
match = _fileSizePattern.firstMatch(valueString);
|
||||
if (match == null) return null;
|
||||
|
||||
valueString = match.group(1)?.trim();
|
||||
if (valueString == null) return null;
|
||||
final valueInt = int.tryParse(valueString);
|
||||
if (valueInt == null) return null;
|
||||
|
||||
var bytes = valueInt;
|
||||
final multiplierString = match.group(2)?.trim();
|
||||
switch (multiplierString) {
|
||||
case 'K':
|
||||
bytes *= kilo;
|
||||
break;
|
||||
case 'M':
|
||||
bytes *= mega;
|
||||
break;
|
||||
case 'G':
|
||||
bytes *= giga;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (op) {
|
||||
case opEqual:
|
||||
return (entry) => (entry.sizeBytes ?? 0) == bytes;
|
||||
case opLower:
|
||||
return (entry) => (entry.sizeBytes ?? 0) < bytes;
|
||||
case opGreater:
|
||||
return (entry) => (entry.sizeBytes ?? 0) > bytes;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,11 +17,15 @@ class SettingsDefaults {
|
|||
static const canUseAnalysisService = true;
|
||||
static const isInstalledAppAccessAllowed = false;
|
||||
static const isErrorReportingAllowed = false;
|
||||
static const tileLayout = TileLayout.grid;
|
||||
static const entryRenamingPattern = '<${DateNamingProcessor.key}, yyyyMMdd-HHmmss> <${NameNamingProcessor.key}>';
|
||||
|
||||
// display
|
||||
static const displayRefreshRateMode = DisplayRefreshRateMode.auto;
|
||||
static const themeBrightness = AvesThemeBrightness.system;
|
||||
static const themeColorMode = AvesThemeColorMode.polychrome;
|
||||
static const tileLayout = TileLayout.grid;
|
||||
static const entryRenamingPattern = '<${DateNamingProcessor.key}, yyyyMMdd-HHmmss> <${NameNamingProcessor.key}>';
|
||||
static const enableDynamicColor = false;
|
||||
static const enableBlurEffect = true; // `enableBlurEffect` has a contextual default value
|
||||
|
||||
// navigation
|
||||
static const mustBackTwiceToExit = true;
|
||||
|
@ -79,7 +83,6 @@ class SettingsDefaults {
|
|||
static const showOverlayInfo = true;
|
||||
static const showOverlayShootingDetails = false;
|
||||
static const showOverlayThumbnailPreview = false;
|
||||
static const enableOverlayBlurEffect = true; // `enableOverlayBlurEffect` has a contextual default value
|
||||
static const viewerUseCutout = true;
|
||||
static const viewerMaxBrightness = false;
|
||||
static const enableMotionPhotoAutoPlay = false;
|
||||
|
@ -122,6 +125,13 @@ class SettingsDefaults {
|
|||
// file picker
|
||||
static const filePickerShowHiddenFiles = false;
|
||||
|
||||
// slideshow
|
||||
static const slideshowRepeat = false;
|
||||
static const slideshowShuffle = false;
|
||||
static const slideshowTransition = ViewerTransition.fade;
|
||||
static const slideshowVideoPlayback = SlideshowVideoPlayback.playMuted;
|
||||
static const slideshowInterval = SlideshowInterval.s5;
|
||||
|
||||
// platform settings
|
||||
static const isRotationLocked = false;
|
||||
static const areAnimationsRemoved = false;
|
||||
|
|
|
@ -2,24 +2,30 @@ enum AccessibilityAnimations { system, disabled, enabled }
|
|||
|
||||
enum AccessibilityTimeout { system, appDefault, s3, s10, s30, s60, s120 }
|
||||
|
||||
enum AvesThemeColorMode { monochrome, polychrome }
|
||||
|
||||
enum AvesThemeBrightness { system, light, dark, black }
|
||||
|
||||
enum AvesThemeColorMode { monochrome, polychrome }
|
||||
|
||||
enum ConfirmationDialog { deleteForever, moveToBin, moveUndatedItems }
|
||||
|
||||
enum CoordinateFormat { dms, decimal }
|
||||
|
||||
enum DisplayRefreshRateMode { auto, highest, lowest }
|
||||
|
||||
enum EntryBackground { black, white, checkered }
|
||||
|
||||
enum HomePageSetting { collection, albums }
|
||||
|
||||
enum KeepScreenOn { never, viewerOnly, always }
|
||||
|
||||
enum DisplayRefreshRateMode { auto, highest, lowest }
|
||||
enum SlideshowInterval { s3, s5, s10, s30, s60 }
|
||||
|
||||
enum SlideshowVideoPlayback { skip, playMuted, playWithSound }
|
||||
|
||||
enum UnitSystem { metric, imperial }
|
||||
|
||||
enum VideoControls { play, playSeek, playOutside, none }
|
||||
|
||||
enum VideoLoopMode { never, shortOnly, always }
|
||||
|
||||
enum VideoControls { play, playSeek, playOutside, none }
|
||||
enum ViewerTransition { slide, parallax, fade, zoomIn }
|
||||
|
|
36
lib/model/settings/enums/slideshow_interval.dart
Normal file
|
@ -0,0 +1,36 @@
|
|||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'enums.dart';
|
||||
|
||||
extension ExtraSlideshowInterval on SlideshowInterval {
|
||||
String getName(BuildContext context) {
|
||||
switch (this) {
|
||||
case SlideshowInterval.s3:
|
||||
return context.l10n.timeSeconds(3);
|
||||
case SlideshowInterval.s5:
|
||||
return context.l10n.timeSeconds(5);
|
||||
case SlideshowInterval.s10:
|
||||
return context.l10n.timeSeconds(10);
|
||||
case SlideshowInterval.s30:
|
||||
return context.l10n.timeSeconds(30);
|
||||
case SlideshowInterval.s60:
|
||||
return context.l10n.timeMinutes(1);
|
||||
}
|
||||
}
|
||||
|
||||
Duration getDuration() {
|
||||
switch (this) {
|
||||
case SlideshowInterval.s3:
|
||||
return const Duration(seconds: 3);
|
||||
case SlideshowInterval.s5:
|
||||
return const Duration(seconds: 5);
|
||||
case SlideshowInterval.s10:
|
||||
return const Duration(seconds: 10);
|
||||
case SlideshowInterval.s30:
|
||||
return const Duration(seconds: 30);
|
||||
case SlideshowInterval.s60:
|
||||
return const Duration(minutes: 1);
|
||||
}
|
||||
}
|
||||
}
|
17
lib/model/settings/enums/slideshow_video_playback.dart
Normal file
|
@ -0,0 +1,17 @@
|
|||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'enums.dart';
|
||||
|
||||
extension ExtraSlideshowVideoPlayback on SlideshowVideoPlayback {
|
||||
String getName(BuildContext context) {
|
||||
switch (this) {
|
||||
case SlideshowVideoPlayback.skip:
|
||||
return context.l10n.slideshowVideoPlaybackSkip;
|
||||
case SlideshowVideoPlayback.playMuted:
|
||||
return context.l10n.slideshowVideoPlaybackMuted;
|
||||
case SlideshowVideoPlayback.playWithSound:
|
||||
return context.l10n.slideshowVideoPlaybackWithSound;
|
||||
}
|
||||
}
|
||||
}
|
33
lib/model/settings/enums/viewer_transition.dart
Normal file
|
@ -0,0 +1,33 @@
|
|||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:aves/widgets/viewer/controller.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import 'enums.dart';
|
||||
|
||||
extension ExtraViewerTransition on ViewerTransition {
|
||||
String getName(BuildContext context) {
|
||||
switch (this) {
|
||||
case ViewerTransition.slide:
|
||||
return context.l10n.viewerTransitionSlide;
|
||||
case ViewerTransition.parallax:
|
||||
return context.l10n.viewerTransitionParallax;
|
||||
case ViewerTransition.fade:
|
||||
return context.l10n.viewerTransitionFade;
|
||||
case ViewerTransition.zoomIn:
|
||||
return context.l10n.viewerTransitionZoomIn;
|
||||
}
|
||||
}
|
||||
|
||||
TransitionBuilder builder(PageController pageController, int index) {
|
||||
switch (this) {
|
||||
case ViewerTransition.slide:
|
||||
return PageTransitionEffects.slide(pageController, index, parallax: false);
|
||||
case ViewerTransition.parallax:
|
||||
return PageTransitionEffects.slide(pageController, index, parallax: true);
|
||||
case ViewerTransition.fade:
|
||||
return PageTransitionEffects.fade(pageController, index, zoomIn: false);
|
||||
case ViewerTransition.zoomIn:
|
||||
return PageTransitionEffects.fade(pageController, index, zoomIn: true);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,6 +10,7 @@ import 'package:aves/model/settings/enums/enums.dart';
|
|||
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/optional_event_channel.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves_map/aves_map.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
|
@ -19,7 +20,7 @@ import 'package:flutter/services.dart';
|
|||
final Settings settings = Settings._private();
|
||||
|
||||
class Settings extends ChangeNotifier {
|
||||
final EventChannel _platformSettingsChangeChannel = const EventChannel('deckers.thibault/aves/settings_change');
|
||||
final EventChannel _platformSettingsChangeChannel = const OptionalEventChannel('deckers.thibault/aves/settings_change');
|
||||
final StreamController<SettingsChangedEvent> _updateStreamController = StreamController.broadcast();
|
||||
|
||||
Stream<SettingsChangedEvent> get updateStream => _updateStreamController.stream;
|
||||
|
@ -42,15 +43,19 @@ class Settings extends ChangeNotifier {
|
|||
static const isInstalledAppAccessAllowedKey = 'is_installed_app_access_allowed';
|
||||
static const isErrorReportingAllowedKey = 'is_crashlytics_enabled';
|
||||
static const localeKey = 'locale';
|
||||
static const displayRefreshRateModeKey = 'display_refresh_rate_mode';
|
||||
static const themeBrightnessKey = 'theme_brightness';
|
||||
static const themeColorModeKey = 'theme_color_mode';
|
||||
static const catalogTimeZoneKey = 'catalog_time_zone';
|
||||
static const tileExtentPrefixKey = 'tile_extent_';
|
||||
static const tileLayoutPrefixKey = 'tile_layout_';
|
||||
static const entryRenamingPatternKey = 'entry_renaming_pattern';
|
||||
static const topEntryIdsKey = 'top_entry_ids';
|
||||
|
||||
// display
|
||||
static const displayRefreshRateModeKey = 'display_refresh_rate_mode';
|
||||
static const themeBrightnessKey = 'theme_brightness';
|
||||
static const themeColorModeKey = 'theme_color_mode';
|
||||
static const enableDynamicColorKey = 'dynamic_color';
|
||||
static const enableBlurEffectKey = 'enable_overlay_blur_effect';
|
||||
|
||||
// navigation
|
||||
static const mustBackTwiceToExitKey = 'must_back_twice_to_exit';
|
||||
static const keepScreenOnKey = 'keep_screen_on';
|
||||
|
@ -92,7 +97,6 @@ class Settings extends ChangeNotifier {
|
|||
static const showOverlayInfoKey = 'show_overlay_info';
|
||||
static const showOverlayShootingDetailsKey = 'show_overlay_shooting_details';
|
||||
static const showOverlayThumbnailPreviewKey = 'show_overlay_thumbnail_preview';
|
||||
static const enableOverlayBlurEffectKey = 'enable_overlay_blur_effect';
|
||||
static const viewerUseCutoutKey = 'viewer_use_cutout';
|
||||
static const viewerMaxBrightnessKey = 'viewer_max_brightness';
|
||||
static const enableMotionPhotoAutoPlayKey = 'motion_photo_auto_play';
|
||||
|
@ -134,6 +138,13 @@ class Settings extends ChangeNotifier {
|
|||
// file picker
|
||||
static const filePickerShowHiddenFilesKey = 'file_picker_show_hidden_files';
|
||||
|
||||
// slideshow
|
||||
static const slideshowRepeatKey = 'slideshow_loop';
|
||||
static const slideshowShuffleKey = 'slideshow_shuffle';
|
||||
static const slideshowTransitionKey = 'slideshow_transition';
|
||||
static const slideshowVideoPlaybackKey = 'slideshow_video_playback';
|
||||
static const slideshowIntervalKey = 'slideshow_interval';
|
||||
|
||||
// platform settings
|
||||
// cf Android `Settings.System.ACCELEROMETER_ROTATION`
|
||||
static const platformAccelerometerRotationKey = 'accelerometer_rotation';
|
||||
|
@ -161,7 +172,7 @@ class Settings extends ChangeNotifier {
|
|||
Future<void> setContextualDefaults() async {
|
||||
// performance
|
||||
final performanceClass = await deviceService.getPerformanceClass();
|
||||
enableOverlayBlurEffect = performanceClass >= 29;
|
||||
enableBlurEffect = performanceClass >= 29;
|
||||
|
||||
// availability
|
||||
final defaultMapStyle = mobileServices.defaultMapStyle;
|
||||
|
@ -187,8 +198,7 @@ class Settings extends ChangeNotifier {
|
|||
|
||||
set canUseAnalysisService(bool newValue) => setAndNotify(canUseAnalysisServiceKey, newValue);
|
||||
|
||||
// TODO TLAD use `true` for transition (it's unset in v1.5.4), but replace by `SettingsDefaults.isInstalledAppAccessAllowed` in a later release
|
||||
bool get isInstalledAppAccessAllowed => getBoolOrDefault(isInstalledAppAccessAllowedKey, true);
|
||||
bool get isInstalledAppAccessAllowed => getBoolOrDefault(isInstalledAppAccessAllowedKey, SettingsDefaults.isInstalledAppAccessAllowed);
|
||||
|
||||
set isInstalledAppAccessAllowed(bool newValue) => setAndNotify(isInstalledAppAccessAllowedKey, newValue);
|
||||
|
||||
|
@ -249,18 +259,6 @@ class Settings extends ChangeNotifier {
|
|||
return _appliedLocale!;
|
||||
}
|
||||
|
||||
DisplayRefreshRateMode get displayRefreshRateMode => getEnumOrDefault(displayRefreshRateModeKey, SettingsDefaults.displayRefreshRateMode, DisplayRefreshRateMode.values);
|
||||
|
||||
set displayRefreshRateMode(DisplayRefreshRateMode newValue) => setAndNotify(displayRefreshRateModeKey, newValue.toString());
|
||||
|
||||
AvesThemeBrightness get themeBrightness => getEnumOrDefault(themeBrightnessKey, SettingsDefaults.themeBrightness, AvesThemeBrightness.values);
|
||||
|
||||
set themeBrightness(AvesThemeBrightness newValue) => setAndNotify(themeBrightnessKey, newValue.toString());
|
||||
|
||||
AvesThemeColorMode get themeColorMode => getEnumOrDefault(themeColorModeKey, SettingsDefaults.themeColorMode, AvesThemeColorMode.values);
|
||||
|
||||
set themeColorMode(AvesThemeColorMode newValue) => setAndNotify(themeColorModeKey, newValue.toString());
|
||||
|
||||
String get catalogTimeZone => getString(catalogTimeZoneKey) ?? '';
|
||||
|
||||
set catalogTimeZone(String newValue) => setAndNotify(catalogTimeZoneKey, newValue);
|
||||
|
@ -281,6 +279,28 @@ class Settings extends ChangeNotifier {
|
|||
|
||||
set topEntryIds(List<int>? newValue) => setAndNotify(topEntryIdsKey, newValue?.map((id) => id.toString()).whereNotNull().toList());
|
||||
|
||||
// display
|
||||
|
||||
DisplayRefreshRateMode get displayRefreshRateMode => getEnumOrDefault(displayRefreshRateModeKey, SettingsDefaults.displayRefreshRateMode, DisplayRefreshRateMode.values);
|
||||
|
||||
set displayRefreshRateMode(DisplayRefreshRateMode newValue) => setAndNotify(displayRefreshRateModeKey, newValue.toString());
|
||||
|
||||
AvesThemeBrightness get themeBrightness => getEnumOrDefault(themeBrightnessKey, SettingsDefaults.themeBrightness, AvesThemeBrightness.values);
|
||||
|
||||
set themeBrightness(AvesThemeBrightness newValue) => setAndNotify(themeBrightnessKey, newValue.toString());
|
||||
|
||||
AvesThemeColorMode get themeColorMode => getEnumOrDefault(themeColorModeKey, SettingsDefaults.themeColorMode, AvesThemeColorMode.values);
|
||||
|
||||
set themeColorMode(AvesThemeColorMode newValue) => setAndNotify(themeColorModeKey, newValue.toString());
|
||||
|
||||
bool get enableDynamicColor => getBoolOrDefault(enableDynamicColorKey, SettingsDefaults.enableDynamicColor);
|
||||
|
||||
set enableDynamicColor(bool newValue) => setAndNotify(enableDynamicColorKey, newValue);
|
||||
|
||||
bool get enableBlurEffect => getBoolOrDefault(enableBlurEffectKey, SettingsDefaults.enableBlurEffect);
|
||||
|
||||
set enableBlurEffect(bool newValue) => setAndNotify(enableBlurEffectKey, newValue);
|
||||
|
||||
// navigation
|
||||
|
||||
bool get mustBackTwiceToExit => getBoolOrDefault(mustBackTwiceToExitKey, SettingsDefaults.mustBackTwiceToExit);
|
||||
|
@ -441,10 +461,6 @@ class Settings extends ChangeNotifier {
|
|||
|
||||
set showOverlayThumbnailPreview(bool newValue) => setAndNotify(showOverlayThumbnailPreviewKey, newValue);
|
||||
|
||||
bool get enableOverlayBlurEffect => getBoolOrDefault(enableOverlayBlurEffectKey, SettingsDefaults.enableOverlayBlurEffect);
|
||||
|
||||
set enableOverlayBlurEffect(bool newValue) => setAndNotify(enableOverlayBlurEffectKey, newValue);
|
||||
|
||||
bool get viewerUseCutout => getBoolOrDefault(viewerUseCutoutKey, SettingsDefaults.viewerUseCutout);
|
||||
|
||||
set viewerUseCutout(bool newValue) => setAndNotify(viewerUseCutoutKey, newValue);
|
||||
|
@ -567,6 +583,28 @@ class Settings extends ChangeNotifier {
|
|||
|
||||
set filePickerShowHiddenFiles(bool newValue) => setAndNotify(filePickerShowHiddenFilesKey, newValue);
|
||||
|
||||
// slideshow
|
||||
|
||||
bool get slideshowRepeat => getBoolOrDefault(slideshowRepeatKey, SettingsDefaults.slideshowRepeat);
|
||||
|
||||
set slideshowRepeat(bool newValue) => setAndNotify(slideshowRepeatKey, newValue);
|
||||
|
||||
bool get slideshowShuffle => getBoolOrDefault(slideshowShuffleKey, SettingsDefaults.slideshowShuffle);
|
||||
|
||||
set slideshowShuffle(bool newValue) => setAndNotify(slideshowShuffleKey, newValue);
|
||||
|
||||
ViewerTransition get slideshowTransition => getEnumOrDefault(slideshowTransitionKey, SettingsDefaults.slideshowTransition, ViewerTransition.values);
|
||||
|
||||
set slideshowTransition(ViewerTransition newValue) => setAndNotify(slideshowTransitionKey, newValue.toString());
|
||||
|
||||
SlideshowVideoPlayback get slideshowVideoPlayback => getEnumOrDefault(slideshowVideoPlaybackKey, SettingsDefaults.slideshowVideoPlayback, SlideshowVideoPlayback.values);
|
||||
|
||||
set slideshowVideoPlayback(SlideshowVideoPlayback newValue) => setAndNotify(slideshowVideoPlaybackKey, newValue.toString());
|
||||
|
||||
SlideshowInterval get slideshowInterval => getEnumOrDefault(slideshowIntervalKey, SettingsDefaults.slideshowInterval, SlideshowInterval.values);
|
||||
|
||||
set slideshowInterval(SlideshowInterval newValue) => setAndNotify(slideshowIntervalKey, newValue.toString());
|
||||
|
||||
// convenience methods
|
||||
|
||||
int? getInt(String key) => settingsStore.getInt(key);
|
||||
|
@ -695,6 +733,8 @@ class Settings extends ChangeNotifier {
|
|||
break;
|
||||
case isInstalledAppAccessAllowedKey:
|
||||
case isErrorReportingAllowedKey:
|
||||
case enableDynamicColorKey:
|
||||
case enableBlurEffectKey:
|
||||
case showBottomNavigationBarKey:
|
||||
case mustBackTwiceToExitKey:
|
||||
case confirmDeleteForeverKey:
|
||||
|
@ -713,7 +753,6 @@ class Settings extends ChangeNotifier {
|
|||
case showOverlayInfoKey:
|
||||
case showOverlayShootingDetailsKey:
|
||||
case showOverlayThumbnailPreviewKey:
|
||||
case enableOverlayBlurEffectKey:
|
||||
case viewerUseCutoutKey:
|
||||
case viewerMaxBrightnessKey:
|
||||
case enableMotionPhotoAutoPlayKey:
|
||||
|
@ -724,6 +763,8 @@ class Settings extends ChangeNotifier {
|
|||
case subtitleShowOutlineKey:
|
||||
case saveSearchHistoryKey:
|
||||
case filePickerShowHiddenFilesKey:
|
||||
case slideshowRepeatKey:
|
||||
case slideshowShuffleKey:
|
||||
if (newValue is bool) {
|
||||
settingsStore.setBool(key, newValue);
|
||||
} else {
|
||||
|
@ -751,6 +792,9 @@ class Settings extends ChangeNotifier {
|
|||
case unitSystemKey:
|
||||
case accessibilityAnimationsKey:
|
||||
case timeToTakeActionKey:
|
||||
case slideshowTransitionKey:
|
||||
case slideshowVideoPlaybackKey:
|
||||
case slideshowIntervalKey:
|
||||
if (newValue is String) {
|
||||
settingsStore.setString(key, newValue);
|
||||
} else {
|
||||
|
|
|
@ -32,7 +32,7 @@ class CollectionLens with ChangeNotifier {
|
|||
final AChangeNotifier filterChangeNotifier = AChangeNotifier(), sortSectionChangeNotifier = AChangeNotifier();
|
||||
final List<StreamSubscription> _subscriptions = [];
|
||||
int? id;
|
||||
bool listenToSource, groupBursts;
|
||||
bool listenToSource, groupBursts, fixedSort;
|
||||
List<AvesEntry>? fixedSelection;
|
||||
|
||||
List<AvesEntry> _filteredSortedEntries = [];
|
||||
|
@ -45,6 +45,7 @@ class CollectionLens with ChangeNotifier {
|
|||
this.id,
|
||||
this.listenToSource = true,
|
||||
this.groupBursts = true,
|
||||
this.fixedSort = false,
|
||||
this.fixedSelection,
|
||||
}) : filters = (filters ?? {}).whereNotNull().toSet(),
|
||||
sectionFactor = settings.collectionSectionFactor,
|
||||
|
@ -203,6 +204,8 @@ class CollectionLens with ChangeNotifier {
|
|||
}
|
||||
|
||||
void _applySort() {
|
||||
if (fixedSort) return;
|
||||
|
||||
switch (sortFactor) {
|
||||
case EntrySortFactor.date:
|
||||
_filteredSortedEntries.sort(AvesEntry.compareByDate);
|
||||
|
@ -220,6 +223,11 @@ class CollectionLens with ChangeNotifier {
|
|||
}
|
||||
|
||||
void _applySection() {
|
||||
if (fixedSort) {
|
||||
sections = Map.fromEntries([
|
||||
MapEntry(const SectionKey(), _filteredSortedEntries),
|
||||
]);
|
||||
} else {
|
||||
switch (sortFactor) {
|
||||
case EntrySortFactor.date:
|
||||
switch (sectionFactor) {
|
||||
|
@ -252,6 +260,7 @@ class CollectionLens with ChangeNotifier {
|
|||
]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
sections = Map.unmodifiable(sections);
|
||||
_sortedEntries = null;
|
||||
notifyListeners();
|
||||
|
|
|
@ -416,6 +416,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, LocationMixin, TagM
|
|||
}
|
||||
}
|
||||
if (startAnalysisService) {
|
||||
// TODO TLAD [tiramisu] explain foreground service and request POST_NOTIFICATIONS permission
|
||||
await AnalysisService.startService(
|
||||
force: force,
|
||||
entryIds: entries?.map((entry) => entry.id).toList(),
|
||||
|
|
17
lib/model/wallpaper_target.dart
Normal file
|
@ -0,0 +1,17 @@
|
|||
import 'package:aves/widgets/common/extensions/build_context.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
enum WallpaperTarget { home, lock, homeLock }
|
||||
|
||||
extension ExtraWallpaperTarget on WallpaperTarget {
|
||||
String getName(BuildContext context) {
|
||||
switch (this) {
|
||||
case WallpaperTarget.home:
|
||||
return context.l10n.wallpaperTargetHome;
|
||||
case WallpaperTarget.lock:
|
||||
return context.l10n.wallpaperTargetLock;
|
||||
case WallpaperTarget.homeLock:
|
||||
return context.l10n.wallpaperTargetHomeLock;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -44,6 +44,8 @@ class MimeTypes {
|
|||
|
||||
static const anyVideo = 'video/*';
|
||||
|
||||
static const v3gpp = 'video/3gpp';
|
||||
static const asf = 'video/x-ms-asf';
|
||||
static const avi = 'video/avi';
|
||||
static const aviVnd = 'video/vnd.avi';
|
||||
static const flv = 'video/flv';
|
||||
|
@ -56,6 +58,7 @@ class MimeTypes {
|
|||
static const mpeg = 'video/mpeg';
|
||||
static const ogv = 'video/ogg';
|
||||
static const webm = 'video/webm';
|
||||
static const wmv = 'video/x-ms-wmv';
|
||||
|
||||
static const json = 'application/json';
|
||||
static const plainText = 'text/plain';
|
||||
|
@ -76,7 +79,7 @@ class MimeTypes {
|
|||
|
||||
static const Set<String> _knownOpaqueImages = {jpeg};
|
||||
|
||||
static const Set<String> _knownVideos = {avi, aviVnd, flv, flvX, mkv, mov, mp2t, mp2ts, mp4, mpeg, ogv, webm};
|
||||
static const Set<String> _knownVideos = {v3gpp, asf, avi, aviVnd, flv, flvX, mkv, mov, mp2t, mp2ts, mp4, mpeg, ogv, webm, wmv};
|
||||
|
||||
static final Set<String> knownMediaTypes = {
|
||||
anyImage,
|
||||
|
|
53
lib/services/common/optional_event_channel.dart
Normal file
|
@ -0,0 +1,53 @@
|
|||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
// adapted from Flutter `EventChannel` in `/services/platform_channel.dart`
|
||||
// to use an `OptionalMethodChannel` when subscribing to events
|
||||
class OptionalEventChannel extends EventChannel {
|
||||
const OptionalEventChannel(super.name, [super.codec = const StandardMethodCodec(), super.binaryMessenger]);
|
||||
|
||||
@override
|
||||
Stream<dynamic> receiveBroadcastStream([dynamic arguments]) {
|
||||
final MethodChannel methodChannel = OptionalMethodChannel(name, codec);
|
||||
late StreamController<dynamic> controller;
|
||||
controller = StreamController<dynamic>.broadcast(onListen: () async {
|
||||
binaryMessenger.setMessageHandler(name, (reply) async {
|
||||
if (reply == null) {
|
||||
await controller.close();
|
||||
} else {
|
||||
try {
|
||||
controller.add(codec.decodeEnvelope(reply));
|
||||
} on PlatformException catch (e) {
|
||||
controller.addError(e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
});
|
||||
try {
|
||||
await methodChannel.invokeMethod<void>('listen', arguments);
|
||||
} catch (exception, stack) {
|
||||
FlutterError.reportError(FlutterErrorDetails(
|
||||
exception: exception,
|
||||
stack: stack,
|
||||
library: 'services library',
|
||||
context: ErrorDescription('while activating platform stream on channel $name'),
|
||||
));
|
||||
}
|
||||
}, onCancel: () async {
|
||||
binaryMessenger.setMessageHandler(name, null);
|
||||
try {
|
||||
await methodChannel.invokeMethod<void>('cancel', arguments);
|
||||
} catch (exception, stack) {
|
||||
FlutterError.reportError(FlutterErrorDetails(
|
||||
exception: exception,
|
||||
stack: stack,
|
||||
library: 'services library',
|
||||
context: ErrorDescription('while de-activating platform stream on channel $name'),
|
||||
));
|
||||
}
|
||||
});
|
||||
return controller.stream;
|
||||
}
|
||||
}
|
|
@ -22,7 +22,11 @@ class GeocodingService {
|
|||
});
|
||||
return (result as List).cast<Map>().map(Address.fromMap).toList();
|
||||
} on PlatformException catch (e, stack) {
|
||||
if (e.code != 'getAddress-empty' && e.code != 'getAddress-network') {
|
||||
if (!{
|
||||
'getAddress-empty',
|
||||
'getAddress-network',
|
||||
'getAddress-unavailable',
|
||||
}.contains(e.code)) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
}
|
||||
|
|
23
lib/services/wallpaper_service.dart
Normal file
|
@ -0,0 +1,23 @@
|
|||
import 'dart:typed_data';
|
||||
|
||||
import 'package:aves/model/wallpaper_target.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
class WallpaperService {
|
||||
static const platform = MethodChannel('deckers.thibault/aves/wallpaper');
|
||||
|
||||
static Future<bool> set(Uint8List bytes, WallpaperTarget target) async {
|
||||
try {
|
||||
await platform.invokeMethod('setWallpaper', <String, dynamic>{
|
||||
'bytes': bytes,
|
||||
'home': {WallpaperTarget.home, WallpaperTarget.homeLock}.contains(target),
|
||||
'lock': {WallpaperTarget.lock, WallpaperTarget.homeLock}.contains(target),
|
||||
});
|
||||
return true;
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
|
@ -104,6 +104,7 @@ class AIcons {
|
|||
static const IconData setCover = MdiIcons.imageEditOutline;
|
||||
static const IconData share = Icons.share_outlined;
|
||||
static const IconData show = Icons.visibility_outlined;
|
||||
static const IconData slideshow = Icons.slideshow_outlined;
|
||||
static const IconData speed = Icons.speed_outlined;
|
||||
static const IconData stats = Icons.pie_chart_outline_outlined;
|
||||
static const IconData streams = Icons.translate_outlined;
|
||||
|
|
|
@ -7,7 +7,7 @@ import 'package:flutter/services.dart';
|
|||
import 'package:provider/provider.dart';
|
||||
|
||||
class Themes {
|
||||
static const _accentColor = Colors.indigoAccent;
|
||||
static const defaultAccent = Colors.indigoAccent;
|
||||
|
||||
static const _tooltipTheme = TooltipThemeData(
|
||||
verticalOffset: 32,
|
||||
|
@ -19,8 +19,8 @@ class Themes {
|
|||
fontFeatures: [FontFeature.enable('smcp')],
|
||||
);
|
||||
|
||||
static const _snackBarTheme = SnackBarThemeData(
|
||||
actionTextColor: _accentColor,
|
||||
static SnackBarThemeData _snackBarTheme(Color accentColor) => SnackBarThemeData(
|
||||
actionTextColor: accentColor,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
);
|
||||
|
||||
|
@ -35,10 +35,10 @@ class Themes {
|
|||
static const _lightSecondLayer = Color(0xFFF5F5F5); // aka `Colors.grey[100]`
|
||||
static const _lightThirdLayer = Color(0xFFEEEEEE); // aka `Colors.grey[200]`
|
||||
|
||||
static final lightTheme = ThemeData(
|
||||
static ThemeData lightTheme(Color accentColor) => ThemeData(
|
||||
colorScheme: ColorScheme.light(
|
||||
primary: _accentColor,
|
||||
secondary: _accentColor,
|
||||
primary: accentColor,
|
||||
secondary: accentColor,
|
||||
onPrimary: _lightBodyColor,
|
||||
onSecondary: _lightBodyColor,
|
||||
),
|
||||
|
@ -49,8 +49,8 @@ class Themes {
|
|||
// `cardColor` is used by `ExpansionPanel`
|
||||
cardColor: _lightSecondLayer,
|
||||
dialogBackgroundColor: _lightSecondLayer,
|
||||
indicatorColor: _accentColor,
|
||||
toggleableActiveColor: _accentColor,
|
||||
indicatorColor: accentColor,
|
||||
toggleableActiveColor: accentColor,
|
||||
typography: _typography,
|
||||
appBarTheme: AppBarTheme(
|
||||
backgroundColor: _lightFirstLayer,
|
||||
|
@ -66,7 +66,7 @@ class Themes {
|
|||
popupMenuTheme: const PopupMenuThemeData(
|
||||
color: _lightSecondLayer,
|
||||
),
|
||||
snackBarTheme: _snackBarTheme,
|
||||
snackBarTheme: _snackBarTheme(accentColor),
|
||||
tabBarTheme: TabBarTheme(
|
||||
labelColor: _lightTitleColor,
|
||||
unselectedLabelColor: Colors.black54,
|
||||
|
@ -87,10 +87,10 @@ class Themes {
|
|||
static const _darkSecondLayer = Color(0xFF363636);
|
||||
static const _darkThirdLayer = Color(0xFF424242); // aka `Colors.grey[800]`
|
||||
|
||||
static final darkTheme = ThemeData(
|
||||
static ThemeData darkTheme(Color accentColor) => ThemeData(
|
||||
colorScheme: ColorScheme.dark(
|
||||
primary: _accentColor,
|
||||
secondary: _accentColor,
|
||||
primary: accentColor,
|
||||
secondary: accentColor,
|
||||
// surface color is used by the date/time pickers
|
||||
surface: Colors.grey.shade800,
|
||||
onPrimary: _darkBodyColor,
|
||||
|
@ -103,8 +103,8 @@ class Themes {
|
|||
// `cardColor` is used by `ExpansionPanel`
|
||||
cardColor: _darkSecondLayer,
|
||||
dialogBackgroundColor: _darkSecondLayer,
|
||||
indicatorColor: _accentColor,
|
||||
toggleableActiveColor: _accentColor,
|
||||
indicatorColor: accentColor,
|
||||
toggleableActiveColor: accentColor,
|
||||
typography: _typography,
|
||||
appBarTheme: AppBarTheme(
|
||||
backgroundColor: _darkFirstLayer,
|
||||
|
@ -117,7 +117,7 @@ class Themes {
|
|||
popupMenuTheme: const PopupMenuThemeData(
|
||||
color: _darkSecondLayer,
|
||||
),
|
||||
snackBarTheme: _snackBarTheme.copyWith(
|
||||
snackBarTheme: _snackBarTheme(accentColor).copyWith(
|
||||
backgroundColor: Colors.grey.shade800,
|
||||
contentTextStyle: TextStyle(
|
||||
color: _darkBodyColor,
|
||||
|
@ -138,20 +138,23 @@ class Themes {
|
|||
static const _blackSecondLayer = Color(0xFF212121); // aka `Colors.grey[900]`
|
||||
static const _blackThirdLayer = Color(0xFF303030); // aka `Colors.grey[850]`
|
||||
|
||||
static final blackTheme = darkTheme.copyWith(
|
||||
static ThemeData blackTheme(Color accentColor) {
|
||||
final baseTheme = darkTheme(accentColor);
|
||||
return baseTheme.copyWith(
|
||||
// `canvasColor` is used by `Drawer`, `DropdownButton` and `ExpansionTileCard`
|
||||
canvasColor: _blackSecondLayer,
|
||||
scaffoldBackgroundColor: _blackFirstLayer,
|
||||
// `cardColor` is used by `ExpansionPanel`
|
||||
cardColor: _blackSecondLayer,
|
||||
dialogBackgroundColor: _blackSecondLayer,
|
||||
appBarTheme: darkTheme.appBarTheme.copyWith(
|
||||
appBarTheme: baseTheme.appBarTheme.copyWith(
|
||||
backgroundColor: _blackFirstLayer,
|
||||
),
|
||||
popupMenuTheme: darkTheme.popupMenuTheme.copyWith(
|
||||
popupMenuTheme: baseTheme.popupMenuTheme.copyWith(
|
||||
color: _blackSecondLayer,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Color overlayBackgroundColor({
|
||||
required Brightness brightness,
|
||||
|
|
|
@ -108,6 +108,11 @@ class Constants {
|
|||
licenseUrl: 'https://github.com/fluttercommunity/plus_plugins/blob/main/packages/device_info_plus/device_info_plus/LICENSE',
|
||||
sourceUrl: 'https://github.com/fluttercommunity/plus_plugins/tree/main/packages/device_info_plus',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Dynamic Color',
|
||||
license: 'BSD 3-Clause',
|
||||
sourceUrl: 'https://github.com/material-foundation/material-dynamic-color-flutter',
|
||||
),
|
||||
Dependency(
|
||||
name: 'fijkplayer (Aves fork)',
|
||||
license: 'MIT',
|
||||
|
@ -337,6 +342,12 @@ class Constants {
|
|||
license: 'Apache 2.0',
|
||||
sourceUrl: 'https://github.com/jifalops/dart-latlong',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Material Color Utilities',
|
||||
license: 'Apache 2.0',
|
||||
licenseUrl: 'https://github.com/material-foundation/material-color-utilities/tree/main/dart/LICENSE',
|
||||
sourceUrl: 'https://github.com/material-foundation/material-color-utilities/tree/main/dart',
|
||||
),
|
||||
Dependency(
|
||||
name: 'Path',
|
||||
license: 'BSD 3-Clause',
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
import 'package:intl/intl.dart';
|
||||
|
||||
const _kiloDivider = 1024;
|
||||
const _megaDivider = _kiloDivider * _kiloDivider;
|
||||
const _gigaDivider = _megaDivider * _kiloDivider;
|
||||
const _teraDivider = _gigaDivider * _kiloDivider;
|
||||
const kilo = 1024;
|
||||
const mega = kilo * kilo;
|
||||
const giga = mega * kilo;
|
||||
const tera = giga * kilo;
|
||||
|
||||
String formatFileSize(String locale, int size, {int round = 2}) {
|
||||
if (size < _kiloDivider) return '$size B';
|
||||
if (size < kilo) return '$size B';
|
||||
|
||||
final formatter = NumberFormat('0${round > 0 ? '.${'0' * round}' : ''}', locale);
|
||||
if (size < _megaDivider) return '${formatter.format(size / _kiloDivider)} KB';
|
||||
if (size < _gigaDivider) return '${formatter.format(size / _megaDivider)} MB';
|
||||
if (size < _teraDivider) return '${formatter.format(size / _gigaDivider)} GB';
|
||||
return '${formatter.format(size / _teraDivider)} TB';
|
||||
if (size < mega) return '${formatter.format(size / kilo)} KB';
|
||||
if (size < giga) return '${formatter.format(size / mega)} MB';
|
||||
if (size < tera) return '${formatter.format(size / giga)} GB';
|
||||
return '${formatter.format(size / tera)} TB';
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ class AboutCredits extends StatelessWidget {
|
|||
'Español (México)': 'n-berenice',
|
||||
'Italiano': 'glemco',
|
||||
'Português (Brasil)': 'Jonatas De Almeida Barros',
|
||||
'Türkçe': 'metezd',
|
||||
'Русский': 'D3ZOXY',
|
||||
'日本語': 'Maki',
|
||||
'简体中文': '小默, Aerowolf',
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:aves/app_flavor.dart';
|
|||
import 'package:aves/app_mode.dart';
|
||||
import 'package:aves/l10n/l10n.dart';
|
||||
import 'package:aves/model/device.dart';
|
||||
import 'package:aves/model/settings/defaults.dart';
|
||||
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
||||
import 'package:aves/model/settings/enums/display_refresh_rate_mode.dart';
|
||||
import 'package:aves/model/settings/enums/enums.dart';
|
||||
|
@ -15,6 +16,7 @@ import 'package:aves/model/source/collection_lens.dart';
|
|||
import 'package:aves/model/source/collection_source.dart';
|
||||
import 'package:aves/model/source/media_store_source.dart';
|
||||
import 'package:aves/services/accessibility_service.dart';
|
||||
import 'package:aves/services/common/optional_event_channel.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves/theme/colors.dart';
|
||||
import 'package:aves/theme/durations.dart';
|
||||
|
@ -30,11 +32,13 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
|
|||
import 'package:aves/widgets/common/providers/highlight_info_provider.dart';
|
||||
import 'package:aves/widgets/home_page.dart';
|
||||
import 'package:aves/widgets/welcome_page.dart';
|
||||
import 'package:dynamic_color/dynamic_color.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:fijkplayer/fijkplayer.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:material_color_utilities/material_color_utilities.dart';
|
||||
import 'package:overlay_support/overlay_support.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
@ -70,6 +74,7 @@ class AvesApp extends StatefulWidget {
|
|||
class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
||||
final ValueNotifier<AppMode> appModeNotifier = ValueNotifier(AppMode.main);
|
||||
late Future<void> _appSetup;
|
||||
late Future<CorePalette?> _dynamicColorPaletteLoader;
|
||||
final _mediaStoreSource = MediaStoreSource();
|
||||
final Debouncer _mediaStoreChangeDebouncer = Debouncer(delay: Durations.mediaContentChangeDebounceDelay);
|
||||
final Set<String> changedUris = {};
|
||||
|
@ -77,10 +82,10 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
// observers are not registered when using the same list object with different items
|
||||
// the list itself needs to be reassigned
|
||||
List<NavigatorObserver> _navigatorObservers = [AvesApp.pageRouteObserver];
|
||||
final EventChannel _mediaStoreChangeChannel = const EventChannel('deckers.thibault/aves/media_store_change');
|
||||
final EventChannel _newIntentChannel = const EventChannel('deckers.thibault/aves/intent');
|
||||
final EventChannel _analysisCompletionChannel = const EventChannel('deckers.thibault/aves/analysis_events');
|
||||
final EventChannel _errorChannel = const EventChannel('deckers.thibault/aves/error');
|
||||
final EventChannel _mediaStoreChangeChannel = const OptionalEventChannel('deckers.thibault/aves/media_store_change');
|
||||
final EventChannel _newIntentChannel = const OptionalEventChannel('deckers.thibault/aves/intent');
|
||||
final EventChannel _analysisCompletionChannel = const OptionalEventChannel('deckers.thibault/aves/analysis_events');
|
||||
final EventChannel _errorChannel = const OptionalEventChannel('deckers.thibault/aves/error');
|
||||
|
||||
Widget getFirstPage({Map? intentData}) => settings.hasAcceptedTerms ? HomePage(intentData: intentData) : const WelcomePage();
|
||||
|
||||
|
@ -89,6 +94,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
super.initState();
|
||||
EquatableConfig.stringify = true;
|
||||
_appSetup = _setup();
|
||||
_dynamicColorPaletteLoader = DynamicColorPlugin.getCorePalette();
|
||||
_mediaStoreChangeChannel.receiveBroadcastStream().listen((event) => _onMediaStoreChange(event as String?));
|
||||
_newIntentChannel.receiveBroadcastStream().listen((event) => _onNewIntent(event as Map?));
|
||||
_analysisCompletionChannel.receiveBroadcastStream().listen((event) => _onAnalysisCompletion());
|
||||
|
@ -120,16 +126,18 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
: Scaffold(
|
||||
body: snapshot.hasError ? _buildError(snapshot.error!) : const SizedBox(),
|
||||
);
|
||||
return Selector<Settings, Tuple3<Locale?, bool, AvesThemeBrightness>>(
|
||||
selector: (context, s) => Tuple3(
|
||||
return Selector<Settings, Tuple4<Locale?, bool, AvesThemeBrightness, bool>>(
|
||||
selector: (context, s) => Tuple4(
|
||||
s.locale,
|
||||
s.initialized ? s.accessibilityAnimations.animate : true,
|
||||
s.initialized ? s.themeBrightness : AvesThemeBrightness.system,
|
||||
s.initialized ? s.themeBrightness : SettingsDefaults.themeBrightness,
|
||||
s.initialized ? s.enableDynamicColor : SettingsDefaults.enableDynamicColor,
|
||||
),
|
||||
builder: (context, s, child) {
|
||||
final settingsLocale = s.item1;
|
||||
final areAnimationsEnabled = s.item2;
|
||||
final themeBrightness = s.item3;
|
||||
final enableDynamicColor = s.item4;
|
||||
|
||||
final pageTransitionsTheme = areAnimationsEnabled
|
||||
// Flutter has various page transition implementations for Android:
|
||||
|
@ -144,6 +152,19 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
// strip page transitions used by `MaterialPageRoute`
|
||||
: const DirectPageTransitionsTheme();
|
||||
|
||||
return FutureBuilder<CorePalette?>(
|
||||
future: _dynamicColorPaletteLoader,
|
||||
builder: (context, snapshot) {
|
||||
const defaultAccent = Themes.defaultAccent;
|
||||
Color lightAccent = defaultAccent, darkAccent = defaultAccent;
|
||||
if (enableDynamicColor) {
|
||||
// `DynamicColorBuilder` from package `dynamic_color` provides light/dark
|
||||
// palettes with a primary color from tones too dark/light (40/80),
|
||||
// so we derive the color with adjusted tones (60/70)
|
||||
final tonalPalette = snapshot.data?.primary;
|
||||
lightAccent = Color(tonalPalette?.get(60) ?? defaultAccent.value);
|
||||
darkAccent = Color(tonalPalette?.get(70) ?? defaultAccent.value);
|
||||
}
|
||||
return MaterialApp(
|
||||
navigatorKey: AvesApp.navigatorKey,
|
||||
home: home,
|
||||
|
@ -157,8 +178,8 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
),
|
||||
),
|
||||
onGenerateTitle: (context) => context.l10n.appName,
|
||||
theme: Themes.lightTheme,
|
||||
darkTheme: themeBrightness == AvesThemeBrightness.black ? Themes.blackTheme : Themes.darkTheme,
|
||||
theme: Themes.lightTheme(lightAccent),
|
||||
darkTheme: themeBrightness == AvesThemeBrightness.black ? Themes.blackTheme(darkAccent) : Themes.darkTheme(darkAccent),
|
||||
themeMode: themeBrightness.appThemeMode,
|
||||
locale: settingsLocale,
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
|
@ -169,6 +190,8 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -207,6 +230,8 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
break;
|
||||
case AppMode.pickMediaInternal:
|
||||
case AppMode.pickFilterInternal:
|
||||
case AppMode.setWallpaper:
|
||||
case AppMode.slideshow:
|
||||
case AppMode.view:
|
||||
break;
|
||||
}
|
||||
|
@ -223,7 +248,14 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
if (!settings.initialized) return;
|
||||
|
||||
final stopwatch = Stopwatch()..start();
|
||||
final screenSize = window.physicalSize / window.devicePixelRatio;
|
||||
final Size screenSize;
|
||||
try {
|
||||
screenSize = window.physicalSize / window.devicePixelRatio;
|
||||
} catch (error) {
|
||||
// view may no longer be usable
|
||||
return;
|
||||
}
|
||||
|
||||
var tileExtent = settings.getTileExtent(CollectionPage.routeName);
|
||||
if (tileExtent == 0) {
|
||||
tileExtent = screenSize.shortestSide / CollectionGrid.columnCountDefault;
|
||||
|
|
|
@ -284,6 +284,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
padding: EdgeInsets.zero,
|
||||
child: PopupMenuItemExpansionPanel<EntrySetAction>(
|
||||
enabled: canApplyEditActions,
|
||||
value: 'edit',
|
||||
icon: AIcons.edit,
|
||||
title: context.l10n.collectionActionEdit,
|
||||
items: [
|
||||
|
@ -477,6 +478,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
case EntrySetAction.addShortcut:
|
||||
// browsing or selecting
|
||||
case EntrySetAction.map:
|
||||
case EntrySetAction.slideshow:
|
||||
case EntrySetAction.stats:
|
||||
case EntrySetAction.rescan:
|
||||
case EntrySetAction.emptyBin:
|
||||
|
|
|
@ -29,6 +29,7 @@ import 'package:aves/widgets/common/grid/section_layout.dart';
|
|||
import 'package:aves/widgets/common/grid/selector.dart';
|
||||
import 'package:aves/widgets/common/grid/sliver.dart';
|
||||
import 'package:aves/widgets/common/grid/theme.dart';
|
||||
import 'package:aves/widgets/common/identity/buttons.dart';
|
||||
import 'package:aves/widgets/common/identity/empty.dart';
|
||||
import 'package:aves/widgets/common/identity/scroll_thumb.dart';
|
||||
import 'package:aves/widgets/common/providers/tile_extent_controller_provider.dart';
|
||||
|
@ -39,6 +40,7 @@ import 'package:flutter/gestures.dart';
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:tuple/tuple.dart';
|
||||
|
||||
|
@ -97,6 +99,7 @@ class _CollectionGridContent extends StatelessWidget {
|
|||
final sectionedListLayoutProvider = ValueListenableBuilder<double>(
|
||||
valueListenable: context.select<TileExtentController, ValueNotifier<double>>((controller) => controller.extentNotifier),
|
||||
builder: (context, thumbnailExtent, child) {
|
||||
assert(thumbnailExtent > 0);
|
||||
return Selector<TileExtentController, Tuple4<double, int, double, double>>(
|
||||
selector: (context, c) => Tuple4(c.viewportSize.width, c.columnCount, c.spacing, c.horizontalPadding),
|
||||
builder: (context, c, child) {
|
||||
|
@ -305,13 +308,15 @@ class _CollectionScrollView extends StatefulWidget {
|
|||
State<_CollectionScrollView> createState() => _CollectionScrollViewState();
|
||||
}
|
||||
|
||||
class _CollectionScrollViewState extends State<_CollectionScrollView> {
|
||||
class _CollectionScrollViewState extends State<_CollectionScrollView> with WidgetsBindingObserver {
|
||||
Timer? _scrollMonitoringTimer;
|
||||
bool _checkingStoragePermission = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_registerWidget(widget);
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -323,6 +328,7 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> {
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
_unregisterWidget(widget);
|
||||
_stopScrollMonitoringTimer();
|
||||
super.dispose();
|
||||
|
@ -340,6 +346,26 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> {
|
|||
widget.scrollController.removeListener(_onScrollChange);
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
switch (state) {
|
||||
case AppLifecycleState.inactive:
|
||||
case AppLifecycleState.paused:
|
||||
case AppLifecycleState.detached:
|
||||
break;
|
||||
case AppLifecycleState.resumed:
|
||||
if (_checkingStoragePermission) {
|
||||
_checkingStoragePermission = false;
|
||||
_isStoragePermissionGranted.then((granted) {
|
||||
if (granted) {
|
||||
widget.collection.source.init();
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final scrollView = _buildScrollView(widget.appBar, widget.collection);
|
||||
|
@ -423,23 +449,47 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> {
|
|||
valueListenable: collection.source.stateNotifier,
|
||||
builder: (context, sourceState, child) {
|
||||
if (sourceState == SourceState.loading) {
|
||||
return const SizedBox.shrink();
|
||||
return const SizedBox();
|
||||
}
|
||||
|
||||
return FutureBuilder<bool>(
|
||||
future: _isStoragePermissionGranted,
|
||||
builder: (context, snapshot) {
|
||||
final granted = snapshot.data ?? true;
|
||||
Widget? bottom = granted
|
||||
? null
|
||||
: Padding(
|
||||
padding: const EdgeInsets.only(top: 16),
|
||||
child: AvesOutlinedButton(
|
||||
label: context.l10n.collectionEmptyGrantAccessButtonLabel,
|
||||
onPressed: () async {
|
||||
if (await openAppSettings()) {
|
||||
_checkingStoragePermission = true;
|
||||
}
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
if (collection.filters.any((filter) => filter is FavouriteFilter)) {
|
||||
return EmptyContent(
|
||||
icon: AIcons.favourite,
|
||||
text: context.l10n.collectionEmptyFavourites,
|
||||
bottom: bottom,
|
||||
);
|
||||
}
|
||||
if (collection.filters.any((filter) => filter is MimeFilter && filter.mime == MimeTypes.anyVideo)) {
|
||||
return EmptyContent(
|
||||
icon: AIcons.video,
|
||||
text: context.l10n.collectionEmptyVideos,
|
||||
bottom: bottom,
|
||||
);
|
||||
}
|
||||
return EmptyContent(
|
||||
icon: AIcons.image,
|
||||
text: context.l10n.collectionEmptyImages,
|
||||
bottom: bottom,
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
|
@ -519,4 +569,6 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> {
|
|||
}
|
||||
return crumbs;
|
||||
}
|
||||
|
||||
Future<bool> get _isStoragePermissionGranted => Permission.storage.status.then((status) => status.isGranted);
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import 'package:aves/widgets/dialogs/entry_editors/rename_entry_set_dialog.dart'
|
|||
import 'package:aves/widgets/map/map_page.dart';
|
||||
import 'package:aves/widgets/search/search_delegate.dart';
|
||||
import 'package:aves/widgets/stats/stats_page.dart';
|
||||
import 'package:aves/widgets/viewer/slideshow_page.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
|
@ -73,6 +74,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
|
|||
return appMode == AppMode.main && isTrash;
|
||||
// browsing or selecting
|
||||
case EntrySetAction.map:
|
||||
case EntrySetAction.slideshow:
|
||||
case EntrySetAction.stats:
|
||||
return appMode == AppMode.main;
|
||||
case EntrySetAction.rescan:
|
||||
|
@ -124,6 +126,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
|
|||
case EntrySetAction.emptyBin:
|
||||
return !isSelecting && hasItems;
|
||||
case EntrySetAction.map:
|
||||
case EntrySetAction.slideshow:
|
||||
case EntrySetAction.stats:
|
||||
case EntrySetAction.rescan:
|
||||
return (!isSelecting && hasItems) || (isSelecting && hasSelection);
|
||||
|
@ -169,6 +172,9 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
|
|||
case EntrySetAction.map:
|
||||
_goToMap(context);
|
||||
break;
|
||||
case EntrySetAction.slideshow:
|
||||
_goToSlideshow(context);
|
||||
break;
|
||||
case EntrySetAction.stats:
|
||||
_goToStats(context);
|
||||
break;
|
||||
|
@ -543,6 +549,27 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
|
|||
);
|
||||
}
|
||||
|
||||
void _goToSlideshow(BuildContext context) {
|
||||
final collection = context.read<CollectionLens>();
|
||||
final entries = _getTargetItems(context);
|
||||
|
||||
Navigator.push(
|
||||
context,
|
||||
MaterialPageRoute(
|
||||
settings: const RouteSettings(name: SlideshowPage.routeName),
|
||||
builder: (context) {
|
||||
return SlideshowPage(
|
||||
collection: CollectionLens(
|
||||
source: collection.source,
|
||||
filters: collection.filters,
|
||||
fixedSelection: entries.toList(),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _goToStats(BuildContext context) {
|
||||
final collection = context.read<CollectionLens>();
|
||||
final entries = _getTargetItems(context);
|
||||
|
|
|
@ -54,6 +54,8 @@ class InteractiveTile extends StatelessWidget {
|
|||
Navigator.pop(context, entry);
|
||||
break;
|
||||
case AppMode.pickFilterInternal:
|
||||
case AppMode.setWallpaper:
|
||||
case AppMode.slideshow:
|
||||
case AppMode.view:
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -122,7 +122,7 @@ mixin FeedbackMixin {
|
|||
Future<void> showOpReport<T>({
|
||||
required BuildContext context,
|
||||
required Stream<T> opStream,
|
||||
required int itemCount,
|
||||
int? itemCount,
|
||||
VoidCallback? onCancel,
|
||||
void Function(Set<T> processed)? onDone,
|
||||
}) {
|
||||
|
@ -144,7 +144,7 @@ mixin FeedbackMixin {
|
|||
|
||||
class ReportOverlay<T> extends StatefulWidget {
|
||||
final Stream<T> opStream;
|
||||
final int itemCount;
|
||||
final int? itemCount;
|
||||
final VoidCallback? onCancel;
|
||||
final void Function(Set<T> processed) onDone;
|
||||
|
||||
|
@ -212,7 +212,7 @@ class _ReportOverlayState<T> extends State<ReportOverlay<T>> with SingleTickerPr
|
|||
builder: (context, snapshot) {
|
||||
final processedCount = processed.length.toDouble();
|
||||
final total = widget.itemCount;
|
||||
final percent = total != 0 ? min(1.0, processedCount / total) : 1.0;
|
||||
final percent = total == null || total == 0 ? 0.0 : min(1.0, processedCount / total);
|
||||
return FadeTransition(
|
||||
opacity: _animation,
|
||||
child: Stack(
|
||||
|
@ -243,10 +243,12 @@ class _ReportOverlayState<T> extends State<ReportOverlay<T>> with SingleTickerPr
|
|||
backgroundColor: Theme.of(context).colorScheme.onSurface.withOpacity(.2),
|
||||
progressColor: progressColor,
|
||||
animation: animate,
|
||||
center: Text(
|
||||
center: total != null
|
||||
? Text(
|
||||
NumberFormat.percentPattern().format(percent),
|
||||
style: const TextStyle(fontSize: fontSize),
|
||||
),
|
||||
)
|
||||
: null,
|
||||
animateFromLastPercent: true,
|
||||
),
|
||||
if (widget.onCancel != null)
|
||||
|
@ -305,12 +307,13 @@ class _FeedbackMessageState extends State<_FeedbackMessage> with SingleTickerPro
|
|||
if (start != null && stop != null) {
|
||||
_totalDurationMillis = stop.difference(start).inMilliseconds;
|
||||
final remainingDuration = stop.difference(DateTime.now());
|
||||
final effectiveDuration = remainingDuration > Duration.zero ? remainingDuration : const Duration(milliseconds: 1);
|
||||
_animationController = AnimationController(
|
||||
duration: remainingDuration,
|
||||
duration: effectiveDuration,
|
||||
vsync: this,
|
||||
);
|
||||
_remainingDurationMillis = IntTween(
|
||||
begin: remainingDuration.inMilliseconds,
|
||||
begin: effectiveDuration.inMilliseconds,
|
||||
end: 0,
|
||||
).animate(CurvedAnimation(
|
||||
parent: _animationController!,
|
||||
|
|
|
@ -55,25 +55,27 @@ class MenuIconTheme extends StatelessWidget {
|
|||
|
||||
class PopupMenuItemExpansionPanel<T> extends StatefulWidget {
|
||||
final bool enabled;
|
||||
final String value;
|
||||
final ValueNotifier<String?> expandedNotifier;
|
||||
final IconData icon;
|
||||
final String title;
|
||||
final List<PopupMenuEntry<T>> items;
|
||||
|
||||
const PopupMenuItemExpansionPanel({
|
||||
PopupMenuItemExpansionPanel({
|
||||
super.key,
|
||||
this.enabled = true,
|
||||
required this.value,
|
||||
ValueNotifier<String?>? expandedNotifier,
|
||||
required this.icon,
|
||||
required this.title,
|
||||
required this.items,
|
||||
});
|
||||
}) : expandedNotifier = expandedNotifier ?? ValueNotifier(null);
|
||||
|
||||
@override
|
||||
State<PopupMenuItemExpansionPanel<T>> createState() => _PopupMenuItemExpansionPanelState<T>();
|
||||
}
|
||||
|
||||
class _PopupMenuItemExpansionPanelState<T> extends State<PopupMenuItemExpansionPanel<T>> {
|
||||
bool _isExpanded = false;
|
||||
|
||||
// ref `_kMenuHorizontalPadding` used in `PopupMenuItem`
|
||||
static const double _horizontalPadding = 16;
|
||||
|
||||
|
@ -86,9 +88,12 @@ class _PopupMenuItemExpansionPanelState<T> extends State<PopupMenuItemExpansionP
|
|||
}
|
||||
final animationDuration = context.select<DurationsData, Duration>((v) => v.expansionTileAnimation);
|
||||
|
||||
Widget child = ExpansionPanelList(
|
||||
Widget child = ValueListenableBuilder<String?>(
|
||||
valueListenable: widget.expandedNotifier,
|
||||
builder: (context, expandedValue, child) {
|
||||
return ExpansionPanelList(
|
||||
expansionCallback: (index, isExpanded) {
|
||||
setState(() => _isExpanded = !isExpanded);
|
||||
widget.expandedNotifier.value = isExpanded ? null : widget.value;
|
||||
},
|
||||
animationDuration: animationDuration,
|
||||
expandedHeaderPadding: EdgeInsets.zero,
|
||||
|
@ -113,12 +118,14 @@ class _PopupMenuItemExpansionPanelState<T> extends State<PopupMenuItemExpansionP
|
|||
const PopupMenuDivider(height: 0),
|
||||
],
|
||||
),
|
||||
isExpanded: _isExpanded,
|
||||
isExpanded: expandedValue == widget.value,
|
||||
canTapOnHeader: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
if (!widget.enabled) {
|
||||
child = IgnorePointer(child: child);
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:flutter/material.dart';
|
|||
class QueryBar extends StatefulWidget {
|
||||
final ValueNotifier<String> queryNotifier;
|
||||
final FocusNode? focusNode;
|
||||
final EdgeInsetsGeometry? leadingPadding;
|
||||
final IconData? icon;
|
||||
final String? hintText;
|
||||
final bool editable;
|
||||
|
@ -15,6 +16,7 @@ class QueryBar extends StatefulWidget {
|
|||
super.key,
|
||||
required this.queryNotifier,
|
||||
this.focusNode,
|
||||
this.leadingPadding,
|
||||
this.icon,
|
||||
this.hintText,
|
||||
this.editable = true,
|
||||
|
@ -60,7 +62,7 @@ class _QueryBarState extends State<QueryBar> {
|
|||
focusNode: widget.focusNode ?? FocusNode(),
|
||||
decoration: InputDecoration(
|
||||
icon: Padding(
|
||||
padding: const EdgeInsetsDirectional.only(start: 16),
|
||||
padding: widget.leadingPadding ?? const EdgeInsetsDirectional.only(start: 16),
|
||||
child: Icon(widget.icon ?? AIcons.filter),
|
||||
),
|
||||
hintText: widget.hintText ?? MaterialLocalizations.of(context).searchFieldLabel,
|
||||
|
|
|
@ -206,7 +206,7 @@ class _AvesFloatingBarState extends State<AvesFloatingBar> with RouteAware {
|
|||
return ValueListenableBuilder<bool>(
|
||||
valueListenable: _isBlurAllowedNotifier,
|
||||
builder: (context, isBlurAllowed, child) {
|
||||
final blurred = isBlurAllowed && context.select<Settings, bool>((s) => s.enableOverlayBlurEffect);
|
||||
final blurred = isBlurAllowed && context.select<Settings, bool>((s) => s.enableBlurEffect);
|
||||
return Container(
|
||||
foregroundDecoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
|
|
|
@ -140,6 +140,7 @@ class _AvesFilterChipState extends State<AvesFilterChip> {
|
|||
_subscriptions.add(settings.updateStream.where((event) => event.key == Settings.themeColorModeKey).listen((_) {
|
||||
// delay so that contextual colors reflect the new settings
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) return;
|
||||
_onCoverColorChange(null);
|
||||
});
|
||||
}));
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
|
|||
class EmptyContent extends StatelessWidget {
|
||||
final IconData? icon;
|
||||
final String text;
|
||||
final Widget? bottom;
|
||||
final AlignmentGeometry alignment;
|
||||
final double fontSize;
|
||||
final bool safeBottom;
|
||||
|
@ -13,6 +14,7 @@ class EmptyContent extends StatelessWidget {
|
|||
super.key,
|
||||
this.icon,
|
||||
required this.text,
|
||||
this.bottom,
|
||||
this.alignment = const FractionalOffset(.5, .35),
|
||||
this.fontSize = 22,
|
||||
this.safeBottom = true,
|
||||
|
@ -20,7 +22,7 @@ class EmptyContent extends StatelessWidget {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const color = Colors.blueGrey;
|
||||
final color = Theme.of(context).colorScheme.secondary.withOpacity(.5);
|
||||
return Padding(
|
||||
padding: safeBottom
|
||||
? EdgeInsets.only(
|
||||
|
@ -48,6 +50,7 @@ class EmptyContent extends StatelessWidget {
|
|||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
if (bottom != null) bottom!,
|
||||
],
|
||||
),
|
||||
),
|
||||
|
|