fullscreen: decode image from URI instead of path

This commit is contained in:
Thibault Deckers 2020-03-17 16:54:57 +09:00
parent 1474d4fcb9
commit 9357a49f4a
4 changed files with 108 additions and 4 deletions

View file

@ -7,6 +7,9 @@ import android.os.Looper;
import androidx.annotation.NonNull;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import deckers.thibault.aves.model.ImageEntry;
@ -38,6 +41,9 @@ public class ImageFileHandler implements MethodChannel.MethodCallHandler {
case "getImageEntry":
getImageEntry(call, result);
break;
case "readAsBytes":
readAsBytes(call, result);
break;
case "getImageBytes":
getImageBytes(call, result);
break;
@ -59,6 +65,25 @@ public class ImageFileHandler implements MethodChannel.MethodCallHandler {
}
}
private void readAsBytes(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
String uri = call.argument("uri");
byte[] data = null;
try (InputStream is = activity.getContentResolver().openInputStream(Uri.parse(uri))) {
if (is != null) {
data = getBytes(is);
}
} catch (IOException ex) {
// ignore
}
if (data != null) {
result.success(data);
} else {
result.error("readAsBytes-null", "failed to read bytes from uri=" + uri, null);
}
}
private void getImageBytes(@NonNull MethodCall call, @NonNull MethodChannel.Result result) {
Map entryMap = call.argument("entry");
Integer width = call.argument("width");
@ -189,4 +214,18 @@ public class ImageFileHandler implements MethodChannel.MethodCallHandler {
}
});
}
// convenience methods
private byte[] getBytes(InputStream inputStream) throws IOException {
ByteArrayOutputStream byteBuffer = new ByteArrayOutputStream();
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int len = 0;
while ((len = inputStream.read(buffer)) != -1) {
byteBuffer.write(buffer, 0, len);
}
return byteBuffer.toByteArray();
}
}

View file

@ -29,6 +29,18 @@ class ImageFileService {
return null;
}
static Future<Uint8List> readAsBytes(String uri) async {
try {
final result = await platform.invokeMethod('readAsBytes', <String, dynamic>{
'uri': uri,
});
return result as Uint8List;
} on PlatformException catch (e) {
debugPrint('readAsBytes failed with exception=${e.message}');
}
return Uint8List(0);
}
static Future<Uint8List> getImageBytes(ImageEntry entry, int width, int height) async {
if (width > 0 && height > 0) {
// debugPrint('getImageBytes width=$width path=${entry.path}');

View file

@ -0,0 +1,55 @@
import 'dart:typed_data';
import 'dart:ui' as ui show Codec;
import 'package:aves/model/image_file_service.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
class UriImage extends ImageProvider<UriImage> {
const UriImage(this.uri, {this.scale = 1.0})
: assert(uri != null),
assert(scale != null);
final String uri;
final double scale;
@override
Future<UriImage> obtainKey(ImageConfiguration configuration) {
return SynchronousFuture<UriImage>(this);
}
@override
ImageStreamCompleter load(UriImage key, DecoderCallback decode) {
return MultiFrameImageStreamCompleter(
codec: _loadAsync(key, decode),
scale: key.scale,
informationCollector: () sync* {
yield ErrorDescription('Uri: $uri');
},
);
}
Future<ui.Codec> _loadAsync(UriImage key, DecoderCallback decode) async {
assert(key == this);
final Uint8List bytes = await ImageFileService.readAsBytes(uri);
if (bytes.lengthInBytes == 0) {
return null;
}
return await decode(bytes);
}
@override
bool operator ==(Object other) {
if (other.runtimeType != runtimeType) return false;
return other is UriImage && other.uri == uri && other.scale == scale;
}
@override
int get hashCode => hashValues(uri, scale);
@override
String toString() => '${objectRuntimeType(this, 'UriImage')}("$uri", scale: $scale)';
}

View file

@ -1,10 +1,8 @@
import 'dart:io';
import 'package:aves/model/image_entry.dart';
import 'package:aves/widgets/fullscreen/image_uri.dart';
import 'package:aves/widgets/fullscreen/video.dart';
import 'package:flutter/material.dart';
import 'package:photo_view/photo_view.dart';
import 'package:transparent_image/transparent_image.dart';
import 'package:tuple/tuple.dart';
import 'package:video_player/video_player.dart';
@ -48,7 +46,7 @@ class ImageView extends StatelessWidget {
return PhotoView(
// key includes size and orientation to refresh when the image is rotated
key: ValueKey('${entry.orientationDegrees}_${entry.width}_${entry.height}_${entry.path}'),
imageProvider: entry.path != null ? FileImage(File(entry.path)) : MemoryImage(kTransparentImage),
imageProvider: UriImage(entry.uri),
loadingBuilder: (context, event) => const Center(
child: SizedBox(
width: 64,