Skip to content

Commit 8815a79

Browse files
committed
Merge branch 'develop'
2 parents 60a778f + 4ef0beb commit 8815a79

35 files changed

+741
-417
lines changed

.github/workflows/check.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ jobs:
1414
steps:
1515
- uses: subosito/flutter-action@v1
1616
with:
17-
flutter-version: '1.17.5'
17+
channel: beta
18+
flutter-version: '1.22.0-12.1.pre'
1819

1920
- name: Clone the repository.
2021
uses: actions/checkout@v2

.github/workflows/release.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ jobs:
1616

1717
- uses: subosito/flutter-action@v1
1818
with:
19-
flutter-version: '1.17.5'
19+
channel: beta
20+
flutter-version: '1.22.0-12.1.pre'
2021

2122
# Workaround for this Android Gradle Plugin issue (supposedly fixed in AGP 4.1):
2223
# https://issuetracker.google.com/issues/144111441
@@ -49,8 +50,8 @@ jobs:
4950
echo "${{ secrets.KEY_JKS }}" > release.keystore.asc
5051
gpg -d --passphrase "${{ secrets.KEY_JKS_PASSPHRASE }}" --batch release.keystore.asc > $AVES_STORE_FILE
5152
rm release.keystore.asc
52-
flutter build apk
53-
flutter build appbundle
53+
flutter build apk --bundle-sksl-path shaders_1.22.0-12.1.pre.sksl.json
54+
flutter build appbundle --bundle-sksl-path shaders_1.22.0-12.1.pre.sksl.json
5455
rm $AVES_STORE_FILE
5556
env:
5657
AVES_STORE_FILE: ${{ github.workspace }}/key.jks

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,20 +12,18 @@ Aves is a gallery and metadata explorer app. It is built for Android, with Flutt
1212

1313
## Features
1414

15-
- support raster images: JPEG, PNG, GIF, WEBP, BMP, WBMP, HEIC (from Android Pie), DNG
15+
- support raster images: BMP, DNG, GIF, HEIC (from Android Pie), ICO, JPEG, PNG, WBMP, WEBP
1616
- support animated images: GIF, WEBP
1717
- support vector images: SVG
18-
- support videos: MP4, AVI & probably others
18+
- support videos: MP4, AVI, AVCHD & probably others
1919
- search and filter by country, place, XMP tag, type (animated, raster, vector, video)
20-
- bulk delete, share, copy, move
2120
- favorites
2221
- statistics
23-
- handle intents to view or pick images
2422
- support Android API 24 ~ 30 (Nougat ~ R)
23+
- Android integration (app shortcuts, handle view/pick intents)
2524

2625
## Known Issues
2726

