1+ package com.troplo.privateuploader
2+
3+ import android.Manifest
4+ import android.app.ActivityManager
5+ import android.app.ActivityManager.RunningAppProcessInfo
6+ import android.app.NotificationChannel
7+ import android.app.NotificationManager
8+ import android.app.PendingIntent
9+ import android.content.Context
10+ import android.content.Intent
11+ import android.content.pm.PackageManager
12+ import android.util.Log
13+ import androidx.core.app.ActivityCompat
14+ import androidx.core.app.NotificationCompat
15+ import androidx.core.app.NotificationManagerCompat
16+ import androidx.core.app.Person
17+ import androidx.core.app.RemoteInput
18+ import androidx.work.OneTimeWorkRequest
19+ import androidx.work.WorkManager
20+ import androidx.work.Worker
21+ import androidx.work.WorkerParameters
22+ import com.google.firebase.messaging.FirebaseMessagingService
23+ import com.google.firebase.messaging.RemoteMessage
24+ import com.troplo.privateuploader.api.TpuApi
25+ import com.troplo.privateuploader.api.TpuFunctions
26+ import com.troplo.privateuploader.data.model.FCMTokenRequest
27+ import com.troplo.privateuploader.data.model.MessageEventFirebase
28+ import kotlinx.coroutines.CoroutineScope
29+ import kotlinx.coroutines.Dispatchers
30+ import kotlinx.coroutines.launch
31+
32+
33+ class FirebaseChatService : FirebaseMessagingService () {
34+ private val messages = mutableMapOf<Int , MutableList <NotificationCompat .MessagingStyle .Message >>()
35+
36+ private fun isAppOnForeground (context : Context ): Boolean {
37+ val activityManager = context.getSystemService(ACTIVITY_SERVICE ) as ActivityManager
38+ val appProcesses = activityManager.runningAppProcesses ? : return false
39+ val packageName = context.packageName
40+ for (appProcess in appProcesses) {
41+ if (appProcess.importance == RunningAppProcessInfo .IMPORTANCE_FOREGROUND && appProcess.processName == packageName) {
42+ return true
43+ }
44+ }
45+ return false
46+ }
47+
48+ override fun onMessageReceived (remoteMessage : RemoteMessage ) {
49+ Log .d(TAG , " [NewChatService] Message received" )
50+ if (isAppOnForeground(this )) {
51+ Log .d(TAG , " [NewChatService] App is on foreground" )
52+ return
53+ }
54+ sendNotification(
55+ MessageEventFirebase (
56+ content = remoteMessage.data[" content" ] ? : " " ,
57+ userId = remoteMessage.data[" userId" ]?.toInt() ? : 0 ,
58+ username = remoteMessage.data[" username" ] ? : " " ,
59+ createdAt = remoteMessage.data[" createdAt" ] ? : " " ,
60+ chatName = remoteMessage.data[" chatName" ] ? : " " ,
61+ associationId = remoteMessage.data[" associationId" ]?.toInt() ? : 0 ,
62+ avatar = remoteMessage.data[" avatar" ] ? : " " ,
63+ id = remoteMessage.data[" id" ]?.toInt() ? : 0
64+ )
65+ )
66+ }
67+ // [END receive_message]
68+
69+ private fun needsToBeScheduled () = true
70+
71+ // [START on_new_token]
72+ /* *
73+ * Called if the FCM registration token is updated. This may occur if the security of
74+ * the previous token had been compromised. Note that this is called when the
75+ * FCM registration token is initially generated so this is where you would retrieve the token.
76+ */
77+ override fun onNewToken (token : String ) {
78+ Log .d(TAG , " Refreshed token: $token " )
79+
80+ // If you want to send messages to this application instance or
81+ // manage this apps subscriptions on the server side, send the
82+ // FCM registration token to your app server.
83+ sendRegistrationToServer(token)
84+ }
85+ // [END on_new_token]
86+
87+ private fun scheduleJob () {
88+ // [START dispatch_job]
89+ val work = OneTimeWorkRequest .Builder (MyWorker ::class .java)
90+ .build()
91+ WorkManager .getInstance(this )
92+ .beginWith(work)
93+ .enqueue()
94+ // [END dispatch_job]
95+ }
96+
97+ private fun handleNow () {
98+ Log .d(TAG , " Short lived task is done." )
99+ }
100+
101+ private fun sendRegistrationToServer (token : String? ) {
102+ if (token != null ) {
103+ Log .d(" $TAG .FCMToken" , token)
104+ CoroutineScope (Dispatchers .IO ).launch {
105+ TpuApi .retrofitService.registerFcmToken(FCMTokenRequest (token))
106+ }
107+ }
108+ }
109+
110+ private fun sendNotification (message : MessageEventFirebase ) {
111+ Log .d(" TPU.Untagged" , " [ChatService] Sending notification" )
112+
113+ // Add any additional configuration to the notification builder as needed
114+ if (ActivityCompat .checkSelfPermission(
115+ this ,
116+ Manifest .permission.POST_NOTIFICATIONS
117+ ) != PackageManager .PERMISSION_GRANTED
118+ ) {
119+ Log .d(" TPU.Untagged" , " [ChatService] No permission to post notifications" )
120+ // TODO: Consider calling
121+ // ActivityCompat#requestPermissions
122+ // here to request the missing permissions, and then overriding
123+ // public void onRequestPermissionsResult(int requestCode, String[] permissions,
124+ // int[] grantResults)
125+ // to handle the case where the user grants the permission. See the documentation
126+ // for ActivityCompat#requestPermissions for more details.
127+ return
128+ }
129+ asyncLoadIcon(message.avatar, this ) {
130+ try {
131+ Log .d(" TPU.Untagged" , " [ChatService] Loaded icon" )
132+ val chatPartner = Person .Builder ().apply {
133+ setName(message.username)
134+ setKey(message.userId.toString())
135+ setIcon(it)
136+ setImportant(false )
137+ }.build()
138+
139+ val notificationManager = NotificationManagerCompat .from(this )
140+ val channel = NotificationChannel (
141+ " communications" ,
142+ " Messages from Communications" ,
143+ NotificationManager .IMPORTANCE_HIGH
144+ )
145+ notificationManager.createNotificationChannel(channel)
146+ if (messages[message.associationId] == null ) messages[message.associationId] = mutableListOf ()
147+ messages[message.associationId]?.add(
148+ NotificationCompat .MessagingStyle .Message (
149+ message.content,
150+ TpuFunctions .getDate(message.createdAt)?.time ? : 0 ,
151+ chatPartner
152+ )
153+ )
154+
155+ val style = NotificationCompat .MessagingStyle (chatPartner)
156+ .setConversationTitle(message.chatName)
157+
158+ for (msg in messages[message.associationId]!! ) {
159+ style.addMessage(msg)
160+ }
161+
162+ val replyIntent = Intent (this , InlineNotificationActivity ::class .java)
163+ replyIntent.putExtra(" chatId" , 69 )
164+ val replyPendingIntent = PendingIntent .getBroadcast(this , 0 , replyIntent, PendingIntent .FLAG_MUTABLE )
165+
166+ val remoteInput = RemoteInput .Builder (" content" )
167+ .setLabel(" Reply" )
168+ .build()
169+
170+ val replyAction = NotificationCompat .Action .Builder (
171+ R .drawable.tpu_logo,
172+ " Reply" ,
173+ replyPendingIntent
174+ )
175+ .addRemoteInput(remoteInput)
176+ .setAllowGeneratedReplies(true )
177+ .build()
178+
179+ val builder: NotificationCompat .Builder = NotificationCompat .Builder (this , " communications" )
180+ .addPerson(chatPartner)
181+ .setStyle(style)
182+ .setContentText(message.content)
183+ .setContentTitle(message.username)
184+ .setSmallIcon(R .drawable.tpu_logo)
185+ .setWhen(TpuFunctions .getDate(message.createdAt)?.time ? : 0 )
186+ .addAction(replyAction)
187+ val res = notificationManager.notify(message.associationId, builder.build())
188+ Log .d(" TPU.Untagged" , " [ChatService] Notification sent, $res " )
189+ } catch (e: Exception ) {
190+ Log .d(" TPU.Untagged" , " [ChatService] Error sending notification, ${e.printStackTrace()} " )
191+ }
192+ }
193+ }
194+
195+ companion object {
196+ private const val TAG = " FirebaseChatService"
197+ }
198+
199+ internal class MyWorker (appContext : Context , workerParams : WorkerParameters ) : Worker(appContext, workerParams) {
200+ override fun doWork (): Result {
201+ // TODO(developer): add long running task here.
202+ return Result .success()
203+ }
204+ }
205+ }
0 commit comments