improved error logging
This commit is contained in:
parent
a7eaf55ed3
commit
53a7387db7
12 changed files with 91 additions and 66 deletions
|
@ -52,10 +52,12 @@ public class ImageDecodeTask extends AsyncTask<ImageDecodeTask.Params, Void, Ima
|
||||||
static class Result {
|
static class Result {
|
||||||
Params params;
|
Params params;
|
||||||
byte[] data;
|
byte[] data;
|
||||||
|
String errorDetails;
|
||||||
|
|
||||||
Result(Params params, byte[] data) {
|
Result(Params params, byte[] data, String errorDetails) {
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.data = data;
|
this.data = data;
|
||||||
|
this.errorDetails = errorDetails;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -70,8 +72,8 @@ public class ImageDecodeTask extends AsyncTask<ImageDecodeTask.Params, Void, Ima
|
||||||
protected Result doInBackground(Params... params) {
|
protected Result doInBackground(Params... params) {
|
||||||
Params p = params[0];
|
Params p = params[0];
|
||||||
Bitmap bitmap = null;
|
Bitmap bitmap = null;
|
||||||
|
Exception exception = null;
|
||||||
if (!this.isCancelled()) {
|
if (!this.isCancelled()) {
|
||||||
Exception exception = null;
|
|
||||||
Integer w = p.width;
|
Integer w = p.width;
|
||||||
Integer h = p.height;
|
Integer h = p.height;
|
||||||
// fetch low quality thumbnails when size is not specified
|
// fetch low quality thumbnails when size is not specified
|
||||||
|
@ -97,13 +99,10 @@ public class ImageDecodeTask extends AsyncTask<ImageDecodeTask.Params, Void, Ima
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
exception = e;
|
exception = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bitmap == null) {
|
|
||||||
Log.e(LOG_TAG, "failed to load thumbnail for uri=" + p.entry.uri + ", path=" + p.entry.path, exception);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
Log.d(LOG_TAG, "getThumbnail with uri=" + p.entry.uri + " cancelled");
|
Log.d(LOG_TAG, "getThumbnail with uri=" + p.entry.uri + " cancelled");
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] data = null;
|
byte[] data = null;
|
||||||
if (bitmap != null) {
|
if (bitmap != null) {
|
||||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
|
@ -112,7 +111,15 @@ public class ImageDecodeTask extends AsyncTask<ImageDecodeTask.Params, Void, Ima
|
||||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream);
|
bitmap.compress(Bitmap.CompressFormat.JPEG, 90, stream);
|
||||||
data = stream.toByteArray();
|
data = stream.toByteArray();
|
||||||
}
|
}
|
||||||
return new Result(p, data);
|
|
||||||
|
String errorDetails = null;
|
||||||
|
if (exception != null) {
|
||||||
|
errorDetails = exception.getMessage();
|
||||||
|
if (errorDetails != null && !errorDetails.isEmpty()) {
|
||||||
|
errorDetails = errorDetails.split("\n", 2)[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Result(p, data, errorDetails);
|
||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||||
|
@ -205,7 +212,7 @@ public class ImageDecodeTask extends AsyncTask<ImageDecodeTask.Params, Void, Ima
|
||||||
if (result.data != null) {
|
if (result.data != null) {
|
||||||
r.success(result.data);
|
r.success(result.data);
|
||||||
} else {
|
} else {
|
||||||
r.error("getThumbnail-null", "failed to get thumbnail for uri=" + uri + ", path=" + entry.path, null);
|
r.error("getThumbnail-null", "failed to get thumbnail for uri=" + uri + ", path=" + entry.path, result.errorDetails);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -326,11 +326,8 @@ public class MetadataHandler implements MethodChannel.MethodCallHandler {
|
||||||
metadataMap.putAll(getVideoCatalogMetadataByMediaMetadataRetriever(uri));
|
metadataMap.putAll(getVideoCatalogMetadataByMediaMetadataRetriever(uri));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (metadataMap.isEmpty()) {
|
// report success even when empty
|
||||||
result.error("getCatalogMetadata-failure", "failed to get catalog metadata for uri=" + uri + ", extension=" + extension, null);
|
result.success(metadataMap);
|
||||||
} else {
|
|
||||||
result.success(metadataMap);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Map<String, Object> getCatalogMetadataByImageMetadataReader(String uri, String mimeType, String extension) {
|
private Map<String, Object> getCatalogMetadataByImageMetadataReader(String uri, String mimeType, String extension) {
|
||||||
|
|
|
@ -128,7 +128,11 @@ public class ImageByteStreamHandler implements EventChannel.StreamHandler {
|
||||||
error("getImage-image-decode-null", "failed to get image from uri=" + uri, null);
|
error("getImage-image-decode-null", "failed to get image from uri=" + uri, null);
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
error("getImage-image-decode-exception", "failed to get image from uri=" + uri, e.getMessage());
|
String errorDetails = e.getMessage();
|
||||||
|
if (errorDetails != null && !errorDetails.isEmpty()) {
|
||||||
|
errorDetails = errorDetails.split("\n", 2)[0];
|
||||||
|
}
|
||||||
|
error("getImage-image-decode-exception", "failed to get image from uri=" + uri, errorDetails);
|
||||||
} finally {
|
} finally {
|
||||||
Glide.with(activity).clear(target);
|
Glide.with(activity).clear(target);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
package deckers.thibault.aves.decoder;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.GlideBuilder;
|
||||||
|
import com.bumptech.glide.Registry;
|
||||||
|
import com.bumptech.glide.annotation.GlideModule;
|
||||||
|
import com.bumptech.glide.load.DecodeFormat;
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.ExifInterfaceImageHeaderParser;
|
||||||
|
import com.bumptech.glide.module.AppGlideModule;
|
||||||
|
import com.bumptech.glide.request.RequestOptions;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
@GlideModule
|
||||||
|
public class AvesAppGlideModule extends AppGlideModule {
|
||||||
|
@Override
|
||||||
|
public void applyOptions(@NotNull Context context, @NonNull GlideBuilder builder) {
|
||||||
|
builder.setDefaultRequestOptions(new RequestOptions().format(DecodeFormat.PREFER_RGB_565));
|
||||||
|
|
||||||
|
// hide noisy warning (e.g. for images that can't be decoded)
|
||||||
|
builder.setLogLevel(Log.ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
|
||||||
|
// prevent ExifInterface error logs
|
||||||
|
// cf https://github.com/bumptech/glide/issues/3383
|
||||||
|
glide.getRegistry().getImageHeaderParsers().removeIf(parser -> parser instanceof ExifInterfaceImageHeaderParser);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isManifestParsingEnabled() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,23 +0,0 @@
|
||||||
package deckers.thibault.aves.decoder;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
|
||||||
|
|
||||||
import com.bumptech.glide.Glide;
|
|
||||||
import com.bumptech.glide.Registry;
|
|
||||||
import com.bumptech.glide.annotation.GlideModule;
|
|
||||||
import com.bumptech.glide.load.resource.bitmap.ExifInterfaceImageHeaderParser;
|
|
||||||
import com.bumptech.glide.module.LibraryGlideModule;
|
|
||||||
|
|
||||||
@GlideModule
|
|
||||||
public class NoExifInterfaceGlideModule extends LibraryGlideModule {
|
|
||||||
@Override
|
|
||||||
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
|
|
||||||
super.registerComponents(context, glide, registry);
|
|
||||||
// prevent ExifInterface error logs
|
|
||||||
// cf https://github.com/bumptech/glide/issues/3383
|
|
||||||
glide.getRegistry().getImageHeaderParsers().removeIf(parser -> parser instanceof ExifInterfaceImageHeaderParser);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -5,29 +5,16 @@ import android.content.Context;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
|
|
||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
import com.bumptech.glide.GlideBuilder;
|
|
||||||
import com.bumptech.glide.Registry;
|
import com.bumptech.glide.Registry;
|
||||||
import com.bumptech.glide.annotation.GlideModule;
|
import com.bumptech.glide.annotation.GlideModule;
|
||||||
import com.bumptech.glide.load.DecodeFormat;
|
import com.bumptech.glide.module.LibraryGlideModule;
|
||||||
import com.bumptech.glide.module.AppGlideModule;
|
|
||||||
import com.bumptech.glide.request.RequestOptions;
|
|
||||||
|
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
|
|
||||||
@GlideModule
|
@GlideModule
|
||||||
public class VideoThumbnailGlideModule extends AppGlideModule {
|
public class VideoThumbnailGlideModule extends LibraryGlideModule {
|
||||||
@Override
|
|
||||||
public void applyOptions(@NonNull Context context, @NonNull GlideBuilder builder) {
|
|
||||||
builder.setDefaultRequestOptions(new RequestOptions().format(DecodeFormat.PREFER_RGB_565));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
|
public void registerComponents(@NonNull Context context, @NonNull Glide glide, @NonNull Registry registry) {
|
||||||
registry.append(VideoThumbnail.class, InputStream.class, new VideoThumbnailLoader.Factory());
|
registry.append(VideoThumbnail.class, InputStream.class, new VideoThumbnailLoader.Factory());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isManifestParsingEnabled() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -318,8 +318,8 @@ class ImageEntry {
|
||||||
locality: address.locality,
|
locality: address.locality,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} catch (exception, stack) {
|
} catch (error, stackTrace) {
|
||||||
debugPrint('$runtimeType locate failed with path=$path coordinates=$coordinates exception=$exception\n$stack');
|
debugPrint('$runtimeType locate failed with path=$path coordinates=$coordinates error=$error\n$stackTrace');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ class AndroidAppService {
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
debugPrint('getAppIcon failed with code=${e.code}, exception=${e.message}, details=${e.details}');
|
debugPrint('getAppIcon failed with code=${e.code}, exception=${e.message}, details=${e.details}');
|
||||||
}
|
}
|
||||||
return Uint8List(0);
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Map> getEnv() async {
|
static Future<Map> getEnv() async {
|
||||||
|
|
|
@ -100,12 +100,12 @@ class ImageFileService {
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
debugPrint('getImage failed with code=${e.code}, exception=${e.message}, details=${e.details}');
|
debugPrint('getImage failed with code=${e.code}, exception=${e.message}, details=${e.details}');
|
||||||
}
|
}
|
||||||
return Future.sync(() => Uint8List(0));
|
return Future.sync(() => null);
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Uint8List> getThumbnail(ImageEntry entry, double width, double height, {Object taskKey, int priority}) {
|
static Future<Uint8List> getThumbnail(ImageEntry entry, double width, double height, {Object taskKey, int priority}) {
|
||||||
if (entry.isSvg) {
|
if (entry.isSvg) {
|
||||||
return Future.sync(() => Uint8List(0));
|
return Future.sync(() => null);
|
||||||
}
|
}
|
||||||
return servicePolicy.call(
|
return servicePolicy.call(
|
||||||
() async {
|
() async {
|
||||||
|
@ -120,7 +120,7 @@ class ImageFileService {
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
debugPrint('getThumbnail failed with code=${e.code}, exception=${e.message}, details=${e.details}');
|
debugPrint('getThumbnail failed with code=${e.code}, exception=${e.message}, details=${e.details}');
|
||||||
}
|
}
|
||||||
return Uint8List(0);
|
return null;
|
||||||
},
|
},
|
||||||
// debugLabel: 'getThumbnail width=$width, height=$height entry=${entry.filenameWithoutExtension}',
|
// debugLabel: 'getThumbnail width=$width, height=$height entry=${entry.filenameWithoutExtension}',
|
||||||
priority: priority ?? (width == 0 || height == 0 ? ServiceCallPriority.getFastThumbnail : ServiceCallPriority.getSizedThumbnail),
|
priority: priority ?? (width == 0 || height == 0 ? ServiceCallPriority.getFastThumbnail : ServiceCallPriority.getSizedThumbnail),
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'dart:ui' as ui show Codec;
|
import 'dart:ui' as ui show Codec;
|
||||||
|
|
||||||
import 'package:aves/services/android_app_service.dart';
|
import 'package:aves/services/android_app_service.dart';
|
||||||
|
@ -38,8 +37,14 @@ class AppIconImage extends ImageProvider<AppIconImageKey> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ui.Codec> _loadAsync(AppIconImageKey key, DecoderCallback decode) async {
|
Future<ui.Codec> _loadAsync(AppIconImageKey key, DecoderCallback decode) async {
|
||||||
final bytes = await AndroidAppService.getAppIcon(key.packageName, key.size);
|
try {
|
||||||
return await decode(bytes ?? Uint8List(0));
|
final bytes = await AndroidAppService.getAppIcon(key.packageName, key.size);
|
||||||
|
if (bytes == null) return null;
|
||||||
|
return await decode(bytes);
|
||||||
|
} catch (error) {
|
||||||
|
debugPrint('$runtimeType _loadAsync failed with packageName=$packageName, error=$error');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'dart:ui' as ui show Codec;
|
import 'dart:ui' as ui show Codec;
|
||||||
|
|
||||||
import 'package:aves/model/image_entry.dart';
|
import 'package:aves/model/image_entry.dart';
|
||||||
|
@ -49,8 +48,14 @@ class ThumbnailProvider extends ImageProvider<ThumbnailProviderKey> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ui.Codec> _loadAsync(ThumbnailProviderKey key, DecoderCallback decode) async {
|
Future<ui.Codec> _loadAsync(ThumbnailProviderKey key, DecoderCallback decode) async {
|
||||||
final bytes = await ImageFileService.getThumbnail(key.entry, extent, extent, taskKey: _cancellationKey);
|
try {
|
||||||
return await decode(bytes ?? Uint8List(0));
|
final bytes = await ImageFileService.getThumbnail(key.entry, extent, extent, taskKey: _cancellationKey);
|
||||||
|
if (bytes == null) return null;
|
||||||
|
return await decode(bytes);
|
||||||
|
} catch (error) {
|
||||||
|
debugPrint('$runtimeType _loadAsync failed with path=${entry.path}, error=$error');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:typed_data';
|
|
||||||
import 'dart:ui' as ui show Codec;
|
import 'dart:ui' as ui show Codec;
|
||||||
|
|
||||||
import 'package:aves/services/image_file_service.dart';
|
import 'package:aves/services/image_file_service.dart';
|
||||||
|
@ -56,7 +55,11 @@ class UriImage extends ImageProvider<UriImage> {
|
||||||
));
|
));
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
return await decode(bytes ?? Uint8List(0));
|
if (bytes == null) return null;
|
||||||
|
return await decode(bytes);
|
||||||
|
} catch (error) {
|
||||||
|
debugPrint('$runtimeType _loadAsync failed with mimeType=$mimeType, uri=$uri, error=$error');
|
||||||
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
unawaited(chunkEvents.close());
|
unawaited(chunkEvents.close());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue