Add push notification extensions (#4005)
* add wav * add sound to config * add extension to `updateExtensions.sh` * add ios source files * add a build extension * add a new module * use correct type on ios * update the build plugin * add android handler * create a patch for expo-notifications * basic android implementation * add entitlements for notifications extension * add some generic logic for ios * add age check logic * add extension to app config * remove dash * move directory * rename again * update privacy manifest * add prefs storage ios * better types * create interface for setting and getting prefs * add notifications prefs for android * add functions to module * add types to js * add prefs context * add web stub * wrap the app * fix types * more preferences for ios * add a test toggle * swap vars * update patch * fix patch error * fix typo * sigh * sigh * get stored prefs on launch * anotehr type * simplify * about finished * comment * adjust plugin * use supported file types * update NSE * futureproof ios * futureproof android * update sound file name * handle initialization * more cleanup * update js types * strict js types * set the notification channel * rm * add silent channel * add mute logic * update patch * podfile * adjust channels * fix android channel * update readme * oreo or higher * nit * don't use getValue * nit
This commit is contained in:
parent
31868b255f
commit
bf7b66d5c1
38 changed files with 1297 additions and 12 deletions
|
@ -0,0 +1,2 @@
|
|||
<manifest>
|
||||
</manifest>
|
|
@ -0,0 +1,39 @@
|
|||
package expo.modules.backgroundnotificationhandler
|
||||
|
||||
import android.content.Context
|
||||
import com.google.firebase.messaging.RemoteMessage
|
||||
|
||||
class BackgroundNotificationHandler(
|
||||
private val context: Context,
|
||||
private val notifInterface: BackgroundNotificationHandlerInterface
|
||||
) {
|
||||
fun handleMessage(remoteMessage: RemoteMessage) {
|
||||
if (ExpoBackgroundNotificationHandlerModule.isForegrounded) {
|
||||
// We'll let expo-notifications handle the notification if the app is foregrounded
|
||||
return
|
||||
}
|
||||
|
||||
if (remoteMessage.data["reason"] == "chat-message") {
|
||||
mutateWithChatMessage(remoteMessage)
|
||||
}
|
||||
|
||||
notifInterface.showMessage(remoteMessage)
|
||||
}
|
||||
|
||||
private fun mutateWithChatMessage(remoteMessage: RemoteMessage) {
|
||||
if (NotificationPrefs(context).getBoolean("playSoundChat")) {
|
||||
// If oreo or higher
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||
remoteMessage.data["channelId"] = "chat-messages"
|
||||
} else {
|
||||
remoteMessage.data["sound"] = "dm.mp3"
|
||||
}
|
||||
} else {
|
||||
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
|
||||
remoteMessage.data["channelId"] = "chat-messages-muted"
|
||||
} else {
|
||||
remoteMessage.data["sound"] = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package expo.modules.backgroundnotificationhandler
|
||||
|
||||
import com.google.firebase.messaging.RemoteMessage
|
||||
|
||||
interface BackgroundNotificationHandlerInterface {
|
||||
fun showMessage(remoteMessage: RemoteMessage)
|
||||
}
|
|
@ -0,0 +1,70 @@
|
|||
package expo.modules.backgroundnotificationhandler
|
||||
|
||||
import expo.modules.kotlin.modules.Module
|
||||
import expo.modules.kotlin.modules.ModuleDefinition
|
||||
|
||||
class ExpoBackgroundNotificationHandlerModule : Module() {
|
||||
companion object {
|
||||
var isForegrounded = false
|
||||
}
|
||||
|
||||
override fun definition() = ModuleDefinition {
|
||||
Name("ExpoBackgroundNotificationHandler")
|
||||
|
||||
OnCreate {
|
||||
NotificationPrefs(appContext.reactContext).initialize()
|
||||
}
|
||||
|
||||
OnActivityEntersForeground {
|
||||
isForegrounded = true
|
||||
}
|
||||
|
||||
OnActivityEntersBackground {
|
||||
isForegrounded = false
|
||||
}
|
||||
|
||||
AsyncFunction("getAllPrefsAsync") {
|
||||
return@AsyncFunction NotificationPrefs(appContext.reactContext).getAllPrefs()
|
||||
}
|
||||
|
||||
AsyncFunction("getBoolAsync") { forKey: String ->
|
||||
return@AsyncFunction NotificationPrefs(appContext.reactContext).getBoolean(forKey)
|
||||
}
|
||||
|
||||
AsyncFunction("getStringAsync") { forKey: String ->
|
||||
return@AsyncFunction NotificationPrefs(appContext.reactContext).getString(forKey)
|
||||
}
|
||||
|
||||
AsyncFunction("getStringArrayAsync") { forKey: String ->
|
||||
return@AsyncFunction NotificationPrefs(appContext.reactContext).getStringArray(forKey)
|
||||
}
|
||||
|
||||
AsyncFunction("setBoolAsync") { forKey: String, value: Boolean ->
|
||||
NotificationPrefs(appContext.reactContext).setBoolean(forKey, value)
|
||||
}
|
||||
|
||||
AsyncFunction("setStringAsync") { forKey: String, value: String ->
|
||||
NotificationPrefs(appContext.reactContext).setString(forKey, value)
|
||||
}
|
||||
|
||||
AsyncFunction("setStringArrayAsync") { forKey: String, value: Array<String> ->
|
||||
NotificationPrefs(appContext.reactContext).setStringArray(forKey, value)
|
||||
}
|
||||
|
||||
AsyncFunction("addToStringArrayAsync") { forKey: String, string: String ->
|
||||
NotificationPrefs(appContext.reactContext).addToStringArray(forKey, string)
|
||||
}
|
||||
|
||||
AsyncFunction("removeFromStringArrayAsync") { forKey: String, string: String ->
|
||||
NotificationPrefs(appContext.reactContext).removeFromStringArray(forKey, string)
|
||||
}
|
||||
|
||||
AsyncFunction("addManyToStringArrayAsync") { forKey: String, strings: Array<String> ->
|
||||
NotificationPrefs(appContext.reactContext).addManyToStringArray(forKey, strings)
|
||||
}
|
||||
|
||||
AsyncFunction("removeManyFromStringArrayAsync") { forKey: String, strings: Array<String> ->
|
||||
NotificationPrefs(appContext.reactContext).removeManyFromStringArray(forKey, strings)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
package expo.modules.backgroundnotificationhandler
|
||||
|
||||
import android.content.Context
|
||||
|
||||
val DEFAULTS = mapOf<String, Any>(
|
||||
"playSoundChat" to true,
|
||||
"playSoundFollow" to false,
|
||||
"playSoundLike" to false,
|
||||
"playSoundMention" to false,
|
||||
"playSoundQuote" to false,
|
||||
"playSoundReply" to false,
|
||||
"playSoundRepost" to false,
|
||||
"mutedThreads" to mapOf<String, List<String>>()
|
||||
)
|
||||
|
||||
class NotificationPrefs (private val context: Context?) {
|
||||
private val prefs = context?.getSharedPreferences("xyz.blueskyweb.app", Context.MODE_PRIVATE)
|
||||
?: throw Error("Context is null")
|
||||
|
||||
fun initialize() {
|
||||
prefs
|
||||
.edit()
|
||||
.apply {
|
||||
DEFAULTS.forEach { (key, value) ->
|
||||
if (prefs.contains(key)) {
|
||||
return@forEach
|
||||
}
|
||||
|
||||
when (value) {
|
||||
is Boolean -> {
|
||||
putBoolean(key, value)
|
||||
}
|
||||
is String -> {
|
||||
putString(key, value)
|
||||
}
|
||||
is Array<*> -> {
|
||||
putStringSet(key, value.map { it.toString() }.toSet())
|
||||
}
|
||||
is Map<*, *> -> {
|
||||
putStringSet(key, value.map { it.toString() }.toSet())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun getAllPrefs(): MutableMap<String, *> {
|
||||
return prefs.all
|
||||
}
|
||||
|
||||
fun getBoolean(key: String): Boolean {
|
||||
return prefs.getBoolean(key, false)
|
||||
}
|
||||
|
||||
fun getString(key: String): String? {
|
||||
return prefs.getString(key, null)
|
||||
}
|
||||
|
||||
fun getStringArray(key: String): Array<String>? {
|
||||
return prefs.getStringSet(key, null)?.toTypedArray()
|
||||
}
|
||||
|
||||
fun setBoolean(key: String, value: Boolean) {
|
||||
prefs
|
||||
.edit()
|
||||
.apply {
|
||||
putBoolean(key, value)
|
||||
}
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun setString(key: String, value: String) {
|
||||
prefs
|
||||
.edit()
|
||||
.apply {
|
||||
putString(key, value)
|
||||
}
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun setStringArray(key: String, value: Array<String>) {
|
||||
prefs
|
||||
.edit()
|
||||
.apply {
|
||||
putStringSet(key, value.toSet())
|
||||
}
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun addToStringArray(key: String, string: String) {
|
||||
prefs
|
||||
.edit()
|
||||
.apply {
|
||||
val set = prefs.getStringSet(key, null)?.toMutableSet() ?: mutableSetOf()
|
||||
set.add(string)
|
||||
putStringSet(key, set)
|
||||
}
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun removeFromStringArray(key: String, string: String) {
|
||||
prefs
|
||||
.edit()
|
||||
.apply {
|
||||
val set = prefs.getStringSet(key, null)?.toMutableSet() ?: mutableSetOf()
|
||||
set.remove(string)
|
||||
putStringSet(key, set)
|
||||
}
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun addManyToStringArray(key: String, strings: Array<String>) {
|
||||
prefs
|
||||
.edit()
|
||||
.apply {
|
||||
val set = prefs.getStringSet(key, null)?.toMutableSet() ?: mutableSetOf()
|
||||
set.addAll(strings.toSet())
|
||||
putStringSet(key, set)
|
||||
}
|
||||
.apply()
|
||||
}
|
||||
|
||||
fun removeManyFromStringArray(key: String, strings: Array<String>) {
|
||||
prefs
|
||||
.edit()
|
||||
.apply {
|
||||
val set = prefs.getStringSet(key, null)?.toMutableSet() ?: mutableSetOf()
|
||||
set.removeAll(strings.toSet())
|
||||
putStringSet(key, set)
|
||||
}
|
||||
.apply()
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue