Merge branch 'develop'
2
.flutter
|
@ -1 +1 @@
|
|||
Subproject commit 80c2e84975bbd28ecf5f8d4bd4ca5a2490bfc819
|
||||
Subproject commit 5874a72aa4c779a02553007c47dacbefba2374dc
|
6
.github/dependabot.yml
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: github-actions
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
12
.github/workflows/check.yml
vendored
|
@ -4,14 +4,24 @@ 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@v4
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Get packages for the Flutter project.
|
||||
run: scripts/pub_get_all.sh
|
||||
|
|
27
.github/workflows/dependency-review.yml
vendored
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Dependency Review Action
|
||||
#
|
||||
# This Action will scan dependency manifest files that change as part of a Pull Request,
|
||||
# surfacing known-vulnerable versions of the packages declared or updated in the PR.
|
||||
# Once installed, if the workflow run is marked as required,
|
||||
# PRs introducing known-vulnerable packages will be blocked from merging.
|
||||
#
|
||||
# Source repository: https://github.com/actions/dependency-review-action
|
||||
name: 'Dependency Review'
|
||||
on: [pull_request]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
dependency-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@5a2ce3f5b92ee19cbb1541a4984c76d921601d7c # v4.3.4
|
24
.github/workflows/release.yml
vendored
|
@ -10,13 +10,18 @@ jobs:
|
|||
name: Build and release artifacts.
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-java@v4
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/setup-java@6a0805fcefea3d4657a47ac4c165951e33482018 # v4.2.2
|
||||
with:
|
||||
distribution: 'zulu'
|
||||
java-version: '17'
|
||||
|
||||
- name: Clone the repository.
|
||||
uses: actions/checkout@v4
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Get packages for the Flutter project.
|
||||
run: scripts/pub_get_all.sh
|
||||
|
@ -66,14 +71,14 @@ jobs:
|
|||
AVES_GOOGLE_API_KEY: ${{ secrets.AVES_GOOGLE_API_KEY }}
|
||||
|
||||
- name: Create a release with the APK and App Bundle.
|
||||
uses: ncipollo/release-action@v1
|
||||
uses: ncipollo/release-action@2c591bcc8ecdcd2db72b97d6147f871fcd833ba5 # v1.14.0
|
||||
with:
|
||||
artifacts: "outputs/*"
|
||||
body: "[Changelog](https://github.com/${{ github.repository }}/blob/develop/CHANGELOG.md#${{ github.ref_name }})"
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Upload app bundle
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@834a144ee995460fba8ed112a2fc961b36a5ec5a # v4.3.6
|
||||
with:
|
||||
name: appbundle
|
||||
path: outputs/app-play-release.aab
|
||||
|
@ -83,15 +88,20 @@ jobs:
|
|||
needs: [ build ]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
|
||||
- name: Get appbundle from artifacts.
|
||||
uses: actions/download-artifact@v4
|
||||
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4.1.8
|
||||
with:
|
||||
name: appbundle
|
||||
|
||||
- name: Release app to beta channel.
|
||||
uses: r0adkll/upload-google-play@v1.1.3
|
||||
uses: r0adkll/upload-google-play@935ef9c68bb393a8e6116b1575626a7f5be3a7fb # v1.1.3
|
||||
with:
|
||||
serviceAccountJsonPlainText: ${{ secrets.PLAYSTORE_ACCOUNT_KEY }}
|
||||
packageName: deckers.thibault.aves
|
||||
|
|
76
.github/workflows/scorecards.yml
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
# This workflow uses actions that are not certified by GitHub. They are provided
|
||||
# by a third-party and are governed by separate terms of service, privacy
|
||||
# policy, and support documentation.
|
||||
|
||||
name: Scorecard supply-chain security
|
||||
on:
|
||||
# For Branch-Protection check. Only the default branch is supported. See
|
||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection
|
||||
branch_protection_rule:
|
||||
# To guarantee Maintained check is occasionally updated. See
|
||||
# https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained
|
||||
schedule:
|
||||
- cron: '20 7 * * 2'
|
||||
push:
|
||||
branches: ["develop"]
|
||||
|
||||
# Declare default permissions as read only.
|
||||
permissions: read-all
|
||||
|
||||
jobs:
|
||||
analysis:
|
||||
name: Scorecard analysis
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
# Needed to upload the results to code-scanning dashboard.
|
||||
security-events: write
|
||||
# Needed to publish results and get a badge (see publish_results below).
|
||||
id-token: write
|
||||
contents: read
|
||||
actions: read
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: "Checkout code"
|
||||
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4.1.7
|
||||
with:
|
||||
persist-credentials: false
|
||||
|
||||
- name: "Run analysis"
|
||||
uses: ossf/scorecard-action@62b2cac7ed8198b15735ed49ab1e5cf35480ba46 # v2.4.0
|
||||
with:
|
||||
results_file: results.sarif
|
||||
results_format: sarif
|
||||
# (Optional) "write" PAT token. Uncomment the `repo_token` line below if:
|
||||
# - you want to enable the Branch-Protection check on a *public* repository, or
|
||||
# - you are installing Scorecards on a *private* repository
|
||||
# To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat.
|
||||
# repo_token: ${{ secrets.SCORECARD_TOKEN }}
|
||||
|
||||
# Public repositories:
|
||||
# - Publish results to OpenSSF REST API for easy access by consumers
|
||||
# - Allows the repository to include the Scorecard badge.
|
||||
# - See https://github.com/ossf/scorecard-action#publishing-results.
|
||||
# For private repositories:
|
||||
# - `publish_results` will always be set to `false`, regardless
|
||||
# of the value entered here.
|
||||
publish_results: true
|
||||
|
||||
# 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
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
retention-days: 5
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@4dd16135b69a43b6c8efb853346f8437d92d3c93 # v3.26.6
|
||||
with:
|
||||
sarif_file: results.sarif
|
18
.pre-commit-config.yaml
Normal file
|
@ -0,0 +1,18 @@
|
|||
repos:
|
||||
- repo: https://github.com/gherynos/pre-commit-java
|
||||
rev: v0.2.4
|
||||
hooks:
|
||||
- id: Checkstyle
|
||||
- repo: https://github.com/gitleaks/gitleaks
|
||||
rev: v8.16.3
|
||||
hooks:
|
||||
- id: gitleaks
|
||||
- repo: https://github.com/jumanjihouse/pre-commit-hooks
|
||||
rev: 3.0.0
|
||||
hooks:
|
||||
- id: shellcheck
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
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.10"></a>[v1.11.10] - 2024-09-01
|
||||
|
||||
### Added
|
||||
|
||||
- Swedish translation (thanks Shift18, Andreas Håll)
|
||||
|
||||
### Changed
|
||||
|
||||
- request notification permission when launching scanning service
|
||||
- upgraded Flutter to stable v3.24.1
|
||||
|
||||
### Fixed
|
||||
|
||||
- duplicates from new item loading/refreshing
|
||||
|
||||
## <a id="v1.11.9"></a>[v1.11.9] - 2024-08-07
|
||||
|
||||
### Added
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
-->
|
||||
<uses-sdk tools:overrideLibrary="com.arthenica.ffmpegkit.flutter" />
|
||||
|
||||
<!-- from Android 11, we should define <queries> to make other apps visible to this app -->
|
||||
<!-- from Android 11 (API 30), we should define <queries> to make other apps visible to this app -->
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
@ -92,7 +92,7 @@
|
|||
<data android:mimeType="video/*" />
|
||||
</intent>
|
||||
<!--
|
||||
from Android 11, `url_launcher` method `canLaunchUrl()` will return false,
|
||||
from Android 11 (API 30), `url_launcher` method `canLaunchUrl()` will return false,
|
||||
if appropriate intents are not declared, cf https://pub.dev/packages/url_launcher#configuration=
|
||||
-->
|
||||
<!-- to open https URLs -->
|
||||
|
|
|
@ -96,7 +96,7 @@ object PermissionManager {
|
|||
segments.volumePath?.let { volumePath ->
|
||||
val dirSet = dirsPerVolume[volumePath] ?: HashSet()
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
// request primary directory on volume from Android 11
|
||||
// request primary directory on volume from Android 11 (API 30)
|
||||
val relativeDir = segments.relativeDir
|
||||
if (relativeDir != null) {
|
||||
val dirSegments = relativeDir.split(File.separator).takeWhile { it.isNotEmpty() }
|
||||
|
@ -172,7 +172,6 @@ object PermissionManager {
|
|||
val accessibleDirs = HashSet(getGrantedDirs(context))
|
||||
accessibleDirs.addAll(context.getExternalFilesDirs(null).filterNotNull().map { it.path })
|
||||
|
||||
// from API 19 / Android 4.4 / KitKat, removable storage requires access permission, at the file level
|
||||
// from API 21 / Android 5.0 / Lollipop, removable storage requires access permission, but directory access grant is possible
|
||||
// from API 30 / Android 11 / R, any storage requires access permission
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.Q) {
|
||||
|
|
|
@ -565,7 +565,7 @@ object StorageUtils {
|
|||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && isMediaStoreContentUri(uri)) {
|
||||
val path = uri.path
|
||||
path ?: return uri
|
||||
// from Android 11, accessing the original URI for a `file` or `downloads` media content yields a `SecurityException`
|
||||
// from Android 11 (API 30), accessing the original URI for a `file` or `downloads` media content yields a `SecurityException`
|
||||
if (path.startsWith(IMAGE_PATH_ROOT) || path.startsWith(VIDEO_PATH_ROOT)) {
|
||||
// "Caller must hold ACCESS_MEDIA_LOCATION permission to access original"
|
||||
if (context.checkSelfPermission(Manifest.permission.ACCESS_MEDIA_LOCATION) == PackageManager.PERMISSION_GRANTED) {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="app_name">Aves</string>
|
||||
<string name="app_widget_label">Foto Ram</string>
|
||||
<string name="app_widget_label">Fotoram</string>
|
||||
<string name="wallpaper">Bakgrund</string>
|
||||
<string name="safe_mode_shortcut_short_label">Felsäkert läge</string>
|
||||
<string name="videos_shortcut_short_label">Videor</string>
|
||||
|
|
|
@ -6808,6 +6808,11 @@ public class ExifInterfaceFork {
|
|||
}
|
||||
firstIfdOffset -= 8;
|
||||
if (firstIfdOffset > 0) {
|
||||
// TLAD start
|
||||
if (firstIfdOffset > ATTRIBUTE_SIZE_DANGER_THRESHOLD) {
|
||||
throw new IOException("dangerous IFD offset=" + firstIfdOffset);
|
||||
}
|
||||
// TLAD end
|
||||
dataInputStream.skipFully(firstIfdOffset);
|
||||
}
|
||||
}
|
||||
|
|
3
fastlane/metadata/android/en-US/changelogs/129.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
In v1.11.10:
|
||||
- enjoy the app in Swedish
|
||||
Full changelog available on GitHub
|
3
fastlane/metadata/android/en-US/changelogs/12901.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
In v1.11.10:
|
||||
- enjoy the app in Swedish
|
||||
Full changelog available on GitHub
|
|
@ -1,5 +1,5 @@
|
|||
<i>Aves</i> kan hantera alla typer av bilder och videor, inklusive vanliga JPEG- och MP4-filer, men även mer exotiska filer som <b>flersidiga TIFF-filer, SVG-filer, gamla AVI-filer och mycket mer</b>! Den skannar din mediasamling för att identifiera <b>rörelsefoton</b>, <b>panoramor</b> (även kallade fotosfärer), <b>360° videor</b>, samnt <b>GeoTIFF</b> filer.
|
||||
<i>Aves</i> kan hantera alla typer av bilder och videor, inklusive dina vanliga JPEG- och MP4-filer, men även mer exotiska filer som <b>flersidiga TIFF-filer, SVG-filer, gamla AVI-filer och mycket mer</b>! Den skannar din mediasamling för att identifiera <b>rörelsefoton</b>, <b>panoramabilder</b> (även kallade fotosfärer), <b>360° videor</b>, samt <b>GeoTIFF</b> filer.
|
||||
|
||||
<b>Navigering och sökhantering</b> är än viktigt del av <i>Aves</i>. Målet är att användarna på ett smidigt sätt ska kunna gå från album till foton till taggar till kartor, osv.
|
||||
<b>Navigering och sökhantering</b> är än viktigt del av <i>Aves</i>. Målet är att användarna på ett smidigt sätt ska kunna gå från album till foton till etiketter till kartor, osv.
|
||||
|
||||
<i>Aves</i> integrerar med Android (från KitKat till Android 14, inklusive Android TV) med funktioner som <b>widgetar</b>, <b>appgenvägar</b>, <b>skärmsläckare</b> och <b>global sökhantering.</b> Den fungerar också som en <b>mediavisare och mediaväljare</b>.
|
||||
|
|
BIN
fastlane/metadata/android/sv/images/featureGraphic.png
Normal file
After Width: | Height: | Size: 19 KiB |
BIN
fastlane/metadata/android/sv/images/phoneScreenshots/1.png
Normal file
After Width: | Height: | Size: 281 KiB |
BIN
fastlane/metadata/android/sv/images/phoneScreenshots/2.png
Normal file
After Width: | Height: | Size: 496 KiB |
BIN
fastlane/metadata/android/sv/images/phoneScreenshots/3.png
Normal file
After Width: | Height: | Size: 200 KiB |
BIN
fastlane/metadata/android/sv/images/phoneScreenshots/4.png
Normal file
After Width: | Height: | Size: 116 KiB |
BIN
fastlane/metadata/android/sv/images/phoneScreenshots/5.png
Normal file
After Width: | Height: | Size: 78 KiB |
BIN
fastlane/metadata/android/sv/images/phoneScreenshots/6.png
Normal file
After Width: | Height: | Size: 326 KiB |
BIN
fastlane/metadata/android/sv/images/phoneScreenshots/7.png
Normal file
After Width: | Height: | Size: 335 KiB |
|
@ -1374,5 +1374,21 @@
|
|||
"videoRepeatActionSetEnd": "Ende festlegen",
|
||||
"@videoRepeatActionSetEnd": {},
|
||||
"chipActionShowCollection": "In Sammlung anzeigen",
|
||||
"@chipActionShowCollection": {}
|
||||
"@chipActionShowCollection": {},
|
||||
"sortByDuration": "Nach Dauer",
|
||||
"@sortByDuration": {},
|
||||
"sortOrderShortestFirst": "Kurze zuerst",
|
||||
"@sortOrderShortestFirst": {},
|
||||
"sortOrderLongestFirst": "Lange zuerst",
|
||||
"@sortOrderLongestFirst": {},
|
||||
"explorerActionSelectStorageVolume": "Speicher auswählen",
|
||||
"@explorerActionSelectStorageVolume": {},
|
||||
"selectStorageVolumeDialogTitle": "Speicher auswählen",
|
||||
"@selectStorageVolumeDialogTitle": {},
|
||||
"setHomeCustom": "Benutzerdefiniert",
|
||||
"@setHomeCustom": {},
|
||||
"chipActionGoToExplorerPage": "Im Explorer anzeigen",
|
||||
"@chipActionGoToExplorerPage": {},
|
||||
"explorerPageTitle": "Explorer",
|
||||
"@explorerPageTitle": {}
|
||||
}
|
||||
|
|
|
@ -544,5 +544,15 @@
|
|||
"authenticateToConfigureVault": "वॉल्ट को कॉन्फ़िगर करने के लिए प्रमाणीकरण करें",
|
||||
"@authenticateToConfigureVault": {},
|
||||
"renameAlbumDialogLabelAlreadyExistsHelper": "डायरेक्टरी पहले से मौजूद",
|
||||
"@renameAlbumDialogLabelAlreadyExistsHelper": {}
|
||||
"@renameAlbumDialogLabelAlreadyExistsHelper": {},
|
||||
"videoRepeatActionSetEnd": "एण्ड सेट करे",
|
||||
"@videoRepeatActionSetEnd": {},
|
||||
"chipActionShowCountryStates": "राज्यों को दिखाएं",
|
||||
"@chipActionShowCountryStates": {},
|
||||
"chipActionConfigureVault": "वॉल्ट को कॉन्फ़िगर करें",
|
||||
"@chipActionConfigureVault": {},
|
||||
"videoActionABRepeat": "A-B दोहराव",
|
||||
"@videoActionABRepeat": {},
|
||||
"videoRepeatActionSetStart": "स्टार्ट सेट करे",
|
||||
"@videoRepeatActionSetStart": {}
|
||||
}
|
||||
|
|
|
@ -1380,5 +1380,15 @@
|
|||
"selectStorageVolumeDialogTitle": "Pilih Penyimpanan",
|
||||
"@selectStorageVolumeDialogTitle": {},
|
||||
"setHomeCustom": "Kustom",
|
||||
"@setHomeCustom": {}
|
||||
"@setHomeCustom": {},
|
||||
"explorerPageTitle": "Explorer",
|
||||
"@explorerPageTitle": {},
|
||||
"sortOrderLongestFirst": "Yang terpanjang dulu",
|
||||
"@sortOrderLongestFirst": {},
|
||||
"sortByDuration": "Berdasarkan durasi",
|
||||
"@sortByDuration": {},
|
||||
"sortOrderShortestFirst": "Yang terpendek dulu",
|
||||
"@sortOrderShortestFirst": {},
|
||||
"chipActionGoToExplorerPage": "Tampilkan di Explorer",
|
||||
"@chipActionGoToExplorerPage": {}
|
||||
}
|
||||
|
|
|
@ -1374,5 +1374,21 @@
|
|||
"renameProcessorHash": "Hash",
|
||||
"@renameProcessorHash": {},
|
||||
"settingsForceWesternArabicNumeralsTile": "Forza numeri arabi",
|
||||
"@settingsForceWesternArabicNumeralsTile": {}
|
||||
"@settingsForceWesternArabicNumeralsTile": {},
|
||||
"sortByDuration": "Per durata",
|
||||
"@sortByDuration": {},
|
||||
"sortOrderLongestFirst": "Prima i più lunghi",
|
||||
"@sortOrderLongestFirst": {},
|
||||
"sortOrderShortestFirst": "Prima i più corti",
|
||||
"@sortOrderShortestFirst": {},
|
||||
"explorerPageTitle": "Esploratore",
|
||||
"@explorerPageTitle": {},
|
||||
"explorerActionSelectStorageVolume": "Seleziona il supporto",
|
||||
"@explorerActionSelectStorageVolume": {},
|
||||
"selectStorageVolumeDialogTitle": "Seleziona Supporto",
|
||||
"@selectStorageVolumeDialogTitle": {},
|
||||
"setHomeCustom": "Personalizzato",
|
||||
"@setHomeCustom": {},
|
||||
"chipActionGoToExplorerPage": "Mostra nell'Esploratore",
|
||||
"@chipActionGoToExplorerPage": {}
|
||||
}
|
||||
|
|
|
@ -1193,7 +1193,7 @@
|
|||
"@chipActionFilterIn": {},
|
||||
"filterAspectRatioPortraitLabel": "縦向き",
|
||||
"@filterAspectRatioPortraitLabel": {},
|
||||
"filterNoAddressLabel": "位置情報なし",
|
||||
"filterNoAddressLabel": "アドレスなし",
|
||||
"@filterNoAddressLabel": {},
|
||||
"keepScreenOnVideoPlayback": "動画再生時",
|
||||
"@keepScreenOnVideoPlayback": {},
|
||||
|
@ -1370,5 +1370,11 @@
|
|||
"cropAspectRatioOriginal": "オリジナル",
|
||||
"@cropAspectRatioOriginal": {},
|
||||
"stopTooltip": "停止",
|
||||
"@stopTooltip": {}
|
||||
"@stopTooltip": {},
|
||||
"explorerPageTitle": "エクスプローラー",
|
||||
"@explorerPageTitle": {},
|
||||
"chipActionGoToExplorerPage": "エクスプローラーで表示",
|
||||
"@chipActionGoToExplorerPage": {},
|
||||
"filterLocatedLabel": "位置情報あり",
|
||||
"@filterLocatedLabel": {}
|
||||
}
|
||||
|
|
|
@ -1378,5 +1378,17 @@
|
|||
"chipActionGoToExplorerPage": "Mostrar no Explorador",
|
||||
"@chipActionGoToExplorerPage": {},
|
||||
"explorerPageTitle": "Explorador",
|
||||
"@explorerPageTitle": {}
|
||||
"@explorerPageTitle": {},
|
||||
"sortByDuration": "Por duração",
|
||||
"@sortByDuration": {},
|
||||
"sortOrderShortestFirst": "Mais curtos primeiro",
|
||||
"@sortOrderShortestFirst": {},
|
||||
"sortOrderLongestFirst": "Mais longos primeiro",
|
||||
"@sortOrderLongestFirst": {},
|
||||
"explorerActionSelectStorageVolume": "Selecione o armazenamento",
|
||||
"@explorerActionSelectStorageVolume": {},
|
||||
"selectStorageVolumeDialogTitle": "Selecione o Armazenamento",
|
||||
"@selectStorageVolumeDialogTitle": {},
|
||||
"setHomeCustom": "Personalizada",
|
||||
"@setHomeCustom": {}
|
||||
}
|
||||
|
|
|
@ -1532,5 +1532,21 @@
|
|||
"videoRepeatActionSetEnd": "Setează sfârșitul",
|
||||
"@videoRepeatActionSetEnd": {},
|
||||
"chipActionShowCollection": "Afișați în colecție",
|
||||
"@chipActionShowCollection": {}
|
||||
"@chipActionShowCollection": {},
|
||||
"sortByDuration": "După durată",
|
||||
"@sortByDuration": {},
|
||||
"setHomeCustom": "Personalizat",
|
||||
"@setHomeCustom": {},
|
||||
"sortOrderShortestFirst": "Cel mai scurt mai întâi",
|
||||
"@sortOrderShortestFirst": {},
|
||||
"sortOrderLongestFirst": "Cel mai lung mai întâi",
|
||||
"@sortOrderLongestFirst": {},
|
||||
"explorerActionSelectStorageVolume": "Selectează spațiu de stocare",
|
||||
"@explorerActionSelectStorageVolume": {},
|
||||
"selectStorageVolumeDialogTitle": "Selectează spațiu de stocare",
|
||||
"@selectStorageVolumeDialogTitle": {},
|
||||
"chipActionGoToExplorerPage": "Afișare în Explorer",
|
||||
"@chipActionGoToExplorerPage": {},
|
||||
"explorerPageTitle": "Explorer",
|
||||
"@explorerPageTitle": {}
|
||||
}
|
||||
|
|
|
@ -96,6 +96,11 @@ class Contributors {
|
|||
Contributor('Stephan Paternotte', 'stephan@paternottes.net'),
|
||||
Contributor('Tung Anh', 'buihuutunganh2007@gmail.com'),
|
||||
Contributor('Adrien N', 'adriennathaniel1999@gmail.com'),
|
||||
Contributor('Shift18', 'bribable.lawyer@posteo.net'),
|
||||
Contributor('Andreas Håll', 'ante_skalman@hotmail.com'),
|
||||
Contributor('Scorza9999', 'oliva.scorza@gmail.com'),
|
||||
Contributor('Taufan', 'taufanxxx@gmail.com'),
|
||||
Contributor('Leo Aaua Felix', 'g00g7el@gmail.com'),
|
||||
// Contributor('Alvi Khan', 'aveenalvi@gmail.com'), // Bengali
|
||||
// Contributor('Htet Oo Hlaing', 'htetoh2006@outlook.com'), // Burmese
|
||||
// Contributor('Khant', 'khant@users.noreply.hosted.weblate.org'), // Burmese
|
||||
|
@ -113,8 +118,6 @@ class Contributors {
|
|||
// Contributor('Subham Jena', 'subhamjena8465@gmail.com'), // Odia
|
||||
// Contributor('Prasanta-Hembram', 'Prasantahembram720@gmail.com'), // Santali
|
||||
// Contributor('mytja', 'mamnju21@gmail.com'), // Slovenian
|
||||
// Contributor('Shift18', 'bribable.lawyer@posteo.net'), // Swedish
|
||||
// Contributor('Andreas Håll', 'ante_skalman@hotmail.com'), // Swedish
|
||||
// Contributor('Nattapong K', 'mixer5056@gmail.com'), // Thai
|
||||
};
|
||||
}
|
||||
|
|
|
@ -29,10 +29,10 @@ class AppInventory {
|
|||
areAppNamesReadyNotifier.value = false;
|
||||
}
|
||||
|
||||
bool isPotentialAppDir(String dir) => _potentialAppDirs.contains(dir);
|
||||
bool isPotentialAppDir(String dir) => _potentialAppDirs.contains(Package.normalizePotentialDir(dir));
|
||||
|
||||
String? getAlbumAppPackageName(String albumPath) {
|
||||
final dir = pContext.split(albumPath).last;
|
||||
final dir = Package.normalizePotentialDir(pContext.split(albumPath).last);
|
||||
final package = _launcherPackages.firstWhereOrNull((v) => v.potentialDirs.contains(dir));
|
||||
return package?.packageName;
|
||||
}
|
||||
|
@ -71,7 +71,11 @@ class Package {
|
|||
currentLabel,
|
||||
englishLabel,
|
||||
...ownedDirs,
|
||||
].whereNotNull().toSet();
|
||||
].whereNotNull().map(normalizePotentialDir).toSet();
|
||||
|
||||
static String normalizePotentialDir(String dir) {
|
||||
return dir.replaceAll('_', ' ').trim().toLowerCase();
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() => '$runtimeType#${shortHash(this)}{packageName=$packageName, categoryLauncher=$categoryLauncher, isSystem=$isSystem, currentLabel=$currentLabel, englishLabel=$englishLabel, ownedDirs=$ownedDirs}';
|
||||
|
|
|
@ -32,7 +32,7 @@ class Covers {
|
|||
Covers._private();
|
||||
|
||||
Future<void> init() async {
|
||||
_rows = await metadataDb.loadAllCovers();
|
||||
_rows = await localMediaDb.loadAllCovers();
|
||||
}
|
||||
|
||||
int get count => _rows.length;
|
||||
|
@ -59,7 +59,7 @@ class Covers {
|
|||
|
||||
final oldRows = _rows.where((row) => row.filter == filter).toSet();
|
||||
_rows.removeAll(oldRows);
|
||||
await metadataDb.removeCovers({filter});
|
||||
await localMediaDb.removeCovers({filter});
|
||||
|
||||
final oldRow = oldRows.firstOrNull;
|
||||
final oldEntry = oldRow?.entryId;
|
||||
|
@ -74,7 +74,7 @@ class Covers {
|
|||
color: color,
|
||||
);
|
||||
_rows.add(row);
|
||||
await metadataDb.addCovers({row});
|
||||
await localMediaDb.addCovers({row});
|
||||
}
|
||||
|
||||
if (oldEntry != entryId) _entryChangeStreamController.add({filter});
|
||||
|
@ -103,7 +103,7 @@ class Covers {
|
|||
}
|
||||
|
||||
Future<void> clear() async {
|
||||
await metadataDb.clearCovers();
|
||||
await localMediaDb.clearCovers();
|
||||
_rows.clear();
|
||||
|
||||
_entryChangeStreamController.add(null);
|
||||
|
|
|
@ -8,7 +8,7 @@ import 'package:aves/model/metadata/trash.dart';
|
|||
import 'package:aves/model/vaults/details.dart';
|
||||
import 'package:aves/model/viewer/video_playback.dart';
|
||||
|
||||
abstract class MetadataDb {
|
||||
abstract class LocalMediaDb {
|
||||
int get nextId;
|
||||
|
||||
Future<void> init();
|
||||
|
@ -27,12 +27,14 @@ abstract class MetadataDb {
|
|||
|
||||
Future<Set<AvesEntry>> loadEntriesById(Set<int> ids);
|
||||
|
||||
Future<void> saveEntries(Set<AvesEntry> entries);
|
||||
Future<void> insertEntries(Set<AvesEntry> entries);
|
||||
|
||||
Future<void> updateEntry(int id, AvesEntry entry);
|
||||
|
||||
Future<Set<AvesEntry>> searchLiveEntries(String query, {int? limit});
|
||||
|
||||
Future<Set<AvesEntry>> searchLiveDuplicates(int origin, Set<AvesEntry>? entries);
|
||||
|
||||
// date taken
|
||||
|
||||
Future<void> clearDates();
|
|
@ -1,8 +1,8 @@
|
|||
import 'dart:io';
|
||||
|
||||
import 'package:aves/model/covers.dart';
|
||||
import 'package:aves/model/db/db_metadata.dart';
|
||||
import 'package:aves/model/db/db_metadata_sqflite_upgrade.dart';
|
||||
import 'package:aves/model/db/db.dart';
|
||||
import 'package:aves/model/db/db_sqflite_upgrade.dart';
|
||||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/model/favourites.dart';
|
||||
import 'package:aves/model/filters/filters.dart';
|
||||
|
@ -16,7 +16,7 @@ import 'package:collection/collection.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
|
||||
class SqfliteMetadataDb implements MetadataDb {
|
||||
class SqfliteLocalMediaDb implements LocalMediaDb {
|
||||
late Database _db;
|
||||
|
||||
Future<String> get path async => pContext.join(await getDatabasesPath(), 'metadata.db');
|
||||
|
@ -108,11 +108,11 @@ class SqfliteMetadataDb implements MetadataDb {
|
|||
', resumeTimeMillis INTEGER'
|
||||
')');
|
||||
},
|
||||
onUpgrade: MetadataDbUpgrader.upgradeDb,
|
||||
onUpgrade: LocalMediaDbUpgrader.upgradeDb,
|
||||
version: 11,
|
||||
);
|
||||
|
||||
final maxIdRows = await _db.rawQuery('SELECT max(id) AS maxId FROM $entryTable');
|
||||
final maxIdRows = await _db.rawQuery('SELECT MAX(id) AS maxId FROM $entryTable');
|
||||
_lastId = (maxIdRows.firstOrNull?['maxId'] as int?) ?? 0;
|
||||
}
|
||||
|
||||
|
@ -209,7 +209,7 @@ class SqfliteMetadataDb implements MetadataDb {
|
|||
Future<Set<AvesEntry>> loadEntriesById(Set<int> ids) => _getByIds(ids, entryTable, AvesEntry.fromMap);
|
||||
|
||||
@override
|
||||
Future<void> saveEntries(Set<AvesEntry> entries) async {
|
||||
Future<void> insertEntries(Set<AvesEntry> entries) async {
|
||||
if (entries.isEmpty) return;
|
||||
final stopwatch = Stopwatch()..start();
|
||||
final batch = _db.batch();
|
||||
|
@ -246,6 +246,28 @@ class SqfliteMetadataDb implements MetadataDb {
|
|||
return rows.map(AvesEntry.fromMap).toSet();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Set<AvesEntry>> searchLiveDuplicates(int origin, Set<AvesEntry>? entries) async {
|
||||
String where = 'origin = ? AND trashed = ?';
|
||||
if (entries != null) {
|
||||
where += ' AND contentId IN (${entries.map((v) => v.contentId).join(',')})';
|
||||
}
|
||||
final rows = await _db.rawQuery(
|
||||
'SELECT *, MAX(id) AS id'
|
||||
' FROM $entryTable'
|
||||
' WHERE $where'
|
||||
' GROUP BY contentId'
|
||||
' HAVING COUNT(id) > 1',
|
||||
[origin, 0],
|
||||
);
|
||||
final duplicates = rows.map(AvesEntry.fromMap).toSet();
|
||||
if (duplicates.isNotEmpty) {
|
||||
debugPrint('Found duplicates=$duplicates');
|
||||
}
|
||||
// return most recent duplicate for each duplicated content ID
|
||||
return duplicates;
|
||||
}
|
||||
|
||||
// date taken
|
||||
|
||||
@override
|
|
@ -1,18 +1,18 @@
|
|||
import 'package:aves/model/db/db_metadata_sqflite.dart';
|
||||
import 'package:aves/model/db/db_sqflite.dart';
|
||||
import 'package:collection/collection.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:sqflite/sqflite.dart';
|
||||
|
||||
class MetadataDbUpgrader {
|
||||
static const entryTable = SqfliteMetadataDb.entryTable;
|
||||
static const dateTakenTable = SqfliteMetadataDb.dateTakenTable;
|
||||
static const metadataTable = SqfliteMetadataDb.metadataTable;
|
||||
static const addressTable = SqfliteMetadataDb.addressTable;
|
||||
static const favouriteTable = SqfliteMetadataDb.favouriteTable;
|
||||
static const coverTable = SqfliteMetadataDb.coverTable;
|
||||
static const vaultTable = SqfliteMetadataDb.vaultTable;
|
||||
static const trashTable = SqfliteMetadataDb.trashTable;
|
||||
static const videoPlaybackTable = SqfliteMetadataDb.videoPlaybackTable;
|
||||
class LocalMediaDbUpgrader {
|
||||
static const entryTable = SqfliteLocalMediaDb.entryTable;
|
||||
static const dateTakenTable = SqfliteLocalMediaDb.dateTakenTable;
|
||||
static const metadataTable = SqfliteLocalMediaDb.metadataTable;
|
||||
static const addressTable = SqfliteLocalMediaDb.addressTable;
|
||||
static const favouriteTable = SqfliteLocalMediaDb.favouriteTable;
|
||||
static const coverTable = SqfliteLocalMediaDb.coverTable;
|
||||
static const vaultTable = SqfliteLocalMediaDb.vaultTable;
|
||||
static const trashTable = SqfliteLocalMediaDb.trashTable;
|
||||
static const videoPlaybackTable = SqfliteLocalMediaDb.videoPlaybackTable;
|
||||
|
||||
// warning: "ALTER TABLE ... RENAME COLUMN ..." is not supported
|
||||
// on SQLite <3.25.0, bundled on older Android devices
|
|
@ -432,8 +432,8 @@ class AvesEntry with AvesEntryBase {
|
|||
if (isFlipped is bool) this.isFlipped = isFlipped;
|
||||
|
||||
if (persist) {
|
||||
await metadataDb.saveEntries({this});
|
||||
if (catalogMetadata != null) await metadataDb.saveCatalogMetadata({catalogMetadata!});
|
||||
await localMediaDb.updateEntry(id, this);
|
||||
if (catalogMetadata != null) await localMediaDb.saveCatalogMetadata({catalogMetadata!});
|
||||
}
|
||||
|
||||
await _onVisualFieldChanged(oldMimeType, oldDateModifiedSecs, oldRotationDegrees, oldIsFlipped);
|
||||
|
@ -451,7 +451,7 @@ class AvesEntry with AvesEntryBase {
|
|||
_tags = null;
|
||||
|
||||
if (persist) {
|
||||
await metadataDb.removeIds({id}, dataTypes: dataTypes);
|
||||
await localMediaDb.removeIds({id}, dataTypes: dataTypes);
|
||||
}
|
||||
|
||||
final updatedEntry = await mediaFetchService.getEntry(uri, mimeType);
|
||||
|
|
|
@ -15,7 +15,7 @@ class Favourites with ChangeNotifier {
|
|||
Favourites._private();
|
||||
|
||||
Future<void> init() async {
|
||||
_rows = await metadataDb.loadAllFavourites();
|
||||
_rows = await localMediaDb.loadAllFavourites();
|
||||
}
|
||||
|
||||
int get count => _rows.length;
|
||||
|
@ -29,7 +29,7 @@ class Favourites with ChangeNotifier {
|
|||
Future<void> add(Set<AvesEntry> entries) async {
|
||||
final newRows = entries.map(_entryToRow).toSet();
|
||||
|
||||
await metadataDb.addFavourites(newRows);
|
||||
await localMediaDb.addFavourites(newRows);
|
||||
_rows.addAll(newRows);
|
||||
|
||||
notifyListeners();
|
||||
|
@ -40,14 +40,14 @@ class Favourites with ChangeNotifier {
|
|||
Future<void> removeIds(Set<int> entryIds) async {
|
||||
final removedRows = _rows.where((row) => entryIds.contains(row.entryId)).toSet();
|
||||
|
||||
await metadataDb.removeFavourites(removedRows);
|
||||
await localMediaDb.removeFavourites(removedRows);
|
||||
removedRows.forEach(_rows.remove);
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
Future<void> clear() async {
|
||||
await metadataDb.clearFavourites();
|
||||
await localMediaDb.clearFavourites();
|
||||
_rows.clear();
|
||||
|
||||
notifyListeners();
|
||||
|
|
|
@ -136,7 +136,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
|
|||
late Map<int?, int?> _savedDates;
|
||||
|
||||
Future<void> loadDates() async {
|
||||
_savedDates = Map.unmodifiable(await metadataDb.loadDates());
|
||||
_savedDates = Map.unmodifiable(await localMediaDb.loadDates());
|
||||
}
|
||||
|
||||
Set<CollectionFilter> _getAppHiddenFilters() => {
|
||||
|
@ -217,7 +217,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
|
|||
final ids = entries.map((entry) => entry.id).toSet();
|
||||
await favourites.removeIds(ids);
|
||||
await covers.removeIds(ids);
|
||||
await metadataDb.removeIds(ids);
|
||||
await localMediaDb.removeIds(ids);
|
||||
|
||||
ids.forEach((id) => _entryById.remove);
|
||||
_rawEntries.removeAll(entries);
|
||||
|
@ -278,10 +278,10 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
|
|||
if (persist) {
|
||||
await covers.moveEntry(entry);
|
||||
final id = entry.id;
|
||||
await metadataDb.updateEntry(id, entry);
|
||||
await metadataDb.updateCatalogMetadata(id, entry.catalogMetadata);
|
||||
await metadataDb.updateAddress(id, entry.addressDetails);
|
||||
await metadataDb.updateTrash(id, entry.trashDetails);
|
||||
await localMediaDb.updateEntry(id, entry);
|
||||
await localMediaDb.updateCatalogMetadata(id, entry.catalogMetadata);
|
||||
await localMediaDb.updateAddress(id, entry.addressDetails);
|
||||
await localMediaDb.updateTrash(id, entry.trashDetails);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -352,7 +352,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
|
|||
if (sourceEntry != null) {
|
||||
fromAlbums.add(sourceEntry.directory);
|
||||
movedEntries.add(sourceEntry.copyWith(
|
||||
id: metadataDb.nextId,
|
||||
id: localMediaDb.nextId,
|
||||
uri: newFields['uri'] as String?,
|
||||
path: newFields['path'] as String?,
|
||||
contentId: newFields['contentId'] as int?,
|
||||
|
@ -366,9 +366,9 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
|
|||
debugPrint('failed to find source entry with uri=$sourceUri');
|
||||
}
|
||||
});
|
||||
await metadataDb.saveEntries(movedEntries);
|
||||
await metadataDb.saveCatalogMetadata(movedEntries.map((entry) => entry.catalogMetadata).whereNotNull().toSet());
|
||||
await metadataDb.saveAddresses(movedEntries.map((entry) => entry.addressDetails).whereNotNull().toSet());
|
||||
await localMediaDb.insertEntries(movedEntries);
|
||||
await localMediaDb.saveCatalogMetadata(movedEntries.map((entry) => entry.catalogMetadata).whereNotNull().toSet());
|
||||
await localMediaDb.saveAddresses(movedEntries.map((entry) => entry.addressDetails).whereNotNull().toSet());
|
||||
} else {
|
||||
await Future.forEach<MoveOpEvent>(movedOps, (movedOp) async {
|
||||
final newFields = movedOp.newFields;
|
||||
|
@ -455,7 +455,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
|
|||
await deviceService.requestGarbageCollection();
|
||||
await Future.forEach(entries, (entry) async {
|
||||
await entry.catalog(background: background, force: dataTypes.contains(EntryDataType.catalog), persist: persist);
|
||||
await metadataDb.updateCatalogMetadata(entry.id, entry.catalogMetadata);
|
||||
await localMediaDb.updateCatalogMetadata(entry.id, entry.catalogMetadata);
|
||||
});
|
||||
onCatalogMetadataChanged();
|
||||
}
|
||||
|
@ -463,7 +463,7 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
|
|||
if (dataTypes.contains(EntryDataType.address)) {
|
||||
await Future.forEach(entries, (entry) async {
|
||||
await entry.locate(background: background, force: dataTypes.contains(EntryDataType.address), geocoderLocale: settings.appliedLocale);
|
||||
await metadataDb.updateAddress(entry.id, entry.addressDetails);
|
||||
await localMediaDb.updateAddress(entry.id, entry.addressDetails);
|
||||
});
|
||||
onAddressMetadataChanged();
|
||||
}
|
||||
|
@ -497,7 +497,6 @@ abstract class CollectionSource with SourceBase, AlbumMixin, CountryMixin, Place
|
|||
}
|
||||
}
|
||||
if (startAnalysisService) {
|
||||
// TODO TLAD [tiramisu] explain foreground service and request POST_NOTIFICATIONS permission
|
||||
await AnalysisService.startService(
|
||||
force: force,
|
||||
entryIds: entries?.map((entry) => entry.id).toList(),
|
||||
|
|
|
@ -24,7 +24,7 @@ mixin LocationMixin on CountryMixin, StateMixin {
|
|||
List<String> sortedPlaces = List.unmodifiable([]);
|
||||
|
||||
Future<void> loadAddresses({Set<int>? ids}) async {
|
||||
final saved = await (ids != null ? metadataDb.loadAddressesById(ids) : metadataDb.loadAddresses());
|
||||
final saved = await (ids != null ? localMediaDb.loadAddressesById(ids) : localMediaDb.loadAddresses());
|
||||
final idMap = entryById;
|
||||
saved.forEach((metadata) => idMap[metadata.id]?.addressDetails = metadata);
|
||||
invalidateEntries();
|
||||
|
@ -37,7 +37,7 @@ mixin LocationMixin on CountryMixin, StateMixin {
|
|||
|
||||
final unlocatedIds = candidateEntries.where((entry) => !entry.hasGps).map((entry) => entry.id).toSet();
|
||||
if (unlocatedIds.isNotEmpty) {
|
||||
await metadataDb.removeIds(unlocatedIds, dataTypes: {EntryDataType.address});
|
||||
await localMediaDb.removeIds(unlocatedIds, dataTypes: {EntryDataType.address});
|
||||
onAddressMetadataChanged();
|
||||
}
|
||||
}
|
||||
|
@ -71,7 +71,7 @@ mixin LocationMixin on CountryMixin, StateMixin {
|
|||
setProgress(done: ++progressDone, total: progressTotal);
|
||||
});
|
||||
if (newAddresses.isNotEmpty) {
|
||||
await metadataDb.saveAddresses(Set.unmodifiable(newAddresses));
|
||||
await localMediaDb.saveAddresses(Set.unmodifiable(newAddresses));
|
||||
onAddressMetadataChanged();
|
||||
}
|
||||
}
|
||||
|
@ -129,7 +129,7 @@ mixin LocationMixin on CountryMixin, StateMixin {
|
|||
if (entry.hasFineAddress) {
|
||||
newAddresses.add(entry.addressDetails!);
|
||||
if (newAddresses.length >= commitCountThreshold) {
|
||||
await metadataDb.saveAddresses(Set.unmodifiable(newAddresses));
|
||||
await localMediaDb.saveAddresses(Set.unmodifiable(newAddresses));
|
||||
onAddressMetadataChanged();
|
||||
newAddresses.clear();
|
||||
}
|
||||
|
@ -141,7 +141,7 @@ mixin LocationMixin on CountryMixin, StateMixin {
|
|||
setProgress(done: ++progressDone, total: progressTotal);
|
||||
}
|
||||
if (newAddresses.isNotEmpty) {
|
||||
await metadataDb.saveAddresses(Set.unmodifiable(newAddresses));
|
||||
await localMediaDb.saveAddresses(Set.unmodifiable(newAddresses));
|
||||
onAddressMetadataChanged();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:aves/model/covers.dart';
|
||||
import 'package:aves/model/entry/entry.dart';
|
||||
|
@ -57,7 +56,7 @@ class MediaStoreSource extends CollectionSource {
|
|||
Future<void> _loadEssentials() async {
|
||||
final stopwatch = Stopwatch()..start();
|
||||
state = SourceState.loading;
|
||||
await metadataDb.init();
|
||||
await localMediaDb.init();
|
||||
await vaults.init();
|
||||
await favourites.init();
|
||||
await covers.init();
|
||||
|
@ -67,8 +66,8 @@ class MediaStoreSource extends CollectionSource {
|
|||
if (currentTimeZoneOffset != catalogTimeZoneOffset) {
|
||||
// clear catalog metadata to get correct date/times when moving to a different time zone
|
||||
debugPrint('$runtimeType clear catalog metadata to get correct date/times');
|
||||
await metadataDb.clearDates();
|
||||
await metadataDb.clearCatalogMetadata();
|
||||
await localMediaDb.clearDates();
|
||||
await localMediaDb.clearCatalogMetadata();
|
||||
settings.catalogTimeZoneRawOffsetMillis = currentTimeZoneOffset;
|
||||
}
|
||||
}
|
||||
|
@ -92,13 +91,13 @@ class MediaStoreSource extends CollectionSource {
|
|||
final topIds = settings.topEntryIds?.toSet();
|
||||
if (topIds != null) {
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} load ${topIds.length} top entries');
|
||||
topEntries.addAll(await metadataDb.loadEntriesById(topIds));
|
||||
topEntries.addAll(await localMediaDb.loadEntriesById(topIds));
|
||||
addEntries(topEntries);
|
||||
}
|
||||
}
|
||||
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} fetch known entries');
|
||||
final knownEntries = await metadataDb.loadEntries(origin: EntryOrigins.mediaStoreContent, directory: directory);
|
||||
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');
|
||||
|
@ -146,7 +145,7 @@ class MediaStoreSource extends CollectionSource {
|
|||
// clean up obsolete entries
|
||||
if (removedEntries.isNotEmpty) {
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} remove obsolete entries');
|
||||
await metadataDb.removeIds(removedEntries.map((entry) => entry.id).toSet());
|
||||
await localMediaDb.removeIds(removedEntries.map((entry) => entry.id).toSet());
|
||||
}
|
||||
|
||||
// verify paths because some apps move files without updating their `last modified date`
|
||||
|
@ -159,46 +158,44 @@ class MediaStoreSource extends CollectionSource {
|
|||
});
|
||||
|
||||
// items to add to the collection
|
||||
final pendingNewEntries = <AvesEntry>{};
|
||||
final newEntries = <AvesEntry>{};
|
||||
|
||||
// recover untracked trash items
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} recover untracked entries');
|
||||
if (directory == null) {
|
||||
pendingNewEntries.addAll(await recoverUntrackedTrashItems());
|
||||
newEntries.addAll(await recoverUntrackedTrashItems());
|
||||
}
|
||||
|
||||
// fetch new & modified entries
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} fetch new entries');
|
||||
// refresh after the first 10 entries, then after 100 more, then every 1000 entries
|
||||
var refreshCount = 10;
|
||||
const refreshCountMax = 1000;
|
||||
final allNewEntries = <AvesEntry>{};
|
||||
void addPendingEntries() {
|
||||
allNewEntries.addAll(pendingNewEntries);
|
||||
addEntries(pendingNewEntries);
|
||||
pendingNewEntries.clear();
|
||||
}
|
||||
|
||||
mediaStoreService.getEntries(_safeMode, knownDateByContentId, directory: directory).listen(
|
||||
(entry) {
|
||||
// when discovering modified entry with known content ID,
|
||||
// reuse known entry ID to overwrite it while preserving favourites, etc.
|
||||
final contentId = entry.contentId;
|
||||
final existingEntry = knownContentIds.contains(contentId) ? knownLiveEntries.firstWhereOrNull((entry) => entry.contentId == contentId) : null;
|
||||
entry.id = existingEntry?.id ?? metadataDb.nextId;
|
||||
entry.id = existingEntry?.id ?? localMediaDb.nextId;
|
||||
|
||||
pendingNewEntries.add(entry);
|
||||
if (pendingNewEntries.length >= refreshCount) {
|
||||
refreshCount = min(refreshCount * 10, refreshCountMax);
|
||||
addPendingEntries();
|
||||
}
|
||||
newEntries.add(entry);
|
||||
},
|
||||
onDone: () async {
|
||||
addPendingEntries();
|
||||
|
||||
if (allNewEntries.isNotEmpty) {
|
||||
if (newEntries.isNotEmpty) {
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} save new entries');
|
||||
await metadataDb.saveEntries(allNewEntries);
|
||||
await localMediaDb.insertEntries(newEntries);
|
||||
|
||||
// TODO TLAD find duplication cause
|
||||
final duplicates = await localMediaDb.searchLiveDuplicates(EntryOrigins.mediaStoreContent, newEntries);
|
||||
if (duplicates.isNotEmpty) {
|
||||
unawaited(reportService.recordError(Exception('Loading entries yielded duplicates=${duplicates.join(', ')}'), StackTrace.current));
|
||||
// post-error cleanup
|
||||
await localMediaDb.removeIds(duplicates.map((v) => v.id).toSet());
|
||||
for (final duplicate in duplicates) {
|
||||
final duplicateId = duplicate.id;
|
||||
newEntries.removeWhere((v) => duplicateId == v.id);
|
||||
}
|
||||
}
|
||||
|
||||
addEntries(newEntries);
|
||||
|
||||
// new entries include existing entries with obsolete paths
|
||||
// so directories may be added, but also removed or simply have their content summary changed
|
||||
|
@ -224,7 +221,7 @@ class MediaStoreSource extends CollectionSource {
|
|||
notifyAlbumsChanged();
|
||||
|
||||
debugPrint('$runtimeType refresh ${stopwatch.elapsed} done');
|
||||
unawaited(reportService.log('Source refresh complete in ${stopwatch.elapsed.inSeconds}s for ${knownEntries.length} known, ${allNewEntries.length} new, ${removedEntries.length} removed'));
|
||||
unawaited(reportService.log('Source refresh complete in ${stopwatch.elapsed.inSeconds}s for ${knownEntries.length} known, ${newEntries.length} new, ${removedEntries.length} removed'));
|
||||
},
|
||||
onError: (error) => debugPrint('$runtimeType stream error=$error'),
|
||||
);
|
||||
|
@ -242,7 +239,7 @@ class MediaStoreSource extends CollectionSource {
|
|||
state = SourceState.loading;
|
||||
|
||||
debugPrint('$runtimeType refreshUris ${changedUris.length} uris');
|
||||
final uriByContentId = Map.fromEntries(changedUris.map((uri) {
|
||||
final changedUriByContentId = Map.fromEntries(changedUris.map((uri) {
|
||||
final pathSegments = Uri.parse(uri).pathSegments;
|
||||
// e.g. URI `content://media/` has no path segment
|
||||
if (pathSegments.isEmpty) return null;
|
||||
|
@ -253,16 +250,16 @@ class MediaStoreSource extends CollectionSource {
|
|||
}).whereNotNull());
|
||||
|
||||
// clean up obsolete entries
|
||||
final obsoleteContentIds = (await mediaStoreService.checkObsoleteContentIds(uriByContentId.keys.toList())).toSet();
|
||||
final obsoleteUris = obsoleteContentIds.map((contentId) => uriByContentId[contentId]).whereNotNull().toSet();
|
||||
final obsoleteContentIds = (await mediaStoreService.checkObsoleteContentIds(changedUriByContentId.keys.toList())).toSet();
|
||||
final obsoleteUris = obsoleteContentIds.map((contentId) => changedUriByContentId[contentId]).whereNotNull().toSet();
|
||||
await removeEntries(obsoleteUris, includeTrash: false);
|
||||
obsoleteContentIds.forEach(uriByContentId.remove);
|
||||
obsoleteContentIds.forEach(changedUriByContentId.remove);
|
||||
|
||||
// fetch new entries
|
||||
final tempUris = <String>{};
|
||||
final newEntries = <AvesEntry>{}, entriesToRefresh = <AvesEntry>{};
|
||||
final existingDirectories = <String>{};
|
||||
for (final kv in uriByContentId.entries) {
|
||||
for (final kv in changedUriByContentId.entries) {
|
||||
final contentId = kv.key;
|
||||
final uri = kv.value;
|
||||
final sourceEntry = await mediaFetchService.getEntry(uri, null);
|
||||
|
@ -276,7 +273,7 @@ class MediaStoreSource extends CollectionSource {
|
|||
if (existingEntry != null) {
|
||||
entriesToRefresh.add(existingEntry);
|
||||
} else {
|
||||
sourceEntry.id = metadataDb.nextId;
|
||||
sourceEntry.id = localMediaDb.nextId;
|
||||
newEntries.add(sourceEntry);
|
||||
}
|
||||
final existingDirectory = existingEntry?.directory;
|
||||
|
@ -303,8 +300,22 @@ class MediaStoreSource extends CollectionSource {
|
|||
state = SourceState.ready;
|
||||
|
||||
if (newEntries.isNotEmpty) {
|
||||
await localMediaDb.insertEntries(newEntries);
|
||||
|
||||
// TODO TLAD find duplication cause
|
||||
final duplicates = await localMediaDb.searchLiveDuplicates(EntryOrigins.mediaStoreContent, newEntries);
|
||||
if (duplicates.isNotEmpty) {
|
||||
unawaited(reportService.recordError(Exception('Refreshing entries yielded duplicates=${duplicates.join(', ')}'), StackTrace.current));
|
||||
// post-error cleanup
|
||||
await localMediaDb.removeIds(duplicates.map((v) => v.id).toSet());
|
||||
for (final duplicate in duplicates) {
|
||||
final duplicateId = duplicate.id;
|
||||
newEntries.removeWhere((v) => duplicateId == v.id);
|
||||
tempUris.add(duplicate.uri);
|
||||
}
|
||||
}
|
||||
|
||||
addEntries(newEntries);
|
||||
await metadataDb.saveEntries(newEntries);
|
||||
await analyze(analysisController, entries: newEntries);
|
||||
}
|
||||
|
||||
|
@ -346,7 +357,7 @@ class MediaStoreSource extends CollectionSource {
|
|||
// vault
|
||||
|
||||
Future<void> _loadVaultEntries(String? directory) async {
|
||||
addEntries(await metadataDb.loadEntries(origin: EntryOrigins.vault, directory: directory));
|
||||
addEntries(await localMediaDb.loadEntries(origin: EntryOrigins.vault, directory: directory));
|
||||
}
|
||||
|
||||
Future<void> _refreshVaultEntries({
|
||||
|
@ -367,7 +378,7 @@ class MediaStoreSource extends CollectionSource {
|
|||
final sourceEntry = await mediaFetchService.getEntry(uri, null, allowUnsized: true);
|
||||
if (sourceEntry != null) {
|
||||
newEntries.add(sourceEntry.copyWith(
|
||||
id: metadataDb.nextId,
|
||||
id: localMediaDb.nextId,
|
||||
origin: EntryOrigins.vault,
|
||||
));
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ mixin TagMixin on SourceBase {
|
|||
List<String> sortedTags = List.unmodifiable([]);
|
||||
|
||||
Future<void> loadCatalogMetadata({Set<int>? ids}) async {
|
||||
final saved = await (ids != null ? metadataDb.loadCatalogMetadataById(ids) : metadataDb.loadCatalogMetadata());
|
||||
final saved = await (ids != null ? localMediaDb.loadCatalogMetadataById(ids) : localMediaDb.loadCatalogMetadata());
|
||||
final idMap = entryById;
|
||||
saved.forEach((metadata) => idMap[metadata.id]?.catalogMetadata = metadata);
|
||||
invalidateEntries();
|
||||
|
@ -48,7 +48,7 @@ mixin TagMixin on SourceBase {
|
|||
if (entry.isCatalogued) {
|
||||
newMetadata.add(entry.catalogMetadata!);
|
||||
if (newMetadata.length >= commitCountThreshold) {
|
||||
await metadataDb.saveCatalogMetadata(Set.unmodifiable(newMetadata));
|
||||
await localMediaDb.saveCatalogMetadata(Set.unmodifiable(newMetadata));
|
||||
onCatalogMetadataChanged();
|
||||
newMetadata.clear();
|
||||
}
|
||||
|
@ -59,7 +59,7 @@ mixin TagMixin on SourceBase {
|
|||
}
|
||||
setProgress(done: ++progressDone, total: progressTotal);
|
||||
}
|
||||
await metadataDb.saveCatalogMetadata(Set.unmodifiable(newMetadata));
|
||||
await localMediaDb.saveCatalogMetadata(Set.unmodifiable(newMetadata));
|
||||
onCatalogMetadataChanged();
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ mixin TrashMixin on SourceBase {
|
|||
static const Duration binKeepDuration = Duration(days: 30);
|
||||
|
||||
Future<void> loadTrashDetails() async {
|
||||
final saved = await metadataDb.loadAllTrashDetails();
|
||||
final saved = await localMediaDb.loadAllTrashDetails();
|
||||
final idMap = entryById;
|
||||
saved.forEach((details) => idMap[details.id]?.trashDetails = details);
|
||||
}
|
||||
|
@ -63,13 +63,13 @@ mixin TrashMixin on SourceBase {
|
|||
entry.trashed = true;
|
||||
entry.trashDetails = _buildTrashDetails(id);
|
||||
// persist
|
||||
await metadataDb.updateEntry(id, entry);
|
||||
await metadataDb.updateTrash(id, entry.trashDetails);
|
||||
await localMediaDb.updateEntry(id, entry);
|
||||
await localMediaDb.updateTrash(id, entry.trashDetails);
|
||||
} else {
|
||||
// there is no matching entry
|
||||
final sourceEntry = await mediaFetchService.getEntry(uri, null, allowUnsized: true);
|
||||
if (sourceEntry != null) {
|
||||
final id = metadataDb.nextId;
|
||||
final id = localMediaDb.nextId;
|
||||
sourceEntry.id = id;
|
||||
sourceEntry.path = pContext.join(recoveryPath, pContext.basename(untrackedPath));
|
||||
sourceEntry.trashed = true;
|
||||
|
|
|
@ -23,7 +23,7 @@ class Vaults extends ChangeNotifier {
|
|||
Vaults._private();
|
||||
|
||||
Future<void> init() async {
|
||||
_rows = await metadataDb.loadAllVaults();
|
||||
_rows = await localMediaDb.loadAllVaults();
|
||||
_vaultDirPaths = null;
|
||||
final screenStateStream = Platform.isAndroid ? AvesScreenState().screenStateStream : null;
|
||||
if (screenStateStream != null) {
|
||||
|
@ -44,7 +44,7 @@ class Vaults extends ChangeNotifier {
|
|||
VaultDetails? detailsForPath(String dirPath) => _rows.firstWhereOrNull((v) => v.path == dirPath);
|
||||
|
||||
Future<void> create(VaultDetails details) async {
|
||||
await metadataDb.addVaults({details});
|
||||
await localMediaDb.addVaults({details});
|
||||
|
||||
_rows.add(details);
|
||||
_vaultDirPaths = null;
|
||||
|
@ -56,7 +56,7 @@ class Vaults extends ChangeNotifier {
|
|||
final details = dirPaths.map(detailsForPath).whereNotNull().toSet();
|
||||
if (details.isEmpty) return;
|
||||
|
||||
await metadataDb.removeVaults(details);
|
||||
await localMediaDb.removeVaults(details);
|
||||
|
||||
await Future.forEach(details, (v) => securityService.writeValue(v.passKey, null));
|
||||
|
||||
|
@ -74,7 +74,7 @@ class Vaults extends ChangeNotifier {
|
|||
if (newName == null) return;
|
||||
|
||||
final newDetails = oldDetails.copyWith(name: newName);
|
||||
await metadataDb.updateVault(oldDetails.name, newDetails);
|
||||
await localMediaDb.updateVault(oldDetails.name, newDetails);
|
||||
|
||||
final pass = await securityService.readValue(oldDetails.passKey);
|
||||
if (pass != null) {
|
||||
|
@ -96,7 +96,7 @@ class Vaults extends ChangeNotifier {
|
|||
final oldDetails = detailsForPath(newDetails.path);
|
||||
if (oldDetails == null) return;
|
||||
|
||||
await metadataDb.updateVault(newDetails.name, newDetails);
|
||||
await localMediaDb.updateVault(newDetails.name, newDetails);
|
||||
|
||||
_rows
|
||||
..remove(oldDetails)
|
||||
|
@ -104,7 +104,7 @@ class Vaults extends ChangeNotifier {
|
|||
}
|
||||
|
||||
Future<void> clear() async {
|
||||
await metadataDb.clearVaults();
|
||||
await localMediaDb.clearVaults();
|
||||
_rows.clear();
|
||||
_vaultDirPaths = null;
|
||||
}
|
||||
|
@ -146,7 +146,7 @@ class Vaults extends ChangeNotifier {
|
|||
final newEntries = await recoverUntrackedItems(source, dirPath);
|
||||
if (newEntries.isNotEmpty) {
|
||||
source.addEntries(newEntries);
|
||||
await metadataDb.saveEntries(newEntries);
|
||||
await localMediaDb.insertEntries(newEntries);
|
||||
unawaited(source.analyze(null, entries: newEntries));
|
||||
}
|
||||
|
||||
|
@ -168,7 +168,7 @@ class Vaults extends ChangeNotifier {
|
|||
final uri = Uri.file(untrackedPath).toString();
|
||||
final sourceEntry = await mediaFetchService.getEntry(uri, null, allowUnsized: true);
|
||||
if (sourceEntry != null) {
|
||||
sourceEntry.id = metadataDb.nextId;
|
||||
sourceEntry.id = localMediaDb.nextId;
|
||||
sourceEntry.origin = EntryOrigins.vault;
|
||||
newEntries.add(sourceEntry);
|
||||
} else {
|
||||
|
|
|
@ -13,6 +13,7 @@ import 'package:aves_model/aves_model.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
|
||||
class AnalysisService {
|
||||
static const _platform = MethodChannel('deckers.thibault/aves/analysis');
|
||||
|
@ -29,6 +30,10 @@ class AnalysisService {
|
|||
}
|
||||
|
||||
static Future<void> startService({required bool force, List<int>? entryIds}) async {
|
||||
// from Android 13 (API 33), notifications are off by default,
|
||||
// so the user needs to grant the permission to see the service notification
|
||||
unawaited(Permission.notification.request());
|
||||
|
||||
await reportService.log('Start analysis service${entryIds != null ? ' for ${entryIds.length} items' : ''}');
|
||||
try {
|
||||
await _platform.invokeMethod('startAnalysis', <String, dynamic>{
|
||||
|
@ -48,7 +53,7 @@ Future<void> _init() async {
|
|||
WidgetsFlutterBinding.ensureInitialized();
|
||||
initPlatformServices();
|
||||
await androidFileUtils.init();
|
||||
await metadataDb.init();
|
||||
await localMediaDb.init();
|
||||
await device.init();
|
||||
await mobileServices.init();
|
||||
await settings.init(monitorPlatformSettings: false);
|
||||
|
|
|
@ -43,7 +43,7 @@ class PlatformAppService implements AppService {
|
|||
'com.sony.playmemories.mobile': {'Imaging Edge Mobile'},
|
||||
'nekox.messenger': {'NekoX'},
|
||||
'org.telegram.messenger': {'Telegram Images', 'Telegram Video'},
|
||||
'com.whatsapp': {'Whatsapp', 'WhatsApp Animated Gifs', 'WhatsApp Documents', 'WhatsApp Images', 'WhatsApp Video'}
|
||||
'com.whatsapp': {'WhatsApp Animated Gifs', 'WhatsApp Documents', 'WhatsApp Images', 'WhatsApp Video'}
|
||||
};
|
||||
|
||||
@override
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import 'package:aves/model/availability.dart';
|
||||
import 'package:aves/model/db/db_metadata.dart';
|
||||
import 'package:aves/model/db/db_metadata_sqflite.dart';
|
||||
import 'package:aves/model/db/db.dart';
|
||||
import 'package:aves/model/db/db_sqflite.dart';
|
||||
import 'package:aves/model/settings/store_shared_pref.dart';
|
||||
import 'package:aves/services/app_service.dart';
|
||||
import 'package:aves/services/device_service.dart';
|
||||
|
@ -32,7 +32,7 @@ final SettingsStore settingsStore = SharedPrefSettingsStore();
|
|||
|
||||
final p.Context pContext = getIt<p.Context>();
|
||||
final AvesAvailability availability = getIt<AvesAvailability>();
|
||||
final MetadataDb metadataDb = getIt<MetadataDb>();
|
||||
final LocalMediaDb localMediaDb = getIt<LocalMediaDb>();
|
||||
final AvesVideoControllerFactory videoControllerFactory = getIt<AvesVideoControllerFactory>();
|
||||
final AvesVideoMetadataFetcher videoMetadataFetcher = getIt<AvesVideoMetadataFetcher>();
|
||||
|
||||
|
@ -54,7 +54,7 @@ final WindowService windowService = getIt<WindowService>();
|
|||
void initPlatformServices() {
|
||||
getIt.registerLazySingleton<p.Context>(p.Context.new);
|
||||
getIt.registerLazySingleton<AvesAvailability>(LiveAvesAvailability.new);
|
||||
getIt.registerLazySingleton<MetadataDb>(SqfliteMetadataDb.new);
|
||||
getIt.registerLazySingleton<LocalMediaDb>(SqfliteLocalMediaDb.new);
|
||||
getIt.registerLazySingleton<AvesVideoControllerFactory>(MpvVideoControllerFactory.new);
|
||||
getIt.registerLazySingleton<AvesVideoMetadataFetcher>(FfmpegVideoMetadataFetcher.new);
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ Future<void> _init() async {
|
|||
|
||||
// service initialization for path context, database
|
||||
initPlatformServices();
|
||||
await metadataDb.init();
|
||||
await localMediaDb.init();
|
||||
|
||||
// `intl` initialization for date formatting
|
||||
await initializeDateFormatting();
|
||||
|
@ -55,8 +55,8 @@ Future<List<Map<String, String?>>> _getSuggestions(dynamic args) async {
|
|||
debugPrint('getSuggestions query=$query, locale=$locale use24hour=$use24hour');
|
||||
|
||||
if (query is String && locale is String) {
|
||||
final entries = (await metadataDb.searchLiveEntries(query, limit: 9)).toList();
|
||||
final catalogMetadata = await metadataDb.loadCatalogMetadataById(entries.map((entry) => entry.id).toSet());
|
||||
final entries = (await localMediaDb.searchLiveEntries(query, limit: 9)).toList();
|
||||
final catalogMetadata = await localMediaDb.loadCatalogMetadataById(entries.map((entry) => entry.id).toSet());
|
||||
catalogMetadata.forEach((metadata) => entries.firstWhereOrNull((entry) => entry.id == metadata.id)?.catalogMetadata = metadata);
|
||||
entries.sort(AvesEntrySort.compareByDate);
|
||||
|
||||
|
|
|
@ -71,7 +71,6 @@ class AvesApp extends StatefulWidget {
|
|||
'or', // Odia
|
||||
'sat', // Santali
|
||||
'sl', // Slovenian
|
||||
'sv', // Swedish
|
||||
'th', // Thai
|
||||
}.map(Locale.new).toSet();
|
||||
static final List<Locale> supportedLocales = AppLocalizations.supportedLocales.where((v) => !_unsupportedLocales.contains(v)).toList();
|
||||
|
@ -648,10 +647,12 @@ class _AvesAppState extends State<AvesApp> with WidgetsBindingObserver {
|
|||
|
||||
Future<void> _onAnalysisCompletion() async {
|
||||
debugPrint('Analysis completed');
|
||||
if (_mediaStoreSource.initState != SourceInitializationState.none) {
|
||||
await _mediaStoreSource.loadCatalogMetadata();
|
||||
await _mediaStoreSource.loadAddresses();
|
||||
_mediaStoreSource.updateDerivedFilters();
|
||||
}
|
||||
}
|
||||
|
||||
void _onError(String? error) => reportService.recordError(error, null);
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ import 'package:aves/widgets/collection/draggable_thumb_label.dart';
|
|||
import 'package:aves/widgets/collection/grid/list_details_theme.dart';
|
||||
import 'package:aves/widgets/collection/grid/section_layout.dart';
|
||||
import 'package:aves/widgets/collection/grid/tile.dart';
|
||||
import 'package:aves/widgets/common/action_mixins/feedback.dart';
|
||||
import 'package:aves/widgets/common/basic/draggable_scrollbar/scrollbar.dart';
|
||||
import 'package:aves/widgets/common/basic/insets.dart';
|
||||
import 'package:aves/widgets/common/behaviour/routes.dart';
|
||||
|
@ -587,7 +588,13 @@ class _CollectionScrollViewState extends State<_CollectionScrollView> with Widge
|
|||
valueListenable: collection.source.stateNotifier,
|
||||
builder: (context, sourceState, child) {
|
||||
if (sourceState == SourceState.loading) {
|
||||
return const SizedBox();
|
||||
return EmptyContent(
|
||||
text: context.l10n.sourceStateLoading,
|
||||
bottom: const Padding(
|
||||
padding: EdgeInsets.only(top: 16),
|
||||
child: ReportProgressIndicator(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return FutureBuilder<bool>(
|
||||
|
|
|
@ -166,6 +166,9 @@ class ReportOverlay<T> extends StatefulWidget {
|
|||
final VoidCallback? onCancel;
|
||||
final void Function(Set<T> processed) onDone;
|
||||
|
||||
static const double diameter = 160.0;
|
||||
static const double strokeWidth = 8.0;
|
||||
|
||||
const ReportOverlay({
|
||||
super.key,
|
||||
required this.opStream,
|
||||
|
@ -186,8 +189,6 @@ class _ReportOverlayState<T> extends State<ReportOverlay<T>> with SingleTickerPr
|
|||
Stream<T> get opStream => widget.opStream;
|
||||
|
||||
static const double fontSize = 18.0;
|
||||
static const double diameter = 160.0;
|
||||
static const double strokeWidth = 8.0;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -222,6 +223,8 @@ class _ReportOverlayState<T> extends State<ReportOverlay<T>> with SingleTickerPr
|
|||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const diameter = ReportOverlay.diameter;
|
||||
const strokeWidth = ReportOverlay.strokeWidth;
|
||||
final percentFormatter = NumberFormat.percentPattern(context.locale);
|
||||
|
||||
final theme = Theme.of(context);
|
||||
|
@ -249,16 +252,7 @@ class _ReportOverlayState<T> extends State<ReportOverlay<T>> with SingleTickerPr
|
|||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
if (animate)
|
||||
Container(
|
||||
width: diameter,
|
||||
height: diameter,
|
||||
padding: const EdgeInsets.all(strokeWidth / 2),
|
||||
child: CircularProgressIndicator(
|
||||
color: progressColor.withOpacity(.1),
|
||||
strokeWidth: strokeWidth,
|
||||
),
|
||||
),
|
||||
if (animate) const ReportProgressIndicator(opacity: .1),
|
||||
CircularPercentIndicator(
|
||||
percent: percent,
|
||||
lineWidth: strokeWidth,
|
||||
|
@ -301,6 +295,32 @@ class _ReportOverlayState<T> extends State<ReportOverlay<T>> with SingleTickerPr
|
|||
}
|
||||
}
|
||||
|
||||
class ReportProgressIndicator extends StatelessWidget {
|
||||
final double opacity;
|
||||
|
||||
const ReportProgressIndicator({
|
||||
super.key,
|
||||
this.opacity = 1,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
const diameter = ReportOverlay.diameter;
|
||||
const strokeWidth = ReportOverlay.strokeWidth;
|
||||
final progressColor = Theme.of(context).colorScheme.primary;
|
||||
|
||||
return Container(
|
||||
width: diameter,
|
||||
height: diameter,
|
||||
padding: const EdgeInsets.all(strokeWidth / 2),
|
||||
child: CircularProgressIndicator(
|
||||
color: progressColor.withOpacity(opacity),
|
||||
strokeWidth: strokeWidth,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _FeedbackMessage extends StatefulWidget {
|
||||
final FeedbackType type;
|
||||
final String message;
|
||||
|
|
|
@ -70,7 +70,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
|||
),
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () => metadataDb.reset().then((_) => _reload()),
|
||||
onPressed: () => localMediaDb.reset().then((_) => _reload()),
|
||||
child: const Text('Reset'),
|
||||
),
|
||||
],
|
||||
|
@ -93,7 +93,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
|||
),
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () => metadataDb.clearEntries().then((_) => _reload()),
|
||||
onPressed: () => localMediaDb.clearEntries().then((_) => _reload()),
|
||||
child: const Text('Clear'),
|
||||
),
|
||||
],
|
||||
|
@ -114,7 +114,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
|||
),
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () => metadataDb.clearDates().then((_) => _reload()),
|
||||
onPressed: () => localMediaDb.clearDates().then((_) => _reload()),
|
||||
child: const Text('Clear'),
|
||||
),
|
||||
],
|
||||
|
@ -135,7 +135,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
|||
),
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () => metadataDb.clearCatalogMetadata().then((_) => _reload()),
|
||||
onPressed: () => localMediaDb.clearCatalogMetadata().then((_) => _reload()),
|
||||
child: const Text('Clear'),
|
||||
),
|
||||
],
|
||||
|
@ -156,7 +156,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
|||
),
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () => metadataDb.clearAddresses().then((_) => _reload()),
|
||||
onPressed: () => localMediaDb.clearAddresses().then((_) => _reload()),
|
||||
child: const Text('Clear'),
|
||||
),
|
||||
],
|
||||
|
@ -177,7 +177,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
|||
),
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () => metadataDb.clearTrashDetails().then((_) => _reload()),
|
||||
onPressed: () => localMediaDb.clearTrashDetails().then((_) => _reload()),
|
||||
child: const Text('Clear'),
|
||||
),
|
||||
],
|
||||
|
@ -261,7 +261,7 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
|||
),
|
||||
const SizedBox(width: 8),
|
||||
ElevatedButton(
|
||||
onPressed: () => metadataDb.clearVideoPlayback().then((_) => _reload()),
|
||||
onPressed: () => localMediaDb.clearVideoPlayback().then((_) => _reload()),
|
||||
child: const Text('Clear'),
|
||||
),
|
||||
],
|
||||
|
@ -281,16 +281,16 @@ class _DebugAppDatabaseSectionState extends State<DebugAppDatabaseSection> with
|
|||
}
|
||||
|
||||
void _startDbReport() {
|
||||
_dbFileSizeLoader = metadataDb.dbFileSize();
|
||||
_dbEntryLoader = metadataDb.loadEntries();
|
||||
_dbDateLoader = metadataDb.loadDates();
|
||||
_dbMetadataLoader = metadataDb.loadCatalogMetadata();
|
||||
_dbAddressLoader = metadataDb.loadAddresses();
|
||||
_dbTrashLoader = metadataDb.loadAllTrashDetails();
|
||||
_dbVaultsLoader = metadataDb.loadAllVaults();
|
||||
_dbFavouritesLoader = metadataDb.loadAllFavourites();
|
||||
_dbCoversLoader = metadataDb.loadAllCovers();
|
||||
_dbVideoPlaybackLoader = metadataDb.loadAllVideoPlayback();
|
||||
_dbFileSizeLoader = localMediaDb.dbFileSize();
|
||||
_dbEntryLoader = localMediaDb.loadEntries();
|
||||
_dbDateLoader = localMediaDb.loadDates();
|
||||
_dbMetadataLoader = localMediaDb.loadCatalogMetadata();
|
||||
_dbAddressLoader = localMediaDb.loadAddresses();
|
||||
_dbTrashLoader = localMediaDb.loadAllTrashDetails();
|
||||
_dbVaultsLoader = localMediaDb.loadAllVaults();
|
||||
_dbFavouritesLoader = localMediaDb.loadAllFavourites();
|
||||
_dbCoversLoader = localMediaDb.loadAllCovers();
|
||||
_dbVideoPlaybackLoader = localMediaDb.loadAllVideoPlayback();
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
|
|
|
@ -245,7 +245,7 @@ class _HomePageState extends State<HomePage> {
|
|||
|
||||
Future<void> _initViewerEssentials() async {
|
||||
// for video playback storage
|
||||
await metadataDb.init();
|
||||
await localMediaDb.init();
|
||||
}
|
||||
|
||||
bool _isViewerSourceable(AvesEntry? viewerEntry) {
|
||||
|
|
|
@ -29,6 +29,7 @@ class SupportedLocales {
|
|||
'ro': 'Română',
|
||||
'ru': 'Русский',
|
||||
'sk': 'Slovenčina',
|
||||
'sv': 'Svenska',
|
||||
'tr': 'Türkçe',
|
||||
'uk': 'Українська',
|
||||
'vi': 'Tiếng Việt',
|
||||
|
|
|
@ -40,12 +40,12 @@ class _DbTabState extends State<DbTab> {
|
|||
|
||||
void _loadDatabase() {
|
||||
final id = entry.id;
|
||||
_dbDateLoader = metadataDb.loadDates().then((values) => values[id]);
|
||||
_dbEntryLoader = metadataDb.loadEntriesById({id}).then((values) => values.firstOrNull);
|
||||
_dbMetadataLoader = metadataDb.loadCatalogMetadata().then((values) => values.firstWhereOrNull((row) => row.id == id));
|
||||
_dbAddressLoader = metadataDb.loadAddresses().then((values) => values.firstWhereOrNull((row) => row.id == id));
|
||||
_dbTrashDetailsLoader = metadataDb.loadAllTrashDetails().then((values) => values.firstWhereOrNull((row) => row.id == id));
|
||||
_dbVideoPlaybackLoader = metadataDb.loadVideoPlayback(id);
|
||||
_dbDateLoader = localMediaDb.loadDates().then((values) => values[id]);
|
||||
_dbEntryLoader = localMediaDb.loadEntriesById({id}).then((values) => values.firstOrNull);
|
||||
_dbMetadataLoader = localMediaDb.loadCatalogMetadata().then((values) => values.firstWhereOrNull((row) => row.id == id));
|
||||
_dbAddressLoader = localMediaDb.loadAddresses().then((values) => values.firstWhereOrNull((row) => row.id == id));
|
||||
_dbTrashDetailsLoader = localMediaDb.loadAllTrashDetails().then((values) => values.firstWhereOrNull((row) => row.id == id));
|
||||
_dbVideoPlaybackLoader = localMediaDb.loadVideoPlayback(id);
|
||||
setState(() {});
|
||||
}
|
||||
|
||||
|
@ -93,6 +93,15 @@ class _DbTabState extends State<DbTab> {
|
|||
},
|
||||
child: const Text('Untrack entry'),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
final duplicates = {entry.copyWith(id: localMediaDb.nextId)};
|
||||
final source = context.read<CollectionSource>();
|
||||
source.addEntries(duplicates);
|
||||
await localMediaDb.insertEntries(duplicates);
|
||||
},
|
||||
child: const Text('Duplicate entry'),
|
||||
),
|
||||
InfoRowGroup(
|
||||
info: {
|
||||
'uri': data.uri,
|
||||
|
@ -184,7 +193,7 @@ class _DbTabState extends State<DbTab> {
|
|||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
entry.trashDetails = null;
|
||||
await metadataDb.updateTrash(entry.id, entry.trashDetails);
|
||||
await localMediaDb.updateTrash(entry.id, entry.trashDetails);
|
||||
_loadDatabase();
|
||||
},
|
||||
child: const Text('Remove details'),
|
||||
|
|
|
@ -568,9 +568,12 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
|
|||
} else if (notification is CastNotification) {
|
||||
_cast(notification.enabled);
|
||||
} else if (notification is FullImageLoadedNotification) {
|
||||
final viewStateController = context.read<ViewStateConductor>().getOrCreateController(notification.entry);
|
||||
// microtask so that listeners do not trigger during build
|
||||
scheduleMicrotask(() => viewStateController.fullImageNotifier.value = notification.image);
|
||||
scheduleMicrotask(() {
|
||||
if (!mounted) return;
|
||||
final viewStateController = context.read<ViewStateConductor>().getController(notification.entry);
|
||||
viewStateController?.fullImageNotifier.value = notification.image;
|
||||
});
|
||||
} else if (notification is EntryDeletedNotification) {
|
||||
_onEntryRemoved(context, notification.entries);
|
||||
} else if (notification is EntryMovedNotification) {
|
||||
|
|
|
@ -16,12 +16,12 @@ class DatabasePlaybackStateHandler extends PlaybackStateHandler {
|
|||
|
||||
@override
|
||||
Future<int?> getResumeTime({required int entryId, required BuildContext context}) async {
|
||||
final playback = await metadataDb.loadVideoPlayback(entryId);
|
||||
final playback = await localMediaDb.loadVideoPlayback(entryId);
|
||||
final resumeTime = playback?.resumeTimeMillis ?? 0;
|
||||
if (resumeTime == 0) return null;
|
||||
|
||||
// clear on retrieval
|
||||
await metadataDb.removeVideoPlayback({entryId});
|
||||
await localMediaDb.removeVideoPlayback({entryId});
|
||||
|
||||
switch (settings.videoResumptionMode) {
|
||||
case VideoResumptionMode.never:
|
||||
|
@ -54,14 +54,14 @@ class DatabasePlaybackStateHandler extends PlaybackStateHandler {
|
|||
@override
|
||||
Future<void> saveResumeTime({required int entryId, required int position, required double progress}) async {
|
||||
if (resumeTimeSaveMinProgress < progress && progress < resumeTimeSaveMaxProgress) {
|
||||
await metadataDb.addVideoPlayback({
|
||||
await localMediaDb.addVideoPlayback({
|
||||
VideoPlaybackRow(
|
||||
entryId: entryId,
|
||||
resumeTimeMillis: position,
|
||||
)
|
||||
});
|
||||
} else {
|
||||
await metadataDb.removeVideoPlayback({entryId});
|
||||
await localMediaDb.removeVideoPlayback({entryId});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: _flutterfire_internals
|
||||
sha256: b1595874fbc8f7a50da90f5d8f327bb0bfd6a95dc906c390efe991540c3b54aa
|
||||
sha256: "9371d13b8ee442e3bfc08a24e3a1b3742c839abbfaf5eef11b79c4b862c89bf7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.40"
|
||||
version: "1.3.41"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -68,42 +68,42 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_core
|
||||
sha256: "3187f4f8e49968573fd7403011dca67ba95aae419bc0d8131500fae160d94f92"
|
||||
sha256: "06537da27db981947fa535bb91ca120b4e9cb59cb87278dbdde718558cafc9ff"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.0"
|
||||
version: "3.4.0"
|
||||
firebase_core_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_platform_interface
|
||||
sha256: "3c3a1e92d6f4916c32deea79c4a7587aa0e9dbbe5889c7a16afcf005a485ee02"
|
||||
sha256: f7d7180c7f99babd4b4c517754d41a09a4943a0f7a69b65c894ca5c68ba66315
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.2.0"
|
||||
version: "5.2.1"
|
||||
firebase_core_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_web
|
||||
sha256: e8d1e22de72cb21cdcfc5eed7acddab3e99cd83f3b317f54f7a96c32f25fd11e
|
||||
sha256: "362e52457ed2b7b180964769c1e04d1e0ea0259fdf7025fdfedd019d4ae2bd88"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.17.4"
|
||||
version: "2.17.5"
|
||||
firebase_crashlytics:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_crashlytics
|
||||
sha256: "30260e1b8ad1464b41ca4531b44ce63d752daaf2f12c92ca6cdcd82b270abecc"
|
||||
sha256: "4c9872020c0d97a161362ee6af7000cfdb8666234ddc290a15252ad379bb235a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.4"
|
||||
version: "4.1.0"
|
||||
firebase_crashlytics_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_crashlytics_platform_interface
|
||||
sha256: a75e1826d92ea4e86e4a753c7b5d64b844a362676fa653185f1581c859186d18
|
||||
sha256: ede8a199ff03378857d3c8cbb7fa58d37c27bb5a6b75faf8415ff6925dcaae2a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.6.40"
|
||||
version: "3.6.41"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
|
@ -264,10 +264,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc
|
||||
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.2.4"
|
||||
version: "14.2.5"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -89,10 +89,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: device_info_plus
|
||||
sha256: "93429694c9253d2871b3af80cf11b3cbb5c65660d402ed7bf69854ce4a089f82"
|
||||
sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.1.1"
|
||||
version: "10.1.2"
|
||||
device_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -113,10 +113,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
|
||||
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.3"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -158,10 +158,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: flutter_plugin_android_lifecycle
|
||||
sha256: "9d98bd47ef9d34e803d438f17fd32b116d31009f534a6fa5ce3a1167f189a6de"
|
||||
sha256: "9ee02950848f61c4129af3d6ec84a1cfc0e47931abc746b03e7a3bc3e8ff6eda"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.21"
|
||||
version: "2.0.22"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
|
@ -203,26 +203,26 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: google_maps_flutter
|
||||
sha256: "1998c58100487af9c645ba05961e7eab8b20795611e67b1296311746a55037d4"
|
||||
sha256: "2e302fa3aaf4e2a297f0342d83ebc5e8e9f826e9a716aef473fe7f404ec630a7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.8.0"
|
||||
version: "2.9.0"
|
||||
google_maps_flutter_android:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: google_maps_flutter_android
|
||||
sha256: f34fec69957739245d732667429a6831f97419261f6f3c31cc6489eb3976444e
|
||||
sha256: "60a005bf1ba8d178144e442f6e2d734b0ffc2cc800a05415388472f934ad6d6a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
version: "2.14.4"
|
||||
google_maps_flutter_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_maps_flutter_ios
|
||||
sha256: bfa2583bfb2cf2bcd85c0d12366a2a4f38fff70d75bc1d089685677bd5d3acec
|
||||
sha256: "3a484846fc56f15e47e3de1f5ea80a7ff2b31721d2faa88f390f3b3cf580c953"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.12.0"
|
||||
version: "2.13.0"
|
||||
google_maps_flutter_platform_interface:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -456,10 +456,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "015002c060f1ae9f41a818f2d5640389cc05283e368be19dc8d77cecb43c40c9"
|
||||
sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.5.3"
|
||||
version: "5.5.4"
|
||||
win32_registry:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
|
@ -74,10 +74,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||
sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
version: "3.0.5"
|
||||
dbus:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -98,10 +98,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
|
||||
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.3"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -180,10 +180,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: media_kit
|
||||
sha256: "3289062540e3b8b9746e5c50d95bd78a9289826b7227e253dff806d002b9e67a"
|
||||
sha256: "1f1deee148533d75129a6f38251ff8388e33ee05fc2d20a6a80e57d6051b7b62"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.10+1"
|
||||
version: "1.1.11"
|
||||
media_kit_libs_android_video:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -196,18 +196,18 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: media_kit_native_event_loop
|
||||
sha256: a605cf185499d14d58935b8784955a92a4bf0ff4e19a23de3d17a9106303930e
|
||||
sha256: "7d82e3b3e9ded5c35c3146c5ba1da3118d1dd8ac3435bac7f29f458181471b40"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.8"
|
||||
version: "1.0.9"
|
||||
media_kit_video:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: media_kit_video
|
||||
sha256: c048d11a19e379aebbe810647636e3fc6d18374637e2ae12def4ff8a4b99a882
|
||||
sha256: "2cc3b966679963ba25a4ce5b771e532a521ebde7c6aa20e9802bec95d9916c8f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.4"
|
||||
version: "1.2.5"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -220,10 +220,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: package_info_plus
|
||||
sha256: "4de6c36df77ffbcef0a5aefe04669d33f2d18397fea228277b852a2d4e58e860"
|
||||
sha256: a75164ade98cb7d24cfd0a13c6408927c6b217fa60dee5a7ff5c116a58f28918
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.0.1"
|
||||
version: "8.0.2"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -345,10 +345,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: synchronized
|
||||
sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
|
||||
sha256: a824e842b8a054f91a728b783c177c1e4731f6b124f9192468457a8913371255
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0+1"
|
||||
version: "3.2.0"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -401,18 +401,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: volume_controller
|
||||
sha256: "189bdc7a554f476b412e4c8b2f474562b09d74bc458c23667356bce3ca1d48c9"
|
||||
sha256: c71d4c62631305df63b72da79089e078af2659649301807fa746088f365cb48e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.7"
|
||||
version: "2.0.8"
|
||||
wakelock_plus:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_plus
|
||||
sha256: "4fa83a128b4127619e385f686b4f080a5d2de46cff8e8c94eccac5fcf76550e5"
|
||||
sha256: bf4ee6f17a2fa373ed3753ad0e602b7603f8c75af006d5b9bdade263928c0484
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.7"
|
||||
version: "1.2.8"
|
||||
wakelock_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -425,18 +425,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "97da13628db363c635202ad97068d47c5b8aa555808e7a9411963c533b449b27"
|
||||
sha256: d43c1d6b787bf0afad444700ae7f4db8827f701bc61c255ac8d328c6f4d52062
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.5.1"
|
||||
version: "1.0.0"
|
||||
win32:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "015002c060f1ae9f41a818f2d5640389cc05283e368be19dc8d77cecb43c40c9"
|
||||
sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.5.3"
|
||||
version: "5.5.4"
|
||||
xml:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
174
pubspec.lock
|
@ -13,10 +13,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: _flutterfire_internals
|
||||
sha256: b1595874fbc8f7a50da90f5d8f327bb0bfd6a95dc906c390efe991540c3b54aa
|
||||
sha256: "9371d13b8ee442e3bfc08a24e3a1b3742c839abbfaf5eef11b79c4b862c89bf7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.40"
|
||||
version: "1.3.41"
|
||||
_macros:
|
||||
dependency: transitive
|
||||
description: dart
|
||||
|
@ -157,10 +157,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: bidi
|
||||
sha256: "1a7d0c696324b2089f72e7671fd1f1f64fef44c980f3cebc84e803967c597b63"
|
||||
sha256: "9a712c7ddf708f7c41b1923aa83648a3ed44cfd75b04f72d598c45e5be287f9d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.10"
|
||||
version: "2.0.12"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -215,10 +215,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: connectivity_plus
|
||||
sha256: "3e7d1d9dbae40ae82cbe6c23c518f0c4ffe32764ee9749b9a99d32cbac8734f6"
|
||||
sha256: "2056db5241f96cdc0126bd94459fc4cdc13876753768fc7a31c425e50a7177d0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.4"
|
||||
version: "6.0.5"
|
||||
connectivity_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -247,18 +247,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: coverage
|
||||
sha256: "576aaab8b1abdd452e0f656c3e73da9ead9d7880e15bdc494189d9c1a1baf0db"
|
||||
sha256: "7b594a150942e0d3be99cd45a1d0b5caff27ba5a27f292ed8e8d904ba3f167b5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.0"
|
||||
version: "1.9.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: ff625774173754681d66daaf4a448684fb04b78f902da9cb3d308c19cc5e8bab
|
||||
sha256: ec30d999af904f33454ba22ed9a86162b35e52b44ac4807d1d93c288041d7d27
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
version: "3.0.5"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -303,10 +303,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: device_info_plus
|
||||
sha256: "93429694c9253d2871b3af80cf11b3cbb5c65660d402ed7bf69854ce4a089f82"
|
||||
sha256: a7fd703482b391a87d60b6061d04dfdeab07826b96f9abd8f5ed98068acc0074
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.1.1"
|
||||
version: "10.1.2"
|
||||
device_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -368,10 +368,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: "493f37e7df1804778ff3a53bd691d8692ddf69702cf4c1c1096a2e41b4779e21"
|
||||
sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
version: "2.1.3"
|
||||
ffmpeg_kit_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -401,42 +401,42 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core
|
||||
sha256: "3187f4f8e49968573fd7403011dca67ba95aae419bc0d8131500fae160d94f92"
|
||||
sha256: "06537da27db981947fa535bb91ca120b4e9cb59cb87278dbdde718558cafc9ff"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.3.0"
|
||||
version: "3.4.0"
|
||||
firebase_core_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_platform_interface
|
||||
sha256: "3c3a1e92d6f4916c32deea79c4a7587aa0e9dbbe5889c7a16afcf005a485ee02"
|
||||
sha256: f7d7180c7f99babd4b4c517754d41a09a4943a0f7a69b65c894ca5c68ba66315
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.2.0"
|
||||
version: "5.2.1"
|
||||
firebase_core_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_web
|
||||
sha256: e8d1e22de72cb21cdcfc5eed7acddab3e99cd83f3b317f54f7a96c32f25fd11e
|
||||
sha256: "362e52457ed2b7b180964769c1e04d1e0ea0259fdf7025fdfedd019d4ae2bd88"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.17.4"
|
||||
version: "2.17.5"
|
||||
firebase_crashlytics:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_crashlytics
|
||||
sha256: "30260e1b8ad1464b41ca4531b44ce63d752daaf2f12c92ca6cdcd82b270abecc"
|
||||
sha256: "4c9872020c0d97a161362ee6af7000cfdb8666234ddc290a15252ad379bb235a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.4"
|
||||
version: "4.1.0"
|
||||
firebase_crashlytics_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_crashlytics_platform_interface
|
||||
sha256: a75e1826d92ea4e86e4a753c7b5d64b844a362676fa653185f1581c859186d18
|
||||
sha256: ede8a199ff03378857d3c8cbb7fa58d37c27bb5a6b75faf8415ff6925dcaae2a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.6.40"
|
||||
version: "3.6.41"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -457,10 +457,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: flex_seed_scheme
|
||||
sha256: cc08c81879ecfd2ab840664ce4770980da0b8a319e35f51bcf763849b7f7596b
|
||||
sha256: "86470c8dc470f55dd3e28a6d30e3253a1c176df32903263d7daeabfc0c77dbd4"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
version: "3.2.0"
|
||||
floating:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -545,18 +545,18 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_markdown
|
||||
sha256: "2e8a801b1ded5ea001a4529c97b1f213dcb11c6b20668e081cafb23468593514"
|
||||
sha256: a23c41ee57573e62fc2190a1f36a0480c4d90bde3a8a8d7126e5d5992fb53fb7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.3"
|
||||
version: "0.7.3+1"
|
||||
flutter_plugin_android_lifecycle:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: flutter_plugin_android_lifecycle
|
||||
sha256: "9d98bd47ef9d34e803d438f17fd32b116d31009f534a6fa5ce3a1167f189a6de"
|
||||
sha256: "9ee02950848f61c4129af3d6ec84a1cfc0e47931abc746b03e7a3bc3e8ff6eda"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.21"
|
||||
version: "2.0.22"
|
||||
flutter_staggered_animations:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -640,26 +640,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: google_maps_flutter
|
||||
sha256: "1998c58100487af9c645ba05961e7eab8b20795611e67b1296311746a55037d4"
|
||||
sha256: "2e302fa3aaf4e2a297f0342d83ebc5e8e9f826e9a716aef473fe7f404ec630a7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.8.0"
|
||||
version: "2.9.0"
|
||||
google_maps_flutter_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_maps_flutter_android
|
||||
sha256: f34fec69957739245d732667429a6831f97419261f6f3c31cc6489eb3976444e
|
||||
sha256: "60a005bf1ba8d178144e442f6e2d734b0ffc2cc800a05415388472f934ad6d6a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
version: "2.14.4"
|
||||
google_maps_flutter_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_maps_flutter_ios
|
||||
sha256: bfa2583bfb2cf2bcd85c0d12366a2a4f38fff70d75bc1d089685677bd5d3acec
|
||||
sha256: "3a484846fc56f15e47e3de1f5ea80a7ff2b31721d2faa88f390f3b3cf580c953"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.12.0"
|
||||
version: "2.13.0"
|
||||
google_maps_flutter_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -808,10 +808,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: local_auth_android
|
||||
sha256: e99c44ca0bce08f26f25e2a2e07d3b443d69986e1c3acf67c1449f7d847e3625
|
||||
sha256: e9a3c321e94359a552b1bdd0f98f79885f2b3e27234d270f9bef5cd82b29340c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.43"
|
||||
version: "1.0.44"
|
||||
local_auth_darwin:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -896,10 +896,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: media_kit
|
||||
sha256: "3289062540e3b8b9746e5c50d95bd78a9289826b7227e253dff806d002b9e67a"
|
||||
sha256: "1f1deee148533d75129a6f38251ff8388e33ee05fc2d20a6a80e57d6051b7b62"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.10+1"
|
||||
version: "1.1.11"
|
||||
media_kit_libs_android_video:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -912,18 +912,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: media_kit_native_event_loop
|
||||
sha256: a605cf185499d14d58935b8784955a92a4bf0ff4e19a23de3d17a9106303930e
|
||||
sha256: "7d82e3b3e9ded5c35c3146c5ba1da3118d1dd8ac3435bac7f29f458181471b40"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.8"
|
||||
version: "1.0.9"
|
||||
media_kit_video:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
name: media_kit_video
|
||||
sha256: c048d11a19e379aebbe810647636e3fc6d18374637e2ae12def4ff8a4b99a882
|
||||
sha256: "2cc3b966679963ba25a4ce5b771e532a521ebde7c6aa20e9802bec95d9916c8f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.4"
|
||||
version: "1.2.5"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -944,10 +944,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "2e123074287cc9fd6c09de8336dae606d1ddb88d9ac47358826db698c176a1f2"
|
||||
sha256: "801fd0b26f14a4a58ccb09d5892c3fbdeff209594300a542492cf13fba9d247a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
version: "1.0.6"
|
||||
motion_sensors:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -969,10 +969,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: network_info_plus
|
||||
sha256: "0754302af09e58a60c7cd6800029d00e03c68f289a19fd9df31941ab91da8903"
|
||||
sha256: "6a31fa47c1f6e240f1b60de0a57d65a092ac1af7515247660f03643576984eb8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
version: "6.0.1"
|
||||
network_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1017,10 +1017,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: package_info_plus
|
||||
sha256: "4de6c36df77ffbcef0a5aefe04669d33f2d18397fea228277b852a2d4e58e860"
|
||||
sha256: a75164ade98cb7d24cfd0a13c6408927c6b217fa60dee5a7ff5c116a58f28918
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.0.1"
|
||||
version: "8.0.2"
|
||||
package_info_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1098,10 +1098,10 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: pdf
|
||||
sha256: "81d5522bddc1ef5c28e8f0ee40b71708761753c163e0c93a40df56fd515ea0f0"
|
||||
sha256: "05df53f8791587402493ac97b9869d3824eccbc77d97855f4545cf72df3cae07"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.11.0"
|
||||
version: "3.11.1"
|
||||
pdf_widget_wrapper:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1130,10 +1130,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_android
|
||||
sha256: eaf2a1ec4472775451e88ca6a7b86559ef2f1d1ed903942ed135e38ea0097dca
|
||||
sha256: "76e4ab092c1b240d31177bb64d2b0bea43f43d0e23541ec866151b9f7b2490fa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "12.0.8"
|
||||
version: "12.0.12"
|
||||
permission_handler_apple:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1146,10 +1146,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: permission_handler_html
|
||||
sha256: "6cac773d389e045a8d4f85418d07ad58ef9e42a56e063629ce14c4c26344de24"
|
||||
sha256: af26edbbb1f2674af65a8f4b56e1a6f526156bc273d0e65dd8075fab51c78851
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.2"
|
||||
version: "0.1.3+2"
|
||||
permission_handler_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1330,34 +1330,34 @@ packages:
|
|||
dependency: "direct main"
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: c272f9cabca5a81adc9b0894381e9c1def363e980f960fa903c604c471b22f68
|
||||
sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
version: "2.3.2"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "041be4d9d2dc6079cf342bc8b761b03787e3b71192d658220a56cac9c04a0294"
|
||||
sha256: "480ba4345773f56acda9abf5f50bd966f581dac5d514e5fc4a18c62976bbba7e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
version: "2.3.2"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "671e7a931f55a08aa45be2a13fe7247f2a41237897df434b30d2012388191833"
|
||||
sha256: c4b35f6cb8f63c147312c054ce7c2254c8066745125264f0c88739c417fc9d9f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.0"
|
||||
version: "2.5.2"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "2ba0510d3017f91655b7543e9ee46d48619de2a2af38e5c790423f7007c7ccc1"
|
||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
version: "2.4.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
|
@ -1370,18 +1370,18 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: "59dc807b94d29d52ddbb1b3c0d3b9d0a67fc535a64e62a5542c8db0513fcb6c2"
|
||||
sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
version: "2.4.2"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "398084b47b7f92110683cac45c6dc4aae853db47e470e5ddcd52cab7f7196ab2"
|
||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.0"
|
||||
version: "2.4.1"
|
||||
shelf:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -1431,10 +1431,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: source_map_stack_trace
|
||||
sha256: "84cf769ad83aa6bb61e0aa5a18e53aea683395f196a6f39c4c881fb90ed4f7ae"
|
||||
sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
version: "2.1.2"
|
||||
source_maps:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1471,10 +1471,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: sqflite_common
|
||||
sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4"
|
||||
sha256: "7b41b6c3507854a159e24ae90a8e3e9cc01eb26a477c118d6dca065b5f55453e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
version: "2.5.4+2"
|
||||
stack_trace:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
@ -1528,10 +1528,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: synchronized
|
||||
sha256: "539ef412b170d65ecdafd780f924e5be3f60032a1128df156adad6c5b373d558"
|
||||
sha256: a824e842b8a054f91a728b783c177c1e4731f6b124f9192468457a8913371255
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0+1"
|
||||
version: "3.2.0"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1616,10 +1616,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_android
|
||||
sha256: "94d8ad05f44c6d4e2ffe5567ab4d741b82d62e3c8e288cc1fcea45965edf47c9"
|
||||
sha256: e35a698ac302dd68e41f73250bd9517fe3ab5fa4f18fe4647a0872db61bacbab
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.8"
|
||||
version: "6.3.10"
|
||||
url_launcher_ios:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1632,10 +1632,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: url_launcher_linux
|
||||
sha256: ab360eb661f8879369acac07b6bb3ff09d9471155357da8443fd5d3cf7363811
|
||||
sha256: e2b9622b4007f97f504cd64c0128309dfb978ae66adbe944125ed9e1750f06af
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.1"
|
||||
version: "3.2.0"
|
||||
url_launcher_macos:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1688,26 +1688,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc
|
||||
sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "14.2.4"
|
||||
version: "14.2.5"
|
||||
volume_controller:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: volume_controller
|
||||
sha256: "189bdc7a554f476b412e4c8b2f474562b09d74bc458c23667356bce3ca1d48c9"
|
||||
sha256: c71d4c62631305df63b72da79089e078af2659649301807fa746088f365cb48e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.7"
|
||||
version: "2.0.8"
|
||||
wakelock_plus:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: wakelock_plus
|
||||
sha256: "4fa83a128b4127619e385f686b4f080a5d2de46cff8e8c94eccac5fcf76550e5"
|
||||
sha256: bf4ee6f17a2fa373ed3753ad0e602b7603f8c75af006d5b9bdade263928c0484
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.7"
|
||||
version: "1.2.8"
|
||||
wakelock_plus_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1768,10 +1768,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: win32
|
||||
sha256: "015002c060f1ae9f41a818f2d5640389cc05283e368be19dc8d77cecb43c40c9"
|
||||
sha256: "68d1e89a91ed61ad9c370f9f8b6effed9ae5e0ede22a270bdfa6daf79fc2290a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.5.3"
|
||||
version: "5.5.4"
|
||||
win32_registry:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
@ -1814,4 +1814,4 @@ packages:
|
|||
version: "3.1.2"
|
||||
sdks:
|
||||
dart: ">=3.5.0 <4.0.0"
|
||||
flutter: ">=3.24.0"
|
||||
flutter: ">=3.24.1"
|
||||
|
|
|
@ -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.9+128
|
||||
version: 1.11.10+129
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
# this project bundles Flutter SDK via `flutter_wrapper`
|
||||
# cf https://github.com/passsy/flutter_wrapper
|
||||
flutter: 3.24.0
|
||||
flutter: 3.24.1
|
||||
sdk: '>=3.5.0 <4.0.0'
|
||||
|
||||
# use `scripts/apply_flavor_{flavor}.sh` to set the right dependencies for the flavor
|
||||
|
@ -119,6 +119,10 @@ 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:
|
||||
|
|
28
scripts/extract_apks_libre.sh
Executable file
|
@ -0,0 +1,28 @@
|
|||
#!/bin/bash
|
||||
if [ ! -d "scripts" ]; then
|
||||
cd ..
|
||||
fi
|
||||
|
||||
BUNDLE="/home/tibo/Downloads/app-libre-release.aab"
|
||||
APKS_FULL="/home/tibo/Downloads/app-libre-release.apks"
|
||||
APKS_STRIPPED="/home/tibo/Downloads/app-libre-release_stripped.apks"
|
||||
|
||||
rm "$APKS_FULL"
|
||||
|
||||
# shellcheck disable=SC2001
|
||||
OUTPUT=$(sed "s|\.aab|\.apks|" <<<"$BUNDLE")
|
||||
|
||||
KEYS_PATH="android/key.properties"
|
||||
STORE_PATH=$(sed -n 's|.*storeFile=\(.*\)[\r\n]|\1|p' "$KEYS_PATH")
|
||||
# shellcheck disable=SC1003
|
||||
STORE_PW=$(sed -n 's|.*storePassword=\(.*\)[\r\n]|\1|p' "$KEYS_PATH" | sed 's|\\'\''|'\''|g')
|
||||
KEY_ALIAS=$(sed -n 's|.*keyAlias=\(.*\)[\r\n]|\1|p' "$KEYS_PATH")
|
||||
# shellcheck disable=SC1003
|
||||
KEY_PW=$(sed -n 's|.*keyPassword=\(.*\)[\r\n]|\1|p' "$KEYS_PATH" | sed 's|\\'\''|'\''|g')
|
||||
|
||||
echo "$BUNDLE -> $OUTPUT"
|
||||
bundletool build-apks --bundle="$BUNDLE" --output="$OUTPUT" \
|
||||
--ks="$STORE_PATH" --ks-pass="pass:$STORE_PW" \
|
||||
--ks-key-alias="$KEY_ALIAS" --key-pass="pass:$KEY_PW"
|
||||
|
||||
../apkstripper "$APKS_FULL" "$APKS_STRIPPED"
|
|
@ -1,5 +1,5 @@
|
|||
import 'package:aves/model/covers.dart';
|
||||
import 'package:aves/model/db/db_metadata.dart';
|
||||
import 'package:aves/model/db/db.dart';
|
||||
import 'package:aves/model/entry/entry.dart';
|
||||
import 'package:aves/model/favourites.dart';
|
||||
import 'package:aves/model/filters/filters.dart';
|
||||
|
@ -10,7 +10,7 @@ import 'package:aves/model/vaults/details.dart';
|
|||
import 'package:flutter/foundation.dart';
|
||||
import 'package:test/fake.dart';
|
||||
|
||||
class FakeMetadataDb extends Fake implements MetadataDb {
|
||||
class FakeAvesDb extends Fake implements LocalMediaDb {
|
||||
static int _lastId = 0;
|
||||
|
||||
@override
|
||||
|
@ -28,11 +28,14 @@ class FakeMetadataDb extends Fake implements MetadataDb {
|
|||
Future<Set<AvesEntry>> loadEntries({int? origin, String? directory}) => SynchronousFuture({});
|
||||
|
||||
@override
|
||||
Future<void> saveEntries(Set<AvesEntry> entries) => SynchronousFuture(null);
|
||||
Future<void> insertEntries(Set<AvesEntry> entries) => SynchronousFuture(null);
|
||||
|
||||
@override
|
||||
Future<void> updateEntry(int id, AvesEntry entry) => SynchronousFuture(null);
|
||||
|
||||
@override
|
||||
Future<Set<AvesEntry>> searchLiveDuplicates(int origin, Set<AvesEntry>? entries) => SynchronousFuture({});
|
||||
|
||||
// date taken
|
||||
|
||||
@override
|
|
@ -3,7 +3,7 @@ import 'dart:async';
|
|||
import 'package:aves/l10n/l10n.dart';
|
||||
import 'package:aves/model/availability.dart';
|
||||
import 'package:aves/model/covers.dart';
|
||||
import 'package:aves/model/db/db_metadata.dart';
|
||||
import 'package:aves/model/db/db.dart';
|
||||
import 'package:aves/model/entry/extensions/favourites.dart';
|
||||
import 'package:aves/model/favourites.dart';
|
||||
import 'package:aves/model/filters/album.dart';
|
||||
|
@ -35,7 +35,7 @@ import '../fake/availability.dart';
|
|||
import '../fake/device_service.dart';
|
||||
import '../fake/media_fetch_service.dart';
|
||||
import '../fake/media_store_service.dart';
|
||||
import '../fake/metadata_db.dart';
|
||||
import '../fake/db.dart';
|
||||
import '../fake/metadata_fetch_service.dart';
|
||||
import '../fake/report_service.dart';
|
||||
import '../fake/storage_service.dart';
|
||||
|
@ -58,7 +58,7 @@ void main() {
|
|||
// specify Posix style path context for consistent behaviour when running tests on Windows
|
||||
getIt.registerLazySingleton<p.Context>(() => p.Context(style: p.Style.posix));
|
||||
getIt.registerLazySingleton<AvesAvailability>(FakeAvesAvailability.new);
|
||||
getIt.registerLazySingleton<MetadataDb>(FakeMetadataDb.new);
|
||||
getIt.registerLazySingleton<LocalMediaDb>(FakeAvesDb.new);
|
||||
|
||||
getIt.registerLazySingleton<AppService>(FakeAppService.new);
|
||||
getIt.registerLazySingleton<DeviceService>(FakeDeviceService.new);
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
In v1.11.9:
|
||||
- peruse more options to tag or move via quick actions
|
||||
- read long descriptions right from the overlay
|
||||
- sort videos by duration
|
||||
In v1.11.10:
|
||||
- enjoy the app in Swedish
|
||||
Full changelog available on GitHub
|