feat: Move over the media API

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

View File

@@ -8,27 +8,25 @@ import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MessageCodec
import io.flutter.plugin.common.StandardMessageCodec
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
private fun wrapResult(result: Any?): List<Any?> {
return listOf(result)
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)
)
}
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),
)
}
}
/**
@@ -37,58 +35,61 @@ private fun wrapError(exception: Throwable): List<Any?> {
* @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
class FlutterError(
val code: String,
override val message: String? = null,
val details: Any? = null,
) : Throwable()
/** The type of icon to use when no avatar path is provided. */
enum class FallbackIconType(val raw: Int) {
NONE(0),
PERSON(1),
NOTES(2);
NONE(0),
PERSON(1),
NOTES(2),
;
companion object {
fun ofRaw(raw: Int): FallbackIconType? {
return values().firstOrNull { it.raw == raw }
companion object {
fun ofRaw(raw: Int): FallbackIconType? {
return values().firstOrNull { it.raw == raw }
}
}
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface MoxxyContactsApi {
fun recordSentMessage(name: String, jid: String, avatarPath: String?, fallbackIcon: FallbackIconType)
fun recordSentMessage(name: String, jid: String, avatarPath: String?, fallbackIcon: FallbackIconType)
companion object {
/** The codec used by MoxxyContactsApi. */
val codec: MessageCodec<Any?> by lazy {
StandardMessageCodec()
}
/** Sets up an instance of `MoxxyContactsApi` to handle messages through the `binaryMessenger`. */
@Suppress("UNCHECKED_CAST")
fun setUp(binaryMessenger: BinaryMessenger, api: MoxxyContactsApi?) {
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyContactsApi.recordSentMessage", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val nameArg = args[0] as String
val jidArg = args[1] as String
val avatarPathArg = args[2] as String?
val fallbackIconArg = FallbackIconType.ofRaw(args[3] as Int)!!
var wrapped: List<Any?>
try {
api.recordSentMessage(nameArg, jidArg, avatarPathArg, fallbackIconArg)
wrapped = listOf<Any?>(null)
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
companion object {
/** The codec used by MoxxyContactsApi. */
val codec: MessageCodec<Any?> by lazy {
StandardMessageCodec()
}
/** Sets up an instance of `MoxxyContactsApi` to handle messages through the `binaryMessenger`. */
@Suppress("UNCHECKED_CAST")
fun setUp(binaryMessenger: BinaryMessenger, api: MoxxyContactsApi?) {
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyContactsApi.recordSentMessage", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val nameArg = args[0] as String
val jidArg = args[1] as String
val avatarPathArg = args[2] as String?
val fallbackIconArg = FallbackIconType.ofRaw(args[3] as Int)!!
var wrapped: List<Any?>
try {
api.recordSentMessage(nameArg, jidArg, avatarPathArg, fallbackIconArg)
wrapped = listOf<Any?>(null)
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}
}
}

View File

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

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,27 +8,25 @@ import io.flutter.plugin.common.BasicMessageChannel
import io.flutter.plugin.common.BinaryMessenger
import io.flutter.plugin.common.MessageCodec
import io.flutter.plugin.common.StandardMessageCodec
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
private fun wrapResult(result: Any?): List<Any?> {
return listOf(result)
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)
)
}
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),
)
}
}
/**
@@ -37,91 +35,93 @@ private fun wrapError(exception: Throwable): List<Any?> {
* @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
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 MoxxyPlatformApi {
fun getPersistentDataPath(): String
fun getCacheDataPath(): String
fun openBatteryOptimisationSettings()
fun isIgnoringBatteryOptimizations(): Boolean
fun getPersistentDataPath(): String
fun getCacheDataPath(): String
fun openBatteryOptimisationSettings()
fun isIgnoringBatteryOptimizations(): Boolean
companion object {
/** The codec used by MoxxyPlatformApi. */
val codec: MessageCodec<Any?> by lazy {
StandardMessageCodec()
companion object {
/** The codec used by MoxxyPlatformApi. */
val codec: MessageCodec<Any?> by lazy {
StandardMessageCodec()
}
/** Sets up an instance of `MoxxyPlatformApi` to handle messages through the `binaryMessenger`. */
@Suppress("UNCHECKED_CAST")
fun setUp(binaryMessenger: BinaryMessenger, api: MoxxyPlatformApi?) {
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyPlatformApi.getPersistentDataPath", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
var wrapped: List<Any?>
try {
wrapped = listOf<Any?>(api.getPersistentDataPath())
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyPlatformApi.getCacheDataPath", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
var wrapped: List<Any?>
try {
wrapped = listOf<Any?>(api.getCacheDataPath())
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyPlatformApi.openBatteryOptimisationSettings", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
var wrapped: List<Any?>
try {
api.openBatteryOptimisationSettings()
wrapped = listOf<Any?>(null)
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyPlatformApi.isIgnoringBatteryOptimizations", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
var wrapped: List<Any?>
try {
wrapped = listOf<Any?>(api.isIgnoringBatteryOptimizations())
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
}
}
/** Sets up an instance of `MoxxyPlatformApi` to handle messages through the `binaryMessenger`. */
@Suppress("UNCHECKED_CAST")
fun setUp(binaryMessenger: BinaryMessenger, api: MoxxyPlatformApi?) {
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyPlatformApi.getPersistentDataPath", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
var wrapped: List<Any?>
try {
wrapped = listOf<Any?>(api.getPersistentDataPath())
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyPlatformApi.getCacheDataPath", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
var wrapped: List<Any?>
try {
wrapped = listOf<Any?>(api.getCacheDataPath())
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyPlatformApi.openBatteryOptimisationSettings", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
var wrapped: List<Any?>
try {
api.openBatteryOptimisationSettings()
wrapped = listOf<Any?>(null)
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyPlatformApi.isIgnoringBatteryOptimizations", codec)
if (api != null) {
channel.setMessageHandler { _, reply ->
var wrapped: List<Any?>
try {
wrapped = listOf<Any?>(api.isIgnoringBatteryOptimizations())
} catch (exception: Throwable) {
wrapped = wrapError(exception)
}
reply.reply(wrapped)
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}

View File

@@ -27,4 +27,4 @@ class PlatformImplementation(private val context: Context) : MoxxyPlatformApi {
val pm = context.getSystemService(PowerManager::class.java)
return pm.isIgnoringBatteryOptimizations(context.packageName)
}
}
}