Merge branch 'develop'
This commit is contained in:
commit
ce3afd87a0
92 changed files with 1327 additions and 1058 deletions
2
.flutter
2
.flutter
|
@ -1 +1 @@
|
|||
Subproject commit 5874a72aa4c779a02553007c47dacbefba2374dc
|
||||
Subproject commit 2663184aa79047d0a33a14a3b607954f8fdd8730
|
36
.github/workflows/check.yml
vendored
36
.github/workflows/check.yml
vendored
|
@ -1,36 +0,0 @@
|
|||
name: Quality check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- develop
|
||||
pull_request:
|
||||
types: [ opened, synchronize, reopened ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Check code quality.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Clone the repository.
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Get packages for the Flutter project.
|
||||
run: scripts/pub_get_all.sh
|
||||
|
||||
- name: Update the flutter version file.
|
||||
run: scripts/update_flutter_version.sh
|
||||
|
||||
- name: Static analysis.
|
||||
run: ./flutterw analyze
|
||||
|
||||
- name: Unit tests.
|
||||
run: ./flutterw test
|
2
.github/workflows/dependency-review.yml
vendored
2
.github/workflows/dependency-review.yml
vendored
|
@ -17,7 +17,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
|
||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
|
88
.github/workflows/quality-check.yml
vendored
Normal file
88
.github/workflows/quality-check.yml
vendored
Normal file
|
@ -0,0 +1,88 @@
|
|||
name: Quality check
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "develop", "main" ]
|
||||
pull_request:
|
||||
branches: [ "develop", "main" ]
|
||||
types: [ opened, synchronize, reopened ]
|
||||
schedule:
|
||||
- cron: '17 8 * * 3'
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
analyze_flutter:
|
||||
name: Flutter analysis
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Get Flutter packages
|
||||
run: scripts/pub_get_all.sh
|
||||
|
||||
- name: Static analysis.
|
||||
run: ./flutterw analyze
|
||||
|
||||
- name: Unit tests.
|
||||
run: ./flutterw test
|
||||
|
||||
analyze_codeql:
|
||||
name: CodeQL analysis (${{ matrix.language }})
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# required for all workflows
|
||||
security-events: write
|
||||
|
||||
# required to fetch internal or private CodeQL packs
|
||||
packages: read
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- language: java-kotlin
|
||||
build-mode: manual
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
# Building relies on the Android Gradle plugin,
|
||||
# which requires a modern Java version (not the default one).
|
||||
- name: Set up JDK for Android Gradle plugin
|
||||
uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # v4.3.0
|
||||
with:
|
||||
distribution: 'temurin'
|
||||
java-version: '21'
|
||||
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
build-mode: ${{ matrix.build-mode }}
|
||||
|
||||
- if: matrix.build-mode == 'manual'
|
||||
shell: bash
|
||||
# build in profile mode, instead of release,
|
||||
# so that setting up signing environment variables is not required
|
||||
run: |
|
||||
scripts/apply_flavor_play.sh
|
||||
./flutterw build apk --profile -t lib/main_play.dart --flavor play
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
51
.github/workflows/release.yml
vendored
51
.github/workflows/release.yml
vendored
|
@ -5,37 +5,39 @@ on:
|
|||
tags:
|
||||
- v*
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build and release artifacts.
|
||||
release_github:
|
||||
name: GitHub release
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
|
||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2
|
||||
# Building relies on the Android Gradle plugin,
|
||||
# which requires a modern Java version (not the default one).
|
||||
- name: Set up JDK for Android Gradle plugin
|
||||
uses: actions/setup-java@2dfa2011c5b2a0f1489bf9e433881c92c1631f88 # v4.3.0
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '17'
|
||||
distribution: 'temurin'
|
||||
java-version: '21'
|
||||
|
||||
- name: Clone the repository.
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Get packages for the Flutter project.
|
||||
- name: Get Flutter packages
|
||||
run: scripts/pub_get_all.sh
|
||||
|
||||
- name: Update the flutter version file.
|
||||
- name: Update Flutter version file
|
||||
run: scripts/update_flutter_version.sh
|
||||
|
||||
- name: Static analysis.
|
||||
run: ./flutterw analyze
|
||||
|
||||
- name: Unit tests.
|
||||
run: ./flutterw test
|
||||
|
||||
- name: Build signed artifacts.
|
||||
- name: Build signed artifacts
|
||||
# `KEY_JKS` should contain the result of:
|
||||
# gpg -c --armor keystore.jks
|
||||
# `KEY_JKS_PASSPHRASE` should contain the passphrase used for the command above
|
||||
|
@ -70,7 +72,7 @@ jobs:
|
|||
AVES_KEY_PASSWORD: ${{ secrets.AVES_KEY_PASSWORD }}
|
||||
AVES_GOOGLE_API_KEY: ${{ secrets.AVES_GOOGLE_API_KEY }}
|
||||
|
||||
- name: Create a release with the APK and App Bundle.
|
||||
- name: Create GitHub release
|
||||
uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0
|
||||
with:
|
||||
artifacts: "outputs/*"
|
||||
|
@ -78,29 +80,30 @@ jobs:
|
|||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload app bundle
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
||||
with:
|
||||
name: appbundle
|
||||
path: outputs/app-play-release.aab
|
||||
|
||||
release:
|
||||
name: Create beta release on Play Store.
|
||||
release_play:
|
||||
name: Play Store beta release
|
||||
needs: [ build ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
|
||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Get appbundle from artifacts.
|
||||
- name: Get appbundle from artifacts
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: appbundle
|
||||
|
||||
- name: Release app to beta channel.
|
||||
- name: Release to beta channel
|
||||
uses: r0adkll/upload-google-play@935ef9c68bb393a8e6116b1575626a7f5be3a7fb # v1.1.3
|
||||
with:
|
||||
serviceAccountJsonPlainText: ${{ secrets.PLAYSTORE_ACCOUNT_KEY }}
|
||||
|
|
6
.github/workflows/scorecards.yml
vendored
6
.github/workflows/scorecards.yml
vendored
|
@ -31,7 +31,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
|
||||
uses: step-security/harden-runner@91182cccc01eb5e619899d80e4e971d6181294a7 # v2.10.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
@ -63,7 +63,7 @@ jobs:
|
|||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
uses: actions/upload-artifact@50769540e7f4bd5e21e526ee35c689e35e0d6874 # v4.4.0
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
|
@ -71,6 +71,6 @@ jobs:
|
|||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
|
||||
uses: github/codeql-action/upload-sarif@8214744c546c1e5c8f03dde8fab3a7353211988d # v3.26.7
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
|
15
CHANGELOG.md
15
CHANGELOG.md
|
@ -4,6 +4,21 @@ All notable changes to this project will be documented in this file.
|
|||
|
||||
## <a id="unreleased"></a>[Unreleased]
|
||||
|
||||
## <a id="v1.11.11"></a>[v1.11.11] - 2024-09-16
|
||||
|
||||
### Added
|
||||
|
||||
- support opening from the lock screen
|
||||
|
||||
### Changed
|
||||
|
||||
- upgraded Flutter to stable v3.24.3
|
||||
|
||||
### Fixed
|
||||
|
||||
- crash when cataloguing some malformed MP4 files
|
||||
- inconsistent launch screen
|
||||
|
||||
## <a id="v1.11.10"></a>[v1.11.10] - 2024-09-01
|
||||
|
||||
### Added
|
||||
|
|
|
@ -49,8 +49,8 @@ android {
|
|||
ndkVersion '26.1.10909125'
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
sourceCompatibility JavaVersion.VERSION_21
|
||||
targetCompatibility JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
lint {
|
||||
|
@ -156,12 +156,12 @@ android {
|
|||
}
|
||||
|
||||
tasks.withType(KotlinCompile).configureEach {
|
||||
sourceCompatibility = JavaVersion.VERSION_17
|
||||
targetCompatibility = JavaVersion.VERSION_17
|
||||
sourceCompatibility = JavaVersion.VERSION_21
|
||||
targetCompatibility = JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
kotlin {
|
||||
jvmToolchain(17)
|
||||
jvmToolchain(21)
|
||||
}
|
||||
|
||||
flutter {
|
||||
|
@ -189,11 +189,11 @@ dependencies {
|
|||
|
||||
implementation "androidx.appcompat:appcompat:1.7.0"
|
||||
implementation 'androidx.core:core-ktx:1.13.1'
|
||||
implementation 'androidx.lifecycle:lifecycle-process:2.8.4'
|
||||
implementation 'androidx.lifecycle:lifecycle-process:2.8.5'
|
||||
implementation 'androidx.media:media:1.7.0'
|
||||
implementation 'androidx.multidex:multidex:2.0.1'
|
||||
implementation 'androidx.security:security-crypto:1.1.0-alpha06'
|
||||
implementation 'androidx.work:work-runtime-ktx:2.9.0'
|
||||
implementation 'androidx.work:work-runtime-ktx:2.9.1'
|
||||
|
||||
implementation 'com.caverock:androidsvg-aar:1.4'
|
||||
implementation 'com.commonsware.cwac:document:0.5.0'
|
||||
|
@ -208,14 +208,14 @@ dependencies {
|
|||
// - https://jitpack.io/p/deckerst/mp4parser
|
||||
// - https://jitpack.io/p/deckerst/pixymeta-android
|
||||
implementation 'com.github.deckerst:Android-TiffBitmapFactory:90c06eebf4'
|
||||
implementation 'com.github.deckerst.mp4parser:isoparser:4cc0c5d06c'
|
||||
implementation 'com.github.deckerst.mp4parser:muxer:4cc0c5d06c'
|
||||
implementation 'com.github.deckerst.mp4parser:isoparser:86d4b6baa1'
|
||||
implementation 'com.github.deckerst.mp4parser:muxer:86d4b6baa1'
|
||||
implementation 'com.github.deckerst:pixymeta-android:9ec7097f17'
|
||||
implementation project(':exifinterface')
|
||||
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.10.3'
|
||||
|
||||
kapt 'androidx.annotation:annotation:1.8.1'
|
||||
kapt 'androidx.annotation:annotation:1.8.2'
|
||||
ksp "com.github.bumptech.glide:ksp:$glide_version"
|
||||
|
||||
compileOnly rootProject.findProject(':streams_channel')
|
||||
|
|
|
@ -128,6 +128,7 @@
|
|||
android:exported="true"
|
||||
android:hardwareAccelerated="true"
|
||||
android:launchMode="singleTop"
|
||||
android:showWhenLocked="true"
|
||||
android:supportsPictureInPicture="true"
|
||||
android:theme="@style/NormalTheme"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.annotation.SuppressLint
|
|||
import android.app.SearchManager
|
||||
import android.appwidget.AppWidgetManager
|
||||
import android.content.ClipData
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
|
@ -229,6 +230,7 @@ open class MainActivity : FlutterFragmentActivity() {
|
|||
intentStreamHandler.notifyNewIntent(extractIntentData(intent))
|
||||
}
|
||||
|
||||
@Deprecated("Deprecated in Java")
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
when (requestCode) {
|
||||
|
@ -316,6 +318,13 @@ open class MainActivity : FlutterFragmentActivity() {
|
|||
INTENT_DATA_KEY_URI to uri.toString(),
|
||||
)
|
||||
|
||||
val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as android.app.KeyguardManager
|
||||
val isLocked = keyguardManager.isKeyguardLocked
|
||||
if (isLocked) {
|
||||
// device is locked, so access to content is limited to intent URI by default
|
||||
fields[INTENT_DATA_KEY_SECURE_URIS] = listOf(uri.toString())
|
||||
}
|
||||
|
||||
if (action == MediaStore.ACTION_REVIEW_SECURE) {
|
||||
val uris = ArrayList<String>()
|
||||
intent.clipData?.let { clipData ->
|
||||
|
@ -323,8 +332,10 @@ open class MainActivity : FlutterFragmentActivity() {
|
|||
clipData.getItemAt(i).uri?.let { uris.add(it.toString()) }
|
||||
}
|
||||
}
|
||||
if (uris.isNotEmpty()) {
|
||||
fields[INTENT_DATA_KEY_SECURE_URIS] = uris
|
||||
}
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && intent.hasExtra(MediaStore.EXTRA_BRIGHTNESS)) {
|
||||
fields[INTENT_DATA_KEY_BRIGHTNESS] = intent.getFloatExtra(MediaStore.EXTRA_BRIGHTNESS, 0f)
|
||||
}
|
||||
|
|
|
@ -33,6 +33,7 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
|
|||
"getDefaultTimeZoneRawOffsetMillis" -> safe(call, result, ::getDefaultTimeZoneRawOffsetMillis)
|
||||
"getLocales" -> safe(call, result, ::getLocales)
|
||||
"getPerformanceClass" -> safe(call, result, ::getPerformanceClass)
|
||||
"isLocked" -> safe(call, result, ::isLocked)
|
||||
"isSystemFilePickerEnabled" -> safe(call, result, ::isSystemFilePickerEnabled)
|
||||
"requestMediaManagePermission" -> safe(call, result, ::requestMediaManagePermission)
|
||||
"getAvailableHeapSize" -> safe(call, result, ::getAvailableHeapSize)
|
||||
|
@ -49,13 +50,11 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
|
|||
val sdkInt = Build.VERSION.SDK_INT
|
||||
result.success(
|
||||
hashMapOf(
|
||||
"canGrantDirectoryAccess" to (sdkInt >= Build.VERSION_CODES.LOLLIPOP),
|
||||
"canPinShortcut" to ShortcutManagerCompat.isRequestPinShortcutSupported(context),
|
||||
"canRenderFlagEmojis" to (sdkInt >= Build.VERSION_CODES.M),
|
||||
"canRenderSubdivisionFlagEmojis" to (sdkInt >= Build.VERSION_CODES.O),
|
||||
"canRequestManageMedia" to (sdkInt >= Build.VERSION_CODES.S),
|
||||
"canSetLockScreenWallpaper" to (sdkInt >= Build.VERSION_CODES.N),
|
||||
"canUseCrypto" to (sdkInt >= Build.VERSION_CODES.LOLLIPOP),
|
||||
"hasGeocoder" to Geocoder.isPresent(),
|
||||
"isDynamicColorAvailable" to DynamicColors.isDynamicColorAvailable(),
|
||||
"showPinShortcutFeedback" to (sdkInt >= Build.VERSION_CODES.O),
|
||||
|
@ -100,6 +99,12 @@ class DeviceHandler(private val context: Context) : MethodCallHandler {
|
|||
result.success(Build.VERSION.SDK_INT)
|
||||
}
|
||||
|
||||
private fun isLocked(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||
val keyguardManager = context.getSystemService(Context.KEYGUARD_SERVICE) as android.app.KeyguardManager
|
||||
val isLocked = keyguardManager.isKeyguardLocked
|
||||
result.success(isLocked)
|
||||
}
|
||||
|
||||
private fun isSystemFilePickerEnabled(@Suppress("unused_parameter") call: MethodCall, result: MethodChannel.Result) {
|
||||
val enabled = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).resolveActivity(context.packageManager) != null
|
||||
result.success(enabled)
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<style name="NormalTheme" parent="Theme.AppCompat.NoActionBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
<item name="android:windowBackground">@color/window_background_night</item>
|
||||
<item name="android:windowSplashScreenBackground" tools:targetApi="s">@color/window_background_night</item>
|
||||
<item name="android:windowSplashScreenAnimatedIcon" tools:targetApi="s">@mipmap/ic_launcher</item>
|
||||
|
||||
<!-- API28+, draws next to the notch in fullscreen -->
|
||||
<item name="android:windowLayoutInDisplayCutoutMode" tools:targetApi="28">shortEdges</item>
|
||||
|
|
12
android/app/src/main/res/values-sr/strings.xml
Normal file
12
android/app/src/main/res/values-sr/strings.xml
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_widget_label">Okvir Slike</string>
|
||||
<string name="wallpaper">Pozadina</string>
|
||||
<string name="safe_mode_shortcut_short_label">Siguran režim</string>
|
||||
<string name="videos_shortcut_short_label">Snimci</string>
|
||||
<string name="analysis_channel_name">Pretraga medija</string>
|
||||
<string name="analysis_notification_default_title">Skeniranje medija</string>
|
||||
<string name="analysis_notification_action_stop">Stop</string>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="search_shortcut_short_label">Pretraga</string>
|
||||
</resources>
|
|
@ -4,4 +4,6 @@
|
|||
<color name="ic_shortcut_background">#FFFFFF</color>
|
||||
<color name="ic_shortcut_foreground">#455A64</color>
|
||||
<color name="ic_launcher_flavour">#1cc8eb</color>
|
||||
<color name="window_background_day">#FFFFFF</color>
|
||||
<color name="window_background_night">#262626</color>
|
||||
</resources>
|
|
@ -1,7 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources xmlns:tools="http://schemas.android.com/tools">
|
||||
<style name="NormalTheme" parent="Theme.AppCompat.Light.NoActionBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
<item name="android:windowBackground">@color/window_background_day</item>
|
||||
<item name="android:windowSplashScreenBackground" tools:targetApi="s">@color/window_background_day</item>
|
||||
<item name="android:windowSplashScreenAnimatedIcon" tools:targetApi="s">@mipmap/ic_launcher</item>
|
||||
|
||||
<!-- API28+, draws next to the notch in fullscreen -->
|
||||
<item name="android:windowLayoutInDisplayCutoutMode" tools:targetApi="28">shortEdges</item>
|
||||
|
|
|
@ -4,10 +4,10 @@ plugins {
|
|||
|
||||
android {
|
||||
namespace 'androidx.exifinterface.media'
|
||||
compileSdk 34
|
||||
compileSdk 35
|
||||
|
||||
defaultConfig {
|
||||
minSdk 19
|
||||
minSdk 21
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles "consumer-rules.pro"
|
||||
|
@ -20,11 +20,11 @@ android {
|
|||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
sourceCompatibility JavaVersion.VERSION_21
|
||||
targetCompatibility JavaVersion.VERSION_21
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'androidx.annotation:annotation:1.8.0'
|
||||
implementation 'androidx.annotation:annotation:1.8.2'
|
||||
}
|
|
@ -10,7 +10,7 @@ pluginManagement {
|
|||
|
||||
settings.ext.kotlin_version = '1.9.24'
|
||||
settings.ext.ksp_version = "$kotlin_version-1.0.20"
|
||||
settings.ext.agp_version = '8.5.1'
|
||||
settings.ext.agp_version = '8.6.0'
|
||||
|
||||
includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")
|
||||
|
||||
|
|
3
fastlane/metadata/android/en-US/changelogs/130.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/130.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
In v1.11.11:
|
||||
- review photos from the lock screen
|
||||
Full changelog available on GitHub
|
3
fastlane/metadata/android/en-US/changelogs/13001.txt
Normal file
3
fastlane/metadata/android/en-US/changelogs/13001.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
In v1.11.11:
|
||||
- review photos from the lock screen
|
||||
Full changelog available on GitHub
|
5
fastlane/metadata/android/sr/full_description.txt
Normal file
5
fastlane/metadata/android/sr/full_description.txt
Normal file
|
@ -0,0 +1,5 @@
|
|||
<i>Aves</i> can handle all sorts of images and videos, including your typical JPEGs and MP4s, but also more exotic things like <b>multi-page TIFFs, SVGs, old AVIs and more</b>! It scans your media collection to identify <b>motion photos</b>, <b>panoramas</b> (aka photo spheres), <b>360° videos</b>, as well as <b>GeoTIFF</b> files.
|
||||
|
||||
<b>Navigation and search</b> is an important part of <i>Aves</i>. The goal is for users to easily flow from albums to photos to tags to maps, etc.
|
||||
|
||||
<i>Aves</i> integrates with Android (from KitKat to Android 14, including Android TV) with features such as <b>widgets</b>, <b>app shortcuts</b>, <b>screen saver</b> and <b>global search</b> handling. It also works as a <b>media viewer and picker</b>.
|
1
fastlane/metadata/android/sr/short_description.txt
Normal file
1
fastlane/metadata/android/sr/short_description.txt
Normal file
|
@ -0,0 +1 @@
|
|||
Gallery and metadata explorer
|
|
@ -1 +1,119 @@
|
|||
{}
|
||||
{
|
||||
"welcomeMessage": "Velkommen til Aves",
|
||||
"@welcomeMessage": {},
|
||||
"welcomeOptional": "Valgfri",
|
||||
"@welcomeOptional": {},
|
||||
"appName": "Aves",
|
||||
"@appName": {},
|
||||
"welcomeTermsToggle": "Jeg accepterer vilkårene og betingelserne",
|
||||
"@welcomeTermsToggle": {},
|
||||
"itemCount": "{count, plural, =1{{count} fil} other{{count} filer}}",
|
||||
"@itemCount": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"format": "decimalPattern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"columnCount": "{count, plural, =1{{count} kolonne} other{{count} kolonner}}",
|
||||
"@columnCount": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"format": "decimalPattern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeMinutes": "{count, plural, =1{{count} minut} other{{count} minutter}}",
|
||||
"@timeMinutes": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"format": "decimalPattern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"timeDays": "{count, plural, =1{{count} dag} other{{count} dage}}",
|
||||
"@timeDays": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"format": "decimalPattern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"focalLength": "{length} mm",
|
||||
"@focalLength": {
|
||||
"placeholders": {
|
||||
"length": {
|
||||
"type": "String",
|
||||
"example": "5.4"
|
||||
}
|
||||
}
|
||||
},
|
||||
"applyButtonLabel": "UDFØR",
|
||||
"@applyButtonLabel": {},
|
||||
"deleteButtonLabel": "SLET",
|
||||
"@deleteButtonLabel": {},
|
||||
"nextButtonLabel": "NÆSTE",
|
||||
"@nextButtonLabel": {},
|
||||
"showButtonLabel": "VIS",
|
||||
"@showButtonLabel": {},
|
||||
"hideButtonLabel": "SKJUL",
|
||||
"@hideButtonLabel": {},
|
||||
"saveCopyButtonLabel": "GEM KOPI",
|
||||
"@saveCopyButtonLabel": {},
|
||||
"applyTooltip": "Udfør",
|
||||
"@applyTooltip": {},
|
||||
"cancelTooltip": "Fortryd",
|
||||
"@cancelTooltip": {},
|
||||
"nextTooltip": "Næste",
|
||||
"@nextTooltip": {},
|
||||
"showTooltip": "Vis",
|
||||
"@showTooltip": {},
|
||||
"hideTooltip": "Skjul",
|
||||
"@hideTooltip": {},
|
||||
"actionRemove": "Fjern",
|
||||
"@actionRemove": {},
|
||||
"resetTooltip": "Nulstil",
|
||||
"@resetTooltip": {},
|
||||
"saveTooltip": "Gem",
|
||||
"@saveTooltip": {},
|
||||
"chipActionDelete": "Slet",
|
||||
"@chipActionDelete": {},
|
||||
"chipActionGoToAlbumPage": "Vis i Album",
|
||||
"@chipActionGoToAlbumPage": {},
|
||||
"chipActionGoToCountryPage": "Vis i Lande",
|
||||
"@chipActionGoToCountryPage": {},
|
||||
"chipActionGoToPlacePage": "Vis i Steder",
|
||||
"@chipActionGoToPlacePage": {},
|
||||
"chipActionGoToTagPage": "Vis i Tags",
|
||||
"@chipActionGoToTagPage": {},
|
||||
"timeSeconds": "{count, plural, =1{{count} sekund} other{{count} sekunder}}",
|
||||
"@timeSeconds": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"format": "decimalPattern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"continueButtonLabel": "FORTSÆT",
|
||||
"@continueButtonLabel": {},
|
||||
"clearTooltip": "Ryd",
|
||||
"@clearTooltip": {},
|
||||
"previousTooltip": "Forrige",
|
||||
"@previousTooltip": {},
|
||||
"stopTooltip": "Stop",
|
||||
"@stopTooltip": {},
|
||||
"pickTooltip": "Vælg",
|
||||
"@pickTooltip": {},
|
||||
"chipActionShowCollection": "Vis i Samling",
|
||||
"@chipActionShowCollection": {},
|
||||
"doubleBackExitMessage": "Tryk \"tilbage\" igen for at gå ud.",
|
||||
"@doubleBackExitMessage": {},
|
||||
"doNotAskAgain": "Spørg ikke igen",
|
||||
"@doNotAskAgain": {},
|
||||
"sourceStateLoading": "Loader",
|
||||
"@sourceStateLoading": {},
|
||||
"sourceStateLocatingCountries": "Lokaliserer lande",
|
||||
"@sourceStateLocatingCountries": {},
|
||||
"sourceStateLocatingPlaces": "Lokaliserer steder",
|
||||
"@sourceStateLocatingPlaces": {}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"welcomeOptional": "वैकल्पिक",
|
||||
"@welcomeOptional": {},
|
||||
"welcomeTermsToggle": "मैं नियमों और शर्तों पर सहमत हुं",
|
||||
"welcomeTermsToggle": "मैं नियमों और शर्तों से सहमत हूं",
|
||||
"@welcomeTermsToggle": {},
|
||||
"columnCount": "{count, plural, other{{count} कॉलम}}",
|
||||
"@columnCount": {
|
||||
|
@ -246,7 +246,7 @@
|
|||
"@displayRefreshRatePreferLowest": {},
|
||||
"nameConflictStrategyRename": "नाम बदलें",
|
||||
"@nameConflictStrategyRename": {},
|
||||
"unitSystemMetric": "Metric",
|
||||
"unitSystemMetric": "मात्रिक",
|
||||
"@unitSystemMetric": {},
|
||||
"viewerTransitionSlide": "स्लाइड",
|
||||
"@viewerTransitionSlide": {},
|
||||
|
@ -262,9 +262,9 @@
|
|||
"@filterTaggedLabel": {},
|
||||
"mapStyleGoogleTerrain": "गूगल मैप्स (टेरेन)",
|
||||
"@mapStyleGoogleTerrain": {},
|
||||
"themeBrightnessDark": "Dark",
|
||||
"themeBrightnessDark": "डार्क",
|
||||
"@themeBrightnessDark": {},
|
||||
"themeBrightnessBlack": "Black",
|
||||
"themeBrightnessBlack": "काला",
|
||||
"@themeBrightnessBlack": {},
|
||||
"videoControlsPlaySeek": "पिछड़े / आगे की तलाश करें",
|
||||
"@videoControlsPlaySeek": {},
|
||||
|
@ -284,7 +284,7 @@
|
|||
"@mapStyleGoogleHybrid": {},
|
||||
"mapStyleStamenWatercolor": "Stamen Watercolor",
|
||||
"@mapStyleStamenWatercolor": {},
|
||||
"unitSystemImperial": "Imperial",
|
||||
"unitSystemImperial": "इम्पीरियल",
|
||||
"@unitSystemImperial": {},
|
||||
"passwordDialogEnter": "पासवर्ड दर्ज करें",
|
||||
"@passwordDialogEnter": {},
|
||||
|
@ -298,7 +298,7 @@
|
|||
"@albumTierRegular": {},
|
||||
"coordinateFormatDms": "DMS",
|
||||
"@coordinateFormatDms": {},
|
||||
"coordinateFormatDecimal": "Decimal degrees",
|
||||
"coordinateFormatDecimal": "दशमलव डिग्री",
|
||||
"@coordinateFormatDecimal": {},
|
||||
"coordinateDms": "{coordinate} {direction}",
|
||||
"@coordinateDms": {
|
||||
|
@ -491,7 +491,7 @@
|
|||
"@lengthUnitPercent": {},
|
||||
"nameConflictStrategyReplace": "बदलें",
|
||||
"@nameConflictStrategyReplace": {},
|
||||
"themeBrightnessLight": "Light",
|
||||
"themeBrightnessLight": "लाइट",
|
||||
"@themeBrightnessLight": {},
|
||||
"vaultLockTypePassword": "पासवर्ड",
|
||||
"@vaultLockTypePassword": {},
|
||||
|
@ -554,5 +554,421 @@
|
|||
"videoActionABRepeat": "A-B दोहराव",
|
||||
"@videoActionABRepeat": {},
|
||||
"videoRepeatActionSetStart": "स्टार्ट सेट करे",
|
||||
"@videoRepeatActionSetStart": {}
|
||||
"@videoRepeatActionSetStart": {},
|
||||
"keepScreenOnNever": "कभी नहीं",
|
||||
"@keepScreenOnNever": {},
|
||||
"editEntryDialogCopyFromItem": "अन्य आइटम से कॉपी करें",
|
||||
"@editEntryDialogCopyFromItem": {},
|
||||
"durationDialogSeconds": "सेकंड",
|
||||
"@durationDialogSeconds": {},
|
||||
"entryInfoActionEditTitleDescription": "शीर्षक और विवरण संपादित करें",
|
||||
"@entryInfoActionEditTitleDescription": {},
|
||||
"widgetOpenPageCollection": "संग्रह खोलें",
|
||||
"@widgetOpenPageCollection": {},
|
||||
"vaultBinUsageDialogMessage": "कुछ वॉल्ट्स रीसायकल बिन का उपयोग कर रहे हैं।।",
|
||||
"@vaultBinUsageDialogMessage": {},
|
||||
"editEntryLocationDialogSetCustom": "कस्टम स्थान सेट करें",
|
||||
"@editEntryLocationDialogSetCustom": {},
|
||||
"editEntryLocationDialogChooseOnMap": "मानचित्र पर चुनें",
|
||||
"@editEntryLocationDialogChooseOnMap": {},
|
||||
"sourceStateCataloguing": "Cataloguing",
|
||||
"@sourceStateCataloguing": {},
|
||||
"editEntryLocationDialogLongitude": "देशांतर",
|
||||
"@editEntryLocationDialogLongitude": {},
|
||||
"videoStreamSelectionDialogVideo": "वीडियो",
|
||||
"@videoStreamSelectionDialogVideo": {},
|
||||
"keepScreenOnViewerOnly": "केवल व्यूअर पेज",
|
||||
"@keepScreenOnViewerOnly": {},
|
||||
"keepScreenOnAlways": "हमेशा",
|
||||
"@keepScreenOnAlways": {},
|
||||
"renameEntrySetPageInsertTooltip": "फ़ील्ड डालें",
|
||||
"@renameEntrySetPageInsertTooltip": {},
|
||||
"renameEntrySetPagePreviewSectionTitle": "पूर्वावलोकन",
|
||||
"@renameEntrySetPagePreviewSectionTitle": {},
|
||||
"setCoverDialogAuto": "ऑटो",
|
||||
"@setCoverDialogAuto": {},
|
||||
"exportEntryDialogWidth": "चौड़ाई",
|
||||
"@exportEntryDialogWidth": {},
|
||||
"editEntryDialogTargetFieldsHeader": "संशोधित करने के लिए फ़ील्ड",
|
||||
"@editEntryDialogTargetFieldsHeader": {},
|
||||
"editEntryDateDialogTitle": "तारीख और समय",
|
||||
"@editEntryDateDialogTitle": {},
|
||||
"editEntryDateDialogSetCustom": "कस्टम तिथि सेट करें",
|
||||
"@editEntryDateDialogSetCustom": {},
|
||||
"durationDialogHours": "घंटे",
|
||||
"@durationDialogHours": {},
|
||||
"editEntryLocationDialogTitle": "स्थान",
|
||||
"@editEntryLocationDialogTitle": {},
|
||||
"renameProcessorHash": "हैश",
|
||||
"@renameProcessorHash": {},
|
||||
"renameProcessorName": "नाम",
|
||||
"@renameProcessorName": {},
|
||||
"exportEntryDialogFormat": "फॉर्मेट:",
|
||||
"@exportEntryDialogFormat": {},
|
||||
"removeEntryMetadataMotionPhotoXmpWarningDialogMessage": "मोशन फोटो के अंदर वीडियो चलाने के लिए XMP की जरूरत है\n\nक्या आप वास्तव में इसे हटाना चाहते हैं?",
|
||||
"@removeEntryMetadataMotionPhotoXmpWarningDialogMessage": {},
|
||||
"videoSpeedDialogLabel": "चलाने की गति",
|
||||
"@videoSpeedDialogLabel": {},
|
||||
"editEntryLocationDialogLatitude": "अक्षांश",
|
||||
"@editEntryLocationDialogLatitude": {},
|
||||
"removeEntryMetadataDialogTitle": "मेटाडाटा हटाना",
|
||||
"@removeEntryMetadataDialogTitle": {},
|
||||
"filterNoLocationLabel": "लॉकेट नही किया गया",
|
||||
"@filterNoLocationLabel": {},
|
||||
"accessibilityAnimationsRemove": "स्क्रीन प्रभाव को रोकें",
|
||||
"@accessibilityAnimationsRemove": {},
|
||||
"maxBrightnessNever": "कभी नहीं",
|
||||
"@maxBrightnessNever": {},
|
||||
"maxBrightnessAlways": "हमेशा",
|
||||
"@maxBrightnessAlways": {},
|
||||
"widgetTapUpdateWidget": "विजेट अपडेट करें",
|
||||
"@widgetTapUpdateWidget": {},
|
||||
"storageVolumeDescriptionFallbackPrimary": "आंतरिक भंडारण",
|
||||
"@storageVolumeDescriptionFallbackPrimary": {},
|
||||
"editEntryDateDialogExtractFromTitle": "शीर्षक से निकालें",
|
||||
"@editEntryDateDialogExtractFromTitle": {},
|
||||
"editEntryRatingDialogTitle": "रेटिंग",
|
||||
"@editEntryRatingDialogTitle": {},
|
||||
"removeEntryMetadataDialogMore": "अधिक",
|
||||
"@removeEntryMetadataDialogMore": {},
|
||||
"filterLocatedLabel": "लोकेट किया गया",
|
||||
"@filterLocatedLabel": {},
|
||||
"locationPickerUseThisLocationButton": "इस स्थान का उपयोग करें",
|
||||
"@locationPickerUseThisLocationButton": {},
|
||||
"viewerActionLock": "व्यूअर को लॉक करे",
|
||||
"@viewerActionLock": {},
|
||||
"renameEntrySetPagePatternFieldLabel": "नामकरण पैटर्न",
|
||||
"@renameEntrySetPagePatternFieldLabel": {},
|
||||
"videoResumptionModeNever": "कभी नहीं",
|
||||
"@videoResumptionModeNever": {},
|
||||
"videoResumptionModeAlways": "हमेशा",
|
||||
"@videoResumptionModeAlways": {},
|
||||
"renameProcessorCounter": "काउंटर",
|
||||
"@renameProcessorCounter": {},
|
||||
"editEntryDateDialogCopyField": "अन्य तारीख से कॉपी करें",
|
||||
"@editEntryDateDialogCopyField": {},
|
||||
"viewerActionUnlock": "व्यूअर को अनलॉक करे",
|
||||
"@viewerActionUnlock": {},
|
||||
"editorTransformCrop": "क्रॉप",
|
||||
"@editorTransformCrop": {},
|
||||
"cropAspectRatioFree": "फ्री",
|
||||
"@cropAspectRatioFree": {},
|
||||
"nameConflictStrategySkip": "छोड़े",
|
||||
"@nameConflictStrategySkip": {},
|
||||
"albumTierPinned": "पिन किया गया",
|
||||
"@albumTierPinned": {},
|
||||
"albumTierSpecial": "कॉमन",
|
||||
"@albumTierSpecial": {},
|
||||
"overlayHistogramNone": "कोई नहीं",
|
||||
"@overlayHistogramNone": {},
|
||||
"overlayHistogramRGB": "RGB",
|
||||
"@overlayHistogramRGB": {},
|
||||
"overlayHistogramLuminance": "चमक",
|
||||
"@overlayHistogramLuminance": {},
|
||||
"widgetOpenPageViewer": "व्यूअर खोलें",
|
||||
"@widgetOpenPageViewer": {},
|
||||
"addShortcutButtonLabel": "ADD",
|
||||
"@addShortcutButtonLabel": {},
|
||||
"setCoverDialogCustom": "कस्टम",
|
||||
"@setCoverDialogCustom": {},
|
||||
"exportEntryDialogHeight": "ऊंचाई",
|
||||
"@exportEntryDialogHeight": {},
|
||||
"exportEntryDialogQuality": "गुणवत्ता",
|
||||
"@exportEntryDialogQuality": {},
|
||||
"exportEntryDialogWriteMetadata": "मेटाडाटा लिखें",
|
||||
"@exportEntryDialogWriteMetadata": {},
|
||||
"renameEntryDialogLabel": "नया नाम",
|
||||
"@renameEntryDialogLabel": {},
|
||||
"editEntryDateDialogShift": "शिफ्ट",
|
||||
"@editEntryDateDialogShift": {},
|
||||
"durationDialogMinutes": "मिनट",
|
||||
"@durationDialogMinutes": {},
|
||||
"videoStreamSelectionDialogText": "उपशीर्षक",
|
||||
"@videoStreamSelectionDialogText": {},
|
||||
"genericFailureFeedback": "असफल",
|
||||
"@genericFailureFeedback": {},
|
||||
"menuActionSlideshow": "स्लाइड शो",
|
||||
"@menuActionSlideshow": {},
|
||||
"viewDialogReverseSortOrder": "क्रम उलटा करे",
|
||||
"@viewDialogReverseSortOrder": {},
|
||||
"tileLayoutMosaic": "मौज़ेक",
|
||||
"@tileLayoutMosaic": {},
|
||||
"cropAspectRatioSquare": "वर्ग",
|
||||
"@cropAspectRatioSquare": {},
|
||||
"widgetOpenPageHome": "घर खोलें",
|
||||
"@widgetOpenPageHome": {},
|
||||
"viewDialogSortSectionTitle": "क्रम से लगाए",
|
||||
"@viewDialogSortSectionTitle": {},
|
||||
"viewDialogGroupSectionTitle": "समूह में रखे",
|
||||
"@viewDialogGroupSectionTitle": {},
|
||||
"videoStreamSelectionDialogAudio": "ऑडियो",
|
||||
"@videoStreamSelectionDialogAudio": {},
|
||||
"videoStreamSelectionDialogTrack": "ट्रैक",
|
||||
"@videoStreamSelectionDialogTrack": {},
|
||||
"genericSuccessFeedback": "हो गया!",
|
||||
"@genericSuccessFeedback": {},
|
||||
"menuActionMap": "मैप",
|
||||
"@menuActionMap": {},
|
||||
"aboutLinkLicense": "लाइसेंस",
|
||||
"@aboutLinkLicense": {},
|
||||
"editEntryDateDialogSourceFileModifiedDate": "फ़ाइल संशोधित दिनांक",
|
||||
"@editEntryDateDialogSourceFileModifiedDate": {},
|
||||
"coverDialogTabApp": "ऐप",
|
||||
"@coverDialogTabApp": {},
|
||||
"coverDialogTabCover": "ढखें",
|
||||
"@coverDialogTabCover": {},
|
||||
"aboutBugReportButton": "रिपोर्ट दे",
|
||||
"@aboutBugReportButton": {},
|
||||
"deleteSingleAlbumConfirmationDialogMessage": "{count, plural, =1{यह एल्बम और इसमें मौजूद आइटम हटाएं?} other{यह एल्बम और इसमें मौजूद {count} हटाएं?}}",
|
||||
"@deleteSingleAlbumConfirmationDialogMessage": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"format": "decimalPattern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"deleteMultiAlbumConfirmationDialogMessage": "{count, plural, =1{ये एल्बम और उनमें मौजूद आइटम हटाएं?} other{ये एल्बम और उनमें मौजूद {count} आइटम हटाएं?}}",
|
||||
"@deleteMultiAlbumConfirmationDialogMessage": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"format": "decimalPattern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"menuActionSelect": "चूने",
|
||||
"@menuActionSelect": {},
|
||||
"genericDangerWarningDialogMessage": "क्या आपको यकीन है?",
|
||||
"@genericDangerWarningDialogMessage": {},
|
||||
"tooManyItemsErrorDialogMessage": "कम आइटम के साथ पुनः प्रयास करें।",
|
||||
"@tooManyItemsErrorDialogMessage": {},
|
||||
"menuActionSelectAll": "सभी चुने",
|
||||
"@menuActionSelectAll": {},
|
||||
"menuActionSelectNone": "कुछ मत चुने",
|
||||
"@menuActionSelectNone": {},
|
||||
"tileLayoutGrid": "ग्रिड",
|
||||
"@tileLayoutGrid": {},
|
||||
"tileLayoutList": "सूची",
|
||||
"@tileLayoutList": {},
|
||||
"castDialogTitle": "कास्ट डिवाइस",
|
||||
"@castDialogTitle": {},
|
||||
"coverDialogTabColor": "रंग",
|
||||
"@coverDialogTabColor": {},
|
||||
"appPickDialogTitle": "ऐप चुनें",
|
||||
"@appPickDialogTitle": {},
|
||||
"aboutBugCopyInfoButton": "कोपी",
|
||||
"@aboutBugCopyInfoButton": {},
|
||||
"aboutBugReportInstruction": "लॉग और सिस्टम जानकारी के साथ GitHub पर रिपोर्ट करें",
|
||||
"@aboutBugReportInstruction": {},
|
||||
"aboutBugCopyInfoInstruction": "सिस्टम जानकारी कॉपी करें",
|
||||
"@aboutBugCopyInfoInstruction": {},
|
||||
"videoStreamSelectionDialogOff": "बंद",
|
||||
"@videoStreamSelectionDialogOff": {},
|
||||
"aboutBugSectionTitle": "बग रिपोर्ट",
|
||||
"@aboutBugSectionTitle": {},
|
||||
"aboutLinkPolicy": "गोपनीयता नीति",
|
||||
"@aboutLinkPolicy": {},
|
||||
"menuActionConfigureView": "देखें",
|
||||
"@menuActionConfigureView": {},
|
||||
"menuActionStats": "आँकड़े",
|
||||
"@menuActionStats": {},
|
||||
"aboutBugSaveLogInstruction": "ऐप लॉग को फ़ाइल में सहेजें",
|
||||
"@aboutBugSaveLogInstruction": {},
|
||||
"aboutDataUsageSectionTitle": "डेटा उपयोग",
|
||||
"@aboutDataUsageSectionTitle": {},
|
||||
"aboutDataUsageData": "डेटा",
|
||||
"@aboutDataUsageData": {},
|
||||
"aboutDataUsageCache": "कैश",
|
||||
"@aboutDataUsageCache": {},
|
||||
"aboutDataUsageDatabase": "डेटाबेस",
|
||||
"@aboutDataUsageDatabase": {},
|
||||
"editorActionTransform": "परिवर्तन",
|
||||
"@editorActionTransform": {},
|
||||
"videoStreamSelectionDialogNoSelection": "कोई अन्य ट्रैक नहीं हैं।",
|
||||
"@videoStreamSelectionDialogNoSelection": {},
|
||||
"viewDialogLayoutSectionTitle": "लेआउट",
|
||||
"@viewDialogLayoutSectionTitle": {},
|
||||
"appPickDialogNone": "कोई नहीं",
|
||||
"@appPickDialogNone": {},
|
||||
"aboutPageTitle": "बारे में",
|
||||
"@aboutPageTitle": {},
|
||||
"collectionEditFailureFeedback": "{count, plural, =1{1 आइटम एडिट करने में विफल} other{{count} आइटम एडिट करने में विफल}}",
|
||||
"@collectionEditFailureFeedback": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"format": "decimalPattern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"drawerCollectionVideos": "वीडियो",
|
||||
"@drawerCollectionVideos": {},
|
||||
"collectionActionSetHome": "घर के रूप में सेट करें",
|
||||
"@collectionActionSetHome": {},
|
||||
"collectionActionMove": "एल्बम पर जाएँ",
|
||||
"@collectionActionMove": {},
|
||||
"collectionActionRescan": "पुन: स्कैन करे",
|
||||
"@collectionActionRescan": {},
|
||||
"collectionGroupDay": "दिन के अनुसार",
|
||||
"@collectionGroupDay": {},
|
||||
"collectionCopySuccessFeedback": "{count, plural, =1{1 आइटम कॉपी किया गया} other{{count} आइटम कॉपी किए गए}}",
|
||||
"@collectionCopySuccessFeedback": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"format": "decimalPattern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"collectionEmptyImages": "कोई इमेज नहीं",
|
||||
"@collectionEmptyImages": {},
|
||||
"collectionEmptyGrantAccessButtonLabel": "पहुंच प्रदान करें",
|
||||
"@collectionEmptyGrantAccessButtonLabel": {},
|
||||
"collectionActionAddShortcut": "शॉर्टकट जोड़ें",
|
||||
"@collectionActionAddShortcut": {},
|
||||
"collectionRenameFailureFeedback": "{count, plural, =1{1 आइटम का नाम बदलने में विफल} other{{count} आइटम का नाम बदलने में विफल}}",
|
||||
"@collectionRenameFailureFeedback": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"format": "decimalPattern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"drawerCollectionAnimated": "एनिमेटेड",
|
||||
"@drawerCollectionAnimated": {},
|
||||
"aboutCreditsSectionTitle": "क्रेडिट",
|
||||
"@aboutCreditsSectionTitle": {},
|
||||
"aboutLicensesSectionTitle": "ओपन सोर्स लाइसेंस",
|
||||
"@aboutLicensesSectionTitle": {},
|
||||
"aboutLicensesFlutterPackagesSectionTitle": "फ्लटर पैकेज",
|
||||
"@aboutLicensesFlutterPackagesSectionTitle": {},
|
||||
"aboutLicensesShowAllButtonLabel": "सभी लाइसेंस दिखाएं",
|
||||
"@aboutLicensesShowAllButtonLabel": {},
|
||||
"policyPageTitle": "गोपनीयता नीति",
|
||||
"@policyPageTitle": {},
|
||||
"collectionActionEmptyBin": "बीन खाली करे",
|
||||
"@collectionActionEmptyBin": {},
|
||||
"collectionActionCopy": "एल्बम में कॉपी करें",
|
||||
"@collectionActionCopy": {},
|
||||
"collectionGroupAlbum": "एल्बम के अनुसार",
|
||||
"@collectionGroupAlbum": {},
|
||||
"aboutCreditsWorldAtlas1": "यह ऐप TopoJSON फ़ाइल का उपयोग करता है",
|
||||
"@aboutCreditsWorldAtlas1": {},
|
||||
"aboutCreditsWorldAtlas2": "आईएससी लाइसेंस के तहत।",
|
||||
"@aboutCreditsWorldAtlas2": {},
|
||||
"collectionPageTitle": "संग्रह",
|
||||
"@collectionPageTitle": {},
|
||||
"collectionGroupMonth": "महीने के अनुसार",
|
||||
"@collectionGroupMonth": {},
|
||||
"collectionGroupNone": "समूह न बनाएं",
|
||||
"@collectionGroupNone": {},
|
||||
"sectionUnknown": "अज्ञात",
|
||||
"@sectionUnknown": {},
|
||||
"dateYesterday": "कल",
|
||||
"@dateYesterday": {},
|
||||
"dateThisMonth": "इस महीने",
|
||||
"@dateThisMonth": {},
|
||||
"collectionDeleteFailureFeedback": "{count, plural, =1{1 आइटम हटाने में विफल} other{{count} आइटम हटाने में विफल}}",
|
||||
"@collectionDeleteFailureFeedback": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"format": "decimalPattern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"collectionExportFailureFeedback": "{count, plural, =1{1 पन्ना निर्यात करने में विफल} other{{count} पन्ने निर्यात करने में विफल}}",
|
||||
"@collectionExportFailureFeedback": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"format": "decimalPattern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"collectionRenameSuccessFeedback": "{count, plural, =1{1 आइटम का नाम बदला गया} other{{count} आइटम का नाम बदला गया}}",
|
||||
"@collectionRenameSuccessFeedback": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"format": "decimalPattern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"aboutLicensesAndroidLibrariesSectionTitle": "एंड्रॉइड लाइब्रेरीज़",
|
||||
"@aboutLicensesAndroidLibrariesSectionTitle": {},
|
||||
"dateToday": "आज",
|
||||
"@dateToday": {},
|
||||
"collectionActionHideTitleSearch": "शीर्षक फ़िल्टर छुपाएं",
|
||||
"@collectionActionHideTitleSearch": {},
|
||||
"drawerCollectionFavourites": "पसंदीदा",
|
||||
"@drawerCollectionFavourites": {},
|
||||
"drawerCollectionMotionPhotos": "मोशन तस्वीरें",
|
||||
"@drawerCollectionMotionPhotos": {},
|
||||
"collectionMoveFailureFeedback": "{count, plural, =1{1 आइटम ले जाने में विफल} other{{count} आइटम ले जाने में विफल}}",
|
||||
"@collectionMoveFailureFeedback": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"format": "decimalPattern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"collectionEditSuccessFeedback": "{count, plural, =1{1 आइटम संपादित किया गया} other{{count} आइटम संपादित किए गए}}",
|
||||
"@collectionEditSuccessFeedback": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"format": "decimalPattern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"collectionEmptyFavourites": "कोई पसंदीदा नहीं",
|
||||
"@collectionEmptyFavourites": {},
|
||||
"aboutDataUsageClearCache": "कैश साफ़ करें",
|
||||
"@aboutDataUsageClearCache": {},
|
||||
"aboutTranslatorsSectionTitle": "अनुवादक",
|
||||
"@aboutTranslatorsSectionTitle": {},
|
||||
"aboutLicensesBanner": "यह ऐप निम्नलिखित ओपन सोर्स पैकेज और पुस्तकालयों का उपयोग करता है।",
|
||||
"@aboutLicensesBanner": {},
|
||||
"aboutLicensesFlutterPluginsSectionTitle": "फ्लटर प्लगइन्स",
|
||||
"@aboutLicensesFlutterPluginsSectionTitle": {},
|
||||
"aboutLicensesDartPackagesSectionTitle": "डार्ट पैकेज",
|
||||
"@aboutLicensesDartPackagesSectionTitle": {},
|
||||
"collectionCopyFailureFeedback": "{count, plural, =1{1 आइटम कॉपी करने में विफल} other{{count} आइटम कॉपी करने में विफल}}",
|
||||
"@collectionCopyFailureFeedback": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"format": "decimalPattern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"aboutDataUsageInternal": "आंतरिक",
|
||||
"@aboutDataUsageInternal": {},
|
||||
"aboutDataUsageExternal": "बाहरी",
|
||||
"@aboutDataUsageExternal": {},
|
||||
"collectionSelectPageTitle": "आइटम चुनें",
|
||||
"@collectionSelectPageTitle": {},
|
||||
"collectionActionShowTitleSearch": "शीर्षक फ़िल्टर दिखाएं",
|
||||
"@collectionActionShowTitleSearch": {},
|
||||
"collectionActionEdit": "एडिट करे",
|
||||
"@collectionActionEdit": {},
|
||||
"collectionSearchTitlesHintText": "शीर्षक खोजें",
|
||||
"@collectionSearchTitlesHintText": {},
|
||||
"collectionMoveSuccessFeedback": "{count, plural, =1{1 आइटम स्थानांतरित किया गया} other{{count} आइटम स्थानांतरित किए गए}}",
|
||||
"@collectionMoveSuccessFeedback": {
|
||||
"placeholders": {
|
||||
"count": {
|
||||
"format": "decimalPattern"
|
||||
}
|
||||
}
|
||||
},
|
||||
"collectionEmptyVideos": "कोई वीडियो नहीं",
|
||||
"@collectionEmptyVideos": {},
|
||||
"collectionSelectSectionTooltip": "अनुभाग चुनें",
|
||||
"@collectionSelectSectionTooltip": {},
|
||||
"collectionDeselectSectionTooltip": "अनुभाग का चयन रद्द करें",
|
||||
"@collectionDeselectSectionTooltip": {},
|
||||
"drawerAboutButton": "के बारे में",
|
||||
"@drawerAboutButton": {},
|
||||
"drawerSettingsButton": "सेटिंग्स",
|
||||
"@drawerSettingsButton": {},
|
||||
"drawerCollectionAll": "सभी संग्रह",
|
||||
"@drawerCollectionAll": {},
|
||||
"drawerCollectionImages": "इमेजेस",
|
||||
"@drawerCollectionImages": {},
|
||||
"aboutDataUsageMisc": "विविध",
|
||||
"@aboutDataUsageMisc": {}
|
||||
}
|
||||
|
|
|
@ -1467,7 +1467,7 @@
|
|||
"@settingsVideoBackgroundModeDialogTitle": {},
|
||||
"tagEditorDiscardDialogMessage": "Forkast endringer?",
|
||||
"@tagEditorDiscardDialogMessage": {},
|
||||
"tagPlaceholderState": "Tilstand?",
|
||||
"tagPlaceholderState": "Delstat",
|
||||
"@tagPlaceholderState": {},
|
||||
"chipActionShowCountryStates": "Vis tilstander",
|
||||
"@chipActionShowCountryStates": {},
|
||||
|
|
1
lib/l10n/app_sr.arb
Normal file
1
lib/l10n/app_sr.arb
Normal file
|
@ -0,0 +1 @@
|
|||
{}
|
|
@ -7,7 +7,7 @@
|
|||
"@videoActionPlay": {},
|
||||
"viewerActionSettings": "Inställningar",
|
||||
"@viewerActionSettings": {},
|
||||
"albumTierSpecial": "Vanligt förekommande",
|
||||
"albumTierSpecial": "Vanliga",
|
||||
"@albumTierSpecial": {},
|
||||
"displayRefreshRatePreferLowest": "Lägsta intervall",
|
||||
"@displayRefreshRatePreferLowest": {},
|
||||
|
@ -132,7 +132,7 @@
|
|||
"@chipActionRename": {},
|
||||
"chipActionSetCover": "Välj som omslag",
|
||||
"@chipActionSetCover": {},
|
||||
"chipActionShowCountryStates": "Visa landskap",
|
||||
"chipActionShowCountryStates": "Visa delstater",
|
||||
"@chipActionShowCountryStates": {},
|
||||
"chipActionCreateAlbum": "Skapa album",
|
||||
"@chipActionCreateAlbum": {},
|
||||
|
@ -216,7 +216,7 @@
|
|||
"@entryInfoActionEditDate": {},
|
||||
"entryInfoActionEditLocation": "Redigera plats",
|
||||
"@entryInfoActionEditLocation": {},
|
||||
"entryInfoActionEditTitleDescription": "Redigera filnamn och beskrivning",
|
||||
"entryInfoActionEditTitleDescription": "Redigera titel & beskrivning",
|
||||
"@entryInfoActionEditTitleDescription": {},
|
||||
"entryInfoActionEditRating": "Redigera omdöme",
|
||||
"@entryInfoActionEditRating": {},
|
||||
|
@ -337,7 +337,7 @@
|
|||
"@mapStyleGoogleTerrain": {},
|
||||
"mapStyleStamenWatercolor": "Stamen Watercolor (Akvarell)",
|
||||
"@mapStyleStamenWatercolor": {},
|
||||
"maxBrightnessNever": "Alldrig",
|
||||
"maxBrightnessNever": "Aldrig",
|
||||
"@maxBrightnessNever": {},
|
||||
"maxBrightnessAlways": "Alltid",
|
||||
"@maxBrightnessAlways": {},
|
||||
|
@ -395,7 +395,7 @@
|
|||
"@videoPlaybackMuted": {},
|
||||
"videoPlaybackWithSound": "Spela med ljud",
|
||||
"@videoPlaybackWithSound": {},
|
||||
"videoResumptionModeNever": "Alldrig",
|
||||
"videoResumptionModeNever": "Aldrig",
|
||||
"@videoResumptionModeNever": {},
|
||||
"viewerTransitionSlide": "Glid",
|
||||
"@viewerTransitionSlide": {},
|
||||
|
@ -407,7 +407,7 @@
|
|||
"@wallpaperTargetHome": {},
|
||||
"widgetDisplayedItemRandom": "Slumpartat",
|
||||
"@widgetDisplayedItemRandom": {},
|
||||
"widgetDisplayedItemMostRecent": "Senaste",
|
||||
"widgetDisplayedItemMostRecent": "Det senaste",
|
||||
"@widgetDisplayedItemMostRecent": {},
|
||||
"widgetOpenPageHome": "Öppna startsida",
|
||||
"@widgetOpenPageHome": {},
|
||||
|
@ -449,7 +449,7 @@
|
|||
"@hideFilterConfirmationDialogMessage": {},
|
||||
"newAlbumDialogTitle": "Nytt Album",
|
||||
"@newAlbumDialogTitle": {},
|
||||
"newAlbumDialogNameLabelAlreadyExistsHelper": "Mappen existerar redan",
|
||||
"newAlbumDialogNameLabelAlreadyExistsHelper": "Katalogen existerar redan",
|
||||
"@newAlbumDialogNameLabelAlreadyExistsHelper": {},
|
||||
"newAlbumDialogStorageLabel": "Lagring:",
|
||||
"@newAlbumDialogStorageLabel": {},
|
||||
|
@ -475,7 +475,7 @@
|
|||
"@welcomeMessage": {},
|
||||
"nextButtonLabel": "NÄSTA",
|
||||
"@nextButtonLabel": {},
|
||||
"nextTooltip": "Nästs",
|
||||
"nextTooltip": "Nästa",
|
||||
"@nextTooltip": {},
|
||||
"doNotAskAgain": "Fråga inte igen",
|
||||
"@doNotAskAgain": {},
|
||||
|
@ -546,7 +546,7 @@
|
|||
"@patternDialogEnter": {},
|
||||
"patternDialogConfirm": "Bekräfta mönster",
|
||||
"@patternDialogConfirm": {},
|
||||
"renameEntrySetPagePatternFieldLabel": "Namnge mönster",
|
||||
"renameEntrySetPagePatternFieldLabel": "Namngivningsmönster",
|
||||
"@renameEntrySetPagePatternFieldLabel": {},
|
||||
"renameEntrySetPageInsertTooltip": "Infoga fällt",
|
||||
"@renameEntrySetPageInsertTooltip": {},
|
||||
|
@ -570,7 +570,7 @@
|
|||
"@editEntryDialogCopyFromItem": {},
|
||||
"editEntryDateDialogTitle": "Datum & Tid",
|
||||
"@editEntryDateDialogTitle": {},
|
||||
"editEntryDateDialogExtractFromTitle": "Kopiera från filnamn",
|
||||
"editEntryDateDialogExtractFromTitle": "Extrahera från titel",
|
||||
"@editEntryDateDialogExtractFromTitle": {},
|
||||
"editEntryDateDialogShift": "Förskjut",
|
||||
"@editEntryDateDialogShift": {},
|
||||
|
@ -654,7 +654,7 @@
|
|||
"@collectionActionMove": {},
|
||||
"viewDialogSortSectionTitle": "Sortera",
|
||||
"@viewDialogSortSectionTitle": {},
|
||||
"viewDialogGroupSectionTitle": "Grupp",
|
||||
"viewDialogGroupSectionTitle": "Gruppera",
|
||||
"@viewDialogGroupSectionTitle": {},
|
||||
"viewDialogLayoutSectionTitle": "Layout",
|
||||
"@viewDialogLayoutSectionTitle": {},
|
||||
|
@ -682,7 +682,7 @@
|
|||
"@aboutLinkLicense": {},
|
||||
"aboutLinkPolicy": "IntegritetsPolicy",
|
||||
"@aboutLinkPolicy": {},
|
||||
"aboutBugSectionTitle": "FelRapport",
|
||||
"aboutBugSectionTitle": "Felrapport",
|
||||
"@aboutBugSectionTitle": {},
|
||||
"aboutBugSaveLogInstruction": "Spara appens log till en fil",
|
||||
"@aboutBugSaveLogInstruction": {},
|
||||
|
@ -692,7 +692,7 @@
|
|||
"@aboutLicensesShowAllButtonLabel": {},
|
||||
"policyPageTitle": "IntegritetsPolicy",
|
||||
"@policyPageTitle": {},
|
||||
"collectionActionHideTitleSearch": "Göm filnamnsfilter",
|
||||
"collectionActionHideTitleSearch": "Göm titelfilter",
|
||||
"@collectionActionHideTitleSearch": {},
|
||||
"collectionActionAddShortcut": "Lägg till genväg",
|
||||
"@collectionActionAddShortcut": {},
|
||||
|
@ -746,7 +746,7 @@
|
|||
"@entryActionCast": {},
|
||||
"filterTaggedLabel": "Taggad",
|
||||
"@filterTaggedLabel": {},
|
||||
"keepScreenOnNever": "Alldrig",
|
||||
"keepScreenOnNever": "Aldrig",
|
||||
"@keepScreenOnNever": {},
|
||||
"viewerTransitionFade": "Tona ut",
|
||||
"@viewerTransitionFade": {},
|
||||
|
@ -905,7 +905,7 @@
|
|||
"@editEntryDateDialogCopyField": {},
|
||||
"castDialogTitle": "Uppspelningsenheter",
|
||||
"@castDialogTitle": {},
|
||||
"renameEntrySetPageTitle": "Döpa om",
|
||||
"renameEntrySetPageTitle": "Ändra namn",
|
||||
"@renameEntrySetPageTitle": {},
|
||||
"deleteEntriesConfirmationDialogMessage": "{count, plural, =1{Vill du ta bort denna fil?} other{Vill du ta bort dessa {count} filer?}}",
|
||||
"@deleteEntriesConfirmationDialogMessage": {
|
||||
|
@ -921,7 +921,7 @@
|
|||
"@renameAlbumDialogLabelAlreadyExistsHelper": {},
|
||||
"aboutLicensesDartPackagesSectionTitle": "Dart-paket",
|
||||
"@aboutLicensesDartPackagesSectionTitle": {},
|
||||
"collectionActionShowTitleSearch": "Filtrera på filnamn",
|
||||
"collectionActionShowTitleSearch": "Visa titelfilter",
|
||||
"@collectionActionShowTitleSearch": {},
|
||||
"binEntriesConfirmationDialogMessage": "{count, plural, =1{Flytta denna fil tillpapperskorgen?} other{Flytta dessa {count} filer till papperskorgen?}}",
|
||||
"@binEntriesConfirmationDialogMessage": {
|
||||
|
@ -991,11 +991,11 @@
|
|||
"@albumPickPageTitleMove": {},
|
||||
"albumPickPageTitlePick": "Välj Album",
|
||||
"@albumPickPageTitlePick": {},
|
||||
"statePageTitle": "Landskap",
|
||||
"statePageTitle": "Delstater",
|
||||
"@statePageTitle": {},
|
||||
"countryEmpty": "Inga länder",
|
||||
"@countryEmpty": {},
|
||||
"stateEmpty": "Inga landskap",
|
||||
"stateEmpty": "Inga delstater",
|
||||
"@stateEmpty": {},
|
||||
"placePageTitle": "Platser",
|
||||
"@placePageTitle": {},
|
||||
|
@ -1057,9 +1057,9 @@
|
|||
"@tagEmpty": {},
|
||||
"binPageTitle": "Papperskorgen",
|
||||
"@binPageTitle": {},
|
||||
"explorerActionSelectStorageVolume": "Välj lagringsplatts",
|
||||
"explorerActionSelectStorageVolume": "Välj lagringsplats",
|
||||
"@explorerActionSelectStorageVolume": {},
|
||||
"selectStorageVolumeDialogTitle": "Välj Lagringsplatts",
|
||||
"selectStorageVolumeDialogTitle": "Välj Lagringsplats",
|
||||
"@selectStorageVolumeDialogTitle": {},
|
||||
"collectionEditSuccessFeedback": "{count, plural, =1{Ändrat 1 objekt} other{Ändrat {count} objekt}}",
|
||||
"@collectionEditSuccessFeedback": {
|
||||
|
@ -1089,7 +1089,7 @@
|
|||
"@searchDateSectionTitle": {},
|
||||
"searchAlbumsSectionTitle": "Album",
|
||||
"@searchAlbumsSectionTitle": {},
|
||||
"searchStatesSectionTitle": "Landskap",
|
||||
"searchStatesSectionTitle": "Delstater",
|
||||
"@searchStatesSectionTitle": {},
|
||||
"albumPickPageTitleExport": "Exportera till Album",
|
||||
"@albumPickPageTitleExport": {},
|
||||
|
@ -1201,9 +1201,9 @@
|
|||
"@settingsViewerShowMinimap": {},
|
||||
"settingsViewerShowInformation": "Visa information",
|
||||
"@settingsViewerShowInformation": {},
|
||||
"settingsViewerShowInformationSubtitle": "Visa filnamn, datum, plats osv …",
|
||||
"settingsViewerShowInformationSubtitle": "Visa titel, datum, plats, etc.",
|
||||
"@settingsViewerShowInformationSubtitle": {},
|
||||
"settingsViewerShowOverlayThumbnails": "Visa minityrbilder",
|
||||
"settingsViewerShowOverlayThumbnails": "Visa miniatyrbilder",
|
||||
"@settingsViewerShowOverlayThumbnails": {},
|
||||
"settingsViewerShowDescription": "Visa beskrivning",
|
||||
"@settingsViewerShowDescription": {},
|
||||
|
@ -1287,7 +1287,7 @@
|
|||
"@settingsWidgetDisplayedItem": {},
|
||||
"statsPageTitle": "Statistik",
|
||||
"@statsPageTitle": {},
|
||||
"statsTopCountriesSectionTitle": "Vanligast Länder",
|
||||
"statsTopCountriesSectionTitle": "Populära Länder",
|
||||
"@statsTopCountriesSectionTitle": {},
|
||||
"settingsCollectionTile": "Samling",
|
||||
"@settingsCollectionTile": {},
|
||||
|
@ -1301,7 +1301,7 @@
|
|||
"@viewerErrorDoesNotExist": {},
|
||||
"viewerInfoPageTitle": "Info",
|
||||
"@viewerInfoPageTitle": {},
|
||||
"viewerInfoLabelTitle": "Filnamn",
|
||||
"viewerInfoLabelTitle": "Titel",
|
||||
"@viewerInfoLabelTitle": {},
|
||||
"viewerInfoLabelDate": "Datum",
|
||||
"@viewerInfoLabelDate": {},
|
||||
|
@ -1391,9 +1391,9 @@
|
|||
"@settingsCollectionQuickActionEditorPageTitle": {},
|
||||
"settingsCollectionSelectionQuickActionEditorBanner": "Tryck och håll nere för att flytta knappar och välj på så vis vilka åtgärder som skall visas när objekt väljs.",
|
||||
"@settingsCollectionSelectionQuickActionEditorBanner": {},
|
||||
"settingsCollectionBurstPatternsTile": "Namngivningsmönster",
|
||||
"settingsCollectionBurstPatternsTile": "Seriefotograferingsmönster",
|
||||
"@settingsCollectionBurstPatternsTile": {},
|
||||
"settingsCollectionBurstPatternsNone": "Ingen",
|
||||
"settingsCollectionBurstPatternsNone": "Inget",
|
||||
"@settingsCollectionBurstPatternsNone": {},
|
||||
"settingsViewerSectionTitle": "Bildvisare",
|
||||
"@settingsViewerSectionTitle": {},
|
||||
|
@ -1403,7 +1403,7 @@
|
|||
"@settingsViewerUseCutout": {},
|
||||
"settingsViewerMaximumBrightness": "Maximal ljusstyrka",
|
||||
"@settingsViewerMaximumBrightness": {},
|
||||
"settingsMotionPhotoAutoPlay": "Spela automatiskt upp Rörelsefoton",
|
||||
"settingsMotionPhotoAutoPlay": "Spela automatiskt upp rörelsefoton",
|
||||
"@settingsMotionPhotoAutoPlay": {},
|
||||
"settingsImageBackground": "Bakgrund för bilder",
|
||||
"@settingsImageBackground": {},
|
||||
|
@ -1419,7 +1419,7 @@
|
|||
"@settingsViewerQuickActionEditorAvailableButtonsSectionTitle": {},
|
||||
"settingsViewerQuickActionEmpty": "Inga knappar",
|
||||
"@settingsViewerQuickActionEmpty": {},
|
||||
"settingsViewerOverlayTile": "Överblick",
|
||||
"settingsViewerOverlayTile": "Overlay",
|
||||
"@settingsViewerOverlayTile": {},
|
||||
"settingsViewerEnableOverlayBlurEffect": "Effekt för oskärpa",
|
||||
"@settingsViewerEnableOverlayBlurEffect": {},
|
||||
|
@ -1467,7 +1467,7 @@
|
|||
"@settingsHiddenItemsTabFilters": {},
|
||||
"settingsHiddenFiltersEmpty": "Inga dolda filter",
|
||||
"@settingsHiddenFiltersEmpty": {},
|
||||
"addPathTooltip": "Lägg till katalog",
|
||||
"addPathTooltip": "Lägg till sökväg",
|
||||
"@addPathTooltip": {},
|
||||
"settingsRemoveAnimationsTile": "Inaktivera animationer",
|
||||
"@settingsRemoveAnimationsTile": {},
|
||||
|
@ -1507,7 +1507,7 @@
|
|||
"@settingsEnableBin": {},
|
||||
"settingsEnableBinSubtitle": "Behåll borttagna objekt i 30 dagar",
|
||||
"@settingsEnableBinSubtitle": {},
|
||||
"settingsViewerOverlayPageTitle": "Överblick",
|
||||
"settingsViewerOverlayPageTitle": "Overlay",
|
||||
"@settingsViewerOverlayPageTitle": {},
|
||||
"settingsHomeTile": "Startsida",
|
||||
"@settingsHomeTile": {},
|
||||
|
@ -1531,13 +1531,13 @@
|
|||
"@settingsUnitSystemDialogTitle": {},
|
||||
"settingsScreenSaverPageTitle": "Skärmsläckare",
|
||||
"@settingsScreenSaverPageTitle": {},
|
||||
"statsTopStatesSectionTitle": "Vanligast Delstater",
|
||||
"statsTopStatesSectionTitle": "Populära Delstater",
|
||||
"@statsTopStatesSectionTitle": {},
|
||||
"statsTopPlacesSectionTitle": "Vanligast Platser",
|
||||
"statsTopPlacesSectionTitle": "Populära Platser",
|
||||
"@statsTopPlacesSectionTitle": {},
|
||||
"statsTopTagsSectionTitle": "Vanligast Etiketter",
|
||||
"statsTopTagsSectionTitle": "Populära Etiketter",
|
||||
"@statsTopTagsSectionTitle": {},
|
||||
"statsTopAlbumsSectionTitle": "Vanligast Album",
|
||||
"statsTopAlbumsSectionTitle": "Populära Album",
|
||||
"@statsTopAlbumsSectionTitle": {},
|
||||
"viewerInfoLabelResolution": "Upplösning",
|
||||
"@viewerInfoLabelResolution": {},
|
||||
|
@ -1569,6 +1569,6 @@
|
|||
"@tagEditorDiscardDialogMessage": {},
|
||||
"tagPlaceholderCountry": "Land",
|
||||
"@tagPlaceholderCountry": {},
|
||||
"tagPlaceholderState": "Delstat",
|
||||
"tagPlaceholderState": "Stat",
|
||||
"@tagPlaceholderState": {}
|
||||
}
|
||||
|
|
|
@ -105,18 +105,21 @@ class Contributors {
|
|||
// Contributor('Htet Oo Hlaing', 'htetoh2006@outlook.com'), // Burmese
|
||||
// Contributor('Khant', 'khant@users.noreply.hosted.weblate.org'), // Burmese
|
||||
// Contributor('Grooty12', 'Rasmus@rosendahl-kaa.name'), // Danish
|
||||
// Contributor('Victor M', 'victormorita@tuta.io'), // Danish
|
||||
// Contributor('Åzze', 'laitinen.jere222@gmail.com'), // Finnish
|
||||
// Contributor('Olli', 'ollinen@ollit.dev'), // Finnish
|
||||
// Contributor('Idj', 'joneltmp+goahn@gmail.com'), // Hebrew
|
||||
// Contributor('Rohit Burman', 'rohitburman31p@rediffmail.com'), // Hindi
|
||||
// Contributor('AJ07', 'ajaykumarmeena676@gmail.com'), // Hindi
|
||||
// Contributor('Sartaj', 'ssaarrttaajj111@gmail.com'), // Hindi
|
||||
// Contributor('Anurag Samota', 'anuragsamotasamota@gmail.com'), // Hindi
|
||||
// Contributor('Chethan', 'chethan@users.noreply.hosted.weblate.org'), // Kannada
|
||||
// Contributor('GoRaN', 'gorangharib.909@gmail.com'), // Kurdish (Central)
|
||||
// Contributor('Rasti K5', 'rasti.khdhr@gmail.com'), // Kurdish (Central)
|
||||
// Contributor('Raman', 'xysed@tutanota.com'), // Malayalam
|
||||
// Contributor('Subham Jena', 'subhamjena8465@gmail.com'), // Odia
|
||||
// Contributor('Prasanta-Hembram', 'Prasantahembram720@gmail.com'), // Santali
|
||||
// Contributor('Enenra', 'nnra2210@gmail.com'), // Serbian
|
||||
// Contributor('mytja', 'mamnju21@gmail.com'), // Slovenian
|
||||
// Contributor('Nattapong K', 'mixer5056@gmail.com'), // Thai
|
||||
};
|
||||
|
|
|
@ -6,8 +6,12 @@ import 'package:connectivity_plus/connectivity_plus.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
|
||||
abstract class AvesAvailability {
|
||||
Future<void> onNewIntent();
|
||||
|
||||
void onResume();
|
||||
|
||||
bool get isLocked;
|
||||
|
||||
Future<bool> get isConnected;
|
||||
|
||||
Future<bool> get canLocatePlaces;
|
||||
|
@ -16,15 +20,24 @@ abstract class AvesAvailability {
|
|||
}
|
||||
|
||||
class LiveAvesAvailability implements AvesAvailability {
|
||||
bool? _isConnected;
|
||||
bool? _isConnected, _isLocked;
|
||||
|
||||
LiveAvesAvailability() {
|
||||
Connectivity().onConnectivityChanged.listen(_updateConnectivityFromResult);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onNewIntent() async {
|
||||
_isLocked = await deviceService.isLocked();
|
||||
debugPrint('Device is locked=$_isLocked');
|
||||
}
|
||||
|
||||
@override
|
||||
void onResume() => _isConnected = null;
|
||||
|
||||
@override
|
||||
bool get isLocked => _isLocked ?? false;
|
||||
|
||||
@override
|
||||
Future<bool> get isConnected async {
|
||||
if (_isConnected != null) return SynchronousFuture(_isConnected!);
|
||||
|
|
|
@ -9,8 +9,8 @@ final Device device = Device._private();
|
|||
|
||||
class Device {
|
||||
late final String _packageName, _packageVersion, _userAgent;
|
||||
late final bool _canAuthenticateUser, _canGrantDirectoryAccess, _canPinShortcut;
|
||||
late final bool _canRenderFlagEmojis, _canRenderSubdivisionFlagEmojis, _canRequestManageMedia, _canSetLockScreenWallpaper, _canUseCrypto;
|
||||
late final bool _canAuthenticateUser, _canPinShortcut;
|
||||
late final bool _canRenderFlagEmojis, _canRenderSubdivisionFlagEmojis, _canRequestManageMedia, _canSetLockScreenWallpaper;
|
||||
late final bool _hasGeocoder, _isDynamicColorAvailable, _isTelevision, _showPinShortcutFeedback, _supportEdgeToEdgeUIMode, _supportPictureInPicture;
|
||||
|
||||
String get packageName => _packageName;
|
||||
|
@ -21,8 +21,6 @@ class Device {
|
|||
|
||||
bool get canAuthenticateUser => _canAuthenticateUser;
|
||||
|
||||
bool get canGrantDirectoryAccess => _canGrantDirectoryAccess;
|
||||
|
||||
bool get canPinShortcut => _canPinShortcut;
|
||||
|
||||
bool get canRenderFlagEmojis => _canRenderFlagEmojis;
|
||||
|
@ -33,10 +31,6 @@ class Device {
|
|||
|
||||
bool get canSetLockScreenWallpaper => _canSetLockScreenWallpaper;
|
||||
|
||||
bool get canUseCrypto => _canUseCrypto;
|
||||
|
||||
bool get canUseVaults => canAuthenticateUser || canUseCrypto;
|
||||
|
||||
bool get hasGeocoder => _hasGeocoder;
|
||||
|
||||
bool get isDynamicColorAvailable => _isDynamicColorAvailable;
|
||||
|
@ -71,13 +65,11 @@ class Device {
|
|||
}
|
||||
|
||||
final capabilities = await deviceService.getCapabilities();
|
||||
_canGrantDirectoryAccess = capabilities['canGrantDirectoryAccess'] ?? false;
|
||||
_canPinShortcut = capabilities['canPinShortcut'] ?? false;
|
||||
_canRenderFlagEmojis = capabilities['canRenderFlagEmojis'] ?? false;
|
||||
_canRenderSubdivisionFlagEmojis = capabilities['canRenderSubdivisionFlagEmojis'] ?? false;
|
||||
_canRequestManageMedia = capabilities['canRequestManageMedia'] ?? false;
|
||||
_canSetLockScreenWallpaper = capabilities['canSetLockScreenWallpaper'] ?? false;
|
||||
_canUseCrypto = capabilities['canUseCrypto'] ?? false;
|
||||
_hasGeocoder = capabilities['hasGeocoder'] ?? false;
|
||||
_isDynamicColorAvailable = capabilities['isDynamicColorAvailable'] ?? false;
|
||||
_showPinShortcutFeedback = capabilities['showPinShortcutFeedback'] ?? false;
|
||||
|
|
|
@ -9,22 +9,24 @@ class PathFilter extends CollectionFilter {
|
|||
static const type = 'path';
|
||||
|
||||
// including trailing separator
|
||||
final String path;
|
||||
late final String path;
|
||||
|
||||
// without trailing separator
|
||||
final String _rootAlbum;
|
||||
late final String _rootAlbum;
|
||||
|
||||
late final EntryFilter _test;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [path, reversed];
|
||||
|
||||
PathFilter(this.path, {super.reversed = false}) : _rootAlbum = path.substring(0, path.length - 1) {
|
||||
PathFilter(String path, {super.reversed = false}) {
|
||||
this.path = path.endsWith(pContext.separator) ? path : '$path${pContext.separator}';
|
||||
_rootAlbum = this.path.substring(0, this.path.length - 1);
|
||||
_test = (entry) {
|
||||
final dir = entry.directory;
|
||||
if (dir == null) return false;
|
||||
// avoid string building in most cases
|
||||
return dir.startsWith(_rootAlbum) && '$dir${pContext.separator}'.startsWith(path);
|
||||
return dir.startsWith(_rootAlbum) && '$dir${pContext.separator}'.startsWith(this.path);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ class MediaStoreSource extends CollectionSource {
|
|||
bool loadTopEntriesFirst = false,
|
||||
bool canAnalyze = true,
|
||||
}) async {
|
||||
await reportService.log('$runtimeType init directory=$directory');
|
||||
if (_initState == SourceInitializationState.none) {
|
||||
await _loadEssentials();
|
||||
}
|
||||
|
@ -81,7 +82,7 @@ class MediaStoreSource extends CollectionSource {
|
|||
required bool loadTopEntriesFirst,
|
||||
required bool canAnalyze,
|
||||
}) async {
|
||||
debugPrint('$runtimeType refresh start');
|
||||
unawaited(reportService.log('$runtimeType load start'));
|
||||
final stopwatch = Stopwatch()..start();
|
||||
state = SourceState.loading;
|
||||
clearEntries();
|
||||
|
@ -90,17 +91,17 @@ class MediaStoreSource extends CollectionSource {
|
|||
if (loadTopEntriesFirst) {
|
||||
final topIds = settings.topEntryIds?.toSet();
|
||||
if (topIds != null) {
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} load ${topIds.length} top entries');
|
||||
debugPrint('$runtimeType load ${stopwatch.elapsed} load ${topIds.length} top entries');
|
||||
topEntries.addAll(await localMediaDb.loadEntriesById(topIds));
|
||||
addEntries(topEntries);
|
||||
}
|
||||
}
|
||||
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} fetch known entries');
|
||||
debugPrint('$runtimeType load ${stopwatch.elapsed} fetch known entries');
|
||||
final knownEntries = await localMediaDb.loadEntries(origin: EntryOrigins.mediaStoreContent, directory: directory);
|
||||
final knownLiveEntries = knownEntries.where((entry) => !entry.trashed).toSet();
|
||||
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} check obsolete entries');
|
||||
debugPrint('$runtimeType load ${stopwatch.elapsed} check obsolete entries');
|
||||
final knownDateByContentId = Map.fromEntries(knownLiveEntries.map((entry) => MapEntry(entry.contentId, entry.dateModifiedSecs)));
|
||||
final knownContentIds = knownDateByContentId.keys.toList();
|
||||
final removedContentIds = (await mediaStoreService.checkObsoleteContentIds(knownContentIds)).toSet();
|
||||
|
@ -112,14 +113,14 @@ class MediaStoreSource extends CollectionSource {
|
|||
knownEntries.removeAll(removedEntries);
|
||||
|
||||
// show known entries
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} add known entries');
|
||||
debugPrint('$runtimeType load ${stopwatch.elapsed} add known entries');
|
||||
// add entries without notifying, so that the collection is not refreshed
|
||||
// with items that may be hidden right away because of their metadata
|
||||
addEntries(knownEntries, notify: false);
|
||||
|
||||
await _loadVaultEntries(directory);
|
||||
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} load metadata');
|
||||
debugPrint('$runtimeType load ${stopwatch.elapsed} load metadata');
|
||||
if (directory != null) {
|
||||
final ids = knownLiveEntries.map((entry) => entry.id).toSet();
|
||||
await loadCatalogMetadata(ids: ids);
|
||||
|
@ -144,12 +145,12 @@ class MediaStoreSource extends CollectionSource {
|
|||
|
||||
// clean up obsolete entries
|
||||
if (removedEntries.isNotEmpty) {
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} remove obsolete entries');
|
||||
debugPrint('$runtimeType load ${stopwatch.elapsed} remove obsolete entries');
|
||||
await localMediaDb.removeIds(removedEntries.map((entry) => entry.id).toSet());
|
||||
}
|
||||
|
||||
// verify paths because some apps move files without updating their `last modified date`
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} check obsolete paths');
|
||||
debugPrint('$runtimeType load ${stopwatch.elapsed} check obsolete paths');
|
||||
final knownPathByContentId = Map.fromEntries(knownLiveEntries.map((entry) => MapEntry(entry.contentId, entry.path)));
|
||||
final movedContentIds = (await mediaStoreService.checkObsoletePaths(knownPathByContentId)).toSet();
|
||||
movedContentIds.forEach((contentId) {
|
||||
|
@ -161,13 +162,13 @@ class MediaStoreSource extends CollectionSource {
|
|||
final newEntries = <AvesEntry>{};
|
||||
|
||||
// recover untracked trash items
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} recover untracked entries');
|
||||
debugPrint('$runtimeType load ${stopwatch.elapsed} recover untracked entries');
|
||||
if (directory == null) {
|
||||
newEntries.addAll(await recoverUntrackedTrashItems());
|
||||
}
|
||||
|
||||
// fetch new & modified entries
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} fetch new entries');
|
||||
debugPrint('$runtimeType load ${stopwatch.elapsed} fetch new entries');
|
||||
mediaStoreService.getEntries(_safeMode, knownDateByContentId, directory: directory).listen(
|
||||
(entry) {
|
||||
// when discovering modified entry with known content ID,
|
||||
|
@ -180,7 +181,7 @@ class MediaStoreSource extends CollectionSource {
|
|||
},
|
||||
onDone: () async {
|
||||
if (newEntries.isNotEmpty) {
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} save new entries');
|
||||
debugPrint('$runtimeType load ${stopwatch.elapsed} save new entries');
|
||||
await localMediaDb.insertEntries(newEntries);
|
||||
|
||||
// TODO TLAD find duplication cause
|
||||
|
@ -203,7 +204,7 @@ class MediaStoreSource extends CollectionSource {
|
|||
updateDirectories();
|
||||
}
|
||||
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} analyze');
|
||||
debugPrint('$runtimeType load ${stopwatch.elapsed} analyze');
|
||||
Set<AvesEntry>? analysisEntries;
|
||||
final analysisIds = analysisController?.entryIds;
|
||||
if (analysisIds != null) {
|
||||
|
@ -220,8 +221,7 @@ class MediaStoreSource extends CollectionSource {
|
|||
// so we manually notify change for potential home screen filters
|
||||
notifyAlbumsChanged();
|
||||
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} done');
|
||||
unawaited(reportService.log('Source refresh complete in ${stopwatch.elapsed.inSeconds}s for ${knownEntries.length} known, ${newEntries.length} new, ${removedEntries.length} removed'));
|
||||
unawaited(reportService.log('$runtimeType load done in ${stopwatch.elapsed.inSeconds}s for ${knownEntries.length} known, ${newEntries.length} new, ${removedEntries.length} removed'));
|
||||
},
|
||||
onError: (error) => debugPrint('$runtimeType stream error=$error'),
|
||||
);
|
||||
|
@ -238,7 +238,7 @@ class MediaStoreSource extends CollectionSource {
|
|||
|
||||
state = SourceState.loading;
|
||||
|
||||
debugPrint('$runtimeType refreshUris ${changedUris.length} uris');
|
||||
unawaited(reportService.log('$runtimeType refresh start for ${changedUris.length} uris'));
|
||||
final changedUriByContentId = Map.fromEntries(changedUris.map((uri) {
|
||||
final pathSegments = Uri.parse(uri).pathSegments;
|
||||
// e.g. URI `content://media/` has no path segment
|
||||
|
@ -297,8 +297,6 @@ class MediaStoreSource extends CollectionSource {
|
|||
|
||||
invalidateAlbumFilterSummary(directories: existingDirectories);
|
||||
|
||||
state = SourceState.ready;
|
||||
|
||||
if (newEntries.isNotEmpty) {
|
||||
await localMediaDb.insertEntries(newEntries);
|
||||
|
||||
|
@ -323,6 +321,10 @@ class MediaStoreSource extends CollectionSource {
|
|||
await refreshEntries(entriesToRefresh, EntryDataType.values.toSet());
|
||||
}
|
||||
|
||||
unawaited(reportService.log('$runtimeType refresh end for ${changedUris.length} uris'));
|
||||
|
||||
state = SourceState.ready;
|
||||
|
||||
return tempUris;
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ abstract class DeviceService {
|
|||
|
||||
Future<int> getPerformanceClass();
|
||||
|
||||
Future<bool> isLocked();
|
||||
|
||||
Future<bool> isSystemFilePickerEnabled();
|
||||
|
||||
Future<void> requestMediaManagePermission();
|
||||
|
@ -89,6 +91,17 @@ class PlatformDeviceService implements DeviceService {
|
|||
return 0;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isLocked() async {
|
||||
try {
|
||||
final result = await _platform.invokeMethod('isLocked');
|
||||
if (result != null) return result as bool;
|
||||
} on PlatformException catch (e, stack) {
|
||||
await reportService.recordError(e, stack);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> isSystemFilePickerEnabled() async {
|
||||
try {
|
||||
|
|
|
@ -5,10 +5,11 @@ class ADurations {
|
|||
static const transitionMarginMillis = 20;
|
||||
|
||||
// page transition duration also available via `ModalRoute.of(context)!.transitionDuration * timeDilation`
|
||||
static const pageTransitionAnimation = Duration(milliseconds: 300 + transitionMarginMillis); // ref `transitionDuration` used in `MaterialRouteTransitionMixin`
|
||||
static const dialogTransitionAnimation = Duration(milliseconds: 150 + transitionMarginMillis); // ref `transitionDuration` used in `DialogRoute`
|
||||
static const drawerTransitionAnimation = Duration(milliseconds: 246 + transitionMarginMillis); // ref `_kBaseSettleDuration` used in `DrawerControllerState`
|
||||
static const toggleableTransitionAnimation = Duration(milliseconds: 200 + transitionMarginMillis); // ref `_kToggleDuration` used in `ToggleableStateMixin`
|
||||
static const pageTransitionExact = Duration(milliseconds: 300); // ref `transitionDuration` used in `MaterialRouteTransitionMixin`
|
||||
static const pageTransitionLoose = Duration(milliseconds: 300 + transitionMarginMillis); // ref `transitionDuration` used in `MaterialRouteTransitionMixin`
|
||||
static const dialogTransitionLoose = Duration(milliseconds: 150 + transitionMarginMillis); // ref `transitionDuration` used in `DialogRoute`
|
||||
static const drawerTransitionLoose = Duration(milliseconds: 246 + transitionMarginMillis); // ref `_kBaseSettleDuration` used in `DrawerControllerState`
|
||||
static const toggleableTransitionLoose = Duration(milliseconds: 200 + transitionMarginMillis); // ref `_kToggleDuration` used in `ToggleableStateMixin`
|
||||
|
||||
// common animations
|
||||
static const sweeperOpacityAnimation = Duration(milliseconds: 150);
|
||||
|
|
|
@ -32,6 +32,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
|
|||
import 'package:aves/widgets/common/providers/durations_provider.dart';
|
||||
import 'package:aves/widgets/common/providers/highlight_info_provider.dart';
|
||||
import 'package:aves/widgets/common/providers/media_query_data_provider.dart';
|
||||
import 'package:aves/widgets/common/providers/viewer_entry_provider.dart';
|
||||
import 'package:aves/widgets/home_page.dart';
|
||||
import 'package:aves/widgets/navigation/tv_page_transitions.dart';
|
||||
import 'package:aves/widgets/navigation/tv_rail.dart';
|
||||
|
@ -224,6 +225,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
Provider<TvRailController>.value(value: _tvRailController),
|
||||
DurationsProvider(),
|
||||
HighlightInfoProvider(),
|
||||
ViewerEntryProvider(),
|
||||
],
|
||||
child: NotificationListener<PopExitNotification>(
|
||||
onNotification: (notification) {
|
||||
|
@ -411,7 +413,7 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
@override
|
||||
void didHaveMemoryPressure() {
|
||||
super.didHaveMemoryPressure();
|
||||
reportService.log('App memory pressure');
|
||||
debugPrint('App memory pressure');
|
||||
imageCache.clear();
|
||||
}
|
||||
|
||||
|
|
|
@ -691,7 +691,7 @@ class _CollectionAppBarState extends State<CollectionAppBar> with SingleTickerPr
|
|||
routeSettings: const RouteSettings(name: TileViewDialog.routeName),
|
||||
);
|
||||
// wait for the dialog to hide as applying the change may block the UI
|
||||
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
|
||||
await Future.delayed(ADurations.dialogTransitionLoose * timeDilation);
|
||||
if (value != null && initialValue != value) {
|
||||
settings.collectionSortFactor = value.$1!;
|
||||
settings.collectionSectionFactor = value.$2!;
|
||||
|
|
|
@ -40,6 +40,7 @@ import 'package:aves/widgets/common/identity/buttons/outlined_button.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';
|
||||
import 'package:aves/widgets/common/providers/viewer_entry_provider.dart';
|
||||
import 'package:aves/widgets/common/thumbnail/decorated.dart';
|
||||
import 'package:aves/widgets/common/thumbnail/image.dart';
|
||||
import 'package:aves/widgets/common/thumbnail/notifications.dart';
|
||||
|
@ -49,6 +50,7 @@ import 'package:aves/widgets/viewer/entry_viewer_page.dart';
|
|||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
@ -116,6 +118,12 @@ class _CollectionGridContentState extends State<_CollectionGridContent> {
|
|||
final ValueNotifier<bool> _isScrollingNotifier = ValueNotifier(false);
|
||||
final ValueNotifier<AppMode> _selectingAppModeNotifier = ValueNotifier(AppMode.pickFilteredMediaInternal);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => context.read<ViewerEntryNotifier>().value = null);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_focusedItemNotifier.dispose();
|
||||
|
@ -238,9 +246,12 @@ class _CollectionGridContentState extends State<_CollectionGridContent> {
|
|||
);
|
||||
}
|
||||
|
||||
void _goToViewer(CollectionLens collection, AvesEntry entry) {
|
||||
Future<void> _goToViewer(CollectionLens collection, AvesEntry entry) async {
|
||||
// track viewer entry for dynamic hero placeholder
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) => context.read<ViewerEntryNotifier>().value = entry);
|
||||
|
||||
final selection = context.read<Selection<AvesEntry>>();
|
||||
Navigator.maybeOf(context)?.push(
|
||||
await Navigator.maybeOf(context)?.push(
|
||||
TransparentMaterialPageRoute(
|
||||
settings: const RouteSettings(name: EntryViewerPage.routeName),
|
||||
pageBuilder: (context, a, sa) {
|
||||
|
@ -266,6 +277,14 @@ class _CollectionGridContentState extends State<_CollectionGridContent> {
|
|||
},
|
||||
),
|
||||
);
|
||||
|
||||
// reset track viewer entry
|
||||
final animate = context.read<Settings>().animate;
|
||||
if (animate) {
|
||||
// TODO TLAD fix timing when transition is incomplete, e.g. when going back while going to the viewer
|
||||
await Future.delayed(ADurations.pageTransitionExact * timeDilation);
|
||||
}
|
||||
context.read<ViewerEntryNotifier>().value = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -510,7 +510,7 @@ class EntrySetActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAware
|
|||
if (confirmed == null || !confirmed) return null;
|
||||
|
||||
// wait for the dialog to hide as applying the change may block the UI
|
||||
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
|
||||
await Future.delayed(ADurations.dialogTransitionLoose * timeDilation);
|
||||
return supported;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,8 +6,10 @@ import 'package:aves/services/intent_service.dart';
|
|||
import 'package:aves/widgets/collection/grid/list_details.dart';
|
||||
import 'package:aves/widgets/collection/grid/list_details_theme.dart';
|
||||
import 'package:aves/widgets/common/grid/scaling.dart';
|
||||
import 'package:aves/widgets/common/providers/viewer_entry_provider.dart';
|
||||
import 'package:aves/widgets/common/thumbnail/decorated.dart';
|
||||
import 'package:aves/widgets/common/thumbnail/notifications.dart';
|
||||
import 'package:aves/widgets/viewer/hero.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
@ -62,10 +64,7 @@ class InteractiveTile extends StatelessWidget {
|
|||
selectable: true,
|
||||
highlightable: true,
|
||||
isScrollingNotifier: isScrollingNotifier,
|
||||
// hero tag should include a collection identifier, so that it animates
|
||||
// between different views of the entry in the same collection (e.g. thumbnails <-> viewer)
|
||||
// but not between different collection instances, even with the same attributes (e.g. reloading collection page via drawer)
|
||||
heroTagger: () => Object.hashAll([collection.id, entry.id]),
|
||||
heroTagger: () => EntryHeroInfo(collection, entry).tag,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -126,5 +125,20 @@ class Tile extends StatelessWidget {
|
|||
selectable: selectable,
|
||||
highlightable: highlightable,
|
||||
heroTagger: heroTagger,
|
||||
// do not use a hero placeholder but hide the thumbnail matching the viewer entry,
|
||||
// so that it can hero out on an entry and come back with a hero to a different entry
|
||||
heroPlaceholderBuilder: (context, heroSize, child) => child,
|
||||
imageDecorator: (context, child) {
|
||||
return Selector<ViewerEntryNotifier, bool>(
|
||||
selector: (context, v) => v.value == entry,
|
||||
builder: (context, isViewerEntry, child) {
|
||||
return Visibility.maintain(
|
||||
visible: !isViewerEntry,
|
||||
child: child!,
|
||||
);
|
||||
},
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
|
|
@ -81,7 +81,7 @@ mixin FeedbackMixin {
|
|||
final margin = (marginComputer ?? snackBarMarginDefault).call(context);
|
||||
return AnimatedPadding(
|
||||
padding: margin,
|
||||
duration: ADurations.pageTransitionAnimation,
|
||||
duration: ADurations.pageTransitionLoose,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
|
|
17
lib/widgets/common/providers/viewer_entry_provider.dart
Normal file
17
lib/widgets/common/providers/viewer_entry_provider.dart
Normal file
|
@ -0,0 +1,17 @@
|
|||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class ViewerEntryProvider extends ListenableProvider<ViewerEntryNotifier> {
|
||||
ViewerEntryProvider({
|
||||
super.key,
|
||||
super.child,
|
||||
}) : super(
|
||||
create: (context) => ViewerEntryNotifier(null),
|
||||
dispose: (context, value) => value.dispose(),
|
||||
);
|
||||
}
|
||||
|
||||
class ViewerEntryNotifier extends ValueNotifier<AvesEntry?> {
|
||||
ViewerEntryNotifier(super.value);
|
||||
}
|
|
@ -76,7 +76,7 @@ class _SearchPageState extends State<SearchPage> {
|
|||
return;
|
||||
}
|
||||
widget.animation.removeStatusListener(_onAnimationStatusChanged);
|
||||
Future.delayed(ADurations.pageTransitionAnimation * timeDilation).then((_) {
|
||||
Future.delayed(ADurations.pageTransitionLoose * timeDilation).then((_) {
|
||||
if (!mounted) return;
|
||||
_searchFieldFocusNode.requestFocus();
|
||||
});
|
||||
|
|
|
@ -13,6 +13,8 @@ class DecoratedThumbnail extends StatelessWidget {
|
|||
final ValueNotifier<bool>? cancellableNotifier;
|
||||
final bool isMosaic, selectable, highlightable;
|
||||
final Object? Function()? heroTagger;
|
||||
final HeroPlaceholderBuilder? heroPlaceholderBuilder;
|
||||
final TransitionBuilder? imageDecorator;
|
||||
|
||||
static Color borderColor(BuildContext context) => Theme.of(context).dividerColor;
|
||||
|
||||
|
@ -27,6 +29,8 @@ class DecoratedThumbnail extends StatelessWidget {
|
|||
this.selectable = true,
|
||||
this.highlightable = true,
|
||||
this.heroTagger,
|
||||
this.heroPlaceholderBuilder,
|
||||
this.imageDecorator,
|
||||
});
|
||||
|
||||
@override
|
||||
|
@ -50,12 +54,13 @@ class DecoratedThumbnail extends StatelessWidget {
|
|||
isMosaic: isMosaic,
|
||||
cancellableNotifier: cancellableNotifier,
|
||||
heroTag: heroTagger?.call(),
|
||||
heroPlaceholderBuilder: heroPlaceholderBuilder,
|
||||
);
|
||||
|
||||
child = Stack(
|
||||
fit: StackFit.passthrough,
|
||||
children: [
|
||||
child,
|
||||
imageDecorator?.call(context, child) ?? child,
|
||||
ThumbnailEntryOverlay(entry: entry),
|
||||
if (selectable) ...[
|
||||
GridItemSelectionOverlay<AvesEntry>(
|
||||
|
|
|
@ -25,6 +25,7 @@ class ThumbnailImage extends StatefulWidget {
|
|||
final bool showLoadingBackground;
|
||||
final ValueNotifier<bool>? cancellableNotifier;
|
||||
final Object? heroTag;
|
||||
final HeroPlaceholderBuilder? heroPlaceholderBuilder;
|
||||
|
||||
const ThumbnailImage({
|
||||
super.key,
|
||||
|
@ -37,6 +38,7 @@ class ThumbnailImage extends StatefulWidget {
|
|||
this.showLoadingBackground = true,
|
||||
this.cancellableNotifier,
|
||||
this.heroTag,
|
||||
this.heroPlaceholderBuilder,
|
||||
});
|
||||
|
||||
@override
|
||||
|
@ -261,11 +263,12 @@ class _ThumbnailImageState extends State<ThumbnailImage> {
|
|||
},
|
||||
);
|
||||
|
||||
if (animate && widget.heroTag != null) {
|
||||
final heroTag = widget.heroTag;
|
||||
if (animate && heroTag != null) {
|
||||
final background = settings.imageBackground;
|
||||
final backgroundColor = background.isColor ? background.color : null;
|
||||
image = Hero(
|
||||
tag: widget.heroTag!,
|
||||
tag: heroTag,
|
||||
flightShuttleBuilder: (flight, animation, direction, fromHero, toHero) {
|
||||
Widget child = TransitionImage(
|
||||
image: entry.bestCachedThumbnail,
|
||||
|
@ -282,6 +285,7 @@ class _ThumbnailImageState extends State<ThumbnailImage> {
|
|||
}
|
||||
return child;
|
||||
},
|
||||
placeholderBuilder: widget.heroPlaceholderBuilder,
|
||||
transitionOnUserGestures: true,
|
||||
child: image,
|
||||
);
|
||||
|
@ -296,9 +300,10 @@ class _ThumbnailImageState extends State<ThumbnailImage> {
|
|||
extent: extent,
|
||||
);
|
||||
|
||||
if (animate && widget.heroTag != null) {
|
||||
final heroTag = widget.heroTag;
|
||||
if (animate && heroTag != null) {
|
||||
child = Hero(
|
||||
tag: widget.heroTag!,
|
||||
tag: heroTag,
|
||||
flightShuttleBuilder: (flight, animation, direction, fromHero, toHero) {
|
||||
return MediaQueryDataProvider(
|
||||
child: DefaultTextStyle(
|
||||
|
|
|
@ -25,14 +25,11 @@ class _DebugDeviceSectionState extends State<DebugDeviceSection> with AutomaticK
|
|||
'packageVersion': device.packageVersion,
|
||||
'userAgent': device.userAgent,
|
||||
'canAuthenticateUser': '${device.canAuthenticateUser}',
|
||||
'canGrantDirectoryAccess': '${device.canGrantDirectoryAccess}',
|
||||
'canPinShortcut': '${device.canPinShortcut}',
|
||||
'canRenderFlagEmojis': '${device.canRenderFlagEmojis}',
|
||||
'canRenderSubdivisionFlagEmojis': '${device.canRenderSubdivisionFlagEmojis}',
|
||||
'canRequestManageMedia': '${device.canRequestManageMedia}',
|
||||
'canSetLockScreenWallpaper': '${device.canSetLockScreenWallpaper}',
|
||||
'canUseCrypto': '${device.canUseCrypto}',
|
||||
'canUseVaults': '${device.canUseVaults}',
|
||||
'hasGeocoder': '${device.hasGeocoder}',
|
||||
'isDynamicColorAvailable': '${device.isDynamicColorAvailable}',
|
||||
'isTelevision': '${device.isTelevision}',
|
||||
|
|
|
@ -42,11 +42,9 @@ class _EditVaultDialogState extends State<EditVaultDialog> with FeedbackMixin, V
|
|||
|
||||
final List<VaultLockType> _lockTypeOptions = [
|
||||
if (device.canAuthenticateUser) VaultLockType.system,
|
||||
if (device.canUseCrypto) ...[
|
||||
VaultLockType.pattern,
|
||||
VaultLockType.pin,
|
||||
VaultLockType.password,
|
||||
],
|
||||
];
|
||||
|
||||
VaultDetails? get initialDetails => widget.initialDetails;
|
||||
|
|
|
@ -248,7 +248,7 @@ class _AlbumPickPageState extends State<_AlbumPickPage> {
|
|||
if (directory == null) return;
|
||||
|
||||
// wait for the dialog to hide as applying the change may block the UI
|
||||
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
|
||||
await Future.delayed(ADurations.dialogTransitionLoose * timeDilation);
|
||||
|
||||
_pickAlbum(directory);
|
||||
}
|
||||
|
@ -270,7 +270,7 @@ class _AlbumPickPageState extends State<_AlbumPickPage> {
|
|||
if (details == null) return;
|
||||
|
||||
// wait for the dialog to hide as applying the change may block the UI
|
||||
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
|
||||
await Future.delayed(ADurations.dialogTransitionLoose * timeDilation);
|
||||
|
||||
await vaults.create(details);
|
||||
_pickAlbum(details.path);
|
||||
|
|
|
@ -77,7 +77,7 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin
|
|||
|
||||
if (ExtraEntryMapStyle.isHeavy(settings.mapStyle)) {
|
||||
_isPageAnimatingNotifier = ValueNotifier(true);
|
||||
Future.delayed(ADurations.pageTransitionAnimation * timeDilation).then((_) {
|
||||
Future.delayed(ADurations.pageTransitionLoose * timeDilation).then((_) {
|
||||
if (!mounted) return;
|
||||
_isPageAnimatingNotifier.value = false;
|
||||
});
|
||||
|
|
|
@ -14,7 +14,7 @@ Future<void> showSelectionDialog<T>({
|
|||
routeSettings: const RouteSettings(name: AvesSingleSelectionDialog.routeName),
|
||||
);
|
||||
// wait for the dialog to hide as applying the change may block the UI
|
||||
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
|
||||
await Future.delayed(ADurations.dialogTransitionLoose * timeDilation);
|
||||
if (value != null) {
|
||||
onSelection(value);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:aves/app_mode.dart';
|
||||
import 'package:aves/model/device.dart';
|
||||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/model/filters/album.dart';
|
||||
import 'package:aves/model/filters/filters.dart';
|
||||
|
@ -78,12 +77,10 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> with
|
|||
final selectedSingleItem = selectedFilters.length == 1;
|
||||
final isMain = appMode == AppMode.main;
|
||||
|
||||
final canCreate = !settings.isReadOnly && appMode.canCreateFilter && !isSelecting;
|
||||
switch (action) {
|
||||
case ChipSetAction.createAlbum:
|
||||
return canCreate;
|
||||
case ChipSetAction.createVault:
|
||||
return canCreate && device.canUseVaults;
|
||||
return !settings.isReadOnly && appMode.canCreateFilter && !isSelecting;
|
||||
case ChipSetAction.delete:
|
||||
case ChipSetAction.rename:
|
||||
return isMain && isSelecting && !settings.isReadOnly;
|
||||
|
@ -190,7 +187,7 @@ class AlbumChipSetActionDelegate extends ChipSetActionDelegate<AlbumFilter> with
|
|||
routeSettings: const RouteSettings(name: TileViewDialog.routeName),
|
||||
);
|
||||
// wait for the dialog to hide as applying the change may block the UI
|
||||
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
|
||||
await Future.delayed(ADurations.dialogTransitionLoose * timeDilation);
|
||||
if (value != null && initialValue != value) {
|
||||
sortFactor = value.$1!;
|
||||
settings.albumGroupFactor = value.$2!;
|
||||
|
|
|
@ -250,7 +250,7 @@ abstract class ChipSetActionDelegate<T extends CollectionFilter> with FeedbackMi
|
|||
routeSettings: const RouteSettings(name: TileViewDialog.routeName),
|
||||
);
|
||||
// wait for the dialog to hide as applying the change may block the UI
|
||||
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
|
||||
await Future.delayed(ADurations.dialogTransitionLoose * timeDilation);
|
||||
if (value != null && initialValue != value) {
|
||||
sortFactor = value.$1!;
|
||||
tileLayout = value.$3!;
|
||||
|
|
|
@ -90,13 +90,15 @@ class _HomePageState extends State<HomePage> {
|
|||
}
|
||||
|
||||
var appMode = AppMode.main;
|
||||
var error = false;
|
||||
final intentData = widget.intentData ?? await IntentService.getIntentData();
|
||||
final safeMode = intentData[IntentDataKeys.safeMode] ?? false;
|
||||
final intentAction = intentData[IntentDataKeys.action];
|
||||
final safeMode = (intentData[IntentDataKeys.safeMode] as bool?) ?? false;
|
||||
final intentAction = intentData[IntentDataKeys.action] as String?;
|
||||
_initialFilters = null;
|
||||
_initialExplorerPath = null;
|
||||
_secureUris = null;
|
||||
|
||||
await availability.onNewIntent();
|
||||
await androidFileUtils.init();
|
||||
if (!{
|
||||
IntentActions.edit,
|
||||
|
@ -109,61 +111,22 @@ class _HomePageState extends State<HomePage> {
|
|||
|
||||
if (intentData.values.whereNotNull().isNotEmpty) {
|
||||
await reportService.log('Intent data=$intentData');
|
||||
var intentUri = intentData[IntentDataKeys.uri] as String?;
|
||||
final intentMimeType = intentData[IntentDataKeys.mimeType] as String?;
|
||||
|
||||
switch (intentAction) {
|
||||
case IntentActions.view:
|
||||
case IntentActions.widgetOpen:
|
||||
String? uri, mimeType;
|
||||
final widgetId = intentData[IntentDataKeys.widgetId];
|
||||
if (widgetId != null) {
|
||||
// widget settings may be modified in a different process after channel setup
|
||||
await settings.reload();
|
||||
final page = settings.getWidgetOpenPage(widgetId);
|
||||
switch (page) {
|
||||
case WidgetOpenPage.home:
|
||||
case WidgetOpenPage.updateWidget:
|
||||
break;
|
||||
case WidgetOpenPage.collection:
|
||||
_initialFilters = settings.getWidgetCollectionFilters(widgetId);
|
||||
case WidgetOpenPage.viewer:
|
||||
uri = settings.getWidgetUri(widgetId);
|
||||
}
|
||||
unawaited(WidgetService.update(widgetId));
|
||||
} else {
|
||||
uri = intentData[IntentDataKeys.uri];
|
||||
mimeType = intentData[IntentDataKeys.mimeType];
|
||||
}
|
||||
_secureUris = intentData[IntentDataKeys.secureUris];
|
||||
if (uri != null) {
|
||||
_viewerEntry = await _initViewerEntry(
|
||||
uri: uri,
|
||||
mimeType: mimeType,
|
||||
);
|
||||
if (_viewerEntry != null) {
|
||||
appMode = AppMode.view;
|
||||
}
|
||||
}
|
||||
_secureUris = (intentData[IntentDataKeys.secureUris] as List?)?.cast<String>();
|
||||
case IntentActions.edit:
|
||||
_viewerEntry = await _initViewerEntry(
|
||||
uri: intentData[IntentDataKeys.uri],
|
||||
mimeType: intentData[IntentDataKeys.mimeType],
|
||||
);
|
||||
if (_viewerEntry != null) {
|
||||
appMode = AppMode.edit;
|
||||
}
|
||||
case IntentActions.setWallpaper:
|
||||
_viewerEntry = await _initViewerEntry(
|
||||
uri: intentData[IntentDataKeys.uri],
|
||||
mimeType: intentData[IntentDataKeys.mimeType],
|
||||
);
|
||||
if (_viewerEntry != null) {
|
||||
appMode = AppMode.setWallpaper;
|
||||
}
|
||||
case IntentActions.pickItems:
|
||||
// TODO TLAD apply pick mimetype(s)
|
||||
// some apps define multiple types, separated by a space (maybe other signs too, like `,` `;`?)
|
||||
String? pickMimeTypes = intentData[IntentDataKeys.mimeType];
|
||||
final multiple = intentData[IntentDataKeys.allowMultiple] ?? false;
|
||||
debugPrint('pick mimeType=$pickMimeTypes multiple=$multiple');
|
||||
final multiple = (intentData[IntentDataKeys.allowMultiple] as bool?) ?? false;
|
||||
debugPrint('pick mimeType=$intentMimeType multiple=$multiple');
|
||||
appMode = multiple ? AppMode.pickMultipleMediaExternal : AppMode.pickSingleMediaExternal;
|
||||
case IntentActions.pickCollectionFilters:
|
||||
appMode = AppMode.pickCollectionFiltersExternal;
|
||||
|
@ -174,23 +137,64 @@ class _HomePageState extends State<HomePage> {
|
|||
_initialRouteName = ScreenSaverSettingsPage.routeName;
|
||||
case IntentActions.search:
|
||||
_initialRouteName = SearchPage.routeName;
|
||||
_initialSearchQuery = intentData[IntentDataKeys.query];
|
||||
_initialSearchQuery = intentData[IntentDataKeys.query] as String?;
|
||||
case IntentActions.widgetSettings:
|
||||
_initialRouteName = HomeWidgetSettingsPage.routeName;
|
||||
_widgetId = intentData[IntentDataKeys.widgetId] ?? 0;
|
||||
_widgetId = (intentData[IntentDataKeys.widgetId] as int?) ?? 0;
|
||||
case IntentActions.widgetOpen:
|
||||
final widgetId = intentData[IntentDataKeys.widgetId] as int?;
|
||||
if (widgetId == null) {
|
||||
error = true;
|
||||
} else {
|
||||
// widget settings may be modified in a different process after channel setup
|
||||
await settings.reload();
|
||||
final page = settings.getWidgetOpenPage(widgetId);
|
||||
switch (page) {
|
||||
case WidgetOpenPage.collection:
|
||||
_initialFilters = settings.getWidgetCollectionFilters(widgetId);
|
||||
case WidgetOpenPage.viewer:
|
||||
appMode = AppMode.view;
|
||||
intentUri = settings.getWidgetUri(widgetId);
|
||||
case WidgetOpenPage.home:
|
||||
case WidgetOpenPage.updateWidget:
|
||||
break;
|
||||
}
|
||||
unawaited(WidgetService.update(widgetId));
|
||||
}
|
||||
default:
|
||||
// do not use 'route' as extra key, as the Flutter framework acts on it
|
||||
final extraRoute = intentData[IntentDataKeys.page];
|
||||
final extraRoute = intentData[IntentDataKeys.page] as String?;
|
||||
if (allowedShortcutRoutes.contains(extraRoute)) {
|
||||
_initialRouteName = extraRoute;
|
||||
}
|
||||
}
|
||||
if (_initialFilters == null) {
|
||||
final extraFilters = intentData[IntentDataKeys.filters];
|
||||
_initialFilters = extraFilters != null ? (extraFilters as List).cast<String>().map(CollectionFilter.fromJson).whereNotNull().toSet() : null;
|
||||
final extraFilters = (intentData[IntentDataKeys.filters] as List?)?.cast<String>();
|
||||
_initialFilters = extraFilters?.map(CollectionFilter.fromJson).whereNotNull().toSet();
|
||||
}
|
||||
_initialExplorerPath = intentData[IntentDataKeys.explorerPath];
|
||||
_initialExplorerPath = intentData[IntentDataKeys.explorerPath] as String?;
|
||||
|
||||
switch (appMode) {
|
||||
case AppMode.view:
|
||||
case AppMode.edit:
|
||||
case AppMode.setWallpaper:
|
||||
if (intentUri != null) {
|
||||
_viewerEntry = await _initViewerEntry(
|
||||
uri: intentUri,
|
||||
mimeType: intentMimeType,
|
||||
);
|
||||
}
|
||||
error = _viewerEntry == null;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (error) {
|
||||
debugPrint('Failed to init app mode=$appMode for intent data=$intentData. Fallback to main mode.');
|
||||
appMode = AppMode.main;
|
||||
}
|
||||
|
||||
context.read<ValueNotifier<AppMode>>().value = appMode;
|
||||
unawaited(reportService.setCustomKey('app_mode', appMode.toString()));
|
||||
debugPrint('Storage check complete in ${stopwatch.elapsed.inMilliseconds}ms');
|
||||
|
|
|
@ -118,7 +118,7 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin
|
|||
|
||||
if (ExtraEntryMapStyle.isHeavy(settings.mapStyle)) {
|
||||
_isPageAnimatingNotifier.value = true;
|
||||
Future.delayed(ADurations.pageTransitionAnimation * timeDilation).then((_) {
|
||||
Future.delayed(ADurations.pageTransitionLoose * timeDilation).then((_) {
|
||||
if (!mounted) return;
|
||||
_isPageAnimatingNotifier.value = false;
|
||||
});
|
||||
|
@ -142,7 +142,7 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin
|
|||
_subscriptions.add(openingCollection.source.eventBus.on<CatalogMetadataChangedEvent>().listen((e) => _updateRegionCollection()));
|
||||
|
||||
_selectedIndexNotifier.addListener(_onThumbnailIndexChanged);
|
||||
Future.delayed(ADurations.pageTransitionAnimation * timeDilation + const Duration(seconds: 1), () {
|
||||
Future.delayed(ADurations.pageTransitionLoose * timeDilation + const Duration(seconds: 1), () {
|
||||
final regionEntries = regionCollection?.sortedEntries ?? [];
|
||||
final initialEntry = widget.initialEntry ?? regionEntries.firstOrNull;
|
||||
if (initialEntry != null) {
|
||||
|
|
|
@ -6,6 +6,7 @@ import 'package:aves/widgets/common/extensions/build_context.dart';
|
|||
import 'package:aves/widgets/common/identity/empty.dart';
|
||||
import 'package:aves/widgets/common/thumbnail/scroller.dart';
|
||||
import 'package:aves/widgets/map/info_row.dart';
|
||||
import 'package:aves/widgets/viewer/hero.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class MapEntryScroller extends StatefulWidget {
|
||||
|
@ -85,7 +86,7 @@ class _MapEntryScrollerState extends State<MapEntryScroller> {
|
|||
entryBuilder: (index) => index < regionEntries.length ? regionEntries[index] : null,
|
||||
indexNotifier: widget.selectedIndexNotifier,
|
||||
onTap: widget.onTap,
|
||||
heroTagger: (entry) => Object.hashAll([regionCollection?.id, entry.id]),
|
||||
heroTagger: (entry) => EntryHeroInfo(regionCollection, entry).tag,
|
||||
highlightable: true,
|
||||
showLocation: false,
|
||||
);
|
||||
|
|
|
@ -115,7 +115,7 @@ class _AppDrawerState extends State<AppDrawer> {
|
|||
|
||||
Future<void> goTo(String routeName, WidgetBuilder pageBuilder) async {
|
||||
Navigator.maybeOf(context)?.pop();
|
||||
await Future.delayed(ADurations.drawerTransitionAnimation);
|
||||
await Future.delayed(ADurations.drawerTransitionLoose);
|
||||
await Navigator.maybeOf(context)?.push(MaterialPageRoute(
|
||||
settings: RouteSettings(name: routeName),
|
||||
builder: pageBuilder,
|
||||
|
|
|
@ -68,7 +68,7 @@ class SettingsSwitchListTile extends StatelessWidget {
|
|||
Expanded(child: titleWidget),
|
||||
AnimatedOpacity(
|
||||
opacity: current ? 1 : disabledOpacity,
|
||||
duration: ADurations.toggleableTransitionAnimation,
|
||||
duration: ADurations.toggleableTransitionLoose,
|
||||
child: trailing,
|
||||
),
|
||||
],
|
||||
|
|
|
@ -32,7 +32,7 @@ class LocaleTile extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
// wait for the dialog to hide as applying the change may block the UI
|
||||
await Future.delayed(ADurations.pageTransitionAnimation * timeDilation);
|
||||
await Future.delayed(ADurations.pageTransitionLoose * timeDilation);
|
||||
if (value != null) {
|
||||
settings.locale = value == systemLocaleOption ? null : value;
|
||||
}
|
||||
|
|
|
@ -182,7 +182,7 @@ class _FilePickerPageState extends State<FilePickerPage> {
|
|||
title: Text(v.getDescription(context)),
|
||||
onTap: () async {
|
||||
Navigator.maybeOf(context)?.pop();
|
||||
await Future.delayed(ADurations.drawerTransitionAnimation);
|
||||
await Future.delayed(ADurations.drawerTransitionLoose);
|
||||
_goTo(v.path);
|
||||
setState(() {});
|
||||
},
|
||||
|
|
|
@ -185,7 +185,7 @@ class _HiddenPaths extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
// wait for the dialog to hide as applying the change may block the UI
|
||||
await Future.delayed(ADurations.pageTransitionAnimation * timeDilation);
|
||||
await Future.delayed(ADurations.pageTransitionLoose * timeDilation);
|
||||
if (path != null && path.isNotEmpty) {
|
||||
settings.changeFilterVisibility({PathFilter(path)}, false);
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ class PrivacySection extends SettingsSection {
|
|||
SettingsTilePrivacySaveSearchHistory(),
|
||||
if (!settings.useTvLayout) SettingsTilePrivacyEnableBin(),
|
||||
SettingsTilePrivacyHiddenItems(),
|
||||
if (!settings.useTvLayout && device.canGrantDirectoryAccess) SettingsTilePrivacyStorageAccess(),
|
||||
if (!settings.useTvLayout) SettingsTilePrivacyStorageAccess(),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ class _StatsPageState extends State<StatsPage> with FeedbackMixin, VaultAwareMix
|
|||
super.initState();
|
||||
|
||||
_isPageAnimatingNotifier = ValueNotifier(true);
|
||||
Future.delayed(ADurations.pageTransitionAnimation * timeDilation).then((_) {
|
||||
Future.delayed(ADurations.pageTransitionLoose * timeDilation).then((_) {
|
||||
if (!mounted) return;
|
||||
_isPageAnimatingNotifier.value = false;
|
||||
});
|
||||
|
|
|
@ -159,6 +159,10 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
|||
case EntryAction.convertMotionPhotoToStillImage:
|
||||
case EntryAction.viewMotionPhotoVideo:
|
||||
return _metadataActionDelegate.canApply(targetEntry, action);
|
||||
case EntryAction.convert:
|
||||
case EntryAction.copy:
|
||||
case EntryAction.move:
|
||||
return !availability.isLocked;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
@ -471,7 +475,7 @@ class EntryActionDelegate with FeedbackMixin, PermissionAwareMixin, SizeAwareMix
|
|||
if (newName == null || newName.isEmpty || newName == targetEntry.filenameWithoutExtension) return;
|
||||
|
||||
// wait for the dialog to hide as applying the change may block the UI
|
||||
await Future.delayed(ADurations.dialogTransitionAnimation * timeDilation);
|
||||
await Future.delayed(ADurations.dialogTransitionLoose * timeDilation);
|
||||
await rename(
|
||||
context,
|
||||
entriesToNewName: {targetEntry: '$newName${targetEntry.extension}'},
|
||||
|
|
|
@ -18,6 +18,7 @@ import 'package:aves/widgets/aves_app.dart';
|
|||
import 'package:aves/widgets/collection/collection_page.dart';
|
||||
import 'package:aves/widgets/common/action_mixins/feedback.dart';
|
||||
import 'package:aves/widgets/common/basic/insets.dart';
|
||||
import 'package:aves/widgets/common/providers/viewer_entry_provider.dart';
|
||||
import 'package:aves/widgets/viewer/action/video_action_delegate.dart';
|
||||
import 'package:aves/widgets/viewer/controls/controller.dart';
|
||||
import 'package:aves/widgets/viewer/controls/notifications.dart';
|
||||
|
@ -75,7 +76,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
|
|||
late Animation<Offset> _overlayTopOffset;
|
||||
EdgeInsets? _frozenViewInsets, _frozenViewPadding;
|
||||
late VideoActionDelegate _videoActionDelegate;
|
||||
final ValueNotifier<HeroInfo?> _heroInfoNotifier = ValueNotifier(null);
|
||||
final ValueNotifier<EntryHeroInfo?> _heroInfoNotifier = ValueNotifier(null);
|
||||
bool _isEntryTracked = true;
|
||||
Timer? _overlayHidingTimer, _appInactiveReactionTimer;
|
||||
|
||||
|
@ -116,7 +117,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
|
|||
final initialEntry = widget.initialEntry;
|
||||
final entry = entries.firstWhereOrNull((entry) => entry.id == initialEntry.id) ?? entries.firstOrNull;
|
||||
// opening hero, with viewer as target
|
||||
_heroInfoNotifier.value = HeroInfo(collection?.id, entry);
|
||||
_heroInfoNotifier.value = EntryHeroInfo(collection, entry);
|
||||
entryNotifier = viewerController.entryNotifier;
|
||||
entryNotifier.value = entry;
|
||||
_currentEntryIndex = max(0, entry != null ? entries.indexOf(entry) : -1);
|
||||
|
@ -224,7 +225,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
|
|||
|
||||
_onPopInvoked();
|
||||
},
|
||||
child: ValueListenableProvider<HeroInfo?>.value(
|
||||
child: ValueListenableProvider<EntryHeroInfo?>.value(
|
||||
value: _heroInfoNotifier,
|
||||
child: NotificationListener(
|
||||
onNotification: _handleNotification,
|
||||
|
@ -412,17 +413,17 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
|
|||
}
|
||||
|
||||
Widget _buildSlideshowBottomOverlay(Size availableSize) {
|
||||
return SizedBox.fromSize(
|
||||
size: availableSize,
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional.bottomEnd,
|
||||
child: TooltipTheme(
|
||||
return TooltipTheme(
|
||||
data: TooltipTheme.of(context).copyWith(
|
||||
preferBelow: false,
|
||||
),
|
||||
child: SlideshowButtons(
|
||||
child: Align(
|
||||
alignment: AlignmentDirectional.bottomEnd,
|
||||
child: SlideshowBottomOverlay(
|
||||
animationController: _overlayAnimationController,
|
||||
),
|
||||
availableSize: availableSize,
|
||||
viewInsets: _frozenViewInsets,
|
||||
viewPadding: _frozenViewPadding,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -867,7 +868,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
|
|||
}
|
||||
|
||||
// closing hero, with viewer as source
|
||||
final heroInfo = HeroInfo(collection?.id, entryNotifier.value);
|
||||
final heroInfo = EntryHeroInfo(collection, entryNotifier.value);
|
||||
if (_heroInfoNotifier.value != heroInfo) {
|
||||
_heroInfoNotifier.value = heroInfo;
|
||||
// we post closing the viewer page so that hero animation source is ready
|
||||
|
@ -900,6 +901,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
|
|||
predicate: (v) => v < 1,
|
||||
animate: false,
|
||||
);
|
||||
context.read<ViewerEntryNotifier>().value = entry;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,17 +1,20 @@
|
|||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/model/source/collection_lens.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
@immutable
|
||||
class HeroInfo extends Equatable {
|
||||
class EntryHeroInfo extends Equatable {
|
||||
// hero tag should include a collection identifier, so that it animates
|
||||
// between different views of the entry in the same collection (e.g. thumbnails <-> viewer)
|
||||
// but not between different collection instances, even with the same attributes (e.g. reloading collection page via drawer)
|
||||
final int? collectionId;
|
||||
final CollectionLens? collection;
|
||||
final AvesEntry? entry;
|
||||
|
||||
@override
|
||||
List<Object?> get props => [collectionId, entry?.uri];
|
||||
List<Object?> get props => [collection?.id, entry?.uri];
|
||||
|
||||
const HeroInfo(this.collectionId, this.entry);
|
||||
const EntryHeroInfo(this.collection, this.entry);
|
||||
|
||||
int get tag => Object.hashAll([collection?.id, entry?.uri]);
|
||||
}
|
||||
|
|
|
@ -123,7 +123,7 @@ class _InfoPageState extends State<InfoPage> {
|
|||
ShowImageNotification().dispatch(context);
|
||||
_scrollController.animateTo(
|
||||
0,
|
||||
duration: ADurations.pageTransitionAnimation,
|
||||
duration: ADurations.pageTransitionLoose,
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
}
|
||||
|
@ -276,7 +276,7 @@ class _InfoPageContentState extends State<_InfoPageContent> {
|
|||
}
|
||||
|
||||
void _onActionDelegateEvent(ActionEvent<EntryAction> event) {
|
||||
Future.delayed(ADurations.dialogTransitionAnimation).then((_) {
|
||||
Future.delayed(ADurations.dialogTransitionLoose).then((_) {
|
||||
if (event is ActionStartedEvent) {
|
||||
_isEditingMetadataNotifier.value = event.action;
|
||||
} else if (event is ActionEndedEvent) {
|
||||
|
|
|
@ -21,7 +21,7 @@ import 'package:flutter/material.dart';
|
|||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class ViewerBottomOverlay extends StatefulWidget {
|
||||
class ViewerBottomOverlay extends StatelessWidget {
|
||||
final List<AvesEntry> entries;
|
||||
final int index;
|
||||
final CollectionLens? collection;
|
||||
|
@ -33,6 +33,10 @@ class ViewerBottomOverlay extends StatefulWidget {
|
|||
// always keep action buttons in the lower right corner, even with RTL locales
|
||||
static const actionsDirection = TextDirection.ltr;
|
||||
|
||||
AvesEntry? get entry {
|
||||
return index < entries.length ? entries[index] : null;
|
||||
}
|
||||
|
||||
const ViewerBottomOverlay({
|
||||
super.key,
|
||||
required this.entries,
|
||||
|
@ -45,27 +49,6 @@ class ViewerBottomOverlay extends StatefulWidget {
|
|||
required this.multiPageController,
|
||||
});
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _ViewerBottomOverlayState();
|
||||
|
||||
static double actionSafeHeight(BuildContext context) {
|
||||
final mqPaddingBottom = context.select<MediaQueryData, double>((mq) => max(mq.effectiveBottomPadding, mq.systemGestureInsets.bottom));
|
||||
final buttonHeight = ViewerButtons.preferredHeight(context);
|
||||
final thumbnailHeight = (settings.showOverlayThumbnailPreview ? ViewerThumbnailPreview.preferredHeight : 0);
|
||||
return mqPaddingBottom + buttonHeight + thumbnailHeight;
|
||||
}
|
||||
}
|
||||
|
||||
class _ViewerBottomOverlayState extends State<ViewerBottomOverlay> {
|
||||
List<AvesEntry> get entries => widget.entries;
|
||||
|
||||
AvesEntry? get entry {
|
||||
final index = widget.index;
|
||||
return index < entries.length ? entries[index] : null;
|
||||
}
|
||||
|
||||
MultiPageController? get multiPageController => widget.multiPageController;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final mainEntry = entry;
|
||||
|
@ -73,15 +56,15 @@ class _ViewerBottomOverlayState extends State<ViewerBottomOverlay> {
|
|||
|
||||
Widget _buildContent({AvesEntry? pageEntry}) => _BottomOverlayContent(
|
||||
entries: entries,
|
||||
index: widget.index,
|
||||
index: index,
|
||||
mainEntry: mainEntry,
|
||||
pageEntry: pageEntry ?? mainEntry,
|
||||
collection: widget.collection,
|
||||
availableSize: widget.availableSize,
|
||||
viewInsets: widget.viewInsets,
|
||||
viewPadding: widget.viewPadding,
|
||||
collection: collection,
|
||||
availableSize: availableSize,
|
||||
viewInsets: viewInsets,
|
||||
viewPadding: viewPadding,
|
||||
multiPageController: multiPageController,
|
||||
animationController: widget.animationController,
|
||||
animationController: animationController,
|
||||
);
|
||||
|
||||
Widget child = multiPageController != null
|
||||
|
@ -102,6 +85,13 @@ class _ViewerBottomOverlayState extends State<ViewerBottomOverlay> {
|
|||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
static double actionSafeHeight(BuildContext context) {
|
||||
final mqPaddingBottom = context.select<MediaQueryData, double>((mq) => max(mq.effectiveBottomPadding, mq.systemGestureInsets.bottom));
|
||||
final buttonHeight = ViewerButtons.preferredHeight(context);
|
||||
final thumbnailHeight = (settings.showOverlayThumbnailPreview ? ViewerThumbnailPreview.preferredHeight : 0);
|
||||
return mqPaddingBottom + buttonHeight + thumbnailHeight;
|
||||
}
|
||||
}
|
||||
|
||||
class _BottomOverlayContent extends StatefulWidget {
|
||||
|
|
|
@ -221,7 +221,7 @@ class ViewerDetailOverlayContent extends StatelessWidget {
|
|||
rows.add(_buildRatingTagsFullRow(context));
|
||||
}
|
||||
if (showDescription) {
|
||||
rows.add(_buildDescriptionFullRow(context));
|
||||
rows.add(_buildDescriptionFullRow(context, infoMaxWidth));
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
@ -243,13 +243,18 @@ class ViewerDetailOverlayContent extends StatelessWidget {
|
|||
),
|
||||
);
|
||||
|
||||
Widget _buildDescriptionFullRow(BuildContext context) => _buildFullRowSwitcher(
|
||||
Widget _buildDescriptionFullRow(BuildContext context, double infoMaxWidth) => _buildFullRowSwitcher(
|
||||
context: context,
|
||||
visible: details.description != null,
|
||||
builder: (context) => OverlayRowExpander(
|
||||
builder: (context) => SizedBox(
|
||||
// size it so that a long description with multiple short lines
|
||||
// expands to the full width and the scroll bar is at the edge
|
||||
width: infoMaxWidth,
|
||||
child: OverlayRowExpander(
|
||||
expandedNotifier: expandedNotifier,
|
||||
child: OverlayDescriptionRow(description: details.description!),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Widget _buildShootingFullRow(BuildContext context, double subRowWidth) => _buildFullRowSwitcher(
|
||||
|
@ -286,8 +291,14 @@ class ViewerDetailOverlayContent extends StatelessWidget {
|
|||
required double subRowWidth,
|
||||
required bool visible,
|
||||
required WidgetBuilder builder,
|
||||
}) =>
|
||||
AnimatedSwitcher(
|
||||
}) {
|
||||
final child = visible
|
||||
? SizedBox(
|
||||
width: subRowWidth,
|
||||
child: builder(context),
|
||||
)
|
||||
: const SizedBox();
|
||||
return AnimatedSwitcher(
|
||||
duration: context.select<DurationsData, Duration>((v) => v.viewerOverlayChangeAnimation),
|
||||
switchInCurve: Curves.easeInOutCubic,
|
||||
switchOutCurve: Curves.easeInOutCubic,
|
||||
|
@ -295,36 +306,34 @@ class ViewerDetailOverlayContent extends StatelessWidget {
|
|||
opacity: animation,
|
||||
child: child,
|
||||
),
|
||||
child: visible
|
||||
? SizedBox(
|
||||
width: subRowWidth,
|
||||
child: builder(context),
|
||||
)
|
||||
: const SizedBox(),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildFullRowSwitcher({
|
||||
required BuildContext context,
|
||||
required bool visible,
|
||||
required WidgetBuilder builder,
|
||||
}) =>
|
||||
AnimatedSwitcher(
|
||||
}) {
|
||||
final child = visible
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(top: _interRowPadding),
|
||||
child: builder(context),
|
||||
)
|
||||
: const SizedBox();
|
||||
return AnimatedSwitcher(
|
||||
duration: context.select<DurationsData, Duration>((v) => v.viewerOverlayChangeAnimation),
|
||||
switchInCurve: Curves.easeInOutCubic,
|
||||
switchOutCurve: Curves.easeInOutCubic,
|
||||
transitionBuilder: (child, animation) => FadeTransition(
|
||||
opacity: animation,
|
||||
child: SizeTransition(
|
||||
axisAlignment: 1,
|
||||
sizeFactor: animation,
|
||||
axisAlignment: 1,
|
||||
child: child,
|
||||
),
|
||||
),
|
||||
child: visible
|
||||
? Padding(
|
||||
padding: const EdgeInsets.only(top: _interRowPadding),
|
||||
child: builder(context),
|
||||
)
|
||||
: const SizedBox(),
|
||||
child: child,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,21 +1,64 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/view/view.dart';
|
||||
import 'package:aves/widgets/common/extensions/media_query.dart';
|
||||
import 'package:aves/widgets/common/identity/buttons/captioned_button.dart';
|
||||
import 'package:aves/widgets/common/identity/buttons/overlay_button.dart';
|
||||
import 'package:aves/widgets/viewer/controls/intents.dart';
|
||||
import 'package:aves/widgets/viewer/controls/notifications.dart';
|
||||
import 'package:aves/widgets/viewer/overlay/bottom.dart';
|
||||
import 'package:aves/widgets/viewer/overlay/viewer_buttons.dart';
|
||||
import 'package:aves/widgets/viewer/slideshow_page.dart';
|
||||
import 'package:aves_model/aves_model.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class SlideshowBottomOverlay extends StatelessWidget {
|
||||
final AnimationController animationController;
|
||||
final Size availableSize;
|
||||
final EdgeInsets? viewInsets, viewPadding;
|
||||
|
||||
const SlideshowBottomOverlay({
|
||||
super.key,
|
||||
required this.animationController,
|
||||
required this.availableSize,
|
||||
this.viewInsets,
|
||||
this.viewPadding,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Selector<MediaQueryData, double>(
|
||||
selector: (context, mq) => max(mq.effectiveBottomPadding, mq.systemGestureInsets.bottom),
|
||||
builder: (context, mqPaddingBottom, child) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(bottom: mqPaddingBottom),
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
child: SlideshowButtons(
|
||||
availableSize: availableSize,
|
||||
viewInsets: viewInsets,
|
||||
viewPadding: viewPadding,
|
||||
animationController: animationController,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SlideshowButtons extends StatefulWidget {
|
||||
final Size availableSize;
|
||||
final EdgeInsets? viewInsets, viewPadding;
|
||||
final AnimationController animationController;
|
||||
|
||||
const SlideshowButtons({
|
||||
super.key,
|
||||
required this.availableSize,
|
||||
required this.viewInsets,
|
||||
required this.viewPadding,
|
||||
required this.animationController,
|
||||
});
|
||||
|
||||
|
@ -70,7 +113,8 @@ class _SlideshowButtonsState extends State<SlideshowButtons> {
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return FocusableActionDetector(
|
||||
final viewInsetsPadding = (widget.viewInsets ?? EdgeInsets.zero) + (widget.viewPadding ?? EdgeInsets.zero);
|
||||
final viewerButtonRow = FocusableActionDetector(
|
||||
focusNode: _buttonRowFocusScopeNode,
|
||||
shortcuts: settings.useTvLayout
|
||||
? const {
|
||||
|
@ -80,26 +124,53 @@ class _SlideshowButtonsState extends State<SlideshowButtons> {
|
|||
actions: {
|
||||
TvShowLessInfoIntent: CallbackAction<Intent>(onInvoke: (intent) => TvShowLessInfoNotification().dispatch(context)),
|
||||
},
|
||||
child: settings.useTvLayout
|
||||
? Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: _actions.map((action) {
|
||||
return CaptionedButton(
|
||||
scale: _buttonScale,
|
||||
icon: action.getIcon(),
|
||||
caption: action.getText(context),
|
||||
onPressed: () => _onAction(context, action),
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
minimum: EdgeInsets.only(
|
||||
left: viewInsetsPadding.left,
|
||||
right: viewInsetsPadding.right,
|
||||
),
|
||||
child: _buildButtons(context),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
)
|
||||
: SafeArea(
|
||||
|
||||
final availableWidth = widget.availableSize.width;
|
||||
return SizedBox(
|
||||
width: availableWidth,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
viewerButtonRow,
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildButtons(BuildContext context) {
|
||||
if (settings.useTvLayout) {
|
||||
return _buildTvButtonRowContent(context);
|
||||
}
|
||||
|
||||
return SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.only(left: _padding / 2, right: _padding / 2, bottom: _padding),
|
||||
child: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: _actions
|
||||
.map((action) => Padding(
|
||||
textDirection: ViewerBottomOverlay.actionsDirection,
|
||||
children: [
|
||||
const Spacer(),
|
||||
..._actions.map((action) => _buildOverlayButton(context, action)),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOverlayButton(BuildContext context, SlideshowAction action) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: _padding / 2),
|
||||
child: OverlayButton(
|
||||
scale: _buttonScale,
|
||||
|
@ -109,11 +180,22 @@ class _SlideshowButtonsState extends State<SlideshowButtons> {
|
|||
tooltip: action.getText(context),
|
||||
),
|
||||
),
|
||||
))
|
||||
.toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildTvButtonRowContent(BuildContext context) {
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
textDirection: ViewerBottomOverlay.actionsDirection,
|
||||
children: _actions.map((action) {
|
||||
return CaptionedButton(
|
||||
scale: _buttonScale,
|
||||
icon: action.getIcon(),
|
||||
caption: action.getText(context),
|
||||
onPressed: () => _onAction(context, action),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ import 'package:aves/model/entry/extensions/props.dart';
|
|||
import 'package:aves/model/settings/enums/accessibility_animations.dart';
|
||||
import 'package:aves/model/settings/settings.dart';
|
||||
import 'package:aves/model/source/collection_lens.dart';
|
||||
import 'package:aves/services/common/services.dart';
|
||||
import 'package:aves/theme/icons.dart';
|
||||
import 'package:aves/view/view.dart';
|
||||
import 'package:aves/widgets/common/action_controls/quick_choosers/move_button.dart';
|
||||
|
@ -278,6 +279,7 @@ class _ViewerButtonRowContentState extends State<ViewerButtonRowContent> {
|
|||
...topLevelActions.map((action) => _buildPopupMenuItem(context, action, videoController)),
|
||||
if (exportActions.isNotEmpty)
|
||||
PopupMenuExpansionPanel<EntryAction>(
|
||||
enabled: !availability.isLocked,
|
||||
value: 'export',
|
||||
expandedNotifier: _popupExpandedNotifier,
|
||||
icon: AIcons.export,
|
||||
|
@ -345,18 +347,18 @@ class _ViewerButtonRowContentState extends State<ViewerButtonRowContent> {
|
|||
}
|
||||
|
||||
PopupMenuItem<EntryAction> _buildPopupMenuItem(BuildContext context, EntryAction action, AvesVideoController? videoController) {
|
||||
late final bool enabled;
|
||||
var enabled = widget.actionDelegate.canApply(action);
|
||||
switch (action) {
|
||||
case EntryAction.videoCaptureFrame:
|
||||
enabled = videoController?.canCaptureFrameNotifier.value ?? false;
|
||||
enabled &= videoController?.canCaptureFrameNotifier.value ?? false;
|
||||
case EntryAction.videoToggleMute:
|
||||
enabled = videoController?.canMuteNotifier.value ?? false;
|
||||
enabled &= videoController?.canMuteNotifier.value ?? false;
|
||||
case EntryAction.videoSelectStreams:
|
||||
enabled = videoController?.canSelectStreamNotifier.value ?? false;
|
||||
enabled &= videoController?.canSelectStreamNotifier.value ?? false;
|
||||
case EntryAction.videoSetSpeed:
|
||||
enabled = videoController?.canSetSpeedNotifier.value ?? false;
|
||||
enabled &= videoController?.canSetSpeedNotifier.value ?? false;
|
||||
default:
|
||||
enabled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
Widget? child;
|
||||
|
|
|
@ -150,9 +150,9 @@ class _EntryPageViewState extends State<EntryPageView> with TickerProviderStateM
|
|||
|
||||
final animate = context.select<Settings, bool>((v) => v.animate);
|
||||
if (animate) {
|
||||
child = Consumer<HeroInfo?>(
|
||||
child = Consumer<EntryHeroInfo?>(
|
||||
builder: (context, info, child) => Hero(
|
||||
tag: info != null && info.entry == mainEntry ? Object.hashAll([info.collectionId, mainEntry.id]) : hashCode,
|
||||
tag: info != null && info.entry == mainEntry ? info.tag : hashCode,
|
||||
transitionOnUserGestures: true,
|
||||
child: child!,
|
||||
),
|
||||
|
@ -414,7 +414,11 @@ class _EntryPageViewState extends State<EntryPageView> with TickerProviderStateM
|
|||
onScaleUpdate: onScaleUpdate,
|
||||
onScaleEnd: onScaleEnd,
|
||||
onFling: _onFling,
|
||||
onTap: (c, s, a, p) => _onTap(alignment: a),
|
||||
onTap: (c, s, a, p) {
|
||||
if (c.mounted) {
|
||||
_onTap(alignment: a);
|
||||
}
|
||||
},
|
||||
onDoubleTap: onDoubleTap,
|
||||
child: child,
|
||||
);
|
||||
|
|
20
plugins/aves_platform_meta/android/.gitignore
vendored
20
plugins/aves_platform_meta/android/.gitignore
vendored
|
@ -1,9 +1,13 @@
|
|||
*.iml
|
||||
.gradle
|
||||
gradle-wrapper.jar
|
||||
/.gradle
|
||||
/captures/
|
||||
/gradlew
|
||||
/gradlew.bat
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.cxx
|
||||
GeneratedPluginRegistrant.java
|
||||
|
||||
# Remember to never publicly share your keystore.
|
||||
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
|
||||
key.properties
|
||||
**/*.keystore
|
||||
**/*.jks
|
||||
|
|
|
@ -33,8 +33,8 @@ android {
|
|||
compileSdk 34
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
sourceCompatibility JavaVersion.VERSION_21
|
||||
targetCompatibility JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
|
|
Binary file not shown.
160
plugins/aves_platform_meta/android/gradlew
vendored
160
plugins/aves_platform_meta/android/gradlew
vendored
|
@ -1,160 +0,0 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS=""
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn ( ) {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die ( ) {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
esac
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin, switch paths to Windows format before running java
|
||||
if $cygwin ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=$((i+1))
|
||||
done
|
||||
case $i in
|
||||
(0) set -- ;;
|
||||
(1) set -- "$args0" ;;
|
||||
(2) set -- "$args0" "$args1" ;;
|
||||
(3) set -- "$args0" "$args1" "$args2" ;;
|
||||
(4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
(5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
(6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
(7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
(8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
(9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
|
||||
function splitJvmOpts() {
|
||||
JVM_OPTS=("$@")
|
||||
}
|
||||
eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
|
||||
JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
|
||||
|
||||
exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
|
90
plugins/aves_platform_meta/android/gradlew.bat
vendored
90
plugins/aves_platform_meta/android/gradlew.bat
vendored
|
@ -1,90 +0,0 @@
|
|||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS=
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto init
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:init
|
||||
@rem Get command-line arguments, handling Windowz variants
|
||||
|
||||
if not "%OS%" == "Windows_NT" goto win9xME_args
|
||||
if "%@eval[2+2]" == "4" goto 4NT_args
|
||||
|
||||
:win9xME_args
|
||||
@rem Slurp the command line arguments.
|
||||
set CMD_LINE_ARGS=
|
||||
set _SKIP=2
|
||||
|
||||
:win9xME_args_slurp
|
||||
if "x%~1" == "x" goto execute
|
||||
|
||||
set CMD_LINE_ARGS=%*
|
||||
goto execute
|
||||
|
||||
:4NT_args
|
||||
@rem Get arguments from the 4NT Shell from JP Software
|
||||
set CMD_LINE_ARGS=%$
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
|
@ -5,10 +5,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: _flutterfire_internals
|
||||
sha256: "9371d13b8ee442e3bfc08a24e3a1b3742c839abbfaf5eef11b79c4b862c89bf7"
|
||||
sha256: ddc6f775260b89176d329dee26f88b9469ef46aa3228ff6a0b91caf2b2989692
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.41"
|
||||
version: "1.3.42"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -68,10 +68,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_core
|
||||
sha256: "06537da27db981947fa535bb91ca120b4e9cb59cb87278dbdde718558cafc9ff"
|
||||
sha256: "40921de9795fbf5887ed5c0adfdf4972d5a8d7ae7e1b2bb98dea39bc02626a88"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.0"
|
||||
version: "3.4.1"
|
||||
firebase_core_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -84,26 +84,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_web
|
||||
sha256: "362e52457ed2b7b180964769c1e04d1e0ea0259fdf7025fdfedd019d4ae2bd88"
|
||||
sha256: f4ee170441ca141c5f9ee5ad8737daba3ee9c8e7efb6902aee90b4fbd178ce25
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.17.5"
|
||||
version: "2.18.0"
|
||||
firebase_crashlytics:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_crashlytics
|
||||
sha256: "4c9872020c0d97a161362ee6af7000cfdb8666234ddc290a15252ad379bb235a"
|
||||
sha256: c4fdbb14ba6f36794f89dc27fb5c759c9cc67ecbaeb079edc4dba515bbf9f555
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
version: "4.1.1"
|
||||
firebase_crashlytics_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_crashlytics_platform_interface
|
||||
sha256: ede8a199ff03378857d3c8cbb7fa58d37c27bb5a6b75faf8415ff6925dcaae2a
|
||||
sha256: "891d6f7ba4b93672d0e1265f27b6a9dccd56ba2cc30ce6496586b32d1d8770ac"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.6.41"
|
||||
version: "3.6.42"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -272,10 +272,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
||||
sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
version: "1.0.0"
|
||||
sdks:
|
||||
dart: ">=3.5.0 <4.0.0"
|
||||
flutter: ">=3.18.0-18.0.pre.54"
|
||||
flutter: ">=3.22.0"
|
||||
|
|
20
plugins/aves_screen_state/android/.gitignore
vendored
20
plugins/aves_screen_state/android/.gitignore
vendored
|
@ -1,9 +1,13 @@
|
|||
*.iml
|
||||
.gradle
|
||||
gradle-wrapper.jar
|
||||
/.gradle
|
||||
/captures/
|
||||
/gradlew
|
||||
/gradlew.bat
|
||||
/local.properties
|
||||
/.idea/workspace.xml
|
||||
/.idea/libraries
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.cxx
|
||||
GeneratedPluginRegistrant.java
|
||||
|
||||
# Remember to never publicly share your keystore.
|
||||
# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
|
||||
key.properties
|
||||
**/*.keystore
|
||||
**/*.jks
|
||||
|
|
|
@ -4,7 +4,7 @@ version '1.0-SNAPSHOT'
|
|||
buildscript {
|
||||
ext {
|
||||
kotlin_version = '1.9.24'
|
||||
agp_version = '8.5.1'
|
||||
agp_version = '8.6.0'
|
||||
}
|
||||
|
||||
repositories {
|
||||
|
@ -30,11 +30,11 @@ apply plugin: 'kotlin-android'
|
|||
|
||||
android {
|
||||
namespace 'deckers.thibault.aves.aves_screen_state'
|
||||
compileSdk 34
|
||||
compileSdk 35
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_17
|
||||
targetCompatibility JavaVersion.VERSION_17
|
||||
sourceCompatibility JavaVersion.VERSION_21
|
||||
targetCompatibility JavaVersion.VERSION_21
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
|
|
Binary file not shown.
234
plugins/aves_screen_state/android/gradlew
vendored
234
plugins/aves_screen_state/android/gradlew
vendored
|
@ -1,234 +0,0 @@
|
|||
#!/bin/sh
|
||||
|
||||
#
|
||||
# Copyright © 2015-2021 the original authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
#
|
||||
# Gradle start up script for POSIX generated by Gradle.
|
||||
#
|
||||
# Important for running:
|
||||
#
|
||||
# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
|
||||
# noncompliant, but you have some other compliant shell such as ksh or
|
||||
# bash, then to run this script, type that shell name before the whole
|
||||
# command line, like:
|
||||
#
|
||||
# ksh Gradle
|
||||
#
|
||||
# Busybox and similar reduced shells will NOT work, because this script
|
||||
# requires all of these POSIX shell features:
|
||||
# * functions;
|
||||
# * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
|
||||
# «${var#prefix}», «${var%suffix}», and «$( cmd )»;
|
||||
# * compound commands having a testable exit status, especially «case»;
|
||||
# * various built-in commands including «command», «set», and «ulimit».
|
||||
#
|
||||
# Important for patching:
|
||||
#
|
||||
# (2) This script targets any POSIX shell, so it avoids extensions provided
|
||||
# by Bash, Ksh, etc; in particular arrays are avoided.
|
||||
#
|
||||
# The "traditional" practice of packing multiple parameters into a
|
||||
# space-separated string is a well documented source of bugs and security
|
||||
# problems, so this is (mostly) avoided, by progressively accumulating
|
||||
# options in "$@", and eventually passing that to Java.
|
||||
#
|
||||
# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
|
||||
# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
|
||||
# see the in-line comments for details.
|
||||
#
|
||||
# There are tweaks for specific operating systems such as AIX, CygWin,
|
||||
# Darwin, MinGW, and NonStop.
|
||||
#
|
||||
# (3) This script is generated from the Groovy template
|
||||
# https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
|
||||
# within the Gradle project.
|
||||
#
|
||||
# You can find Gradle at https://github.com/gradle/gradle/.
|
||||
#
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
|
||||
# Resolve links: $0 may be a link
|
||||
app_path=$0
|
||||
|
||||
# Need this for daisy-chained symlinks.
|
||||
while
|
||||
APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path
|
||||
[ -h "$app_path" ]
|
||||
do
|
||||
ls=$( ls -ld "$app_path" )
|
||||
link=${ls#*' -> '}
|
||||
case $link in #(
|
||||
/*) app_path=$link ;; #(
|
||||
*) app_path=$APP_HOME$link ;;
|
||||
esac
|
||||
done
|
||||
|
||||
APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=${0##*/}
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD=maximum
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
} >&2
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
} >&2
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "$( uname )" in #(
|
||||
CYGWIN* ) cygwin=true ;; #(
|
||||
Darwin* ) darwin=true ;; #(
|
||||
MSYS* | MINGW* ) msys=true ;; #(
|
||||
NONSTOP* ) nonstop=true ;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD=$JAVA_HOME/jre/sh/java
|
||||
else
|
||||
JAVACMD=$JAVA_HOME/bin/java
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD=java
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
|
||||
case $MAX_FD in #(
|
||||
max*)
|
||||
MAX_FD=$( ulimit -H -n ) ||
|
||||
warn "Could not query maximum file descriptor limit"
|
||||
esac
|
||||
case $MAX_FD in #(
|
||||
'' | soft) :;; #(
|
||||
*)
|
||||
ulimit -n "$MAX_FD" ||
|
||||
warn "Could not set maximum file descriptor limit to $MAX_FD"
|
||||
esac
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command, stacking in reverse order:
|
||||
# * args from the command line
|
||||
# * the main class name
|
||||
# * -classpath
|
||||
# * -D...appname settings
|
||||
# * --module-path (only if needed)
|
||||
# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if "$cygwin" || "$msys" ; then
|
||||
APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
|
||||
CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
|
||||
|
||||
JAVACMD=$( cygpath --unix "$JAVACMD" )
|
||||
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
for arg do
|
||||
if
|
||||
case $arg in #(
|
||||
-*) false ;; # don't mess with options #(
|
||||
/?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath
|
||||
[ -e "$t" ] ;; #(
|
||||
*) false ;;
|
||||
esac
|
||||
then
|
||||
arg=$( cygpath --path --ignore --mixed "$arg" )
|
||||
fi
|
||||
# Roll the args list around exactly as many times as the number of
|
||||
# args, so each arg winds up back in the position where it started, but
|
||||
# possibly modified.
|
||||
#
|
||||
# NB: a `for` loop captures its iteration list before it begins, so
|
||||
# changing the positional parameters here affects neither the number of
|
||||
# iterations, nor the values presented in `arg`.
|
||||
shift # remove old arg
|
||||
set -- "$@" "$arg" # push replacement arg
|
||||
done
|
||||
fi
|
||||
|
||||
# Collect all arguments for the java command;
|
||||
# * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
|
||||
# shell script including quotes and variable substitutions, so put them in
|
||||
# double quotes to make sure that they get re-expanded; and
|
||||
# * put everything else in single quotes, so that it's not re-expanded.
|
||||
|
||||
set -- \
|
||||
"-Dorg.gradle.appname=$APP_BASE_NAME" \
|
||||
-classpath "$CLASSPATH" \
|
||||
org.gradle.wrapper.GradleWrapperMain \
|
||||
"$@"
|
||||
|
||||
# Use "xargs" to parse quoted args.
|
||||
#
|
||||
# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
|
||||
#
|
||||
# In Bash we could simply go:
|
||||
#
|
||||
# readarray ARGS < <( xargs -n1 <<<"$var" ) &&
|
||||
# set -- "${ARGS[@]}" "$@"
|
||||
#
|
||||
# but POSIX shell has neither arrays nor command substitution, so instead we
|
||||
# post-process each arg (as a line of input to sed) to backslash-escape any
|
||||
# character that might be a shell metacharacter, then use eval to reverse
|
||||
# that process (while maintaining the separation between arguments), and wrap
|
||||
# the whole thing up as a single "set" statement.
|
||||
#
|
||||
# This will of course break if any of these variables contains a newline or
|
||||
# an unmatched quote.
|
||||
#
|
||||
|
||||
eval "set -- $(
|
||||
printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
|
||||
xargs -n1 |
|
||||
sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
|
||||
tr '\n' ' '
|
||||
)" '"$@"'
|
||||
|
||||
exec "$JAVACMD" "$@"
|
89
plugins/aves_screen_state/android/gradlew.bat
vendored
89
plugins/aves_screen_state/android/gradlew.bat
vendored
|
@ -1,89 +0,0 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
|
@ -211,10 +211,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: google_maps_flutter_android
|
||||
sha256: "60a005bf1ba8d178144e442f6e2d734b0ffc2cc800a05415388472f934ad6d6a"
|
||||
sha256: "36e75af1d0bd4c7391eacdaedf9ca7632c5b9709c5ec618b04489b79ea2b3f82"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.14.4"
|
||||
version: "2.14.6"
|
||||
google_maps_flutter_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -227,10 +227,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: google_maps_flutter_platform_interface
|
||||
sha256: "4f6930fd668bf5d40feb2695d5695dbc0c35e5542b557a34ad35be491686d2ba"
|
||||
sha256: "099874463dc4c9bff04fe4b2b8cf7284d2455c2deead8f9a59a87e1b9f028c69"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.9.0"
|
||||
version: "2.9.2"
|
||||
google_maps_flutter_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -385,10 +385,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
sha256: "83d37c7ad7aaf9aa8e275490669535c8080377cfa7a7004c24dfac53afffaa90"
|
||||
sha256: f33d6bb662f0e4f79dcd7ada2e6170f3b3a2530c28fc41f49a411ddedd576a77
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.4.2"
|
||||
version: "4.5.0"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
62
pubspec.lock
62
pubspec.lock
|
@ -13,10 +13,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: _flutterfire_internals
|
||||
sha256: "9371d13b8ee442e3bfc08a24e3a1b3742c839abbfaf5eef11b79c4b862c89bf7"
|
||||
sha256: ddc6f775260b89176d329dee26f88b9469ef46aa3228ff6a0b91caf2b2989692
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.41"
|
||||
version: "1.3.42"
|
||||
_macros:
|
||||
dependency: transitive
|
||||
description: dart
|
||||
|
@ -239,18 +239,18 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: country_code
|
||||
sha256: f69ccd5163b1ca43011be9632e33ebe7ffac65e49ce2afcd3e3e5228af5d91fc
|
||||
sha256: af9f06f6ccf873ff447214c7820509867ee6f791780cc3061f0e51f7f358ee2b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
version: "1.1.0"
|
||||
coverage:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: coverage
|
||||
sha256: "7b594a150942e0d3be99cd45a1d0b5caff27ba5a27f292ed8e8d904ba3f167b5"
|
||||
sha256: c1fb2dce3c0085f39dc72668e85f8e0210ec7de05345821ff58530567df345a5
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
version: "1.9.2"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -401,10 +401,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core
|
||||
sha256: "06537da27db981947fa535bb91ca120b4e9cb59cb87278dbdde718558cafc9ff"
|
||||
sha256: "40921de9795fbf5887ed5c0adfdf4972d5a8d7ae7e1b2bb98dea39bc02626a88"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.0"
|
||||
version: "3.4.1"
|
||||
firebase_core_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -417,26 +417,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_web
|
||||
sha256: "362e52457ed2b7b180964769c1e04d1e0ea0259fdf7025fdfedd019d4ae2bd88"
|
||||
sha256: f4ee170441ca141c5f9ee5ad8737daba3ee9c8e7efb6902aee90b4fbd178ce25
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.17.5"
|
||||
version: "2.18.0"
|
||||
firebase_crashlytics:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_crashlytics
|
||||
sha256: "4c9872020c0d97a161362ee6af7000cfdb8666234ddc290a15252ad379bb235a"
|
||||
sha256: c4fdbb14ba6f36794f89dc27fb5c759c9cc67ecbaeb079edc4dba515bbf9f555
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.0"
|
||||
version: "4.1.1"
|
||||
firebase_crashlytics_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_crashlytics_platform_interface
|
||||
sha256: ede8a199ff03378857d3c8cbb7fa58d37c27bb5a6b75faf8415ff6925dcaae2a
|
||||
sha256: "891d6f7ba4b93672d0e1265f27b6a9dccd56ba2cc30ce6496586b32d1d8770ac"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.6.41"
|
||||
version: "3.6.42"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -457,10 +457,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: flex_seed_scheme
|
||||
sha256: "86470c8dc470f55dd3e28a6d30e3253a1c176df32903263d7daeabfc0c77dbd4"
|
||||
sha256: "7d97ba5c20f0e5cb1e3e2c17c865e1f797d129de31fc1f75d2dcce9470d6373c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.0"
|
||||
version: "3.3.0"
|
||||
floating:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -648,10 +648,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: google_maps_flutter_android
|
||||
sha256: "60a005bf1ba8d178144e442f6e2d734b0ffc2cc800a05415388472f934ad6d6a"
|
||||
sha256: "36e75af1d0bd4c7391eacdaedf9ca7632c5b9709c5ec618b04489b79ea2b3f82"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.14.4"
|
||||
version: "2.14.6"
|
||||
google_maps_flutter_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -664,10 +664,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: google_maps_flutter_platform_interface
|
||||
sha256: "4f6930fd668bf5d40feb2695d5695dbc0c35e5542b557a34ad35be491686d2ba"
|
||||
sha256: "099874463dc4c9bff04fe4b2b8cf7284d2455c2deead8f9a59a87e1b9f028c69"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.9.0"
|
||||
version: "2.9.2"
|
||||
google_maps_flutter_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1154,10 +1154,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_platform_interface
|
||||
sha256: fe0ffe274d665be8e34f9c59705441a7d248edebbe5d9e3ec2665f88b79358ea
|
||||
sha256: e9c8eadee926c4532d0305dff94b85bf961f16759c3af791486613152af4b4f9
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.2"
|
||||
version: "4.2.3"
|
||||
permission_handler_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1218,10 +1218,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: printing
|
||||
sha256: cc4b256a5a89d5345488e3318897b595867f5181b8c5ed6fc63bfa5f2044aec3
|
||||
sha256: de1889f30b34029fc46e5de6a9841498850b23d32942a9ee810ca36b0cb1b234
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.13.1"
|
||||
version: "5.13.2"
|
||||
process:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1402,10 +1402,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: shelf_static
|
||||
sha256: a41d3f53c4adf0f57480578c1d61d90342cd617de7fc8077b1304643c2d85c1e
|
||||
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
version: "1.1.3"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1672,10 +1672,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
sha256: "83d37c7ad7aaf9aa8e275490669535c8080377cfa7a7004c24dfac53afffaa90"
|
||||
sha256: f33d6bb662f0e4f79dcd7ada2e6170f3b3a2530c28fc41f49a411ddedd576a77
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.4.2"
|
||||
version: "4.5.0"
|
||||
vector_math:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -1728,10 +1728,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
||||
sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
version: "1.0.0"
|
||||
web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1814,4 +1814,4 @@ packages:
|
|||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.5.0 <4.0.0"
|
||||
flutter: ">=3.24.1"
|
||||
flutter: ">=3.24.3"
|
||||
|
|
|
@ -7,13 +7,13 @@ repository: https://github.com/deckerst/aves
|
|||
# - play changelog: /whatsnew/whatsnew-en-US
|
||||
# - izzy changelog: /fastlane/metadata/android/en-US/changelogs/XXX01.txt
|
||||
# - libre changelog: /fastlane/metadata/android/en-US/changelogs/XXX.txt
|
||||
version: 1.11.10+129
|
||||
version: 1.11.11+130
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
# this project bundles Flutter SDK via `flutter_wrapper`
|
||||
# cf https://github.com/passsy/flutter_wrapper
|
||||
flutter: 3.24.1
|
||||
flutter: 3.24.3
|
||||
sdk: '>=3.5.0 <4.0.0'
|
||||
|
||||
# use `scripts/apply_flavor_{flavor}.sh` to set the right dependencies for the flavor
|
||||
|
@ -119,10 +119,6 @@ dependency_overrides:
|
|||
# media_kit_video v1.2.4 depends on a specific old version of screen_brightness
|
||||
media_kit_video: ^1.0.0
|
||||
screen_brightness: ^1.0.0
|
||||
# Because printing >=5.13.2 depends on web ^1.0.0 and firebase_core_web >=2.12.0 depends on web ^0.5.1, printing >=5.13.2 is incompatible with firebase_core_web >=2.12.0.
|
||||
# And because firebase_core 3.3.0 depends on firebase_core_web ^2.17.4, printing >=5.13.2 is incompatible with firebase_core 3.3.0.
|
||||
# So, because aves depends on both firebase_core 3.3.0 and printing 5.13.2, version solving failed.
|
||||
printing: "5.13.1"
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -93,9 +93,14 @@ void main() {
|
|||
final subImage = FakeMediaStoreService.newImage(subAlbum, 'image1');
|
||||
final siblingImage = FakeMediaStoreService.newImage(siblingAlbum, 'image1');
|
||||
|
||||
final path = PathFilter('$rootAlbum/');
|
||||
expect(path.test(rootImage), true);
|
||||
expect(path.test(subImage), true);
|
||||
expect(path.test(siblingImage), false);
|
||||
final untrailedPath = PathFilter(rootAlbum);
|
||||
expect(untrailedPath.test(rootImage), true);
|
||||
expect(untrailedPath.test(subImage), true);
|
||||
expect(untrailedPath.test(siblingImage), false);
|
||||
|
||||
final trailedPath = PathFilter('$rootAlbum/');
|
||||
expect(trailedPath.test(rootImage), true);
|
||||
expect(trailedPath.test(subImage), true);
|
||||
expect(trailedPath.test(siblingImage), false);
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
In v1.11.10:
|
||||
- enjoy the app in Swedish
|
||||
In v1.11.11:
|
||||
- review photos from the lock screen
|
||||
Full changelog available on GitHub
|
Loading…
Reference in a new issue