diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index 34cf63f..44161b0 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -57,6 +57,8 @@ android {
dependencies {
implementation("com.google.android.material:material:1.12.0")
+ implementation("com.github.bumptech.glide:glide:4.16.0")
+ implementation("com.squareup.okhttp3:okhttp:4.12.0")
}
flutter {
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 90c7c6f..a29f020 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -95,6 +95,19 @@
+
+
+
+
+
+
+
+
Unit) {
+ val prefs = context.getSharedPreferences("FlutterSharedPreferences", Context.MODE_PRIVATE)
+ val token = prefs.getString("flutter.token", null)
+ val serverUrl = prefs.getString("flutter.serverUrl", null)
+
+ if (token == null || serverUrl == null) {
+ return
+ }
+
+ val url = "$serverUrl/chat/$roomId/messages"
+
+ val json = JSONObject()
+ json.put("content", content)
+ json.put("replied_message_id", repliedMessageId)
+
+ val requestBody = json.toString().toRequestBody("application/json; charset=utf-8".toMediaType())
+
+ val request = Request.Builder()
+ .url(url)
+ .post(requestBody)
+ .addHeader("Authorization", "AtField $token")
+ .build()
+
+ client.newCall(request).enqueue(object : Callback {
+ override fun onFailure(call: Call, e: IOException) {
+ // Handle failure
+ callback()
+ }
+
+ override fun onResponse(call: Call, response: Response) {
+ // Handle success
+ callback()
+ }
+ })
+ }
+}
diff --git a/android/app/src/main/kotlin/dev/solsynth/solian/receiver/ReplyReceiver.kt b/android/app/src/main/kotlin/dev/solsynth/solian/receiver/ReplyReceiver.kt
new file mode 100644
index 0000000..c7f03bf
--- /dev/null
+++ b/android/app/src/main/kotlin/dev/solsynth/solian/receiver/ReplyReceiver.kt
@@ -0,0 +1,27 @@
+package dev.solsynth.solian.receiver
+
+import android.app.NotificationManager
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import androidx.core.app.RemoteInput
+import dev.solsynth.solian.network.ApiClient
+
+class ReplyReceiver : BroadcastReceiver() {
+ override fun onReceive(context: Context, intent: Intent) {
+ val remoteInput = RemoteInput.getResultsFromIntent(intent)
+ if (remoteInput != null) {
+ val replyText = remoteInput.getCharSequence("key_text_reply").toString()
+ val roomId = intent.getStringExtra("room_id")
+ val messageId = intent.getStringExtra("message_id")
+ val notificationId = intent.getIntExtra("notification_id", 0)
+
+ if (roomId != null && messageId != null) {
+ ApiClient(context).sendMessage(roomId, replyText, messageId) {
+ val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
+ notificationManager.cancel(notificationId)
+ }
+ }
+ }
+ }
+}
diff --git a/android/app/src/main/kotlin/dev/solsynth/solian/service/MessagingService.kt b/android/app/src/main/kotlin/dev/solsynth/solian/service/MessagingService.kt
new file mode 100644
index 0000000..e4a2d7d
--- /dev/null
+++ b/android/app/src/main/kotlin/dev/solsynth/solian/service/MessagingService.kt
@@ -0,0 +1,101 @@
+package dev.solsynth.solian.service
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.graphics.Bitmap
+import android.graphics.drawable.Drawable
+import android.os.Build
+import androidx.core.app.NotificationCompat
+import androidx.core.app.NotificationManagerCompat
+import androidx.core.app.RemoteInput
+import com.bumptech.glide.Glide
+import com.bumptech.glide.request.target.CustomTarget
+import com.bumptech.glide.request.transition.Transition
+import com.google.firebase.messaging.FirebaseMessagingService
+import com.google.firebase.messaging.RemoteMessage
+import dev.solsynth.solian.MainActivity
+import dev.solsynth.solian.receiver.ReplyReceiver
+import org.json.JSONObject
+
+class MessagingService: FirebaseMessagingService() {
+ override fun onMessageReceived(remoteMessage: RemoteMessage) {
+ val type = remoteMessage.data["type"]
+ if (type == "messages.new") {
+ handleMessageNotification(remoteMessage)
+ } else {
+ // Handle other notification types
+ }
+ }
+
+ private fun handleMessageNotification(remoteMessage: RemoteMessage) {
+ val data = remoteMessage.data
+ val metaString = data["meta"] ?: return
+ val meta = JSONObject(metaString)
+
+ val pfp = meta.optString("pfp", null)
+ val roomId = meta.optString("room_id", null)
+ val messageId = meta.optString("message_id", null)
+
+ val notificationId = System.currentTimeMillis().toInt()
+
+ val replyLabel = "Reply"
+ val remoteInput = RemoteInput.Builder("key_text_reply")
+ .setLabel(replyLabel)
+ .build()
+
+ val replyIntent = Intent(this, ReplyReceiver::class.java).apply {
+ putExtra("room_id", roomId)
+ putExtra("message_id", messageId)
+ putExtra("notification_id", notificationId)
+ }
+
+ val pendingIntentFlags = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ } else {
+ PendingIntent.FLAG_UPDATE_CURRENT
+ }
+
+ val replyPendingIntent = PendingIntent.getBroadcast(
+ applicationContext,
+ notificationId,
+ replyIntent,
+ pendingIntentFlags
+ )
+
+ val action = NotificationCompat.Action.Builder(
+ android.R.drawable.ic_menu_send,
+ replyLabel,
+ replyPendingIntent
+ )
+ .addRemoteInput(remoteInput)
+ .build()
+
+ val intent = Intent(this, MainActivity::class.java)
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ val pendingIntent = PendingIntent.getActivity(this, 0, intent, pendingIntentFlags)
+
+ val notificationBuilder = NotificationCompat.Builder(this, "messages")
+ .setSmallIcon(android.R.drawable.ic_dialog_info)
+ .setContentTitle(remoteMessage.notification?.title)
+ .setContentText(remoteMessage.notification?.body)
+ .setPriority(NotificationCompat.PRIORITY_HIGH)
+ .setContentIntent(pendingIntent)
+ .addAction(action)
+
+ if (pfp != null) {
+ Glide.with(applicationContext)
+ .asBitmap()
+ .load(pfp)
+ .into(object : CustomTarget() {
+ override fun onResourceReady(resource: Bitmap, transition: Transition?) {
+ notificationBuilder.setLargeIcon(resource)
+ NotificationManagerCompat.from(applicationContext).notify(notificationId, notificationBuilder.build())
+ }
+
+ override fun onLoadCleared(placeholder: Drawable?) {}
+ })
+ } else {
+ NotificationManagerCompat.from(this).notify(notificationId, notificationBuilder.build())
+ }
+ }
+}