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()) + } + } +}