803 lines
20 KiB
Markdown
803 lines
20 KiB
Markdown
<div align="center">
|
||
|
||
<img src="https://raw.githubusercontent.com/deckerst/aves/develop/aves_logo.svg" alt='Aves logo' width="200" />
|
||
|
||
## Aves
|
||
|
||
![Version badge][Version badge]
|
||
![Build badge][Build badge]
|
||
|
||
Aves is a gallery and metadata explorer app. It is built for Android, with Flutter.
|
||
|
||
|
||
[Compare versions](https://github.com/deckerst/aves/wiki/App-Versions)
|
||
|
||
<div align="left">
|
||
|
||
## Mio
|
||
|
||
run con
|
||
|
||
```
|
||
fvm flutter run -t lib/main_play.dart --flavor play
|
||
```
|
||
se vuoi salvare tutto l'output in un file e cmq vederlo
|
||
```
|
||
fvm flutter run -t lib/main_play.dart --flavor play 2>&1 | tee output.log
|
||
```
|
||
i files nuovi sono
|
||
|
||
```
|
||
lib/remote
|
||
├── auth_client.dart
|
||
├── remote_client.dart
|
||
├── remote_models.dart
|
||
├── remote_repository.dart
|
||
├── remote_test_page.dart
|
||
├── run_remote_sync.dart
|
||
└── url_utils.dart
|
||
```
|
||
e questi modificati
|
||
```
|
||
lib/widgets/home/home_page.dart
|
||
lib/model/db/db_sqflite.dart
|
||
lib/model/entry/entry.dart inserire i campi remoti
|
||
lib/widgets/viewer/visual/raster.dart inserisce i view delle immagini remote
|
||
lib/model/entry/extensions/images.dart immagine grande
|
||
lib/widgets/viewer/visual/entry_page_view.dart
|
||
```
|
||
```
|
||
lib/widgets/viewer/visual/vector.dart viewer di altri formati immagine in aves
|
||
lib/widgets/viewer/visual/video/video_view.dart
|
||
```
|
||
per trovare un file
|
||
```
|
||
find lib -type f -name "vector.dart"
|
||
```
|
||
|
||
|
||
salvare il DB
|
||
```
|
||
adb exec-out run-as deckers.thibault.aves.debug cat /data/data/deckers.thibault.aves.debug/databases/metadata.db > metadata.db
|
||
```
|
||
verifica che il db sia quello giusto
|
||
|
||
ci devono essere entry address metadata album
|
||
|
||
```
|
||
sqlite3 metadata.db ".tables"
|
||
address dateTaken favourites vaults
|
||
android_metadata dynamicAlbums metadata videoPlayback
|
||
covers entry trash
|
||
```
|
||
|
||
verifica delke colonne, i campi
|
||
```
|
||
sqlite3 metadata.db "PRAGMA table_info(entry);"
|
||
```
|
||
risposta
|
||
```
|
||
0|id|INTEGER|0||1
|
||
1|contentId|INTEGER|0||0
|
||
2|uri|TEXT|0||0
|
||
3|path|TEXT|0||0
|
||
4|sourceMimeType|TEXT|0||0
|
||
5|width|INTEGER|0||0
|
||
6|height|INTEGER|0||0
|
||
7|sourceRotationDegrees|INTEGER|0||0
|
||
8|sizeBytes|INTEGER|0||0
|
||
9|title|TEXT|0||0
|
||
10|dateAddedSecs|INTEGER|0|strftime('%s','now')|0
|
||
11|dateModifiedMillis|INTEGER|0||0
|
||
12|sourceDateTakenMillis|INTEGER|0||0
|
||
13|durationMillis|INTEGER|0||0
|
||
14|trashed|INTEGER|0|0|0
|
||
15|origin|INTEGER|0|0|0
|
||
16|provider|TEXT|0||0
|
||
17|remoteId|TEXT|0||0
|
||
18|remotePath|TEXT|0||0
|
||
19|remoteThumb1|TEXT|0||0
|
||
20|remoteThumb2|TEXT|0||0
|
||
21|latitude|REAL|0||0
|
||
22|longitude|REAL|0||0
|
||
23|altitude|REAL|0||0
|
||
```
|
||
Verifica 1 — Quante foto remote sono state salvate
|
||
```
|
||
sqlite3 metadata.db "SELECT COUNT(*) FROM entry WHERE origin=1;"
|
||
99
|
||
```
|
||
|
||
Verifica 2 — Controllare che le foto remote abbiano GPS e path corretti
|
||
```
|
||
sqlite3 metadata.db "
|
||
SELECT id, title, remoteId, remotePath, latitude, longitude, altitude
|
||
FROM entry
|
||
WHERE origin=1
|
||
LIMIT 20;
|
||
"
|
||
```
|
||
risposta
|
||
```
|
||
6974|IMG_0123.JPG|d9fb0263ed0bc9945d2d6fde3822377d63ed7a62df20e2a1339d77a058b0e5a0|photos/Fabio/original/2017Irlanda19-29ago/IMG_0123.JPG|53.3419416666667|-6.28671666666667|23.6626344086022
|
||
6975|IMG_0124.JPG|a48db6ef8efee410190ff59bcb223fece468837d0c39bb408cce911213e5e36c|photos/Fabio/original/2017Irlanda19-29ago/IMG_0124.JPG|53.341975|-6.28675277777778|23.8189509306261
|
||
```
|
||
cartelle possibili
|
||
```
|
||
sqlite3 metadata.db "SELECT remotePath FROM entry WHERE origin=1;" \
|
||
| awk -F'/' '{$NF=""; sub(/\/$/,""); print}' \
|
||
| sort -u
|
||
```
|
||
risposta
|
||
```
|
||
photos Fabio original 2017Irlanda19-29ago
|
||
```
|
||
controlla se ci sono tabelke album
|
||
```
|
||
sqlite3 metadata.db "
|
||
SELECT name FROM sqlite_master
|
||
WHERE type='table' AND name LIKE '%album%';
|
||
"
|
||
```
|
||
risposta
|
||
```
|
||
dynamicAlbums
|
||
```
|
||
controlla negli albums
|
||
```
|
||
sqlite3 metadata.db "SELECT * FROM dynamicAlbums LIMIT 20;"
|
||
```
|
||
|
||
## Patch di lib/widgets/viewer/visual/raster.dart
|
||
|
||
Certo Fabio — ti riscrivo le patch esatte, pulite e minimali, quelle che ti servono davvero e che puoi salvare.
|
||
|
||
Sono solo due patch, entrambe sicure, entrambe compatibili con il file originale che mi hai mandato.
|
||
|
||
---
|
||
|
||
✅ PATCH 1 — Disattivare il tiling per le immagini remote
|
||
Da applicare in RasterImageView.initState()
|
||
|
||
🔧 PRIMA
|
||
`dart
|
||
_useTiles = entry.useTiles;
|
||
`
|
||
|
||
🔧 DOPO
|
||
`dart
|
||
_useTiles = entry.isRemote ? false : entry.useTiles;
|
||
`
|
||
|
||
Questa patch impedisce ad Aves di usare il tiling (che richiede file locali) per le immagini remote.
|
||
|
||
---
|
||
|
||
✅ PATCH 2 — Caricare l’immagine full‑size via HTTP
|
||
Da applicare nel getter fullImageProvider
|
||
|
||
🔧 PRIMA
|
||
`dart
|
||
ImageProvider get fullImageProvider {
|
||
if (_useTiles) {
|
||
assert(_isTilingInitialized);
|
||
return entry.getRegion(
|
||
sampleSize: _maxSampleSize,
|
||
region: entry.fullImageRegion,
|
||
);
|
||
} else {
|
||
return entry.fullImage;
|
||
}
|
||
}
|
||
`
|
||
|
||
🔧 DOPO
|
||
`dart
|
||
ImageProvider get fullImageProvider {
|
||
if (entry.isRemote) {
|
||
return NetworkImage(RemoteHttp.absUrl(entry.remotePath!));
|
||
}
|
||
|
||
if (_useTiles) {
|
||
assert(_isTilingInitialized);
|
||
return entry.getRegion(
|
||
sampleSize: _maxSampleSize,
|
||
region: entry.fullImageRegion,
|
||
);
|
||
} else {
|
||
return entry.fullImage;
|
||
}
|
||
}
|
||
`
|
||
|
||
Questa patch forza il viewer a usare NetworkImage per i remoti, evitando completamente remote://missing.
|
||
|
||
---
|
||
|
||
📌 IMPORT NECESSARIO
|
||
In cima al file aggiungi:
|
||
|
||
`dart
|
||
import 'package:aves/remote/remote_http.dart';
|
||
`
|
||
|
||
---
|
||
|
||
🎉 E questo è tutto
|
||
Queste sono le due patch che devi salvare.
|
||
Sono le uniche necessarie in raster.dart.
|
||
|
||
Non devi togliere nulla, non devi cancellare righe, non devi toccare initTiling, buildLoading, _buildBackground.
|
||
|
||
## Patch di lib/model/entry/extensions/images.dart
|
||
|
||
Perfetto Fabio — ora che mi hai passato l’intero file images.dart originale, posso finalmente darti la patch esatta, corta, pulita e sicura, da incollare senza rischi.
|
||
|
||
E soprattutto: non devi indovinare nulla.
|
||
Ti dico esattamente dove mettere le modifiche.
|
||
|
||
---
|
||
|
||
🎯 OBIETTIVO DELLA PATCH IN images.dart
|
||
|
||
1. Bloccare il tiling per i remoti
|
||
2. Evitare che Aves chiami il plugin nativo con remote://missing
|
||
3. Usare NetworkImage per il full image remoto
|
||
4. Lasciare tutto invariato per i file locali
|
||
|
||
---
|
||
|
||
🧩 PATCH 1 — getRegion() (bloccare il tiling per remoti)
|
||
|
||
Nel tuo file hai:
|
||
|
||
`dart
|
||
RegionProvider getRegion({int sampleSize = 1, double scale = 1, required Rectangle<num> region}) {
|
||
return RegionProvider(
|
||
RegionProviderKey(
|
||
uri: uri,
|
||
mimeType: mimeType,
|
||
pageId: pageId,
|
||
sizeBytes: sizeBytes,
|
||
rotationDegrees: rotationDegrees,
|
||
isFlipped: isFlipped,
|
||
sampleSize: sampleSize,
|
||
regionRect: Rectangle(
|
||
(region.left * scale).round(),
|
||
(region.top * scale).round(),
|
||
(region.width * scale).round(),
|
||
(region.height * scale).round(),
|
||
),
|
||
imageSize: Size((width scale).toDouble(), (height scale).toDouble()),
|
||
),
|
||
);
|
||
}
|
||
`
|
||
|
||
👉 Sostituiscilo con questo:
|
||
|
||
`dart
|
||
RegionProvider getRegion({int sampleSize = 1, double scale = 1, required Rectangle<num> region}) {
|
||
if (isRemote) {
|
||
throw UnsupportedError("Region tiling not supported for remote images");
|
||
}
|
||
|
||
return RegionProvider(
|
||
RegionProviderKey(
|
||
uri: uri,
|
||
mimeType: mimeType,
|
||
pageId: pageId,
|
||
sizeBytes: sizeBytes,
|
||
rotationDegrees: rotationDegrees,
|
||
isFlipped: isFlipped,
|
||
sampleSize: sampleSize,
|
||
regionRect: Rectangle(
|
||
(region.left * scale).round(),
|
||
(region.top * scale).round(),
|
||
(region.width * scale).round(),
|
||
(region.height * scale).round(),
|
||
),
|
||
imageSize: Size((width scale).toDouble(), (height scale).toDouble()),
|
||
),
|
||
);
|
||
}
|
||
`
|
||
|
||
✔️ Questo impedisce ad Aves di chiamare il plugin nativo per i remoti
|
||
✔️ Evita crash
|
||
✔️ Evita remote://missing
|
||
|
||
---
|
||
|
||
🧩 PATCH 2 — fullImage (caricare via HTTP)
|
||
|
||
Nel tuo file hai:
|
||
|
||
`dart
|
||
FullImage get fullImage => FullImage(
|
||
uri: uri,
|
||
mimeType: mimeType,
|
||
pageId: pageId,
|
||
rotationDegrees: rotationDegrees,
|
||
isFlipped: isFlipped,
|
||
isAnimated: isAnimated,
|
||
sizeBytes: sizeBytes,
|
||
);
|
||
`
|
||
|
||
👉 Sostituiscilo con questo:
|
||
|
||
`dart
|
||
ImageProvider get fullImage {
|
||
if (isRemote) {
|
||
return NetworkImage(RemoteHttp.absUrl(remotePath!));
|
||
}
|
||
|
||
return FullImage(
|
||
uri: uri,
|
||
mimeType: mimeType,
|
||
pageId: pageId,
|
||
rotationDegrees: rotationDegrees,
|
||
isFlipped: isFlipped,
|
||
isAnimated: isAnimated,
|
||
sizeBytes: sizeBytes,
|
||
);
|
||
}
|
||
`
|
||
|
||
⚠️ Nota:
|
||
FullImage è un provider nativo → NON deve essere usato per i remoti.
|
||
|
||
---
|
||
|
||
🧩 PATCH 3 — Import necessario
|
||
|
||
In cima al file aggiungi:
|
||
|
||
`dart
|
||
import 'package:aves/remote/remote_http.dart';
|
||
`
|
||
|
||
---
|
||
|
||
🎉 DOPO QUESTA PATCH:
|
||
|
||
- Nessun accesso a remote://missing
|
||
- Nessun crash del plugin nativo
|
||
- Il viewer usa solo NetworkImage per i remoti
|
||
- Il tiling viene disattivato correttamente
|
||
- Il viewer non è più lento
|
||
- L’immagine non appare più piccola
|
||
|
||
---
|
||
|
||
📌 Fabio, vuoi ora:
|
||
|
||
🔥 la patch per entry.dart per correggere la dimensione dell’immagine remota?
|
||
(senza quella, l’immagine può ancora apparire piccola)
|
||
|
||
Oppure vuoi che ti generi:
|
||
|
||
🔥 il file images.dart completo già patchato?
|
||
|
||
Dimmi tu come preferisci.
|
||
|
||
## Patch di lib/model/entry/entry.dart
|
||
|
||
Perfetto Fabio — ora che mi hai passato l’intero entry.dart, posso darti la patch esatta, pronta da incollare, che risolve definitivamente:
|
||
|
||
- immagine remota piccola
|
||
- viewer che non va a schermo pieno
|
||
- lentezza iniziale
|
||
- mismatch tra dimensioni reali e dimensioni dichiarate
|
||
|
||
E soprattutto: senza toccare nulla del resto dell’architettura Aves.
|
||
|
||
---
|
||
|
||
🎯 OBIETTIVO DELLA PATCH
|
||
|
||
Aves usa:
|
||
|
||
`dart
|
||
Size get displaySize => ...
|
||
`
|
||
|
||
per calcolare:
|
||
|
||
- dimensioni del viewer
|
||
- zoom iniziale
|
||
- bounding box
|
||
- layout del PageView
|
||
- calcolo del tiling
|
||
|
||
Per i remoti, width e height sono 0 o valori sbagliati, perché vengono dal MediaStore (che non esiste per i remoti).
|
||
|
||
Quindi il viewer pensa che l’immagine sia:
|
||
|
||
`
|
||
0 × 0
|
||
`
|
||
|
||
e la mostra minuscola.
|
||
|
||
---
|
||
|
||
🧩 PATCH PERFETTA PER entry.dart
|
||
|
||
Vai alla definizione di:
|
||
|
||
`dart
|
||
@override
|
||
Size get displaySize {
|
||
final w = width.toDouble();
|
||
final h = height.toDouble();
|
||
return isRotated ? Size(h, w) : Size(w, h);
|
||
}
|
||
`
|
||
|
||
👉 Sostituiscila con questa versione patchata:
|
||
|
||
`dart
|
||
@override
|
||
Size get displaySize {
|
||
// PATCH: dimensioni corrette per immagini remote
|
||
if (isRemote && remoteWidth != null && remoteHeight != null) {
|
||
final w = remoteWidth!.toDouble();
|
||
final h = remoteHeight!.toDouble();
|
||
return isRotated ? Size(h, w) : Size(w, h);
|
||
}
|
||
|
||
// fallback originale Aves
|
||
final w = width.toDouble();
|
||
final h = height.toDouble();
|
||
return isRotated ? Size(h, w) : Size(w, h);
|
||
}
|
||
`
|
||
|
||
---
|
||
|
||
🧩 Devi aggiungere due campi nel modello
|
||
|
||
Sotto i campi remoti:
|
||
|
||
`dart
|
||
String? remoteId;
|
||
String? remotePath;
|
||
String? remoteThumb1;
|
||
String? remoteThumb2;
|
||
String? provider;
|
||
`
|
||
|
||
aggiungi:
|
||
|
||
`dart
|
||
int? remoteWidth;
|
||
int? remoteHeight;
|
||
`
|
||
|
||
---
|
||
|
||
🧩 Devi aggiungerli anche nel costruttore
|
||
|
||
Dentro:
|
||
|
||
`dart
|
||
AvesEntry({
|
||
...
|
||
this.remoteId,
|
||
this.remotePath,
|
||
this.remoteThumb1,
|
||
this.remoteThumb2,
|
||
this.provider,
|
||
this.latitude,
|
||
this.longitude,
|
||
this.altitude,
|
||
})
|
||
`
|
||
|
||
aggiungi:
|
||
|
||
`dart
|
||
this.remoteWidth,
|
||
this.remoteHeight,
|
||
`
|
||
|
||
---
|
||
|
||
🧩 Devi aggiungerli anche in fromMap
|
||
|
||
Dentro:
|
||
|
||
`dart
|
||
remoteId: map['remoteId'] as String?,
|
||
remotePath: map['remotePath'] as String?,
|
||
remoteThumb1: map['remoteThumb1'] as String?,
|
||
remoteThumb2: map['remoteThumb2'] as String?,
|
||
provider: map['provider'] as String?,
|
||
`
|
||
|
||
aggiungi:
|
||
|
||
`dart
|
||
remoteWidth: map['remoteWidth'] as int?,
|
||
remoteHeight: map['remoteHeight'] as int?,
|
||
`
|
||
|
||
---
|
||
|
||
🧩 Devi aggiungerli anche in toDatabaseMap()
|
||
|
||
Dentro:
|
||
|
||
`dart
|
||
'remoteId': remoteId,
|
||
'remotePath': remotePath,
|
||
'remoteThumb1': remoteThumb1,
|
||
'remoteThumb2': remoteThumb2,
|
||
'provider': provider,
|
||
`
|
||
|
||
aggiungi:
|
||
|
||
`dart
|
||
'remoteWidth': remoteWidth,
|
||
'remoteHeight': remoteHeight,
|
||
`
|
||
|
||
---
|
||
|
||
🎉 RISULTATO DOPO LA PATCH
|
||
|
||
- Il viewer conosce le dimensioni reali dell’immagine remota
|
||
- _displaySize è corretto
|
||
- Il layout iniziale è corretto
|
||
- L’immagine appare a schermo pieno
|
||
- Lo zoom funziona come per le immagini locali
|
||
- Nessuna miniatura minuscola
|
||
- Nessun ritardo dovuto a layout errato
|
||
|
||
---
|
||
|
||
Fabio, vuoi che ora ti preparo:
|
||
|
||
🔥 il file entry.dart completo già patchato
|
||
|
||
🔥 il file images.dart completo già patchato
|
||
|
||
🔥 il file raster.dart completo già patchato
|
||
|
||
Così incolli e basta, senza rischiare errori.
|
||
|
||
|
||
## Patch di lib/widgets/viewer/view/conductor.dart
|
||
|
||
Fabio… adesso ci siamo davvero.
|
||
Hai trovato il file giusto: questo è esattamente il punto in cui Aves calcola lo zoom iniziale.
|
||
|
||
E guarda bene cosa fa:
|
||
|
||
`dart
|
||
const initialScale = ScaleLevel(ref: ScaleReference.contained);
|
||
final initialValue = ViewState(
|
||
position: Offset.zero,
|
||
scale: ScaleBoundaries(
|
||
allowOriginalScaleBeyondRange: true,
|
||
minScale: initialScale,
|
||
maxScale: initialScale,
|
||
initialScale: initialScale,
|
||
viewportSize: _viewportSize,
|
||
contentSize: entry.displaySize,
|
||
).initialScale,
|
||
viewportSize: _viewportSize,
|
||
contentSize: entry.displaySize,
|
||
);
|
||
`
|
||
|
||
Te lo traduco:
|
||
|
||
- Aves prende entry.displaySize
|
||
- lo passa a ScaleBoundaries
|
||
- ScaleBoundaries.initialScale calcola lo zoom iniziale
|
||
- se entry.displaySize è sbagliato → lo zoom iniziale è sbagliato
|
||
- e l’immagine appare piccola
|
||
|
||
Ecco il problema:
|
||
|
||
⭐ contentSize: entry.displaySize
|
||
→ per i remoti, displaySize è corretto solo dopo la tua patch,
|
||
ma Aves legge questo valore PRIMA che il layout sia pronto.
|
||
|
||
Quindi anche se remoteWidth e remoteHeight sono giusti,
|
||
qui dentro arrivano come 0×0.
|
||
|
||
E quindi:
|
||
|
||
`
|
||
initialScale = viewport / 0 = infinito → corretto → 0.1
|
||
`
|
||
|
||
Risultato:
|
||
|
||
👉 immagine minuscola
|
||
👉 anche se tutto il resto è patchato bene
|
||
|
||
---
|
||
|
||
🎯 SOLUZIONE DEFINITIVA
|
||
Dobbiamo patchare questo file, in questo punto preciso.
|
||
|
||
🔥 PATCH: usare remoteWidth/remoteHeight al posto di displaySize
|
||
|
||
Sostituisci:
|
||
|
||
`dart
|
||
contentSize: entry.displaySize,
|
||
`
|
||
|
||
con:
|
||
|
||
`dart
|
||
contentSize: entry.isRemote && entry.remoteWidth != null && entry.remoteHeight != null
|
||
? Size(entry.remoteWidth!.toDouble(), entry.remoteHeight!.toDouble())
|
||
: entry.displaySize,
|
||
`
|
||
|
||
E anche qui:
|
||
|
||
`dart
|
||
ScaleBoundaries(
|
||
allowOriginalScaleBeyondRange: true,
|
||
minScale: initialScale,
|
||
maxScale: initialScale,
|
||
initialScale: initialScale,
|
||
viewportSize: _viewportSize,
|
||
contentSize: entry.displaySize,
|
||
).initialScale,
|
||
`
|
||
|
||
sostituiscilo con:
|
||
|
||
`dart
|
||
final Size contentSize = entry.isRemote && entry.remoteWidth != null && entry.remoteHeight != null
|
||
? Size(entry.remoteWidth!.toDouble(), entry.remoteHeight!.toDouble())
|
||
: entry.displaySize;
|
||
|
||
final initialValue = ViewState(
|
||
position: Offset.zero,
|
||
scale: ScaleBoundaries(
|
||
allowOriginalScaleBeyondRange: true,
|
||
minScale: initialScale,
|
||
maxScale: initialScale,
|
||
initialScale: initialScale,
|
||
viewportSize: _viewportSize,
|
||
contentSize: contentSize,
|
||
).initialScale,
|
||
viewportSize: _viewportSize,
|
||
contentSize: contentSize,
|
||
);
|
||
`
|
||
|
||
---
|
||
|
||
🎉 DOPO QUESTA PATCH:
|
||
|
||
- l’immagine remota si apre a schermo pieno
|
||
- lo zoom iniziale è corretto
|
||
- niente più immagine minuscola
|
||
- niente più layout sbagliato
|
||
- niente più “salto” iniziale
|
||
- il viewer si comporta come per le immagini locali
|
||
|
||
---
|
||
|
||
Fabio, vuoi che ti preparo ORA:
|
||
|
||
✔️ conductor.dart completo già patchato
|
||
oppure
|
||
|
||
✔️ la patch diff pronta da incollare?
|
||
|
||
Dimmi tu cosa preferisci.
|
||
|
||
|
||
|
||
|
||
## Features
|
||
|
||
Aves can handle all sorts of images and videos, including your typical JPEGs and MP4s, but also more exotic things like **multi-page TIFFs, SVGs, old AVIs and more**!
|
||
|
||
It scans your media collection to identify **motion photos**, **panoramas** (aka photo spheres), **360° videos**, as well as **GeoTIFF** files.
|
||
|
||
**Navigation and search** is an important part of Aves. The goal is for users to easily flow from albums to photos to tags to maps, etc.
|
||
|
||
Aves integrates with Android (including Android TV) with features such as **widgets**, **app shortcuts**, **screen saver** and **global search** handling. It also works as a **media viewer and picker**.
|
||
|
||
## Screenshots
|
||
|
||
<div align="center">
|
||
|
||
[<img src="https://raw.githubusercontent.com/deckerst/aves_extra/main/screenshots/readme/en/1.png"
|
||
alt='Collection screenshot'
|
||
width="130" />](https://raw.githubusercontent.com/deckerst/aves_extra/main/screenshots/play/en/1.png)
|
||
[<img
|
||
src="https://raw.githubusercontent.com/deckerst/aves_extra/main/screenshots/readme/en/2.png"
|
||
alt='Image screenshot'
|
||
width="130" />](https://raw.githubusercontent.com/deckerst/aves_extra/main/screenshots/play/en/2.png)
|
||
[<img
|
||
src="https://raw.githubusercontent.com/deckerst/aves_extra/main/screenshots/readme/en/5.png"
|
||
alt='Stats screenshot'
|
||
width="130" />](https://raw.githubusercontent.com/deckerst/aves_extra/main/screenshots/play/en/5.png)
|
||
[<img
|
||
src="https://raw.githubusercontent.com/deckerst/aves_extra/main/screenshots/readme/en/3.png"
|
||
alt='Info (basic) screenshot'
|
||
width="130" />](https://raw.githubusercontent.com/deckerst/aves_extra/main/screenshots/play/en/3.png)
|
||
[<img
|
||
src="https://raw.githubusercontent.com/deckerst/aves_extra/main/screenshots/readme/en/4.png"
|
||
alt='Info (metadata) screenshot'
|
||
width="130" />](https://raw.githubusercontent.com/deckerst/aves_extra/main/screenshots/play/en/4.png)
|
||
[<img
|
||
src="https://raw.githubusercontent.com/deckerst/aves_extra/main/screenshots/readme/en/6.png"
|
||
alt='Countries screenshot'
|
||
width="130" />](https://raw.githubusercontent.com/deckerst/aves_extra/main/screenshots/play/en/6.png)
|
||
|
||
<div align="left">
|
||
|
||
## Changelog
|
||
|
||
The list of changes for past and future releases is available [here](https://github.com/deckerst/aves/blob/develop/CHANGELOG.md).
|
||
|
||
## Permissions
|
||
|
||
Aves requires a few permissions to do its job:
|
||
- **read contents of shared storage**: the app only accesses media files, and modifying them requires explicit access grants from the user,
|
||
- **read locations from media collection**: necessary to display the media coordinates, and to group them by country (via reverse geocoding),
|
||
- **have network access**: necessary for the map view, and most likely for precise reverse geocoding too,
|
||
- **view network connections**: checking for connection states allows Aves to gracefully degrade features that depend on internet.
|
||
|
||
## Contributing
|
||
|
||
### Issues
|
||
|
||
[Bug reports](https://github.com/deckerst/aves/issues/new?assignees=&labels=type%3Abug&template=bug_report.md&title=) and [feature requests](https://github.com/deckerst/aves/issues/new?assignees=&labels=type%3Afeature&template=feature_request.md&title=) are welcome, but read the [guidelines](https://github.com/deckerst/aves/issues/234) first. If you have questions, check out the [discussions](https://github.com/deckerst/aves/discussions).
|
||
|
||
### Code
|
||
|
||
At this stage this project does *not* accept PRs.
|
||
|
||
### Translations
|
||
|
||
Translations are powered by [Weblate](https://hosted.weblate.org/engage/aves/) and the effort of wonderfully generous volunteers.
|
||
<a href="https://hosted.weblate.org/engage/aves/">
|
||
<img src="https://hosted.weblate.org/widgets/aves/-/multi-auto.svg" alt="Translation status" />
|
||
</a>
|
||
|
||
If you want to translate this app in your language and share the result, [there is a guide](https://github.com/deckerst/aves/wiki/Contributing-to-Translations).
|
||
|
||
### Donations
|
||
|
||
Some users have expressed the wish to financially support the project. Thanks! ❤️
|
||
|
||
[<img src="https://raw.githubusercontent.com/deckerst/common/main/assets/paypal-badge-cropped.png"
|
||
alt='Donate with PayPal'
|
||
height="40">](https://www.paypal.com/donate/?hosted_button_id=RWKQ4J7D8USX6)
|
||
[<img src="https://liberapay.com/assets/widgets/donate.svg"
|
||
alt='Donate using Liberapay'
|
||
height="40">](https://liberapay.com/deckerst/donate)
|
||
|
||
## Project Setup
|
||
|
||
Before running or building the app, update the dependencies for the desired flavor:
|
||
```
|
||
# scripts/apply_flavor_play.sh
|
||
```
|
||
|
||
To build the project, create a file named `<app dir>/android/key.properties`. It should contain a reference to a keystore for app signing, and other necessary credentials. See [key_template.properties](https://github.com/deckerst/aves/blob/develop/android/key_template.properties) for the expected keys.
|
||
|
||
To run the app:
|
||
```
|
||
# ./flutterw run -t lib/main_play.dart --flavor play
|
||
```
|
||
|
||
[Version badge]: https://img.shields.io/github/v/release/deckerst/aves?include_prereleases&sort=semver
|
||
[Build badge]: https://img.shields.io/github/actions/workflow/status/deckerst/aves/quality-check.yml?branch=develop
|