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