1 package host.exp.exponent.notifications
2 
3 import android.app.Notification
4 import android.app.PendingIntent
5 import android.content.Context
6 import android.os.Looper
7 import androidx.core.app.NotificationCompat
8 import androidx.core.app.RemoteInput
9 import com.raizlabs.android.dbflow.sql.language.SQLite
10 import com.raizlabs.android.dbflow.sql.language.Select
11 import host.exp.exponent.kernel.KernelConstants
12 import java.util.*
13 
14 object NotificationActionCenter {
15   const val KEY_TEXT_REPLY = "notification_remote_input"
16 
17   @Synchronized
18   @JvmStatic fun putCategory(categoryId: String?, actions: List<MutableMap<String?, Any?>>) {
19     throwExceptionIfOnMainThread()
20 
21     for (i in actions.indices) {
22       val action = actions[i].apply {
23         this["categoryId"] = categoryId
24       }
25       ActionObject(action, i).save()
26     }
27   }
28 
29   @Synchronized
30   @JvmStatic fun removeCategory(categoryId: String?) {
31     val actions = SQLite.select().from(ActionObject::class.java)
32       .where(ActionObject_Table.categoryId.eq(categoryId))
33       .queryList()
34 
35     for (actionObject in actions) {
36       actionObject.delete()
37     }
38   }
39 
40   @Synchronized
41   fun setCategory(
42     categoryId: String,
43     builder: NotificationCompat.Builder,
44     context: Context,
45     intentProvider: IntentProvider
46   ) {
47     throwExceptionIfOnMainThread()
48 
49     // Expo Go has a permanent notification, so we have to set max priority in order to show up buttons
50     builder.priority = Notification.PRIORITY_MAX
51 
52     val actions = Select().from(ActionObject::class.java)
53       .where(ActionObject_Table.categoryId.eq(categoryId))
54       .orderBy(ActionObject_Table.position, true)
55       .queryList()
56 
57     for (actionObject in actions) {
58       addAction(builder, actionObject, intentProvider, context)
59     }
60   }
61 
62   private fun addAction(
63     builder: NotificationCompat.Builder,
64     actionObject: ActionObject,
65     intentProvider: IntentProvider,
66     context: Context
67   ) {
68     val intent = intentProvider.provide().apply {
69       putExtra(KernelConstants.NOTIFICATION_ACTION_TYPE_KEY, actionObject.actionId)
70     }
71     val pendingIntent = PendingIntent.getActivity(
72       context,
73       UUID.randomUUID().hashCode(),
74       intent,
75       PendingIntent.FLAG_UPDATE_CURRENT
76     )
77     val actionBuilder = NotificationCompat.Action.Builder(
78       0,
79       actionObject.buttonTitle,
80       pendingIntent
81     )
82 
83     if (actionObject.isShouldShowTextInput) {
84       actionBuilder.addRemoteInput(
85         RemoteInput.Builder(KEY_TEXT_REPLY)
86           .setLabel(actionObject.placeholder)
87           .build()
88       )
89     }
90 
91     builder.addAction(actionBuilder.build())
92   }
93 
94   private fun throwExceptionIfOnMainThread() {
95     if (Looper.myLooper() == Looper.getMainLooper()) {
96       throw RuntimeException("Do not use NotificationActionCenter class on the main thread!")
97     }
98   }
99 }
100