diff --git a/.gitignore b/.gitignore index 1b85188..6d2f0c6 100644 --- a/.gitignore +++ b/.gitignore @@ -70,3 +70,7 @@ android/keystores/debug.keystore lib/ .yarn/install-state.gz example/.yarn/install-state.gz + +google-services.json + + diff --git a/android/build.gradle b/android/build.gradle index 0cfaf1e..7fb49f0 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -6,7 +6,7 @@ buildscript { google() mavenCentral() maven { - url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' + url 'https://central.sonatype.com/repository/maven-snapshots/' } } @@ -75,8 +75,13 @@ android { } compileOptions { - sourceCompatibility JavaVersion.VERSION_1_8 - targetCompatibility JavaVersion.VERSION_1_8 + sourceCompatibility JavaVersion.VERSION_17 + targetCompatibility JavaVersion.VERSION_17 + } + + kotlinOptions { + jvmTarget = "17" + freeCompilerArgs += ["-Xskip-metadata-version-check"] } } @@ -85,7 +90,7 @@ repositories { mavenCentral() google() maven { - url 'https://s01.oss.sonatype.org/content/repositories/snapshots/' + url 'https://central.sonatype.com/repository/maven-snapshots/' } } @@ -97,7 +102,7 @@ dependencies { //noinspection GradleDynamicVersion implementation "com.facebook.react:react-native:+" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - implementation "com.movableink.sdk:inked:2.2.0" + implementation "com.movableink.sdk:inked:2.3.0-SNAPSHOT" implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.7.0" } diff --git a/android/gradle.properties b/android/gradle.properties index 14460da..e48164e 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,4 +1,4 @@ -RNMovableInk_kotlinVersion=1.8.0 +RNMovableInk_kotlinVersion=2.2.0 RNMovableInk_minSdkVersion=24 RNMovableInk_targetSdkVersion=33 RNMovableInk_compileSdkVersion=33 diff --git a/android/src/main/java/com/rnmovableink/RNMovableInkModule.kt b/android/src/main/java/com/rnmovableink/RNMovableInkModule.kt index ed4d899..555a146 100644 --- a/android/src/main/java/com/rnmovableink/RNMovableInkModule.kt +++ b/android/src/main/java/com/rnmovableink/RNMovableInkModule.kt @@ -85,37 +85,43 @@ class RNMovableInkModule(reactContext: ReactApplicationContext) : } } -@ReactMethod -fun showInAppMessage(url: String, callback: Callback) { - val activity = currentActivity - if (activity is androidx.lifecycle.LifecycleOwner) { - activity.lifecycleScope.launch { - try { - MIClient.showInAppBrowser( - activity, - url, - listener = object : MovableInAppClient.OnUrlLoadingListener { - override fun onButtonClicked(value: String) { - activity.runOnUiThread { - callback.invoke(value) - } - } - } - ) - } catch (e: Exception) { - activity.runOnUiThread { - callback.invoke("Error: ${e.message}") - } - } - } - } -} + @ReactMethod + fun showInAppMessage(url: String, callback: Callback) { + val activity = currentActivity + if (activity is androidx.lifecycle.LifecycleOwner) { + activity.lifecycleScope.launch { + try { + MIClient.showInAppBrowser( + activity, + url, + listener = object : MovableInAppClient.OnUrlLoadingListener { + override fun onButtonClicked(value: String) { + activity.runOnUiThread { + callback.invoke(value) + } + } + } + ) + } catch (e: Exception) { + activity.runOnUiThread { + callback.invoke("Error: ${e.message}") + } + } + } + } + } @ReactMethod fun setValidPasteboardValues(values: ReadableArray) { MIClient.validPasteboardValues(values.toStringList()) } + @ReactMethod + fun handlePushNotificationOpenedWithContent(properties: ReadableMap) { + val map: Map = properties.toHashMap().mapValues { it.value.toString() } + MIClient.handlePushNotificationOpened(map) + } + fun ReadableArray.toStringList(): List { val stringList = mutableListOf() for (i in 0 until size()) { diff --git a/example/android/app/build.gradle b/example/android/app/build.gradle index b85a3d2..b749a1b 100644 --- a/example/android/app/build.gradle +++ b/example/android/app/build.gradle @@ -1,6 +1,7 @@ apply plugin: "com.android.application" apply plugin: "org.jetbrains.kotlin.android" apply plugin: "com.facebook.react" +apply plugin: "com.google.gms.google-services" // import com.android.build.OutputFile @@ -53,6 +54,14 @@ react { // hermesFlags = ["-O", "-output-source-map"] } +repositories { + google() + mavenCentral() + maven { + url 'https://central.sonatype.com/repository/maven-snapshots/' + } +} + /** * Set this to true to create four separate APKs instead of one, * one for each native architecture. This is useful if you don't @@ -94,7 +103,7 @@ android { compileSdkVersion rootProject.ext.compileSdkVersion - namespace "com.movableink.app" + namespace "com.movableink" defaultConfig { applicationId "com.movableink.app" minSdkVersion rootProject.ext.minSdkVersion @@ -147,6 +156,17 @@ android { // } // } + + kotlinOptions { + jvmTarget = "17" + freeCompilerArgs += ["-Xskip-metadata-version-check"] + } + + packagingOptions { + resources { + excludes += ['META-INF/versions/9/OSGI-INF/MANIFEST.MF'] + } + } } dependencies { @@ -154,6 +174,11 @@ dependencies { implementation("com.facebook.react:react-android") implementation("com.facebook.react:flipper-integration") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.0.0") + + // Firebase for push notifications + implementation platform('com.google.firebase:firebase-bom:32.7.0') + implementation 'com.google.firebase:firebase-messaging' + implementation project(':react-native-notifications') // debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") // debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { diff --git a/example/android/app/src/main/AndroidManifest.xml b/example/android/app/src/main/AndroidManifest.xml index d928d95..ab07093 100644 --- a/example/android/app/src/main/AndroidManifest.xml +++ b/example/android/app/src/main/AndroidManifest.xml @@ -3,14 +3,14 @@ #import #import +#import "RNNotifications.h" @implementation AppDelegate @@ -13,6 +14,8 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( // They will be passed down to the ViewController used by React Native. self.initialProps = @{}; + [RNNotifications startMonitorNotifications]; + return [super application:application didFinishLaunchingWithOptions:launchOptions]; } @@ -27,6 +30,19 @@ - (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserAct restorationHandler:restorationHandler]; } +- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + NSLog(@"Device Token: %@", deviceToken); + [RNNotifications didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; +} + +- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { + [RNNotifications didFailToRegisterForRemoteNotificationsWithError:error]; +} + +- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult result))completionHandler { + [RNNotifications didReceiveBackgroundNotification:userInfo withCompletionHandler:completionHandler]; +} + - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge { #if DEBUG diff --git a/example/ios/MovableInkExample/MovableInkExample.entitlements b/example/ios/MovableInkExample/MovableInkExample.entitlements index 826b6f6..568df8c 100644 --- a/example/ios/MovableInkExample/MovableInkExample.entitlements +++ b/example/ios/MovableInkExample/MovableInkExample.entitlements @@ -2,6 +2,8 @@ + aps-environment + development com.apple.developer.associated-domains applinks:mi-mobile-sandbox.mimobile.xyz diff --git a/example/package.json b/example/package.json index 882e400..bfe1405 100644 --- a/example/package.json +++ b/example/package.json @@ -11,6 +11,7 @@ "dependencies": { "react": "18.2.0", "react-native": "0.73.5", + "react-native-notifications": "^5.2.2", "react-native-svg": "^15.1.0", "react-native-svg-transformer": "^1.3.0" }, diff --git a/example/src/App.tsx b/example/src/App.tsx index 84477a3..45fef50 100644 --- a/example/src/App.tsx +++ b/example/src/App.tsx @@ -1,9 +1,11 @@ import * as React from 'react'; import { StyleSheet, View, Text, Linking, Button } from 'react-native'; import RNMovableInk, { Currency } from '@movable/react-native-sdk'; +import { Notifications, type Registered } from 'react-native-notifications'; export default function App() { const [link, setLink] = React.useState(); + const [token, setToken] = React.useState(); React.useEffect(() => { // If using Deferred Deep Linking, make sure to enable the app install event @@ -13,6 +15,55 @@ export default function App() { RNMovableInk.start(); RNMovableInk.setMIU('00000000-00000000-00000000-00000000'); + // Register for remote notifications + Notifications.registerRemoteNotifications(); + + // Handle device token updates + const registerForNotificationsSubscription = + Notifications.events().registerRemoteNotificationsRegistered( + (event: Registered) => { + console.log(event.deviceToken); + setToken(event.deviceToken); + } + ); + + // Handle notifications received in foreground + const foregroundSubscription = + Notifications.events().registerNotificationReceivedForeground( + (_notification, completion) => { + completion({ alert: true, sound: true, badge: false }); + } + ); + + // Handle notification taps + const openedSubscription = + Notifications.events().registerNotificationOpened( + (notification, completion) => { + console.log('Notification opened:'); + console.log(JSON.stringify(notification.payload)); + + RNMovableInk.handlePushNotificationOpenedWithContent( + notification.payload + ); + + completion(); + } + ); + + Notifications.getInitialNotification() + .then((notification) => { + if (notification) { + console.log('App opened from notification:'); + console.log(`${JSON.stringify(notification, null, 2)}`); + + // Handle the notification with MovableInk + RNMovableInk.handlePushNotificationOpenedWithContent( + notification.payload + ); + } + }) + .catch((err) => console.error('getInitialNotification() failed', err)); + // Get the deep link used to open the app const getInitialURL = async () => { const universalLink = await Linking.getInitialURL(); @@ -42,6 +93,9 @@ export default function App() { return () => { urlListener.remove(); + registerForNotificationsSubscription.remove(); + foregroundSubscription?.remove(); + openedSubscription?.remove(); }; }, []); @@ -56,6 +110,10 @@ export default function App() { return ( Resolved Link: {link} + + Device Token: {'\n'} + {token} +