feat: Move over the media API

This commit is contained in:
PapaTutuWawa 2023-09-08 21:41:10 +02:00
parent f7218f57cb
commit 5dcfc7239a
10 changed files with 353 additions and 160 deletions

View File

@ -17,6 +17,8 @@ import org.moxxy.moxxy_native.contacts.ContactsImplementation
import org.moxxy.moxxy_native.contacts.MoxxyContactsApi import org.moxxy.moxxy_native.contacts.MoxxyContactsApi
import org.moxxy.moxxy_native.cryptography.CryptographyImplementation import org.moxxy.moxxy_native.cryptography.CryptographyImplementation
import org.moxxy.moxxy_native.cryptography.MoxxyCryptographyApi import org.moxxy.moxxy_native.cryptography.MoxxyCryptographyApi
import org.moxxy.moxxy_native.media.MediaImplementation
import org.moxxy.moxxy_native.media.MoxxyMediaApi
import org.moxxy.moxxy_native.notifications.MessagingNotification import org.moxxy.moxxy_native.notifications.MessagingNotification
import org.moxxy.moxxy_native.notifications.MoxxyNotificationsApi import org.moxxy.moxxy_native.notifications.MoxxyNotificationsApi
import org.moxxy.moxxy_native.notifications.NotificationChannel import org.moxxy.moxxy_native.notifications.NotificationChannel
@ -65,6 +67,7 @@ class MoxxyNativePlugin : FlutterPlugin, ActivityAware, MoxxyPickerApi, MoxxyNot
private val cryptographyImplementation = CryptographyImplementation() private val cryptographyImplementation = CryptographyImplementation()
private lateinit var contactsImplementation: ContactsImplementation private lateinit var contactsImplementation: ContactsImplementation
private lateinit var platformImplementation: PlatformImplementation private lateinit var platformImplementation: PlatformImplementation
private val mediaImplementation = MediaImplementation()
override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) { override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
context = flutterPluginBinding.applicationContext context = flutterPluginBinding.applicationContext
@ -77,6 +80,7 @@ class MoxxyNativePlugin : FlutterPlugin, ActivityAware, MoxxyPickerApi, MoxxyNot
MoxxyCryptographyApi.setUp(flutterPluginBinding.binaryMessenger, cryptographyImplementation) MoxxyCryptographyApi.setUp(flutterPluginBinding.binaryMessenger, cryptographyImplementation)
MoxxyContactsApi.setUp(flutterPluginBinding.binaryMessenger, contactsImplementation) MoxxyContactsApi.setUp(flutterPluginBinding.binaryMessenger, contactsImplementation)
MoxxyPlatformApi.setUp(flutterPluginBinding.binaryMessenger, platformImplementation) MoxxyPlatformApi.setUp(flutterPluginBinding.binaryMessenger, platformImplementation)
MoxxyMediaApi.setUp(flutterPluginBinding.binaryMessenger, mediaImplementation)
// Register the picker handler // Register the picker handler
pickerListener = PickerResultListener(context!!) pickerListener = PickerResultListener(context!!)

View File

@ -8,8 +8,6 @@ import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MessageCodec import io.flutter.plugin.common.MessageCodec
import io.flutter.plugin.common.StandardMessageCodec import io.flutter.plugin.common.StandardMessageCodec
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
private fun wrapResult(result: Any?): List<Any?> { private fun wrapResult(result: Any?): List<Any?> {
return listOf(result) return listOf(result)
@ -20,13 +18,13 @@ private fun wrapError(exception: Throwable): List<Any?> {
return listOf( return listOf(
exception.code, exception.code,
exception.message, exception.message,
exception.details exception.details,
) )
} else { } else {
return listOf( return listOf(
exception.javaClass.simpleName, exception.javaClass.simpleName,
exception.toString(), exception.toString(),
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception),
) )
} }
} }
@ -37,17 +35,18 @@ private fun wrapError(exception: Throwable): List<Any?> {
* @property message The error message. * @property message The error message.
* @property details The error details. Must be a datatype supported by the api codec. * @property details The error details. Must be a datatype supported by the api codec.
*/ */
class FlutterError ( class FlutterError(
val code: String, val code: String,
override val message: String? = null, override val message: String? = null,
val details: Any? = null val details: Any? = null,
) : Throwable() ) : Throwable()
/** The type of icon to use when no avatar path is provided. */ /** The type of icon to use when no avatar path is provided. */
enum class FallbackIconType(val raw: Int) { enum class FallbackIconType(val raw: Int) {
NONE(0), NONE(0),
PERSON(1), PERSON(1),
NOTES(2); NOTES(2),
;
companion object { companion object {
fun ofRaw(raw: Int): FallbackIconType? { fun ofRaw(raw: Int): FallbackIconType? {
@ -55,6 +54,7 @@ enum class FallbackIconType(val raw: Int) {
} }
} }
} }
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface MoxxyContactsApi { interface MoxxyContactsApi {
fun recordSentMessage(name: String, jid: String, avatarPath: String?, fallbackIcon: FallbackIconType) fun recordSentMessage(name: String, jid: String, avatarPath: String?, fallbackIcon: FallbackIconType)
@ -64,6 +64,7 @@ interface MoxxyContactsApi {
val codec: MessageCodec<Any?> by lazy { val codec: MessageCodec<Any?> by lazy {
StandardMessageCodec() StandardMessageCodec()
} }
/** Sets up an instance of `MoxxyContactsApi` to handle messages through the `binaryMessenger`. */ /** Sets up an instance of `MoxxyContactsApi` to handle messages through the `binaryMessenger`. */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun setUp(binaryMessenger: BinaryMessenger, api: MoxxyContactsApi?) { fun setUp(binaryMessenger: BinaryMessenger, api: MoxxyContactsApi?) {

View File

@ -17,7 +17,7 @@ class ContactsImplementation(private val context: Context) : MoxxyContactsApi {
name: String, name: String,
jid: String, jid: String,
avatarPath: String?, avatarPath: String?,
fallbackIcon: FallbackIconType fallbackIcon: FallbackIconType,
) { ) {
val pkgName = context.packageName val pkgName = context.packageName
val intent = Intent(context, Class.forName("$pkgName.MainActivity")).apply { val intent = Intent(context, Class.forName("$pkgName.MainActivity")).apply {
@ -49,7 +49,7 @@ class ContactsImplementation(private val context: Context) : MoxxyContactsApi {
shortcutBuilder.setIcon(icon) shortcutBuilder.setIcon(icon)
personBuilder.setIcon(icon) personBuilder.setIcon(icon)
} else { } else {
val resourceId = when(fallbackIcon) { val resourceId = when (fallbackIcon) {
FallbackIconType.NONE, FallbackIconType.PERSON -> R.mipmap.person FallbackIconType.NONE, FallbackIconType.PERSON -> R.mipmap.person
FallbackIconType.NOTES -> R.mipmap.notes FallbackIconType.NOTES -> R.mipmap.notes
} }

View File

@ -0,0 +1,79 @@
// Autogenerated from Pigeon (v11.0.1), do not edit directly.
// See also: https://pub.dev/packages/pigeon
package org.moxxy.moxxy_native.media
import android.util.Log
import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MessageCodec
import io.flutter.plugin.common.StandardMessageCodec
private fun wrapResult(result: Any?): List<Any?> {
return listOf(result)
}
private fun wrapError(exception: Throwable): List<Any?> {
if (exception is FlutterError) {
return listOf(
exception.code,
exception.message,
exception.details,
)
} else {
return listOf(
exception.javaClass.simpleName,
exception.toString(),
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception),
)
}
}
/**
* Error class for passing custom error details to Flutter via a thrown PlatformException.
* @property code The error code.
* @property message The error message.
* @property details The error details. Must be a datatype supported by the api codec.
*/
class FlutterError(
val code: String,
override val message: String? = null,
val details: Any? = null,
) : Throwable()
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface MoxxyMediaApi {
fun generateVideoThumbnail(src: String, dest: String, maxWidth: Long): Boolean
companion object {
/** The codec used by MoxxyMediaApi. */
val codec: MessageCodec<Any?> by lazy {
StandardMessageCodec()
}
/** Sets up an instance of `MoxxyMediaApi` to handle messages through the `binaryMessenger`. */
@Suppress("UNCHECKED_CAST")
fun setUp(binaryMessenger: BinaryMessenger, api: MoxxyMediaApi?) {
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyMediaApi.generateVideoThumbnail", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val srcArg = args[0] as String
val destArg = args[1] as String
val maxWidthArg = args[2].let { if (it is Int) it.toLong() else it as Long }
var wrapped: List<Any?>
try {
wrapped = listOf<Any?>(api.generateVideoThumbnail(srcArg, destArg, maxWidthArg))
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}

View File

@ -0,0 +1,45 @@
package org.moxxy.moxxy_native.media
import android.graphics.Bitmap
import android.media.MediaMetadataRetriever
import android.util.Log
import org.moxxy.moxxy_native.TAG
import java.io.FileOutputStream
class MediaImplementation : MoxxyMediaApi {
override fun generateVideoThumbnail(src: String, dest: String, maxWidth: Long): Boolean {
try {
// Get a frame as a thumbnail
val mmr = MediaMetadataRetriever().apply {
setDataSource(src)
}
val unscaledThumbnail = mmr.getFrameAtTime(0) ?: return false
// Scale down the thumbnail while keeping the aspect ratio
val scalingFactor = maxWidth.toDouble() / unscaledThumbnail.width
Log.d(TAG, "Scaling to $maxWidth from ${unscaledThumbnail.width} with scalingFactor $scalingFactor")
val thumbnail = Bitmap.createScaledBitmap(
unscaledThumbnail,
(unscaledThumbnail.width * scalingFactor).toInt(),
(unscaledThumbnail.height * scalingFactor).toInt(),
false,
)
// Write it to the destination file
val fileOutputStream = FileOutputStream(dest)
thumbnail.compress(Bitmap.CompressFormat.JPEG, 75, fileOutputStream)
// Clean up
fileOutputStream.apply {
flush()
close()
}
// Success
return true
} catch (ex: Exception) {
Log.e(TAG, "Failed to create thumbnail for $src: ${ex.message}")
return false
}
}
}

View File

@ -8,8 +8,6 @@ import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryMessenger import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MessageCodec import io.flutter.plugin.common.MessageCodec
import io.flutter.plugin.common.StandardMessageCodec import io.flutter.plugin.common.StandardMessageCodec
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
private fun wrapResult(result: Any?): List<Any?> { private fun wrapResult(result: Any?): List<Any?> {
return listOf(result) return listOf(result)
@ -20,13 +18,13 @@ private fun wrapError(exception: Throwable): List<Any?> {
return listOf( return listOf(
exception.code, exception.code,
exception.message, exception.message,
exception.details exception.details,
) )
} else { } else {
return listOf( return listOf(
exception.javaClass.simpleName, exception.javaClass.simpleName,
exception.toString(), exception.toString(),
"Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception) "Cause: " + exception.cause + ", Stacktrace: " + Log.getStackTraceString(exception),
) )
} }
} }
@ -37,11 +35,12 @@ private fun wrapError(exception: Throwable): List<Any?> {
* @property message The error message. * @property message The error message.
* @property details The error details. Must be a datatype supported by the api codec. * @property details The error details. Must be a datatype supported by the api codec.
*/ */
class FlutterError ( class FlutterError(
val code: String, val code: String,
override val message: String? = null, override val message: String? = null,
val details: Any? = null val details: Any? = null,
) : Throwable() ) : Throwable()
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */ /** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface MoxxyPlatformApi { interface MoxxyPlatformApi {
fun getPersistentDataPath(): String fun getPersistentDataPath(): String
@ -54,6 +53,7 @@ interface MoxxyPlatformApi {
val codec: MessageCodec<Any?> by lazy { val codec: MessageCodec<Any?> by lazy {
StandardMessageCodec() StandardMessageCodec()
} }
/** Sets up an instance of `MoxxyPlatformApi` to handle messages through the `binaryMessenger`. */ /** Sets up an instance of `MoxxyPlatformApi` to handle messages through the `binaryMessenger`. */
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
fun setUp(binaryMessenger: BinaryMessenger, api: MoxxyPlatformApi?) { fun setUp(binaryMessenger: BinaryMessenger, api: MoxxyPlatformApi?) {

View File

@ -1,5 +1,6 @@
export 'pigeon/contacts.g.dart'; export 'pigeon/contacts.g.dart';
export 'pigeon/cryptography.g.dart'; export 'pigeon/cryptography.g.dart';
export 'pigeon/media.g.dart';
export 'pigeon/notifications.g.dart'; export 'pigeon/notifications.g.dart';
export 'pigeon/picker.g.dart'; export 'pigeon/picker.g.dart';
export 'pigeon/platform.g.dart'; export 'pigeon/platform.g.dart';

47
lib/pigeon/media.g.dart Normal file
View File

@ -0,0 +1,47 @@
// Autogenerated from Pigeon (v11.0.1), do not edit directly.
// See also: https://pub.dev/packages/pigeon
// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import
import 'dart:async';
import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List;
import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer;
import 'package:flutter/services.dart';
class MoxxyMediaApi {
/// Constructor for [MoxxyMediaApi]. The [binaryMessenger] named argument is
/// available for dependency injection. If it is left null, the default
/// BinaryMessenger will be used which routes to the host platform.
MoxxyMediaApi({BinaryMessenger? binaryMessenger})
: _binaryMessenger = binaryMessenger;
final BinaryMessenger? _binaryMessenger;
static const MessageCodec<Object?> codec = StandardMessageCodec();
Future<bool> generateVideoThumbnail(String arg_src, String arg_dest, int arg_maxWidth) async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.moxxy_native.MoxxyMediaApi.generateVideoThumbnail', codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList =
await channel.send(<Object?>[arg_src, arg_dest, arg_maxWidth]) 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 bool?)!;
}
}
}

16
pigeon/media.dart Normal file
View File

@ -0,0 +1,16 @@
import 'package:pigeon/pigeon.dart';
@ConfigurePigeon(
PigeonOptions(
dartOut: 'lib/pigeon/media.g.dart',
kotlinOut: 'android/src/main/kotlin/org/moxxy/moxxy_native/media/MediaApi.kt',
kotlinOptions: KotlinOptions(
package: 'org.moxxy.moxxy_native.media',
),
),
)
@HostApi()
abstract class MoxxyMediaApi {
bool generateVideoThumbnail(String src, String dest, int maxWidth);
}