-
Notifications
You must be signed in to change notification settings - Fork 1.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
Showing
38 changed files
with
1,297 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Binary file not shown.
Binary file not shown.
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>com.apple.security.application-groups</key> | ||
<array> | ||
<string>group.app.bsky</string> | ||
</array> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
<?xml version="1.0" encoding="UTF-8"?> | ||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | ||
<plist version="1.0"> | ||
<dict> | ||
<key>NSExtension</key> | ||
<dict> | ||
<key>NSExtensionPointIdentifier</key> | ||
<string>com.apple.usernotifications.service</string> | ||
<key>NSExtensionPrincipalClass</key> | ||
<string>$(PRODUCT_MODULE_NAME).NotificationService</string> | ||
</dict> | ||
<key>MainAppScheme</key> | ||
<string>bluesky</string> | ||
<key>CFBundleName</key> | ||
<string>$(PRODUCT_NAME)</string> | ||
<key>CFBundleDisplayName</key> | ||
<string>Bluesky Notifications</string> | ||
<key>CFBundleIdentifier</key> | ||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string> | ||
<key>CFBundleVersion</key> | ||
<string>$(CURRENT_PROJECT_VERSION)</string> | ||
<key>CFBundleExecutable</key> | ||
<string>$(EXECUTABLE_NAME)</string> | ||
<key>CFBundlePackageType</key> | ||
<string>$(PRODUCT_BUNDLE_PACKAGE_TYPE)</string> | ||
<key>CFBundleShortVersionString</key> | ||
<string>$(MARKETING_VERSION)</string> | ||
</dict> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import UserNotifications | ||
|
||
let APP_GROUP = "group.app.bsky" | ||
|
||
class NotificationService: UNNotificationServiceExtension { | ||
var prefs = UserDefaults(suiteName: APP_GROUP) | ||
|
||
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) { | ||
guard var bestAttempt = createCopy(request.content), | ||
let reason = request.content.userInfo["reason"] as? String | ||
else { | ||
contentHandler(request.content) | ||
return | ||
} | ||
|
||
if reason == "chat-message" { | ||
mutateWithChatMessage(bestAttempt) | ||
} | ||
|
||
// The badge should always be incremented when in the background | ||
mutateWithBadge(bestAttempt) | ||
|
||
contentHandler(bestAttempt) | ||
} | ||
|
||
override func serviceExtensionTimeWillExpire() { | ||
// If for some reason the alloted time expires, we don't actually want to display a notification | ||
} | ||
|
||
func createCopy(_ content: UNNotificationContent) -> UNMutableNotificationContent? { | ||
return content.mutableCopy() as? UNMutableNotificationContent | ||
} | ||
|
||
func mutateWithBadge(_ content: UNMutableNotificationContent) { | ||
content.badge = 1 | ||
} | ||
|
||
func mutateWithChatMessage(_ content: UNMutableNotificationContent) { | ||
if self.prefs?.bool(forKey: "playSoundChat") == true { | ||
mutateWithDmSound(content) | ||
} | ||
} | ||
|
||
func mutateWithDefaultSound(_ content: UNMutableNotificationContent) { | ||
content.sound = UNNotificationSound.default | ||
} | ||
|
||
func mutateWithDmSound(_ content: UNMutableNotificationContent) { | ||
content.sound = UNNotificationSound(named: UNNotificationSoundName(rawValue: "dm.aiff")) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -38,4 +38,4 @@ | |
<key>CFBundleShortVersionString</key> | ||
<string>$(MARKETING_VERSION)</string> | ||
</dict> | ||
</plist> | ||
</plist> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,4 +7,4 @@ | |
<string>group.app.bsky</string> | ||
</array> | ||
</dict> | ||
</plist> | ||
</plist> |
93 changes: 93 additions & 0 deletions
93
modules/expo-background-notification-handler/android/build.gradle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
apply plugin: 'com.android.library' | ||
apply plugin: 'kotlin-android' | ||
apply plugin: 'maven-publish' | ||
|
||
group = 'expo.modules.backgroundnotificationhandler' | ||
version = '0.5.0' | ||
|
||
buildscript { | ||
def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle") | ||
if (expoModulesCorePlugin.exists()) { | ||
apply from: expoModulesCorePlugin | ||
applyKotlinExpoModulesCorePlugin() | ||
} | ||
|
||
// Simple helper that allows the root project to override versions declared by this library. | ||
ext.safeExtGet = { prop, fallback -> | ||
rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback | ||
} | ||
|
||
// Ensures backward compatibility | ||
ext.getKotlinVersion = { | ||
if (ext.has("kotlinVersion")) { | ||
ext.kotlinVersion() | ||
} else { | ||
ext.safeExtGet("kotlinVersion", "1.8.10") | ||
} | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
dependencies { | ||
classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getKotlinVersion()}") | ||
} | ||
} | ||
|
||
afterEvaluate { | ||
publishing { | ||
publications { | ||
release(MavenPublication) { | ||
from components.release | ||
} | ||
} | ||
repositories { | ||
maven { | ||
url = mavenLocal().url | ||
} | ||
} | ||
} | ||
} | ||
|
||
android { | ||
compileSdkVersion safeExtGet("compileSdkVersion", 33) | ||
|
||
def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION | ||
if (agpVersion.tokenize('.')[0].toInteger() < 8) { | ||
compileOptions { | ||
sourceCompatibility JavaVersion.VERSION_11 | ||
targetCompatibility JavaVersion.VERSION_11 | ||
} | ||
|
||
kotlinOptions { | ||
jvmTarget = JavaVersion.VERSION_11.majorVersion | ||
} | ||
} | ||
|
||
namespace "expo.modules.backgroundnotificationhandler" | ||
defaultConfig { | ||
minSdkVersion safeExtGet("minSdkVersion", 21) | ||
targetSdkVersion safeExtGet("targetSdkVersion", 34) | ||
versionCode 1 | ||
versionName "0.5.0" | ||
} | ||
lintOptions { | ||
abortOnError false | ||
} | ||
publishing { | ||
singleVariant("release") { | ||
withSourcesJar() | ||
} | ||
} | ||
} | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
|
||
dependencies { | ||
implementation project(':expo-modules-core') | ||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}" | ||
implementation 'com.google.firebase:firebase-messaging-ktx:24.0.0' | ||
} |
2 changes: 2 additions & 0 deletions
2
modules/expo-background-notification-handler/android/src/main/AndroidManifest.xml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
<manifest> | ||
</manifest> |
39 changes: 39 additions & 0 deletions
39
...src/main/java/expo/modules/backgroundnotificationhandler/BackgroundNotificationHandler.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 | ||
} | ||
} | ||
} | ||
} |
7 changes: 7 additions & 0 deletions
7
...java/expo/modules/backgroundnotificationhandler/BackgroundNotificationHandlerInterface.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package expo.modules.backgroundnotificationhandler | ||
|
||
import com.google.firebase.messaging.RemoteMessage | ||
|
||
interface BackgroundNotificationHandlerInterface { | ||
fun showMessage(remoteMessage: RemoteMessage) | ||
} |
70 changes: 70 additions & 0 deletions
70
...ava/expo/modules/backgroundnotificationhandler/ExpoBackgroundNotificationHandlerModule.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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) | ||
} | ||
} | ||
} |
Oops, something went wrong.