28-
- privacy: cannot opt out of Crashlytics reporting (cf [flutterfire issue #1143](https://github.com/FirebaseExtended/flutterfire/issues/1143))
2927
- gesture: double tap on image does not zoom on tapped area (cf [photo_view issue #82](https://github.com/renancaraujo/photo_view/issues/82))
3028
- performance: image info page stutters the first time it loads a Google Maps view (cf [flutter issue #28493](https://github.com/flutter/flutter/issues/28493))
3129
- performance: image decoding is slow

android/app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,9 @@
106106
android:value="${googleApiKey}" />
107107
<!-- Don't delete the meta-data below.
108108
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
109+
<meta-data
110+
android:name="firebase_crashlytics_collection_enabled"
111+
android:value="false" />
109112
<meta-data
110113
android:name="flutterEmbedding"
111114
android:value="2" />

android/app/src/main/java/deckers/thibault/aves/channel/calls/MetadataHandler.java

Lines changed: 55 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -199,27 +199,29 @@ private void getAllMetadata(MethodCall call, MethodChannel.Result result) {
199199
private Map<String, String> getVideoAllMetadataByMediaMetadataRetriever(String uri) {
200200
Map<String, String> dirMap = new HashMap<>();
201201
MediaMetadataRetriever retriever = StorageUtils.openMetadataRetriever(context, Uri.parse(uri));
202-
try {
203-
for (Map.Entry<Integer, String> kv : VIDEO_MEDIA_METADATA_KEYS.entrySet()) {
204-
Integer key = kv.getKey();
205-
String value = retriever.extractMetadata(key);
206-
if (value != null) {
207-
switch (key) {
208-
case MediaMetadataRetriever.METADATA_KEY_BITRATE:
209-
value = Formatter.formatFileSize(context, Long.parseLong(value)) + "/sec";
210-
break;
211-
case MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION:
212-
value += "°";
213-
break;
202+
if (retriever != null) {
203+
try {
204+
for (Map.Entry<Integer, String> kv : VIDEO_MEDIA_METADATA_KEYS.entrySet()) {
205+
Integer key = kv.getKey();
206+
String value = retriever.extractMetadata(key);
207+
if (value != null) {
208+
switch (key) {
209+
case MediaMetadataRetriever.METADATA_KEY_BITRATE:
210+
value = Formatter.formatFileSize(context, Long.parseLong(value)) + "/sec";
211+
break;
212+
case MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION:
213+
value += "°";
214+
break;
215+
}
216+
dirMap.put(kv.getValue(), value);
214217
}
215-
dirMap.put(kv.getValue(), value);
216218
}
219+
} catch (Exception e) {
220+
Log.w(LOG_TAG, "failed to get video metadata by MediaMetadataRetriever for uri=" + uri, e);
221+
} finally {
222+
// cannot rely on `MediaMetadataRetriever` being `AutoCloseable` on older APIs
223+
retriever.release();
217224
}
218-
} catch (Exception e) {
219-
Log.w(LOG_TAG, "failed to get video metadata by MediaMetadataRetriever for uri=" + uri, e);
220-
} finally {
221-
// cannot rely on `MediaMetadataRetriever` being `AutoCloseable` on older APIs
222-
retriever.release();
223225
}
224226
return dirMap;
225227
}
@@ -317,45 +319,47 @@ private Map<String, Object> getCatalogMetadataByImageMetadataReader(String uri,
317319
private Map<String, Object> getVideoCatalogMetadataByMediaMetadataRetriever(String uri) {
318320
Map<String, Object> metadataMap = new HashMap<>();
319321
MediaMetadataRetriever retriever = StorageUtils.openMetadataRetriever(context, Uri.parse(uri));
320-
try {
321-
String dateString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE);
322-
String rotationString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
323-
String locationString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION);
324-
325-
if (dateString != null) {
326-
long dateMillis = MetadataHelper.parseVideoMetadataDate(dateString);
327-
// some entries have an invalid default date (19040101T000000.000Z) that is before Epoch time
328-
if (dateMillis > 0) {
329-
metadataMap.put(KEY_DATE_MILLIS, dateMillis);
322+
if (retriever != null) {
323+
try {
324+
String dateString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE);
325+
String rotationString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
326+
String locationString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_LOCATION);
327+
328+
if (dateString != null) {
329+
long dateMillis = MetadataHelper.parseVideoMetadataDate(dateString);
330+
// some entries have an invalid default date (19040101T000000.000Z) that is before Epoch time
331+
if (dateMillis > 0) {
332+
metadataMap.put(KEY_DATE_MILLIS, dateMillis);
333+
}
330334
}
331-
}
332-
if (rotationString != null) {
333-
metadataMap.put(KEY_VIDEO_ROTATION, Integer.parseInt(rotationString));
334-
}
335-
if (locationString != null) {
336-
Matcher locationMatcher = VIDEO_LOCATION_PATTERN.matcher(locationString);
337-
if (locationMatcher.find() && locationMatcher.groupCount() >= 2) {
338-
String latitudeString = locationMatcher.group(1);
339-
String longitudeString = locationMatcher.group(2);
340-
if (latitudeString != null && longitudeString != null) {
341-
try {
342-
double latitude = Double.parseDouble(latitudeString);
343-
double longitude = Double.parseDouble(longitudeString);
344-
if (latitude != 0 && longitude != 0) {
345-
metadataMap.put(KEY_LATITUDE, latitude);
346-
metadataMap.put(KEY_LONGITUDE, longitude);
335+
if (rotationString != null) {
336+
metadataMap.put(KEY_VIDEO_ROTATION, Integer.parseInt(rotationString));
337+
}
338+
if (locationString != null) {
339+
Matcher locationMatcher = VIDEO_LOCATION_PATTERN.matcher(locationString);
340+
if (locationMatcher.find() && locationMatcher.groupCount() >= 2) {
341+
String latitudeString = locationMatcher.group(1);
342+
String longitudeString = locationMatcher.group(2);
343+
if (latitudeString != null && longitudeString != null) {
344+
try {
345+
double latitude = Double.parseDouble(latitudeString);
346+
double longitude = Double.parseDouble(longitudeString);
347+
if (latitude != 0 && longitude != 0) {
348+
metadataMap.put(KEY_LATITUDE, latitude);
349+
metadataMap.put(KEY_LONGITUDE, longitude);
350+
}
351+
} catch (NumberFormatException ex) {
352+
// ignore
347353
}
348-
} catch (NumberFormatException ex) {
349-
// ignore
350354
}
351355
}
352356
}
357+
} catch (Exception e) {
358+
Log.w(LOG_TAG, "failed to get catalog metadata by MediaMetadataRetriever for uri=" + uri, e);
359+
} finally {
360+
// cannot rely on `MediaMetadataRetriever` being `AutoCloseable` on older APIs
361+
retriever.release();
353362
}
354-
} catch (Exception e) {
355-
Log.w(LOG_TAG, "failed to get catalog metadata by MediaMetadataRetriever for uri=" + uri, e);
356-
} finally {
357-
// cannot rely on `MediaMetadataRetriever` being `AutoCloseable` on older APIs
358-
retriever.release();
359363
}
360364
return metadataMap;
361365
}

android/app/src/main/java/deckers/thibault/aves/decoder/VideoThumbnailFetcher.java

Lines changed: 19 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,25 +25,27 @@ class VideoThumbnailFetcher implements DataFetcher<InputStream> {
2525
@Override
2626
public void loadData(@NonNull Priority priority, @NonNull DataCallback<? super InputStream> callback) {
2727
MediaMetadataRetriever retriever = StorageUtils.openMetadataRetriever(model.getContext(), model.getUri());
28-
try {
29-
byte[] picture = retriever.getEmbeddedPicture();
30-
if (picture != null) {
31-
callback.onDataReady(new ByteArrayInputStream(picture));
32-
} else {
33-
// not ideal: bitmap -> byte[] -> bitmap
34-
// but simple fallback and we cache result
35-
ByteArrayOutputStream bos = new ByteArrayOutputStream();
36-
Bitmap bitmap = retriever.getFrameAtTime();
37-
if (bitmap != null) {
38-
bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
28+
if (retriever != null) {
29+
try {
30+
byte[] picture = retriever.getEmbeddedPicture();
31+
if (picture != null) {
32+
callback.onDataReady(new ByteArrayInputStream(picture));
33+
} else {
34+
// not ideal: bitmap -> byte[] -> bitmap
35+
// but simple fallback and we cache result
36+
ByteArrayOutputStream bos = new ByteArrayOutputStream();
37+
Bitmap bitmap = retriever.getFrameAtTime();
38+
if (bitmap != null) {
39+
bitmap.compress(Bitmap.CompressFormat.PNG, 0, bos);
40+
}
41+
callback.onDataReady(new ByteArrayInputStream(bos.toByteArray()));
3942
}
40-
callback.onDataReady(new ByteArrayInputStream(bos.toByteArray()));
43+
} catch (Exception ex) {
44+
callback.onLoadFailed(ex);
45+
} finally {
46+
// cannot rely on `MediaMetadataRetriever` being `AutoCloseable` on older APIs
47+
retriever.release();
4148
}
42-
} catch (Exception ex) {
43-
callback.onLoadFailed(ex);
44-
} finally {
45-
// cannot rely on `MediaMetadataRetriever` being `AutoCloseable` on older APIs
46-
retriever.release();
4749
}
4850
}
4951

android/app/src/main/java/deckers/thibault/aves/model/SourceImageEntry.java

Lines changed: 41 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -135,49 +135,51 @@ public SourceImageEntry fillPreCatalogMetadata(@NonNull Context context) {
135135
// finds: width, height, orientation/rotation, date, title, duration
136136
private void fillByMediaMetadataRetriever(@NonNull Context context) {
137137
MediaMetadataRetriever retriever = StorageUtils.openMetadataRetriever(context, uri);
138-
try {
139-
String width = null, height = null, rotation = null, durationMillis = null;
140-
if (isImage()) {
141-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
142-
width = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH);
143-
height = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT);
144-
rotation = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION);
138+
if (retriever != null) {
139+
try {
140+
String width = null, height = null, rotation = null, durationMillis = null;
141+
if (isImage()) {
142+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
143+
width = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_WIDTH);
144+
height = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_HEIGHT);
145+
rotation = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_IMAGE_ROTATION);
146+
}
147+
} else if (isVideo()) {
148+
width = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
149+
height = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
150+
rotation = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
151+
durationMillis = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
152+
}
153+
if (width != null) {
154+
this.width = Integer.parseInt(width);
155+
}
156+
if (height != null) {
157+
this.height = Integer.parseInt(height);
158+
}
159+
if (rotation != null) {
160+
this.orientationDegrees = Integer.parseInt(rotation);
161+
}
162+
if (durationMillis != null) {
163+
this.durationMillis = Long.parseLong(durationMillis);
145164
}
146-
} else if (isVideo()) {
147-
width = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_WIDTH);
148-
height = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_HEIGHT);
149-
rotation = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
150-
durationMillis = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION);
151-
}
152-
if (width != null) {
153-
this.width = Integer.parseInt(width);
154-
}
155-
if (height != null) {
156-
this.height = Integer.parseInt(height);
157-
}
158-
if (rotation != null) {
159-
this.orientationDegrees = Integer.parseInt(rotation);
160-
}
161-
if (durationMillis != null) {
162-
this.durationMillis = Long.parseLong(durationMillis);
163-
}
164165

165-
String dateString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE);
166-
long dateMillis = MetadataHelper.parseVideoMetadataDate(dateString);
167-
// some entries have an invalid default date (19040101T000000.000Z) that is before Epoch time
168-
if (dateMillis > 0) {
169-
this.sourceDateTakenMillis = dateMillis;
170-
}
166+
String dateString = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DATE);
167+
long dateMillis = MetadataHelper.parseVideoMetadataDate(dateString);
168+
// some entries have an invalid default date (19040101T000000.000Z) that is before Epoch time
169+
if (dateMillis > 0) {
170+
this.sourceDateTakenMillis = dateMillis;
171+
}
171172

172-
String title = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
173-
if (title != null) {
174-
this.title = title;
173+
String title = retriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_TITLE);
174+
if (title != null) {
175+
this.title = title;
176+
}
177+
} catch (Exception e) {
178+
// ignore
179+
} finally {
180+
// cannot rely on `MediaMetadataRetriever` being `AutoCloseable` on older APIs
181+
retriever.release();
175182
}
176-
} catch (Exception e) {
177-
// ignore
178-
} finally {
179-
// cannot rely on `MediaMetadataRetriever` being `AutoCloseable` on older APIs
180-
retriever.release();
181183
}
182184
}
183185

android/app/src/main/java/deckers/thibault/aves/utils/StorageUtils.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -460,7 +460,8 @@ public static MediaMetadataRetriever openMetadataRetriever(@NonNull Context cont
460460
}
461461
retriever.setDataSource(context, uri);
462462
} catch (Exception e) {
463-
Log.e(LOG_TAG, "failed to open MediaMetadataRetriever for uri=" + uri, e);
463+
// unsupported format
464+
return null;
464465
}
465466
return retriever;
466467
}

android/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ buildscript {
77
dependencies {
88
classpath 'com.android.tools.build:gradle:3.6.3'
99
classpath 'com.google.gms:google-services:4.3.3'
10-
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.1.1'
10+
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.2.0'
1111
}
1212
}
1313

0 commit comments

Comments
 (0)