fullscreen: decode image from URI instead of path
This commit is contained in:
parent
1474d4fcb9
commit
9357a49f4a
4 changed files with 108 additions and 4 deletions
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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}');
|
||||
|
|
55
lib/widgets/fullscreen/image_uri.dart
Normal file
55
lib/widgets/fullscreen/image_uri.dart
Normal 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)';
|
||||
}
|
|
@ -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,
|
||||
|
|
Loading…
Reference in a new issue