thumbnail/app icon: use display metrics in Android instead of devicePixelRatio in Flutter
This commit is contained in:
parent
bd4d792179
commit
cbacb923e7
6 changed files with 39 additions and 23 deletions
|
@ -121,12 +121,16 @@ public class AppAdapterHandler implements MethodChannel.MethodCallHandler {
|
||||||
|
|
||||||
private void getAppIcon(MethodCall call, MethodChannel.Result result) {
|
private void getAppIcon(MethodCall call, MethodChannel.Result result) {
|
||||||
String packageName = call.argument("packageName");
|
String packageName = call.argument("packageName");
|
||||||
Integer size = call.argument("size");
|
Double sizeDip = call.argument("sizeDip");
|
||||||
if (packageName == null || size == null) {
|
if (packageName == null || sizeDip == null) {
|
||||||
result.error("getAppIcon-args", "failed because of missing arguments", null);
|
result.error("getAppIcon-args", "failed because of missing arguments", null);
|
||||||
return;
|
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;
|
byte[] data = null;
|
||||||
try {
|
try {
|
||||||
int iconResourceId = context.getPackageManager().getApplicationInfo(packageName, 0).icon;
|
int iconResourceId = context.getPackageManager().getApplicationInfo(packageName, 0).icon;
|
||||||
|
|
|
@ -21,6 +21,7 @@ public class ImageFileHandler implements MethodChannel.MethodCallHandler {
|
||||||
public static final String CHANNEL = "deckers.thibault/aves/image";
|
public static final String CHANNEL = "deckers.thibault/aves/image";
|
||||||
|
|
||||||
private Activity activity;
|
private Activity activity;
|
||||||
|
private float density;
|
||||||
private MediaStoreStreamHandler mediaStoreStreamHandler;
|
private MediaStoreStreamHandler mediaStoreStreamHandler;
|
||||||
|
|
||||||
public ImageFileHandler(Activity activity, MediaStoreStreamHandler mediaStoreStreamHandler) {
|
public ImageFileHandler(Activity activity, MediaStoreStreamHandler mediaStoreStreamHandler) {
|
||||||
|
@ -28,6 +29,13 @@ public class ImageFileHandler implements MethodChannel.MethodCallHandler {
|
||||||
this.mediaStoreStreamHandler = mediaStoreStreamHandler;
|
this.mediaStoreStreamHandler = mediaStoreStreamHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float getDensity() {
|
||||||
|
if (density == 0) {
|
||||||
|
density = activity.getResources().getDisplayMetrics().density;
|
||||||
|
}
|
||||||
|
return density;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
|
public void onMethodCall(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
|
||||||
switch (call.method) {
|
switch (call.method) {
|
||||||
|
@ -63,13 +71,20 @@ public class ImageFileHandler implements MethodChannel.MethodCallHandler {
|
||||||
|
|
||||||
private void getThumbnail(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
|
private void getThumbnail(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
|
||||||
Map entryMap = call.argument("entry");
|
Map entryMap = call.argument("entry");
|
||||||
Integer width = call.argument("width");
|
Double widthDip = call.argument("widthDip");
|
||||||
Integer height = call.argument("height");
|
Double heightDip = call.argument("heightDip");
|
||||||
Integer defaultSize = call.argument("defaultSize");
|
Double defaultSizeDip = call.argument("defaultSizeDip");
|
||||||
if (entryMap == null || defaultSize == null) {
|
|
||||||
|
if (entryMap == null || widthDip == null || heightDip == null || defaultSizeDip == null) {
|
||||||
result.error("getThumbnail-args", "failed because of missing arguments", null);
|
result.error("getThumbnail-args", "failed because of missing arguments", null);
|
||||||
return;
|
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);
|
ImageEntry entry = new ImageEntry(entryMap);
|
||||||
new ImageDecodeTask(activity).execute(new ImageDecodeTask.Params(entry, width, height, defaultSize, result));
|
new ImageDecodeTask(activity).execute(new ImageDecodeTask.Params(entry, width, height, defaultSize, result));
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,11 @@ class AndroidAppService {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Uint8List> getAppIcon(String packageName, int size) async {
|
static Future<Uint8List> getAppIcon(String packageName, double size) async {
|
||||||
try {
|
try {
|
||||||
final result = await platform.invokeMethod('getAppIcon', <String, dynamic>{
|
final result = await platform.invokeMethod('getAppIcon', <String, dynamic>{
|
||||||
'packageName': packageName,
|
'packageName': packageName,
|
||||||
'size': size,
|
'sizeDip': size,
|
||||||
});
|
});
|
||||||
return result as Uint8List;
|
return result as Uint8List;
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
|
|
|
@ -14,6 +14,7 @@ class ImageFileService {
|
||||||
static const platform = MethodChannel('deckers.thibault/aves/image');
|
static const platform = MethodChannel('deckers.thibault/aves/image');
|
||||||
static final StreamsChannel byteChannel = StreamsChannel('deckers.thibault/aves/imagebytestream');
|
static final StreamsChannel byteChannel = StreamsChannel('deckers.thibault/aves/imagebytestream');
|
||||||
static final StreamsChannel opChannel = StreamsChannel('deckers.thibault/aves/imageopstream');
|
static final StreamsChannel opChannel = StreamsChannel('deckers.thibault/aves/imageopstream');
|
||||||
|
static const double thumbnailDefaultSize = 64.0;
|
||||||
|
|
||||||
static Future<void> getImageEntries(SortFactor sort, GroupFactor group) async {
|
static Future<void> getImageEntries(SortFactor sort, GroupFactor group) async {
|
||||||
try {
|
try {
|
||||||
|
@ -76,15 +77,15 @@ class ImageFileService {
|
||||||
return Future.sync(() => Uint8List(0));
|
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(
|
return servicePolicy.call(
|
||||||
() async {
|
() async {
|
||||||
try {
|
try {
|
||||||
final result = await platform.invokeMethod('getThumbnail', <String, dynamic>{
|
final result = await platform.invokeMethod('getThumbnail', <String, dynamic>{
|
||||||
'entry': entry.toMap(),
|
'entry': entry.toMap(),
|
||||||
'width': width,
|
'widthDip': width,
|
||||||
'height': height,
|
'heightDip': height,
|
||||||
'defaultSize': 256,
|
'defaultSizeDip': thumbnailDefaultSize,
|
||||||
});
|
});
|
||||||
return result as Uint8List;
|
return result as Uint8List;
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
|
|
|
@ -21,7 +21,7 @@ class AppIconImage extends ImageProvider<AppIconImageKey> {
|
||||||
Future<AppIconImageKey> obtainKey(ImageConfiguration configuration) {
|
Future<AppIconImageKey> obtainKey(ImageConfiguration configuration) {
|
||||||
return SynchronousFuture<AppIconImageKey>(AppIconImageKey(
|
return SynchronousFuture<AppIconImageKey>(AppIconImageKey(
|
||||||
packageName: packageName,
|
packageName: packageName,
|
||||||
sizePixels: (size * configuration.devicePixelRatio).round(),
|
size: size,
|
||||||
scale: scale,
|
scale: scale,
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
@ -38,28 +38,28 @@ 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.sizePixels);
|
final bytes = await AndroidAppService.getAppIcon(key.packageName, key.size);
|
||||||
return await decode(bytes ?? Uint8List(0));
|
return await decode(bytes ?? Uint8List(0));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AppIconImageKey {
|
class AppIconImageKey {
|
||||||
final String packageName;
|
final String packageName;
|
||||||
final int sizePixels;
|
final double size;
|
||||||
final double scale;
|
final double scale;
|
||||||
|
|
||||||
const AppIconImageKey({
|
const AppIconImageKey({
|
||||||
@required this.packageName,
|
@required this.packageName,
|
||||||
@required this.sizePixels,
|
@required this.size,
|
||||||
this.scale,
|
this.scale,
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
bool operator ==(Object other) {
|
bool operator ==(Object other) {
|
||||||
if (other.runtimeType != runtimeType) return false;
|
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
|
@override
|
||||||
int get hashCode => hashValues(packageName, sizePixels, scale);
|
int get hashCode => hashValues(packageName, size, scale);
|
||||||
}
|
}
|
||||||
|
|
|
@ -34,7 +34,6 @@ class ThumbnailProvider extends ImageProvider<ThumbnailProviderKey> {
|
||||||
ThumbnailProviderKey _buildKey(ImageConfiguration configuration) => ThumbnailProviderKey(
|
ThumbnailProviderKey _buildKey(ImageConfiguration configuration) => ThumbnailProviderKey(
|
||||||
entry: entry,
|
entry: entry,
|
||||||
extent: extent,
|
extent: extent,
|
||||||
devicePixelRatio: configuration.devicePixelRatio,
|
|
||||||
scale: scale,
|
scale: scale,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -50,8 +49,7 @@ class ThumbnailProvider extends ImageProvider<ThumbnailProviderKey> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<ui.Codec> _loadAsync(ThumbnailProviderKey key, DecoderCallback decode) async {
|
Future<ui.Codec> _loadAsync(ThumbnailProviderKey key, DecoderCallback decode) async {
|
||||||
final dimPixels = (extent * key.devicePixelRatio).round();
|
final bytes = await ImageFileService.getThumbnail(key.entry, extent, extent, taskKey: _cancellationKey);
|
||||||
final bytes = await ImageFileService.getThumbnail(key.entry, dimPixels, dimPixels, taskKey: _cancellationKey);
|
|
||||||
return await decode(bytes ?? Uint8List(0));
|
return await decode(bytes ?? Uint8List(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,13 +65,11 @@ class ThumbnailProvider extends ImageProvider<ThumbnailProviderKey> {
|
||||||
class ThumbnailProviderKey {
|
class ThumbnailProviderKey {
|
||||||
final ImageEntry entry;
|
final ImageEntry entry;
|
||||||
final double extent;
|
final double extent;
|
||||||
final double devicePixelRatio; // do not include configuration in key hashcode or == operator
|
|
||||||
final double scale;
|
final double scale;
|
||||||
|
|
||||||
const ThumbnailProviderKey({
|
const ThumbnailProviderKey({
|
||||||
@required this.entry,
|
@required this.entry,
|
||||||
@required this.extent,
|
@required this.extent,
|
||||||
@required this.devicePixelRatio,
|
|
||||||
this.scale,
|
this.scale,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue