Compare commits

..

7 Commits

36 changed files with 1450 additions and 60 deletions

View File

@ -7,6 +7,7 @@ linter:
avoid_positional_boolean_parameters: false avoid_positional_boolean_parameters: false
avoid_bool_literals_in_conditional_expressions: false avoid_bool_literals_in_conditional_expressions: false
file_names: false file_names: false
one_member_abstracts: false
analyzer: analyzer:
exclude: exclude:

View File

@ -2,6 +2,9 @@ package org.moxxy.moxxy_native
const val TAG = "moxxy_native" const val TAG = "moxxy_native"
// The size of buffers to use for various operations
const val BUFFER_SIZE = 4096
// The data key for text entered in the notification's reply field // The data key for text entered in the notification's reply field
const val REPLY_TEXT_KEY = "key_reply_text" const val REPLY_TEXT_KEY = "key_reply_text"

View File

@ -13,6 +13,12 @@ import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.embedding.engine.plugins.activity.ActivityAware import io.flutter.embedding.engine.plugins.activity.ActivityAware
import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding import io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding
import io.flutter.plugin.common.EventChannel import io.flutter.plugin.common.EventChannel
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.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
@ -27,6 +33,8 @@ import org.moxxy.moxxy_native.notifications.showNotificationImpl
import org.moxxy.moxxy_native.picker.FilePickerType import org.moxxy.moxxy_native.picker.FilePickerType
import org.moxxy.moxxy_native.picker.MoxxyPickerApi import org.moxxy.moxxy_native.picker.MoxxyPickerApi
import org.moxxy.moxxy_native.picker.PickerResultListener import org.moxxy.moxxy_native.picker.PickerResultListener
import org.moxxy.moxxy_native.platform.MoxxyPlatformApi
import org.moxxy.moxxy_native.platform.PlatformImplementation
object MoxxyEventChannels { object MoxxyEventChannels {
var notificationChannel: EventChannel? = null var notificationChannel: EventChannel? = null
@ -55,13 +63,26 @@ object NotificationCache {
class MoxxyNativePlugin : FlutterPlugin, ActivityAware, MoxxyPickerApi, MoxxyNotificationsApi { class MoxxyNativePlugin : FlutterPlugin, ActivityAware, MoxxyPickerApi, MoxxyNotificationsApi {
private var context: Context? = null private var context: Context? = null
private var activity: Activity? = null private var activity: Activity? = null
private lateinit var activityClass: Class<Any>
private lateinit var pickerListener: PickerResultListener private lateinit var pickerListener: PickerResultListener
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) { override fun onAttachedToEngine(@NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
context = flutterPluginBinding.applicationContext context = flutterPluginBinding.applicationContext
contactsImplementation = ContactsImplementation(context!!)
platformImplementation = PlatformImplementation(context!!)
// Register the pigeon handlers
MoxxyPickerApi.setUp(flutterPluginBinding.binaryMessenger, this) MoxxyPickerApi.setUp(flutterPluginBinding.binaryMessenger, this)
MoxxyNotificationsApi.setUp(flutterPluginBinding.binaryMessenger, this) MoxxyNotificationsApi.setUp(flutterPluginBinding.binaryMessenger, this)
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!!) pickerListener = PickerResultListener(context!!)
Log.d(TAG, "Attached to engine") Log.d(TAG, "Attached to engine")
} }
@ -72,7 +93,6 @@ class MoxxyNativePlugin : FlutterPlugin, ActivityAware, MoxxyPickerApi, MoxxyNot
override fun onAttachedToActivity(binding: ActivityPluginBinding) { override fun onAttachedToActivity(binding: ActivityPluginBinding) {
activity = binding.activity activity = binding.activity
activityClass = activity!!.javaClass
binding.addActivityResultListener(pickerListener) binding.addActivityResultListener(pickerListener)
Log.d(TAG, "Attached to activity") Log.d(TAG, "Attached to activity")
} }

View File

@ -0,0 +1,95 @@
// Autogenerated from Pigeon (v11.0.1), do not edit directly.
// See also: https://pub.dev/packages/pigeon
package org.moxxy.moxxy_native.contacts
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()
/** The type of icon to use when no avatar path is provided. */
enum class FallbackIconType(val raw: Int) {
NONE(0),
PERSON(1),
NOTES(2),
;
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)
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

@ -0,0 +1,67 @@
package org.moxxy.moxxy_native.contacts
import android.content.Context
import android.content.Intent
import android.graphics.BitmapFactory
import androidx.core.app.Person
import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat
import org.moxxy.moxxy_native.R
/*
* Implementation of Moxxy's contact APIs.
* */
class ContactsImplementation(private val context: Context) : MoxxyContactsApi {
override fun recordSentMessage(
name: String,
jid: String,
avatarPath: String?,
fallbackIcon: FallbackIconType,
) {
val pkgName = context.packageName
val intent = Intent(context, Class.forName("$pkgName.MainActivity")).apply {
action = Intent.ACTION_SEND
// Compatibility with share_handler
putExtra("conversationIdentifier", jid)
}
val shortcutTarget = "$pkgName.dynamic_share_target"
val shortcutBuilder = ShortcutInfoCompat.Builder(context, jid).apply {
setShortLabel(name)
setIsConversation()
setCategories(setOf(shortcutTarget))
setIntent(intent)
setLongLived(true)
}
val personBuilder = Person.Builder().apply {
setKey(jid)
setName(name)
}
// Either set an avatar image OR a fallback icon
if (avatarPath != null) {
val icon = IconCompat.createWithAdaptiveBitmap(
BitmapFactory.decodeFile(avatarPath),
)
shortcutBuilder.setIcon(icon)
personBuilder.setIcon(icon)
} else {
val resourceId = when (fallbackIcon) {
FallbackIconType.NONE, FallbackIconType.PERSON -> R.mipmap.person
FallbackIconType.NOTES -> R.mipmap.notes
}
val icon = IconCompat.createWithResource(context, resourceId)
shortcutBuilder.setIcon(icon)
personBuilder.setIcon(icon)
}
shortcutBuilder.setPerson(personBuilder.build())
ShortcutManagerCompat.addDynamicShortcuts(
context,
listOf(shortcutBuilder.build()),
)
}
}

View File

@ -0,0 +1,192 @@
// Autogenerated from Pigeon (v11.0.1), do not edit directly.
// See also: https://pub.dev/packages/pigeon
package org.moxxy.moxxy_native.cryptography
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
import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer
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()
enum class CipherAlgorithm(val raw: Int) {
AES128GCMNOPADDING(0),
AES256GCMNOPADDING(1),
AES256CBCPKCS7(2),
;
companion object {
fun ofRaw(raw: Int): CipherAlgorithm? {
return values().firstOrNull { it.raw == raw }
}
}
}
/** Generated class from Pigeon that represents data sent in messages. */
data class CryptographyResult(
val plaintextHash: ByteArray,
val ciphertextHash: ByteArray,
) {
companion object {
@Suppress("UNCHECKED_CAST")
fun fromList(list: List<Any?>): CryptographyResult {
val plaintextHash = list[0] as ByteArray
val ciphertextHash = list[1] as ByteArray
return CryptographyResult(plaintextHash, ciphertextHash)
}
}
fun toList(): List<Any?> {
return listOf<Any?>(
plaintextHash,
ciphertextHash,
)
}
}
@Suppress("UNCHECKED_CAST")
private object MoxxyCryptographyApiCodec : StandardMessageCodec() {
override fun readValueOfType(type: Byte, buffer: ByteBuffer): Any? {
return when (type) {
128.toByte() -> {
return (readValue(buffer) as? List<Any?>)?.let {
CryptographyResult.fromList(it)
}
}
else -> super.readValueOfType(type, buffer)
}
}
override fun writeValue(stream: ByteArrayOutputStream, value: Any?) {
when (value) {
is CryptographyResult -> {
stream.write(128)
writeValue(stream, value.toList())
}
else -> super.writeValue(stream, value)
}
}
}
/** Generated interface from Pigeon that represents a handler of messages from Flutter. */
interface MoxxyCryptographyApi {
fun encryptFile(sourcePath: String, destPath: String, key: ByteArray, iv: ByteArray, algorithm: CipherAlgorithm, hashSpec: String, callback: (Result<CryptographyResult?>) -> Unit)
fun decryptFile(sourcePath: String, destPath: String, key: ByteArray, iv: ByteArray, algorithm: CipherAlgorithm, hashSpec: String, callback: (Result<CryptographyResult?>) -> Unit)
fun hashFile(sourcePath: String, hashSpec: String, callback: (Result<ByteArray?>) -> Unit)
companion object {
/** The codec used by MoxxyCryptographyApi. */
val codec: MessageCodec<Any?> by lazy {
MoxxyCryptographyApiCodec
}
/** Sets up an instance of `MoxxyCryptographyApi` to handle messages through the `binaryMessenger`. */
@Suppress("UNCHECKED_CAST")
fun setUp(binaryMessenger: BinaryMessenger, api: MoxxyCryptographyApi?) {
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyCryptographyApi.encryptFile", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val sourcePathArg = args[0] as String
val destPathArg = args[1] as String
val keyArg = args[2] as ByteArray
val ivArg = args[3] as ByteArray
val algorithmArg = CipherAlgorithm.ofRaw(args[4] as Int)!!
val hashSpecArg = args[5] as String
api.encryptFile(sourcePathArg, destPathArg, keyArg, ivArg, algorithmArg, hashSpecArg) { result: Result<CryptographyResult?> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyCryptographyApi.decryptFile", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val sourcePathArg = args[0] as String
val destPathArg = args[1] as String
val keyArg = args[2] as ByteArray
val ivArg = args[3] as ByteArray
val algorithmArg = CipherAlgorithm.ofRaw(args[4] as Int)!!
val hashSpecArg = args[5] as String
api.decryptFile(sourcePathArg, destPathArg, keyArg, ivArg, algorithmArg, hashSpecArg) { result: Result<CryptographyResult?> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
run {
val channel = BasicMessageChannel<Any?>(binaryMessenger, "dev.flutter.pigeon.moxxy_native.MoxxyCryptographyApi.hashFile", codec)
if (api != null) {
channel.setMessageHandler { message, reply ->
val args = message as List<Any?>
val sourcePathArg = args[0] as String
val hashSpecArg = args[1] as String
api.hashFile(sourcePathArg, hashSpecArg) { result: Result<ByteArray?> ->
val error = result.exceptionOrNull()
if (error != null) {
reply.reply(wrapError(error))
} else {
val data = result.getOrNull()
reply.reply(wrapResult(data))
}
}
}
} else {
channel.setMessageHandler(null)
}
}
}
}
}

View File

@ -0,0 +1,169 @@
package org.moxxy.moxxy_native.cryptography
import android.util.Log
import org.moxxy.moxxy_native.BUFFER_SIZE
import org.moxxy.moxxy_native.TAG
import java.io.FileInputStream
import java.security.MessageDigest
import javax.crypto.Cipher
import javax.crypto.CipherOutputStream
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec
import kotlin.concurrent.thread
/*
* Convert the algorithm spec @algorithm to the format that Java/Android understands
* */
private fun getCipherSpecFromInteger(algorithm: CipherAlgorithm): String {
return when (algorithm) {
CipherAlgorithm.AES128GCMNOPADDING -> "AES_128/GCM/NoPadding"
CipherAlgorithm.AES256GCMNOPADDING -> "AES_256/GCM/NoPadding"
CipherAlgorithm.AES256CBCPKCS7 -> "AES_256/CBC/PKCS7PADDING"
}
}
/*
* Implementation of Moxxy's cryptography API
* */
class CryptographyImplementation : MoxxyCryptographyApi {
override fun encryptFile(
sourcePath: String,
destPath: String,
key: ByteArray,
iv: ByteArray,
algorithm: CipherAlgorithm,
hashSpec: String,
callback: (Result<CryptographyResult?>) -> Unit,
) {
thread(start = true) {
val cipherSpec = getCipherSpecFromInteger(algorithm)
val buffer = ByteArray(BUFFER_SIZE)
val secretKey = SecretKeySpec(key, cipherSpec)
val inputStream = FileInputStream(sourcePath)
try {
val digest = MessageDigest.getInstance(hashSpec)
val cipher = Cipher.getInstance(cipherSpec).apply {
init(Cipher.ENCRYPT_MODE, secretKey, IvParameterSpec(iv))
}
val fileOutputStream = HashedFileOutputStream(destPath, hashSpec)
val cipherOutputStream = CipherOutputStream(fileOutputStream, cipher)
var length: Int
while (true) {
length = inputStream.read(buffer)
if (length <= 0) break
digest.update(buffer, 0, length)
cipherOutputStream.write(buffer, 0, length)
}
// Clean up
cipherOutputStream.apply {
flush()
close()
}
// Success
callback(
Result.success(
CryptographyResult(
plaintextHash = digest.digest(),
ciphertextHash = fileOutputStream.digest(),
),
),
)
} catch (ex: Exception) {
Log.e(TAG, "Failed to encrypt file $sourcePath: ${ex.message}")
callback(Result.success(null))
} finally {
// Clean up
inputStream.close()
}
}
}
override fun decryptFile(
sourcePath: String,
destPath: String,
key: ByteArray,
iv: ByteArray,
algorithm: CipherAlgorithm,
hashSpec: String,
callback: (Result<CryptographyResult?>) -> Unit,
) {
thread(start = true) {
val cipherSpec = getCipherSpecFromInteger(algorithm)
val buffer = ByteArray(BUFFER_SIZE)
val secretKey = SecretKeySpec(key, cipherSpec)
val inputStream = FileInputStream(sourcePath)
try {
val digest = MessageDigest.getInstance(hashSpec)
val cipher = Cipher.getInstance(cipherSpec).apply {
init(Cipher.DECRYPT_MODE, secretKey, IvParameterSpec(iv))
}
val fileOutputStream = HashedFileOutputStream(destPath, hashSpec)
val cipherOutputStream = CipherOutputStream(fileOutputStream, cipher)
var length: Int
while (true) {
length = inputStream.read(buffer)
if (length <= 0) break
digest.update(buffer, 0, length)
cipherOutputStream.write(buffer, 0, length)
}
// Clean up
cipherOutputStream.apply {
flush()
close()
}
// Success
callback(
Result.success(
CryptographyResult(
plaintextHash = digest.digest(),
ciphertextHash = fileOutputStream.digest(),
),
),
)
} catch (ex: Exception) {
Log.e(TAG, "Failed to decrypt file $sourcePath: ${ex.message}")
callback(Result.success(null))
} finally {
// Clean up
inputStream.close()
}
}
}
override fun hashFile(
sourcePath: String,
hashSpec: String,
callback: (Result<ByteArray?>) -> Unit,
) {
thread(start = true) {
val buffer = ByteArray(BUFFER_SIZE)
val inputStream = FileInputStream(sourcePath)
try {
val digest = MessageDigest.getInstance(hashSpec)
var length: Int
while (true) {
length = inputStream.read(buffer)
if (length <= 0) break
// Only update the digest if we read more than 0 bytes
digest.update(buffer, 0, length)
}
// Return success
callback(Result.success(digest.digest()))
} catch (ex: Exception) {
Log.e(TAG, "Failed to has file $sourcePath with $hashSpec: ${ex.message}")
callback(Result.success(null))
} finally {
// Clean up
inputStream.close()
}
}
}
}

View File

@ -0,0 +1,25 @@
package org.moxxy.moxxy_native.cryptography
import java.io.FileOutputStream
import java.security.MessageDigest
/*
* A FileOutputStream that continuously hashes whatever it writes to the file.
*/
class HashedFileOutputStream(name: String, hashAlgorithm: String) : FileOutputStream(name) {
private val digest: MessageDigest
init {
this.digest = MessageDigest.getInstance(hashAlgorithm)
}
override fun write(buffer: ByteArray, offset: Int, length: Int) {
super.write(buffer, offset, length)
digest.update(buffer, offset, length)
}
fun digest(): ByteArray {
return digest.digest()
}
}

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

@ -0,0 +1,127 @@
// Autogenerated from Pigeon (v11.0.1), do not edit directly.
// See also: https://pub.dev/packages/pigeon
package org.moxxy.moxxy_native.platform
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 MoxxyPlatformApi {
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()
}
/** 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

@ -0,0 +1,30 @@
package org.moxxy.moxxy_native.platform
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.PowerManager
import android.provider.Settings
class PlatformImplementation(private val context: Context) : MoxxyPlatformApi {
override fun getPersistentDataPath(): String {
return context.filesDir.path
}
override fun getCacheDataPath(): String {
return context.cacheDir.path
}
override fun openBatteryOptimisationSettings() {
val packageUri = Uri.parse("package:${context.packageName}")
val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, packageUri).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK
}
context.startActivity(intent)
}
override fun isIgnoringBatteryOptimizations(): Boolean {
val pm = context.getSystemService(PowerManager::class.java)
return pm.isIgnoringBatteryOptimizations(context.packageName)
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 911 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 828 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

View File

@ -1,3 +1,5 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:moxxy_native/moxxy_native.dart'; import 'package:moxxy_native/moxxy_native.dart';
@ -5,9 +7,16 @@ void main() {
runApp(const MyApp()); runApp(const MyApp());
} }
class MyApp extends StatelessWidget { class MyApp extends StatefulWidget {
const MyApp({super.key}); const MyApp({super.key});
@override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
String? imagePath;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
@ -44,6 +53,49 @@ class MyApp extends StatelessWidget {
}, },
child: const Text('Generic multi-picker'), child: const Text('Generic multi-picker'),
), ),
TextButton(
onPressed: () async {
final result = await MoxxyPickerApi()
.pickFiles(FilePickerType.image, false);
if (result.isEmpty) return;
final encDest = '${result.first!}.enc';
final decDest = '${result.first!}.dec';
final encResult = await MoxxyCryptographyApi().encryptFile(
result.first!,
encDest,
Uint8List.fromList(List.filled(32, 1)),
Uint8List.fromList(List.filled(16, 2)),
CipherAlgorithm.aes256CbcPkcs7,
'SHA-256',
);
if (encResult == null) {
// ignore: avoid_print
print('Failed to encrypt file');
return;
}
final decResult = await MoxxyCryptographyApi().decryptFile(
encDest,
decDest,
Uint8List.fromList(List.filled(32, 1)),
Uint8List.fromList(List.filled(16, 2)),
CipherAlgorithm.aes256CbcPkcs7,
'SHA-256',
);
if (decResult == null) {
// ignore: avoid_print
print('Failed to decrypt file');
return;
}
setState(() {
imagePath = decDest;
});
},
child: const Text('Test cryptography'),
),
if (imagePath != null) Image.file(File(imagePath!)),
], ],
), ),
), ),

View File

@ -0,0 +1 @@

View File

@ -130,54 +130,6 @@ packages:
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.8.2" version: "1.8.2"
permission_handler:
dependency: "direct main"
description:
name: permission_handler
sha256: bc56bfe9d3f44c3c612d8d393bd9b174eb796d706759f9b495ac254e4294baa5
url: "https://pub.dev"
source: hosted
version: "10.4.5"
permission_handler_android:
dependency: transitive
description:
name: permission_handler_android
sha256: "59c6322171c29df93a22d150ad95f3aa19ed86542eaec409ab2691b8f35f9a47"
url: "https://pub.dev"
source: hosted
version: "10.3.6"
permission_handler_apple:
dependency: transitive
description:
name: permission_handler_apple
sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5"
url: "https://pub.dev"
source: hosted
version: "9.1.4"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
sha256: f2343e9fa9c22ae4fd92d4732755bfe452214e7189afcc097380950cf567b4b2
url: "https://pub.dev"
source: hosted
version: "3.11.5"
permission_handler_windows:
dependency: transitive
description:
name: permission_handler_windows
sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098
url: "https://pub.dev"
source: hosted
version: "0.1.3"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: da3fdfeccc4d4ff2da8f8c556704c08f912542c5fb3cf2233ed75372384a034d
url: "https://pub.dev"
source: hosted
version: "2.1.6"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -241,4 +193,4 @@ packages:
version: "2.1.4" version: "2.1.4"
sdks: sdks:
dart: ">=2.19.6 <3.0.0" dart: ">=2.19.6 <3.0.0"
flutter: ">=2.8.0" flutter: ">=2.5.0"

View File

@ -1,2 +1,6 @@
export 'pigeon/contacts.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';

View File

@ -0,0 +1,55 @@
// 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';
/// The type of icon to use when no avatar path is provided.
enum FallbackIconType {
none,
person,
notes,
}
class MoxxyContactsApi {
/// Constructor for [MoxxyContactsApi]. 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.
MoxxyContactsApi({BinaryMessenger? binaryMessenger})
: _binaryMessenger = binaryMessenger;
final BinaryMessenger? _binaryMessenger;
static const MessageCodec<Object?> codec = StandardMessageCodec();
Future<void> recordSentMessage(String arg_name, String arg_jid,
String? arg_avatarPath, FallbackIconType arg_fallbackIcon) async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.moxxy_native.MoxxyContactsApi.recordSentMessage',
codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList = await channel.send(<Object?>[
arg_name,
arg_jid,
arg_avatarPath,
arg_fallbackIcon.index
]) 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 {
return;
}
}
}

View File

@ -0,0 +1,168 @@
// 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';
enum CipherAlgorithm {
aes128GcmNoPadding,
aes256GcmNoPadding,
aes256CbcPkcs7,
}
class CryptographyResult {
CryptographyResult({
required this.plaintextHash,
required this.ciphertextHash,
});
Uint8List plaintextHash;
Uint8List ciphertextHash;
Object encode() {
return <Object?>[
plaintextHash,
ciphertextHash,
];
}
static CryptographyResult decode(Object result) {
result as List<Object?>;
return CryptographyResult(
plaintextHash: result[0]! as Uint8List,
ciphertextHash: result[1]! as Uint8List,
);
}
}
class _MoxxyCryptographyApiCodec extends StandardMessageCodec {
const _MoxxyCryptographyApiCodec();
@override
void writeValue(WriteBuffer buffer, Object? value) {
if (value is CryptographyResult) {
buffer.putUint8(128);
writeValue(buffer, value.encode());
} else {
super.writeValue(buffer, value);
}
}
@override
Object? readValueOfType(int type, ReadBuffer buffer) {
switch (type) {
case 128:
return CryptographyResult.decode(readValue(buffer)!);
default:
return super.readValueOfType(type, buffer);
}
}
}
class MoxxyCryptographyApi {
/// Constructor for [MoxxyCryptographyApi]. 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.
MoxxyCryptographyApi({BinaryMessenger? binaryMessenger})
: _binaryMessenger = binaryMessenger;
final BinaryMessenger? _binaryMessenger;
static const MessageCodec<Object?> codec = _MoxxyCryptographyApiCodec();
Future<CryptographyResult?> encryptFile(
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?>(
'dev.flutter.pigeon.moxxy_native.MoxxyCryptographyApi.encryptFile',
codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList = await channel.send(<Object?>[
arg_sourcePath,
arg_destPath,
arg_key,
arg_iv,
arg_algorithm.index,
arg_hashSpec
]) 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 {
return (replyList[0] as CryptographyResult?);
}
}
Future<CryptographyResult?> decryptFile(
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?>(
'dev.flutter.pigeon.moxxy_native.MoxxyCryptographyApi.decryptFile',
codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList = await channel.send(<Object?>[
arg_sourcePath,
arg_destPath,
arg_key,
arg_iv,
arg_algorithm.index,
arg_hashSpec
]) 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 {
return (replyList[0] as CryptographyResult?);
}
}
Future<Uint8List?> hashFile(
String arg_sourcePath, String arg_hashSpec) async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.moxxy_native.MoxxyCryptographyApi.hashFile', codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList = await channel
.send(<Object?>[arg_sourcePath, arg_hashSpec]) 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 {
return (replyList[0] as Uint8List?);
}
}
}

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

@ -0,0 +1,49 @@
// 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?)!;
}
}
}

123
lib/pigeon/platform.g.dart Normal file
View File

@ -0,0 +1,123 @@
// 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 MoxxyPlatformApi {
/// Constructor for [MoxxyPlatformApi]. 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.
MoxxyPlatformApi({BinaryMessenger? binaryMessenger})
: _binaryMessenger = binaryMessenger;
final BinaryMessenger? _binaryMessenger;
static const MessageCodec<Object?> codec = StandardMessageCodec();
Future<String> getPersistentDataPath() async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.moxxy_native.MoxxyPlatformApi.getPersistentDataPath',
codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList = await channel.send(null) 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 String?)!;
}
}
Future<String> getCacheDataPath() async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.moxxy_native.MoxxyPlatformApi.getCacheDataPath',
codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList = await channel.send(null) 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 String?)!;
}
}
Future<void> openBatteryOptimisationSettings() async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.moxxy_native.MoxxyPlatformApi.openBatteryOptimisationSettings',
codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList = await channel.send(null) 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 {
return;
}
}
Future<bool> isIgnoringBatteryOptimizations() async {
final BasicMessageChannel<Object?> channel = BasicMessageChannel<Object?>(
'dev.flutter.pigeon.moxxy_native.MoxxyPlatformApi.isIgnoringBatteryOptimizations',
codec,
binaryMessenger: _binaryMessenger);
final List<Object?>? replyList = await channel.send(null) 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?)!;
}
}
}

29
pigeon/contacts.dart Normal file
View File

@ -0,0 +1,29 @@
import 'package:pigeon/pigeon.dart';
@ConfigurePigeon(
PigeonOptions(
dartOut: 'lib/pigeon/contacts.g.dart',
kotlinOut:
'android/src/main/kotlin/org/moxxy/moxxy_native/contacts/ContactsApi.kt',
kotlinOptions: KotlinOptions(
package: 'org.moxxy.moxxy_native.contacts',
),
),
)
/// The type of icon to use when no avatar path is provided.
enum FallbackIconType {
none,
person,
notes;
}
@HostApi()
abstract class MoxxyContactsApi {
void recordSentMessage(
String name,
String jid,
String? avatarPath,
FallbackIconType fallbackIcon,
);
}

52
pigeon/cryptography.dart Normal file
View File

@ -0,0 +1,52 @@
import 'package:pigeon/pigeon.dart';
@ConfigurePigeon(
PigeonOptions(
dartOut: 'lib/pigeon/cryptography.g.dart',
kotlinOut:
'android/src/main/kotlin/org/moxxy/moxxy_native/cryptography/CryptographyApi.kt',
kotlinOptions: KotlinOptions(
package: 'org.moxxy.moxxy_native.cryptography',
),
),
)
enum CipherAlgorithm {
aes128GcmNoPadding,
aes256GcmNoPadding,
aes256CbcPkcs7;
}
class CryptographyResult {
const CryptographyResult(this.plaintextHash, this.ciphertextHash);
final Uint8List plaintextHash;
final Uint8List ciphertextHash;
}
@HostApi()
abstract class MoxxyCryptographyApi {
@async
CryptographyResult? encryptFile(
String sourcePath,
String destPath,
Uint8List key,
Uint8List iv,
CipherAlgorithm algorithm,
String hashSpec,
);
@async
CryptographyResult? decryptFile(
String sourcePath,
String destPath,
Uint8List key,
Uint8List iv,
CipherAlgorithm algorithm,
String hashSpec,
);
@async
Uint8List? hashFile(
String sourcePath,
String hashSpec,
);
}

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);
}

View File

@ -3,7 +3,8 @@ import 'package:pigeon/pigeon.dart';
@ConfigurePigeon( @ConfigurePigeon(
PigeonOptions( PigeonOptions(
dartOut: 'lib/pigeon/notifications.g.dart', dartOut: 'lib/pigeon/notifications.g.dart',
kotlinOut: 'android/src/main/kotlin/org/moxxy/moxxy_native/notifications/NotificationsApi.kt', kotlinOut:
'android/src/main/kotlin/org/moxxy/moxxy_native/notifications/NotificationsApi.kt',
kotlinOptions: KotlinOptions( kotlinOptions: KotlinOptions(
package: 'org.moxxy.moxxy_native.notifications', package: 'org.moxxy.moxxy_native.notifications',
), ),
@ -54,9 +55,16 @@ class NotificationMessage {
} }
class MessagingNotification { class MessagingNotification {
const MessagingNotification(this.title, this.id, this.jid, this.messages, const MessagingNotification(
this.channelId, this.isGroupchat, this.extra, this.title,
{this.groupId}); this.id,
this.jid,
this.messages,
this.channelId,
this.isGroupchat,
this.extra, {
this.groupId,
});
/// The title of the conversation. /// The title of the conversation.
final String title; final String title;
@ -91,8 +99,13 @@ enum NotificationIcon {
class RegularNotification { class RegularNotification {
const RegularNotification( const RegularNotification(
this.title, this.body, this.channelId, this.id, this.icon, this.title,
{this.groupId}); this.body,
this.channelId,
this.id,
this.icon, {
this.groupId,
});
/// The title of the notification. /// The title of the notification.
final String title; final String title;
@ -165,6 +178,7 @@ class NotificationGroup {
final String description; final String description;
} }
// ignore: constant_identifier_names
enum NotificationChannelImportance { MIN, HIGH, DEFAULT } enum NotificationChannelImportance { MIN, HIGH, DEFAULT }
class NotificationChannel { class NotificationChannel {

View File

@ -3,13 +3,13 @@ import 'package:pigeon/pigeon.dart';
@ConfigurePigeon( @ConfigurePigeon(
PigeonOptions( PigeonOptions(
dartOut: 'lib/pigeon/picker.g.dart', dartOut: 'lib/pigeon/picker.g.dart',
kotlinOut: 'android/src/main/kotlin/org/moxxy/moxxy_native/picker/PickerApi.kt', kotlinOut:
'android/src/main/kotlin/org/moxxy/moxxy_native/picker/PickerApi.kt',
kotlinOptions: KotlinOptions( kotlinOptions: KotlinOptions(
package: 'org.moxxy.moxxy_native.picker', package: 'org.moxxy.moxxy_native.picker',
), ),
), ),
) )
enum FilePickerType { enum FilePickerType {
/// Pick only image(s) /// Pick only image(s)
image, image,

22
pigeon/platform.dart Normal file
View File

@ -0,0 +1,22 @@
import 'package:pigeon/pigeon.dart';
@ConfigurePigeon(
PigeonOptions(
dartOut: 'lib/pigeon/platform.g.dart',
kotlinOut:
'android/src/main/kotlin/org/moxxy/moxxy_native/platform/PlatformApi.kt',
kotlinOptions: KotlinOptions(
package: 'org.moxxy.moxxy_native.platform',
),
),
)
@HostApi()
abstract class MoxxyPlatformApi {
String getPersistentDataPath();
String getCacheDataPath();
void openBatteryOptimisationSettings();
bool isIgnoringBatteryOptimizations();
}