final commit
This commit is contained in:
parent
2813e72647
commit
42155d9e31
@ -202,7 +202,35 @@ class MyHomePageState extends State<MyHomePage> {
|
|||||||
);
|
);
|
||||||
},
|
},
|
||||||
child: const Text('Thumbnail'),
|
child: const Text('Thumbnail'),
|
||||||
)
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final result = await MoxplatformPlugin.platform.pickFiles(FilePickerType.image, false);
|
||||||
|
print('Picked files $result');
|
||||||
|
},
|
||||||
|
child: const Text('Pick image'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final result = await MoxplatformPlugin.platform.pickFiles(FilePickerType.image, true);
|
||||||
|
print('Picked files $result');
|
||||||
|
},
|
||||||
|
child: const Text('Pick multiple images'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final result = await MoxplatformPlugin.platform.pickFiles(FilePickerType.imageAndVideo, true);
|
||||||
|
print('Picked files $result');
|
||||||
|
},
|
||||||
|
child: const Text('Pick multiple images and videos'),
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () async {
|
||||||
|
final result = await MoxplatformPlugin.platform.pickFiles(FilePickerType.generic, true);
|
||||||
|
print('Picked files $result');
|
||||||
|
},
|
||||||
|
child: const Text('Pick multiple generic files'),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -44,4 +44,5 @@ android {
|
|||||||
dependencies {
|
dependencies {
|
||||||
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
|
implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.0.0'
|
||||||
implementation 'androidx.core:core:1.10.1'
|
implementation 'androidx.core:core:1.10.1'
|
||||||
|
implementation 'androidx.activity:activity:1.7.2'
|
||||||
}
|
}
|
@ -81,6 +81,19 @@ public class Api {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public enum FilePickerType {
|
||||||
|
IMAGE(0),
|
||||||
|
VIDEO(1),
|
||||||
|
IMAGE_AND_VIDEO(2),
|
||||||
|
GENERIC(3);
|
||||||
|
|
||||||
|
final int index;
|
||||||
|
|
||||||
|
private FilePickerType(final int index) {
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Generated class from Pigeon that represents data sent in messages. */
|
/** Generated class from Pigeon that represents data sent in messages. */
|
||||||
public static final class CryptographyResult {
|
public static final class CryptographyResult {
|
||||||
private @NonNull byte[] plaintextHash;
|
private @NonNull byte[] plaintextHash;
|
||||||
@ -211,6 +224,8 @@ public class Api {
|
|||||||
/** Media APIs */
|
/** Media APIs */
|
||||||
@NonNull
|
@NonNull
|
||||||
Boolean generateVideoThumbnail(@NonNull String src, @NonNull String dest, @NonNull Long maxWidth);
|
Boolean generateVideoThumbnail(@NonNull String src, @NonNull String dest, @NonNull Long maxWidth);
|
||||||
|
/** Picker */
|
||||||
|
void pickFiles(@NonNull FilePickerType type, @NonNull Boolean pickMultiple, @NonNull Result<List<String>> result);
|
||||||
|
|
||||||
/** The codec used by MoxplatformApi. */
|
/** The codec used by MoxplatformApi. */
|
||||||
static @NonNull MessageCodec<Object> getCodec() {
|
static @NonNull MessageCodec<Object> getCodec() {
|
||||||
@ -457,6 +472,36 @@ public class Api {
|
|||||||
channel.setMessageHandler(null);
|
channel.setMessageHandler(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
BasicMessageChannel<Object> channel =
|
||||||
|
new BasicMessageChannel<>(
|
||||||
|
binaryMessenger, "dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.pickFiles", getCodec());
|
||||||
|
if (api != null) {
|
||||||
|
channel.setMessageHandler(
|
||||||
|
(message, reply) -> {
|
||||||
|
ArrayList<Object> wrapped = new ArrayList<Object>();
|
||||||
|
ArrayList<Object> args = (ArrayList<Object>) message;
|
||||||
|
FilePickerType typeArg = args.get(0) == null ? null : FilePickerType.values()[(int) args.get(0)];
|
||||||
|
Boolean pickMultipleArg = (Boolean) args.get(1);
|
||||||
|
Result<List<String>> resultCallback =
|
||||||
|
new Result<List<String>>() {
|
||||||
|
public void success(List<String> result) {
|
||||||
|
wrapped.add(0, result);
|
||||||
|
reply.reply(wrapped);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void error(Throwable error) {
|
||||||
|
ArrayList<Object> wrappedError = wrapError(error);
|
||||||
|
reply.reply(wrappedError);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
api.pickFiles(typeArg, pickMultipleArg, resultCallback);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
channel.setMessageHandler(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,8 @@ import static androidx.core.content.ContextCompat.startActivity;
|
|||||||
import static me.polynom.moxplatform_android.ConstantsKt.MOXPLATFORM_FILEPROVIDER_ID;
|
import static me.polynom.moxplatform_android.ConstantsKt.MOXPLATFORM_FILEPROVIDER_ID;
|
||||||
import static me.polynom.moxplatform_android.ConstantsKt.SHARED_PREFERENCES_KEY;
|
import static me.polynom.moxplatform_android.ConstantsKt.SHARED_PREFERENCES_KEY;
|
||||||
import static me.polynom.moxplatform_android.CryptoKt.*;
|
import static me.polynom.moxplatform_android.CryptoKt.*;
|
||||||
|
import static me.polynom.moxplatform_android.PickerKt.filePickerRequest;
|
||||||
|
import static me.polynom.moxplatform_android.PickerKt.onActivityResultImpl;
|
||||||
import static me.polynom.moxplatform_android.RecordSentMessageKt.*;
|
import static me.polynom.moxplatform_android.RecordSentMessageKt.*;
|
||||||
import static me.polynom.moxplatform_android.ThumbnailsKt.generateVideoThumbnailImplementation;
|
import static me.polynom.moxplatform_android.ThumbnailsKt.generateVideoThumbnailImplementation;
|
||||||
|
|
||||||
@ -31,6 +33,8 @@ import android.provider.MediaStore;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Size;
|
import android.util.Size;
|
||||||
|
|
||||||
|
import androidx.activity.result.PickVisualMediaRequest;
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.core.app.NotificationManagerCompat;
|
import androidx.core.app.NotificationManagerCompat;
|
||||||
@ -42,7 +46,11 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
import io.flutter.embedding.engine.plugins.FlutterPlugin;
|
||||||
import io.flutter.embedding.engine.plugins.service.ServiceAware;
|
import io.flutter.embedding.engine.plugins.service.ServiceAware;
|
||||||
@ -55,11 +63,14 @@ import io.flutter.plugin.common.MethodChannel;
|
|||||||
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
|
||||||
import io.flutter.plugin.common.MethodChannel.Result;
|
import io.flutter.plugin.common.MethodChannel.Result;
|
||||||
import io.flutter.plugin.common.PluginRegistry.Registrar;
|
import io.flutter.plugin.common.PluginRegistry.Registrar;
|
||||||
|
import io.flutter.plugin.common.PluginRegistry;
|
||||||
import io.flutter.plugin.common.JSONMethodCodec;
|
import io.flutter.plugin.common.JSONMethodCodec;
|
||||||
|
import io.flutter.embedding.engine.plugins.activity.ActivityAware;
|
||||||
|
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding;
|
||||||
import kotlin.Unit;
|
import kotlin.Unit;
|
||||||
import kotlin.jvm.functions.Function1;
|
import kotlin.jvm.functions.Function1;
|
||||||
|
|
||||||
public class MoxplatformAndroidPlugin extends BroadcastReceiver implements FlutterPlugin, MethodCallHandler, ServiceAware, MoxplatformApi {
|
public class MoxplatformAndroidPlugin extends BroadcastReceiver implements FlutterPlugin, MethodCallHandler, ServiceAware, ActivityAware, PluginRegistry.ActivityResultListener, MoxplatformApi {
|
||||||
public static final String entrypointKey = "entrypoint_handle";
|
public static final String entrypointKey = "entrypoint_handle";
|
||||||
public static final String extraDataKey = "extra_data";
|
public static final String extraDataKey = "extra_data";
|
||||||
private static final String autoStartAtBootKey = "auto_start_at_boot";
|
private static final String autoStartAtBootKey = "auto_start_at_boot";
|
||||||
@ -72,13 +83,17 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
|
|||||||
private MethodChannel channel;
|
private MethodChannel channel;
|
||||||
|
|
||||||
public static Activity activity;
|
public static Activity activity;
|
||||||
|
|
||||||
private Context context;
|
private Context context;
|
||||||
|
|
||||||
public MoxplatformAndroidPlugin() {
|
public MoxplatformAndroidPlugin() {
|
||||||
_instances.add(this);
|
_instances.add(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||||
|
return onActivityResultImpl(context, requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
|
public void onAttachedToEngine(@NonNull FlutterPluginBinding flutterPluginBinding) {
|
||||||
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), methodChannelKey);
|
channel = new MethodChannel(flutterPluginBinding.getBinaryMessenger(), methodChannelKey);
|
||||||
@ -279,4 +294,25 @@ public class MoxplatformAndroidPlugin extends BroadcastReceiver implements Flutt
|
|||||||
public Boolean generateVideoThumbnail(@NonNull String src, @NonNull String dest, @NonNull Long maxWidth) {
|
public Boolean generateVideoThumbnail(@NonNull String src, @NonNull String dest, @NonNull Long maxWidth) {
|
||||||
return generateVideoThumbnailImplementation(src, dest, maxWidth);
|
return generateVideoThumbnailImplementation(src, dest, maxWidth);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void pickFiles(@NonNull FilePickerType type, @NonNull Boolean pickMultiple, @NonNull Api.Result<List<String>> result) {
|
||||||
|
filePickerRequest(context, activity, type, pickMultiple, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAttachedToActivity(ActivityPluginBinding binding) {
|
||||||
|
activity = binding.getActivity();
|
||||||
|
binding.addActivityResultListener(this);
|
||||||
|
Log.d(TAG, "Activity attached");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetachedFromActivity() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDetachedFromActivityForConfigChanges() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReattachedToActivityForConfigChanges(ActivityPluginBinding binding) {}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,226 @@
|
|||||||
|
package me.polynom.moxplatform_android
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.ContentResolver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.database.Cursor
|
||||||
|
import android.net.Uri
|
||||||
|
import android.provider.MediaStore
|
||||||
|
import android.util.Log
|
||||||
|
import android.webkit.MimeTypeMap
|
||||||
|
import androidx.activity.result.PickVisualMediaRequest
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.io.IOException
|
||||||
|
import java.io.InputStream
|
||||||
|
import java.io.OutputStream
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
|
||||||
|
object RequestTracker {
|
||||||
|
val requests: MutableMap<Int, Api.Result<Any>> = mutableMapOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
const val PICK_FILE_REQUEST = 41;
|
||||||
|
const val PICK_FILES_REQUEST = 42;
|
||||||
|
|
||||||
|
fun genericFilePickerRequest(activity: Activity?, pickMultiple: Boolean, result: Api.Result<List<String>>) {
|
||||||
|
val pickIntent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||||
|
addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
type = "*/*"
|
||||||
|
|
||||||
|
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, pickMultiple);
|
||||||
|
}
|
||||||
|
|
||||||
|
RequestTracker.requests[PICK_FILE_REQUEST] = result as Api.Result<Any>;
|
||||||
|
activity?.startActivityForResult(pickIntent, PICK_FILE_REQUEST)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun filePickerRequest(
|
||||||
|
context: Context,
|
||||||
|
activity: Activity?,
|
||||||
|
type: Api.FilePickerType,
|
||||||
|
pickMultiple: Boolean,
|
||||||
|
result: Api.Result<List<String>>
|
||||||
|
) {
|
||||||
|
if (type == Api.FilePickerType.GENERIC) {
|
||||||
|
return genericFilePickerRequest(activity, pickMultiple, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
val pickerType = when (type) {
|
||||||
|
Api.FilePickerType.IMAGE -> ActivityResultContracts.PickVisualMedia.ImageOnly
|
||||||
|
Api.FilePickerType.VIDEO -> ActivityResultContracts.PickVisualMedia.VideoOnly
|
||||||
|
Api.FilePickerType.IMAGE_AND_VIDEO -> ActivityResultContracts.PickVisualMedia.ImageAndVideo
|
||||||
|
// TODO
|
||||||
|
Api.FilePickerType.GENERIC -> ActivityResultContracts.PickVisualMedia.ImageAndVideo
|
||||||
|
}
|
||||||
|
|
||||||
|
val pick = when (pickMultiple) {
|
||||||
|
false -> ActivityResultContracts.PickVisualMedia()
|
||||||
|
true -> ActivityResultContracts.PickMultipleVisualMedia()
|
||||||
|
}
|
||||||
|
|
||||||
|
val requestCode = if (pickMultiple) PICK_FILES_REQUEST else PICK_FILE_REQUEST
|
||||||
|
val pickIntent = pick.createIntent(context, PickVisualMediaRequest(pickerType))
|
||||||
|
RequestTracker.requests[requestCode] = result as Api.Result<Any>
|
||||||
|
Log.d(TAG, "Tracked size ${RequestTracker.requests.size}")
|
||||||
|
|
||||||
|
if (activity == null) {
|
||||||
|
Log.w(TAG, "Activity is null")
|
||||||
|
}
|
||||||
|
activity?.startActivityForResult(pickIntent, requestCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copies the file from the given content URI to a temporary directory, retaining the original
|
||||||
|
* file name if possible.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Each file is placed in its own directory to avoid conflicts according to the following
|
||||||
|
* scheme: {cacheDir}/{randomUuid}/{fileName}
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* File extension is changed to match MIME type of the file, if known. Otherwise, the extension
|
||||||
|
* is left unchanged.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* If the original file name is unknown, a predefined "image_picker" filename is used and the
|
||||||
|
* file extension is deduced from the mime type (with fallback to ".jpg" in case of failure).
|
||||||
|
*/
|
||||||
|
fun getPathFromUri(context: Context, uri: Uri): String? {
|
||||||
|
try {
|
||||||
|
context.contentResolver.openInputStream(uri).use { inputStream ->
|
||||||
|
val uuid = UUID.randomUUID().toString()
|
||||||
|
val targetDirectory = File(context.cacheDir, uuid)
|
||||||
|
targetDirectory.mkdir()
|
||||||
|
// TODO(SynSzakala) according to the docs, `deleteOnExit` does not work reliably on Android; we should preferably
|
||||||
|
// just clear the picked files after the app startup.
|
||||||
|
targetDirectory.deleteOnExit()
|
||||||
|
var fileName = getImageName(context, uri)
|
||||||
|
var extension = getImageExtension(context, uri)
|
||||||
|
if (fileName == null) {
|
||||||
|
Log.w("FileUtils", "Cannot get file name for $uri")
|
||||||
|
if (extension == null) extension = ".jpg"
|
||||||
|
fileName = "image_picker$extension"
|
||||||
|
} else if (extension != null) {
|
||||||
|
fileName = getBaseName(fileName) + extension
|
||||||
|
}
|
||||||
|
val file = File(targetDirectory, fileName)
|
||||||
|
FileOutputStream(file).use { outputStream ->
|
||||||
|
copy(inputStream!!, outputStream)
|
||||||
|
return file.path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
// If closing the output stream fails, we cannot be sure that the
|
||||||
|
// target file was written in full. Flushing the stream merely moves
|
||||||
|
// the bytes into the OS, not necessarily to the file.
|
||||||
|
return null
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
// Calling `ContentResolver#openInputStream()` has been reported to throw a
|
||||||
|
// `SecurityException` on some devices in certain circumstances. Instead of crashing, we
|
||||||
|
// return `null`.
|
||||||
|
//
|
||||||
|
// See https://github.com/flutter/flutter/issues/100025 for more details.
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return extension of image with dot, or null if it's empty.
|
||||||
|
*/
|
||||||
|
private fun getImageExtension(context: Context, uriImage: Uri): String? {
|
||||||
|
val extension: String?
|
||||||
|
extension = try {
|
||||||
|
if (uriImage.scheme == ContentResolver.SCHEME_CONTENT) {
|
||||||
|
val mime = MimeTypeMap.getSingleton()
|
||||||
|
mime.getExtensionFromMimeType(context.contentResolver.getType(uriImage))
|
||||||
|
} else {
|
||||||
|
MimeTypeMap.getFileExtensionFromUrl(
|
||||||
|
Uri.fromFile(File(uriImage.path)).toString()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return if (extension == null || extension.isEmpty()) {
|
||||||
|
null
|
||||||
|
} else ".$extension"
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return name of the image provided by ContentResolver; this may be null.
|
||||||
|
*/
|
||||||
|
private fun getImageName(context: Context, uriImage: Uri): String? {
|
||||||
|
queryImageName(context, uriImage).use { cursor ->
|
||||||
|
return if (cursor == null || !cursor.moveToFirst() || (cursor.columnCount < 1)) null else cursor.getString(
|
||||||
|
0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun queryImageName(context: Context, uriImage: Uri): Cursor? {
|
||||||
|
return context
|
||||||
|
.contentResolver
|
||||||
|
.query(uriImage, arrayOf(MediaStore.MediaColumns.DISPLAY_NAME), null, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Throws(IOException::class)
|
||||||
|
private fun copy(`in`: InputStream, out: OutputStream) {
|
||||||
|
val buffer = ByteArray(4 * 1024)
|
||||||
|
var bytesRead: Int
|
||||||
|
while (`in`.read(buffer).also { bytesRead = it } != -1) {
|
||||||
|
out.write(buffer, 0, bytesRead)
|
||||||
|
}
|
||||||
|
out.flush()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getBaseName(fileName: String): String {
|
||||||
|
val lastDotIndex = fileName.lastIndexOf('.')
|
||||||
|
return if (lastDotIndex < 0) {
|
||||||
|
fileName
|
||||||
|
} else fileName.substring(0, lastDotIndex)
|
||||||
|
// Basename is everything before the last '.'.
|
||||||
|
}
|
||||||
|
|
||||||
|
fun onActivityResultImpl(context: Context, requestCode: Int, resultCode: Int, data: Intent?): Boolean {
|
||||||
|
Log.d(TAG, "Got result for $requestCode with result $resultCode (${data?.action})")
|
||||||
|
if (requestCode == PICK_FILE_REQUEST || requestCode == PICK_FILES_REQUEST) {
|
||||||
|
Log.d(TAG, "Extra data ${data?.data}")
|
||||||
|
val result = RequestTracker.requests.remove(requestCode);
|
||||||
|
if (result == null) {
|
||||||
|
Log.w(TAG, "Untracked response.")
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCode != Activity.RESULT_OK) {
|
||||||
|
// No files picked
|
||||||
|
result!!.success(listOf<String>())
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
val pickedMultiple = requestCode == PICK_FILES_REQUEST
|
||||||
|
val pickedFiles = mutableListOf<String>()
|
||||||
|
if (pickedMultiple) {
|
||||||
|
val intentUris = data!!.clipData
|
||||||
|
if (data!!.clipData != null) {
|
||||||
|
for (i in 0 until data!!.clipData!!.itemCount) {
|
||||||
|
val path = getPathFromUri(context, data!!.clipData!!.getItemAt(i).uri)
|
||||||
|
if (path != null) {
|
||||||
|
pickedFiles.add(path )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val path = getPathFromUri(context, data!!.data!!)
|
||||||
|
if (path != null) {
|
||||||
|
pickedFiles.add(path )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
result!!.success(pickedFiles)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
@ -30,4 +30,10 @@ class AndroidPlatformImplementation extends PlatformImplementation {
|
|||||||
) async {
|
) async {
|
||||||
return MoxplatformInterface.api.generateVideoThumbnail(src, dest, width);
|
return MoxplatformInterface.api.generateVideoThumbnail(src, dest, width);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<String>> pickFiles(FilePickerType type, bool pickMultiple) async {
|
||||||
|
final result = await MoxplatformInterface.api.pickFiles(type, pickMultiple);
|
||||||
|
return result.cast<String>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,13 @@ enum FallbackIconType {
|
|||||||
notes,
|
notes,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum FilePickerType {
|
||||||
|
image,
|
||||||
|
video,
|
||||||
|
imageAndVideo,
|
||||||
|
generic,
|
||||||
|
}
|
||||||
|
|
||||||
class CryptographyResult {
|
class CryptographyResult {
|
||||||
CryptographyResult({
|
CryptographyResult({
|
||||||
required this.plaintextHash,
|
required this.plaintextHash,
|
||||||
@ -82,10 +89,10 @@ class MoxplatformApi {
|
|||||||
/// Platform APIs
|
/// Platform APIs
|
||||||
Future<String> getPersistentDataPath() async {
|
Future<String> getPersistentDataPath() async {
|
||||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.getPersistentDataPath',
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.getPersistentDataPath', codec,
|
||||||
codec,
|
|
||||||
binaryMessenger: _binaryMessenger);
|
binaryMessenger: _binaryMessenger);
|
||||||
final List<Object?>? replyList = await channel.send(null) as List<Object?>?;
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(null) as List<Object?>?;
|
||||||
if (replyList == null) {
|
if (replyList == null) {
|
||||||
throw PlatformException(
|
throw PlatformException(
|
||||||
code: 'channel-error',
|
code: 'channel-error',
|
||||||
@ -109,10 +116,10 @@ class MoxplatformApi {
|
|||||||
|
|
||||||
Future<String> getCacheDataPath() async {
|
Future<String> getCacheDataPath() async {
|
||||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.getCacheDataPath',
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.getCacheDataPath', codec,
|
||||||
codec,
|
|
||||||
binaryMessenger: _binaryMessenger);
|
binaryMessenger: _binaryMessenger);
|
||||||
final List<Object?>? replyList = await channel.send(null) as List<Object?>?;
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(null) as List<Object?>?;
|
||||||
if (replyList == null) {
|
if (replyList == null) {
|
||||||
throw PlatformException(
|
throw PlatformException(
|
||||||
code: 'channel-error',
|
code: 'channel-error',
|
||||||
@ -136,10 +143,10 @@ class MoxplatformApi {
|
|||||||
|
|
||||||
Future<void> openBatteryOptimisationSettings() async {
|
Future<void> openBatteryOptimisationSettings() async {
|
||||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.openBatteryOptimisationSettings',
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.openBatteryOptimisationSettings', codec,
|
||||||
codec,
|
|
||||||
binaryMessenger: _binaryMessenger);
|
binaryMessenger: _binaryMessenger);
|
||||||
final List<Object?>? replyList = await channel.send(null) as List<Object?>?;
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(null) as List<Object?>?;
|
||||||
if (replyList == null) {
|
if (replyList == null) {
|
||||||
throw PlatformException(
|
throw PlatformException(
|
||||||
code: 'channel-error',
|
code: 'channel-error',
|
||||||
@ -158,10 +165,10 @@ class MoxplatformApi {
|
|||||||
|
|
||||||
Future<bool> isIgnoringBatteryOptimizations() async {
|
Future<bool> isIgnoringBatteryOptimizations() async {
|
||||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.isIgnoringBatteryOptimizations',
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.isIgnoringBatteryOptimizations', codec,
|
||||||
codec,
|
|
||||||
binaryMessenger: _binaryMessenger);
|
binaryMessenger: _binaryMessenger);
|
||||||
final List<Object?>? replyList = await channel.send(null) as List<Object?>?;
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(null) as List<Object?>?;
|
||||||
if (replyList == null) {
|
if (replyList == null) {
|
||||||
throw PlatformException(
|
throw PlatformException(
|
||||||
code: 'channel-error',
|
code: 'channel-error',
|
||||||
@ -184,18 +191,12 @@ class MoxplatformApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Contacts APIs
|
/// Contacts APIs
|
||||||
Future<void> recordSentMessage(String arg_name, String arg_jid,
|
Future<void> recordSentMessage(String arg_name, String arg_jid, String? arg_avatarPath, FallbackIconType arg_fallbackIcon) async {
|
||||||
String? arg_avatarPath, FallbackIconType arg_fallbackIcon) async {
|
|
||||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.recordSentMessage',
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.recordSentMessage', codec,
|
||||||
codec,
|
|
||||||
binaryMessenger: _binaryMessenger);
|
binaryMessenger: _binaryMessenger);
|
||||||
final List<Object?>? replyList = await channel.send(<Object?>[
|
final List<Object?>? replyList =
|
||||||
arg_name,
|
await channel.send(<Object?>[arg_name, arg_jid, arg_avatarPath, arg_fallbackIcon.index]) as List<Object?>?;
|
||||||
arg_jid,
|
|
||||||
arg_avatarPath,
|
|
||||||
arg_fallbackIcon.index
|
|
||||||
]) as List<Object?>?;
|
|
||||||
if (replyList == null) {
|
if (replyList == null) {
|
||||||
throw PlatformException(
|
throw PlatformException(
|
||||||
code: 'channel-error',
|
code: 'channel-error',
|
||||||
@ -213,25 +214,12 @@ class MoxplatformApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Cryptography APIs
|
/// Cryptography APIs
|
||||||
Future<CryptographyResult?> encryptFile(
|
Future<CryptographyResult?> encryptFile(String arg_sourcePath, String arg_destPath, Uint8List arg_key, Uint8List arg_iv, CipherAlgorithm arg_algorithm, String arg_hashSpec) async {
|
||||||
String arg_sourcePath,
|
|
||||||
String arg_destPath,
|
|
||||||
Uint8List arg_key,
|
|
||||||
Uint8List arg_iv,
|
|
||||||
CipherAlgorithm arg_algorithm,
|
|
||||||
String arg_hashSpec) async {
|
|
||||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.encryptFile',
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.encryptFile', codec,
|
||||||
codec,
|
|
||||||
binaryMessenger: _binaryMessenger);
|
binaryMessenger: _binaryMessenger);
|
||||||
final List<Object?>? replyList = await channel.send(<Object?>[
|
final List<Object?>? replyList =
|
||||||
arg_sourcePath,
|
await channel.send(<Object?>[arg_sourcePath, arg_destPath, arg_key, arg_iv, arg_algorithm.index, arg_hashSpec]) as List<Object?>?;
|
||||||
arg_destPath,
|
|
||||||
arg_key,
|
|
||||||
arg_iv,
|
|
||||||
arg_algorithm.index,
|
|
||||||
arg_hashSpec
|
|
||||||
]) as List<Object?>?;
|
|
||||||
if (replyList == null) {
|
if (replyList == null) {
|
||||||
throw PlatformException(
|
throw PlatformException(
|
||||||
code: 'channel-error',
|
code: 'channel-error',
|
||||||
@ -248,25 +236,12 @@ class MoxplatformApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<CryptographyResult?> decryptFile(
|
Future<CryptographyResult?> decryptFile(String arg_sourcePath, String arg_destPath, Uint8List arg_key, Uint8List arg_iv, CipherAlgorithm arg_algorithm, String arg_hashSpec) async {
|
||||||
String arg_sourcePath,
|
|
||||||
String arg_destPath,
|
|
||||||
Uint8List arg_key,
|
|
||||||
Uint8List arg_iv,
|
|
||||||
CipherAlgorithm arg_algorithm,
|
|
||||||
String arg_hashSpec) async {
|
|
||||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.decryptFile',
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.decryptFile', codec,
|
||||||
codec,
|
|
||||||
binaryMessenger: _binaryMessenger);
|
binaryMessenger: _binaryMessenger);
|
||||||
final List<Object?>? replyList = await channel.send(<Object?>[
|
final List<Object?>? replyList =
|
||||||
arg_sourcePath,
|
await channel.send(<Object?>[arg_sourcePath, arg_destPath, arg_key, arg_iv, arg_algorithm.index, arg_hashSpec]) as List<Object?>?;
|
||||||
arg_destPath,
|
|
||||||
arg_key,
|
|
||||||
arg_iv,
|
|
||||||
arg_algorithm.index,
|
|
||||||
arg_hashSpec
|
|
||||||
]) as List<Object?>?;
|
|
||||||
if (replyList == null) {
|
if (replyList == null) {
|
||||||
throw PlatformException(
|
throw PlatformException(
|
||||||
code: 'channel-error',
|
code: 'channel-error',
|
||||||
@ -283,14 +258,12 @@ class MoxplatformApi {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Uint8List?> hashFile(
|
Future<Uint8List?> hashFile(String arg_sourcePath, String arg_hashSpec) async {
|
||||||
String arg_sourcePath, String arg_hashSpec) async {
|
|
||||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.hashFile',
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.hashFile', codec,
|
||||||
codec,
|
|
||||||
binaryMessenger: _binaryMessenger);
|
binaryMessenger: _binaryMessenger);
|
||||||
final List<Object?>? replyList = await channel
|
final List<Object?>? replyList =
|
||||||
.send(<Object?>[arg_sourcePath, arg_hashSpec]) as List<Object?>?;
|
await channel.send(<Object?>[arg_sourcePath, arg_hashSpec]) as List<Object?>?;
|
||||||
if (replyList == null) {
|
if (replyList == null) {
|
||||||
throw PlatformException(
|
throw PlatformException(
|
||||||
code: 'channel-error',
|
code: 'channel-error',
|
||||||
@ -308,14 +281,12 @@ class MoxplatformApi {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Media APIs
|
/// Media APIs
|
||||||
Future<bool> generateVideoThumbnail(
|
Future<bool> generateVideoThumbnail(String arg_src, String arg_dest, int arg_maxWidth) async {
|
||||||
String arg_src, String arg_dest, int arg_maxWidth) async {
|
|
||||||
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.generateVideoThumbnail',
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.generateVideoThumbnail', codec,
|
||||||
codec,
|
|
||||||
binaryMessenger: _binaryMessenger);
|
binaryMessenger: _binaryMessenger);
|
||||||
final List<Object?>? replyList = await channel
|
final List<Object?>? replyList =
|
||||||
.send(<Object?>[arg_src, arg_dest, arg_maxWidth]) as List<Object?>?;
|
await channel.send(<Object?>[arg_src, arg_dest, arg_maxWidth]) as List<Object?>?;
|
||||||
if (replyList == null) {
|
if (replyList == null) {
|
||||||
throw PlatformException(
|
throw PlatformException(
|
||||||
code: 'channel-error',
|
code: 'channel-error',
|
||||||
@ -336,4 +307,32 @@ class MoxplatformApi {
|
|||||||
return (replyList[0] as bool?)!;
|
return (replyList[0] as bool?)!;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Picker
|
||||||
|
Future<List<String?>> pickFiles(FilePickerType arg_type, bool arg_pickMultiple) async {
|
||||||
|
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
|
||||||
|
'dev.flutter.pigeon.moxplatform_platform_interface.MoxplatformApi.pickFiles', codec,
|
||||||
|
binaryMessenger: _binaryMessenger);
|
||||||
|
final List<Object?>? replyList =
|
||||||
|
await channel.send(<Object?>[arg_type.index, arg_pickMultiple]) as List<Object?>?;
|
||||||
|
if (replyList == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'channel-error',
|
||||||
|
message: 'Unable to establish connection on channel.',
|
||||||
|
);
|
||||||
|
} else if (replyList.length > 1) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: replyList[0]! as String,
|
||||||
|
message: replyList[1] as String?,
|
||||||
|
details: replyList[2],
|
||||||
|
);
|
||||||
|
} else if (replyList[0] == null) {
|
||||||
|
throw PlatformException(
|
||||||
|
code: 'null-error',
|
||||||
|
message: 'Host platform returned null value for non-null return value.',
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (replyList[0] as List<Object?>?)!.cast<String?>();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import 'package:moxplatform_platform_interface/src/api.g.dart';
|
||||||
|
|
||||||
abstract class PlatformImplementation {
|
abstract class PlatformImplementation {
|
||||||
/// Returns the path where persistent data should be stored.
|
/// Returns the path where persistent data should be stored.
|
||||||
Future<String> getPersistentDataPath();
|
Future<String> getPersistentDataPath();
|
||||||
@ -17,4 +19,6 @@ abstract class PlatformImplementation {
|
|||||||
/// aspect ratio in tact to [width], and write it to [dest]. If we were successful, returns true.
|
/// aspect ratio in tact to [width], and write it to [dest]. If we were successful, returns true.
|
||||||
/// If no thumbnail was generated, returns false.
|
/// If no thumbnail was generated, returns false.
|
||||||
Future<bool> generateVideoThumbnail(String src, String dest, int width);
|
Future<bool> generateVideoThumbnail(String src, String dest, int width);
|
||||||
|
|
||||||
|
Future<List<String>> pickFiles(FilePickerType type, bool pickMultiple);
|
||||||
}
|
}
|
||||||
|
@ -1,3 +1,4 @@
|
|||||||
|
import 'package:moxplatform_platform_interface/src/api.g.dart';
|
||||||
import 'package:moxplatform_platform_interface/src/platform.dart';
|
import 'package:moxplatform_platform_interface/src/platform.dart';
|
||||||
|
|
||||||
class StubPlatformImplementation extends PlatformImplementation {
|
class StubPlatformImplementation extends PlatformImplementation {
|
||||||
@ -22,4 +23,7 @@ class StubPlatformImplementation extends PlatformImplementation {
|
|||||||
int width,
|
int width,
|
||||||
) async =>
|
) async =>
|
||||||
false;
|
false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Future<List<String>> pickFiles(FilePickerType type, bool pickMultiple) async => [];
|
||||||
}
|
}
|
||||||
|
@ -32,6 +32,13 @@ enum FallbackIconType {
|
|||||||
notes;
|
notes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum FilePickerType {
|
||||||
|
image,
|
||||||
|
video,
|
||||||
|
imageAndVideo,
|
||||||
|
generic,
|
||||||
|
}
|
||||||
|
|
||||||
@HostApi()
|
@HostApi()
|
||||||
abstract class MoxplatformApi {
|
abstract class MoxplatformApi {
|
||||||
/// Platform APIs
|
/// Platform APIs
|
||||||
@ -50,4 +57,8 @@ abstract class MoxplatformApi {
|
|||||||
|
|
||||||
/// Media APIs
|
/// Media APIs
|
||||||
bool generateVideoThumbnail(String src, String dest, int maxWidth);
|
bool generateVideoThumbnail(String src, String dest, int maxWidth);
|
||||||
|
|
||||||
|
/// Picker
|
||||||
|
@async
|
||||||
|
List<String> pickFiles(FilePickerType type, bool pickMultiple);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user