thumbnail/app icon: use display metrics in Android instead of devicePixelRatio in Flutter

This commit is contained in:
Thibault Deckers 2020-06-11 14:28:09 +09:00
parent bd4d792179
commit cbacb923e7
6 changed files with 39 additions and 23 deletions

View file

@ -121,12 +121,16 @@ public class AppAdapterHandler implements MethodChannel.MethodCallHandler {
private void getAppIcon(MethodCall call, MethodChannel.Result result) {
String packageName = call.argument("packageName");
Integer size = call.argument("size");
if (packageName == null || size == null) {
Double sizeDip = call.argument("sizeDip");
if (packageName == null || sizeDip == null) {
result.error("getAppIcon-args", "failed because of missing arguments", null);
return;
}
// convert DIP to physical pixels here, instead of using `devicePixelRatio` in Flutter
float density = context.getResources().getDisplayMetrics().density;
int size = (int) Math.round(sizeDip * density);
byte[] data = null;
try {
int iconResourceId = context.getPackageManager().getApplicationInfo(packageName, 0).icon;

View file

@ -21,6 +21,7 @@ public class ImageFileHandler implements MethodChannel.MethodCallHandler {
public static final String CHANNEL = "deckers.thibault/aves/image";
private Activity activity;
private float density;
private MediaStoreStreamHandler mediaStoreStreamHandler;
public ImageFileHandler(Activity activity, MediaStoreStreamHandler mediaStoreStreamHandler) {
@ -28,6 +29,13 @@ public class ImageFileHandler implements MethodChannel.MethodCallHandler {
this.mediaStoreStreamHandler = mediaStoreStreamHandler;
}
public float getDensity() {
if (density == 0) {
density = activity.getResources().getDisplayMetrics().density;
}
return density;
}
@Override
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
switch (call.method) {
@ -63,13 +71,20 @@ public class ImageFileHandler implements MethodChannel.MethodCallHandler {
private void getThumbnail(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
Map entryMap = call.argument("entry");
Integer width = call.argument("width");
Integer height = call.argument("height");
Integer defaultSize = call.argument("defaultSize");
if (entryMap == null || defaultSize == null) {
Double widthDip = call.argument("widthDip");
Double heightDip = call.argument("heightDip");
Double defaultSizeDip = call.argument("defaultSizeDip");
if (entryMap == null || widthDip == null || heightDip == null || defaultSizeDip == null) {
result.error("getThumbnail-args", "failed because of missing arguments", null);
return;
}
// convert DIP to physical pixels here, instead of using `devicePixelRatio` in Flutter
float density = getDensity();
int width = (int) Math.round(widthDip * density);
int height = (int) Math.round(heightDip * density);
int defaultSize = (int) Math.round(defaultSizeDip * density);
ImageEntry entry = new ImageEntry(entryMap);
new ImageDecodeTask(activity).execute(new ImageDecodeTask.Params(entry, width, height, defaultSize, result));
}

View file

@ -16,11 +16,11 @@ class AndroidAppService {
return {};
}
static Future<Uint8List> getAppIcon(String packageName, int size) async {
static Future<Uint8List> getAppIcon(String packageName, double size) async {
try {
final result = await platform.invokeMethod('getAppIcon', <String, dynamic>{
'packageName': packageName,
'size': size,
'sizeDip': size,
});
return result as Uint8List;
} on PlatformException catch (e) {

View file

@ -14,6 +14,7 @@ class ImageFileService {
static const platform = MethodChannel('deckers.thibault/aves/image');
static final StreamsChannel byteChannel = StreamsChannel('deckers.thibault/aves/imagebytestream');
static final StreamsChannel opChannel = StreamsChannel('deckers.thibault/aves/imageopstream');
static const double thumbnailDefaultSize = 64.0;
static Future<void> getImageEntries(SortFactor sort, GroupFactor group) async {
try {
@ -76,15 +77,15 @@ class ImageFileService {
return Future.sync(() => Uint8List(0));
}
static Future<Uint8List> getThumbnail(ImageEntry entry, int width, int height, {Object taskKey, int priority}) {
static Future<Uint8List> getThumbnail(ImageEntry entry, double width, double height, {Object taskKey, int priority}) {
return servicePolicy.call(
() async {
try {
final result = await platform.invokeMethod('getThumbnail', <String, dynamic>{
'entry': entry.toMap(),
'width': width,
'height': height,
'defaultSize': 256,
'widthDip': width,
'heightDip': height,
'defaultSizeDip': thumbnailDefaultSize,
});
return result as Uint8List;
} on PlatformException catch (e) {

View file

@ -21,7 +21,7 @@ class AppIconImage extends ImageProvider<AppIconImageKey> {
Future<AppIconImageKey> obtainKey(ImageConfiguration configuration) {
return SynchronousFuture<AppIconImageKey>(AppIconImageKey(
packageName: packageName,
sizePixels: (size * configuration.devicePixelRatio).round(),
size: size,
scale: scale,
));
}
@ -38,28 +38,28 @@ class AppIconImage extends ImageProvider<AppIconImageKey> {
}
Future<ui.Codec> _loadAsync(AppIconImageKey key, DecoderCallback decode) async {
final bytes = await AndroidAppService.getAppIcon(key.packageName, key.sizePixels);
final bytes = await AndroidAppService.getAppIcon(key.packageName, key.size);
return await decode(bytes ?? Uint8List(0));
}
}
class AppIconImageKey {
final String packageName;
final int sizePixels;
final double size;
final double scale;
const AppIconImageKey({
@required this.packageName,
@required this.sizePixels,
@required this.size,
this.scale,
});
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is AppIconImageKey && other.packageName == packageName && other.sizePixels == sizePixels && other.scale == scale;
return other is AppIconImageKey && other.packageName == packageName && other.size == size && other.scale == scale;
}
@override
int get hashCode => hashValues(packageName, sizePixels, scale);
int get hashCode => hashValues(packageName, size, scale);
}

View file

@ -34,7 +34,6 @@ class ThumbnailProvider extends ImageProvider<ThumbnailProviderKey> {
ThumbnailProviderKey _buildKey(ImageConfiguration configuration) => ThumbnailProviderKey(
entry: entry,
extent: extent,
devicePixelRatio: configuration.devicePixelRatio,
scale: scale,
);
@ -50,8 +49,7 @@ class ThumbnailProvider extends ImageProvider<ThumbnailProviderKey> {
}
Future<ui.Codec> _loadAsync(ThumbnailProviderKey key, DecoderCallback decode) async {
final dimPixels = (extent * key.devicePixelRatio).round();
final bytes = await ImageFileService.getThumbnail(key.entry, dimPixels, dimPixels, taskKey: _cancellationKey);
final bytes = await ImageFileService.getThumbnail(key.entry, extent, extent, taskKey: _cancellationKey);
return await decode(bytes ?? Uint8List(0));
}
@ -67,13 +65,11 @@ class ThumbnailProvider extends ImageProvider<ThumbnailProviderKey> {
class ThumbnailProviderKey {
final ImageEntry entry;
final double extent;
final double devicePixelRatio; // do not include configuration in key hashcode or == operator
final double scale;
const ThumbnailProviderKey({
@required this.entry,
@required this.extent,
@required this.devicePixelRatio,
this.scale,
});