mirror of
https://gitlab.com/nkming2/nc-photos.git
synced 2025-03-13 18:58:53 +01:00
Add image provider backed by content uri
This commit is contained in:
parent
a65c8d824e
commit
c6e6b99128
6 changed files with 161 additions and 0 deletions
64
app/lib/mobile/android/content_uri_image_provider.dart
Normal file
64
app/lib/mobile/android/content_uri_image_provider.dart
Normal file
|
@ -0,0 +1,64 @@
|
|||
import 'dart:ui' as ui show Codec;
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:nc_photos_plugin/nc_photos_plugin.dart';
|
||||
|
||||
class ContentUriImage extends ImageProvider<ContentUriImage>
|
||||
with EquatableMixin {
|
||||
/// Creates an object that decodes a content Uri as an image.
|
||||
const ContentUriImage(
|
||||
this.uri, {
|
||||
this.scale = 1.0,
|
||||
});
|
||||
|
||||
@override
|
||||
obtainKey(ImageConfiguration configuration) {
|
||||
return SynchronousFuture<ContentUriImage>(this);
|
||||
}
|
||||
|
||||
@override
|
||||
load(ContentUriImage key, DecoderCallback decode) {
|
||||
return MultiFrameImageStreamCompleter(
|
||||
codec: _loadAsync(key, decode),
|
||||
scale: key.scale,
|
||||
debugLabel: key.uri,
|
||||
informationCollector: () => <DiagnosticsNode>[
|
||||
ErrorDescription("Content uri: $uri"),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<ui.Codec> _loadAsync(
|
||||
ContentUriImage key, DecoderCallback decode) async {
|
||||
assert(key == this);
|
||||
|
||||
final bytes = await ContentUri.readUri(uri);
|
||||
|
||||
if (bytes.lengthInBytes == 0) {
|
||||
// The file may become available later.
|
||||
PaintingBinding.instance!.imageCache!.evict(key);
|
||||
throw StateError("$uri is empty and cannot be loaded as an image.");
|
||||
}
|
||||
|
||||
return decode(bytes);
|
||||
}
|
||||
|
||||
@override
|
||||
get props => [
|
||||
uri,
|
||||
scale,
|
||||
];
|
||||
|
||||
@override
|
||||
toString() => "${objectRuntimeType(this, "ContentUriImage")} {"
|
||||
"uri: $uri, "
|
||||
"scale: $scale, "
|
||||
"}";
|
||||
|
||||
final String uri;
|
||||
|
||||
/// The scale to place in the [ImageInfo] object of the image.
|
||||
final double scale;
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
package com.nkming.nc_photos.plugin
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import io.flutter.plugin.common.MethodCall
|
||||
import io.flutter.plugin.common.MethodChannel
|
||||
import java.io.FileNotFoundException
|
||||
|
||||
class ContentUriChannelHandler(context: Context) :
|
||||
MethodChannel.MethodCallHandler {
|
||||
companion object {
|
||||
const val METHOD_CHANNEL = "${K.LIB_ID}/content_uri_method"
|
||||
|
||||
private const val TAG = "ContentUriChannelHandler"
|
||||
}
|
||||
|
||||
override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {
|
||||
when (call.method) {
|
||||
"readUri" -> {
|
||||
try {
|
||||
readUri(call.argument("uri")!!, result)
|
||||
} catch (e: Throwable) {
|
||||
result.error("systemException", e.toString(), null)
|
||||
}
|
||||
}
|
||||
|
||||
else -> result.notImplemented()
|
||||
}
|
||||
}
|
||||
|
||||
private fun readUri(uri: String, result: MethodChannel.Result) {
|
||||
val uriTyped = Uri.parse(uri)
|
||||
try {
|
||||
val bytes =
|
||||
context.contentResolver.openInputStream(uriTyped)!!.use {
|
||||
it.readBytes()
|
||||
}
|
||||
result.success(bytes)
|
||||
} catch (e: FileNotFoundException) {
|
||||
result.error("fileNotFoundException", e.toString(), null)
|
||||
}
|
||||
}
|
||||
|
||||
private val context = context
|
||||
}
|
|
@ -62,6 +62,14 @@ class NcPhotosPlugin : FlutterPlugin, ActivityAware {
|
|||
flutterPluginBinding.applicationContext
|
||||
)
|
||||
)
|
||||
|
||||
contentUriMethodChannel = MethodChannel(
|
||||
flutterPluginBinding.binaryMessenger,
|
||||
ContentUriChannelHandler.METHOD_CHANNEL
|
||||
)
|
||||
contentUriMethodChannel.setMethodCallHandler(
|
||||
ContentUriChannelHandler(flutterPluginBinding.applicationContext)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onDetachedFromEngine(
|
||||
|
@ -74,6 +82,7 @@ class NcPhotosPlugin : FlutterPlugin, ActivityAware {
|
|||
nativeEventMethodChannel.setMethodCallHandler(null)
|
||||
mediaStoreMethodChannel.setMethodCallHandler(null)
|
||||
imageProcessorMethodChannel.setMethodCallHandler(null)
|
||||
contentUriMethodChannel.setMethodCallHandler(null)
|
||||
}
|
||||
|
||||
override fun onAttachedToActivity(binding: ActivityPluginBinding) {
|
||||
|
@ -100,6 +109,7 @@ class NcPhotosPlugin : FlutterPlugin, ActivityAware {
|
|||
private lateinit var nativeEventMethodChannel: MethodChannel
|
||||
private lateinit var mediaStoreMethodChannel: MethodChannel
|
||||
private lateinit var imageProcessorMethodChannel: MethodChannel
|
||||
private lateinit var contentUriMethodChannel: MethodChannel
|
||||
|
||||
private lateinit var lockChannelHandler: LockChannelHandler
|
||||
private lateinit var mediaStoreChannelHandler: MediaStoreChannelHandler
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
library nc_photos_plugin;
|
||||
|
||||
export 'src/content_uri.dart';
|
||||
export 'src/exception.dart';
|
||||
export 'src/image_processor.dart';
|
||||
export 'src/lock.dart';
|
||||
|
|
26
plugin/lib/src/content_uri.dart
Normal file
26
plugin/lib/src/content_uri.dart
Normal file
|
@ -0,0 +1,26 @@
|
|||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:nc_photos_plugin/src/exception.dart';
|
||||
import 'package:nc_photos_plugin/src/k.dart' as k;
|
||||
|
||||
class ContentUri {
|
||||
static Future<Uint8List> readUri(String uri) async {
|
||||
try {
|
||||
return await _methodChannel.invokeMethod("readUri", <String, dynamic>{
|
||||
"uri": uri,
|
||||
});
|
||||
} on PlatformException catch (e) {
|
||||
if (e.code == _exceptionFileNotFound) {
|
||||
throw const FileNotFoundException();
|
||||
} else {
|
||||
rethrow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static const _methodChannel = MethodChannel("${k.libId}/content_uri_method");
|
||||
|
||||
static const _exceptionFileNotFound = "fileNotFoundException";
|
||||
}
|
|
@ -1,3 +1,18 @@
|
|||
class FileNotFoundException implements Exception {
|
||||
const FileNotFoundException([this.message]);
|
||||
|
||||
@override
|
||||
toString() {
|
||||
if (message == null) {
|
||||
return "FileNotFoundException";
|
||||
} else {
|
||||
return "FileNotFoundException: $message";
|
||||
}
|
||||
}
|
||||
|
||||
final dynamic message;
|
||||
}
|
||||
|
||||
/// Platform permission is not granted by user
|
||||
class PermissionException implements Exception {
|
||||
const PermissionException([this.message]);
|
||||
|
|
Loading…
Reference in a new issue