diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 000000000..606e2f29c --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +custom: ['https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B33LA4QVUGVBW&source=url'] diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..4a63056b2 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,34 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: '' +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Environment (please complete the following information):** + - Developer OS and version: [e.g. MacOS Mojave] + - Mobile OS and version: [e.g. iOS 13.0.1] + - Cordova CLI version (cordova --version): [e.g. 9.0.0 ] + - Cocoapods version (pod --version): [e.g. 9.0.0] + - Xcode version (xcodebuild -version): [e.g. 11.1.0] + +**Package.json** +Please add your package.json, as to provide the dependencies version. diff --git a/.gitignore b/.gitignore index 9d9f67288..562f376ce 100644 --- a/.gitignore +++ b/.gitignore @@ -47,3 +47,4 @@ Temporary Items .apdisk npm-debug.log +node_modules diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 000000000..9a8fe11a8 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": false, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100, + "tabWidth": 4, +} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..999c287de --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,311 @@ +# Changelog + +## Version 7 + +### Version 7.8.0 (03/02/2020) + +- IOS_FIREBASE_MESSAGING_VERSION upgraded to 7.4.0; +- FCM.hasPermission now supports Android; +- On iOS, getToken will now wait until fcm token is defined to return it; +- FCM.getInitialPushPayload now uses UTF8 instead of ISOLatin; + +### Version 7.7.0 (11/12/2020) + +Downgraded Cordova and Cordova-Android minimal required versions. + +### Version 7.6.1 (11/12/2020) + +Fixed auto install of ionic-specific dependencies on Windows + +### Version 7.6.0 (08/11/2020) + +Added IOS_FIREBASE_MESSAGING_VERSION plugin variable to force a fixed Firebase/Messaging pod version. + +### Version 7.5.0 (08/11/2020) + +For pure Cordova projects, ionic dependencies are skipped for "ionic", "ionic/ngx" and "ionic/v4". + +### Version 7.4.0 (01/11/2020) + +Upgraded Android and Node depenencies. + +### Version 7.3.1 (06/09/2020) + +Android: Avoided setting initialPushPayload from a non-tapped notification. + +### Version 7.3.0 (30/08/2020) + +Removed optional Firebase/Analytics iOS dependency. + +### Version 7.2.0 (23/08/2020) + +Added deleteInstanceId to allow user's "resetting" Firebase session (https://firebase.google.com/support/privacy/manage-iids#delete_an_instance_id). + +### Version 7.1.1 (16/08/2020) + +iOS: Values sent as notification on push payload should never overwrite values sent as data. + +Android: tapping action small refactor. + +Ionic: Removed tslib dependency due to reported incompatibility. + +### Version 7.1.0 (16/08/2020) + +For iOS: Added `title`, `subtitle`, `body` and `badge` to the data given to JS. + +This data is coming from notification part of the push payload, instead of only from data. + +Android has been responding this way for a long time with `title` and `body`. + +### Version 7.0.10 (16/08/2020) + +Defined minimal version of "cordova" package supported as version 9. Due to issues of supporting lower versions and "cordova-ios@5+" without deprecated configuration. + +Removed explicit definition of "Firebase/Analytics" and "Firebase/Messaging" due to lack of matching “>=” definitions between plugins. + +### Version 7.0.9 (21/07/2020) + +getInitialPushPayload: Conversion of NSData* into NSDictonary* fix -- Thanks to @medeirosrafael for debugging and fixing it! + +### Version 7.0.8 (20/07/2020) + +Avoided execution of install_ionic_dependencies.bat after the app is installed; + +Removed old framework dependencies -- Thanks for @QuentinFarizon, for pointing it out! + +### Version 7.0.7 (17/07/2020) + +Removed auto-cdnfy due to service issues; + +Set AndroidXEnabled for cordova-android-9.0.0 (https://cordova.apache.org/announcements/2020/06/29/cordova-android-9.0.0.html). + +### Version 7.0.6 (14/07/2020) + +Simplified ionic/ngx/FCM.d.ts imports + +### Version 7.0.5 (13/07/2020) + +Renamed extension scripts/install_ionic_dependencies.sh to .bat, to have it running on windows. + +### Version 7.0.4 (13/07/2020) + +Simplified ionic/FCM.js and ionic/ngx/FCM.js files to ease building with them. + +Improved scripts/install_ionic_dependencies.sh windows support. + +### Version 7.0.3 (07/07/2020) + +Simplified ionic/v4/FCM.js file by setting the FCM function in the global context. + +### Version 7.0.2 (01/07/2020) + +Simplified .d.ts files by removing the new "type" from imports and exports. + +### Version 7.0.1 (28/06/2020) + +Replaced native to JS context messaging, from JS injection to event subscription. + +### Version 7.0.0 (27/06/2020) + +Breaking update released. Please pay atention to the changes in plugin preferences and API methods. + +## Version 6 + +### Version 6.4.0 (21/05/2020) + +The `FCMPlugin.requestPushPermissionIOS` function now, not only triggers the request alert, but also returns, as boolean, if the permission was given. + +```javascript +FCMPlugin.requestPushPermissionIOS( + function(wasPermissionGiven) { + console.info("Was permission given: "+wasPermissionGiven); + }, + function(error) { + console.error(error); + }, + ios9Support: { + timeout: 10, // How long it will wait for a decision from the user before returning `false` + interval: 0.3 // How long between each permission verification + } +); +``` + +Note: +On iOS 9, there is no way to know if the user has denied the permissions or he has not yet decided. +For this reason, specifically for iOS 9, after presenting the alert, a timed loop waits until it knows that the user has either given the permissions or that the time has expired. +On iOS 10+, the return is given as soon as the user has selected, ignoring these options. + +### Version 6.3.0 (27/04/2020) + +FCMPlugin.createNotificationChannelAndroid improved, now accepting three other options: "sound", "lights" and "vibration", like in: +```javascript +FCMPlugin.createNotificationChannelAndroid({ + id: "urgent_alert", // required + name: "Urgent Alert", // required + description: "Very urgent message alert", + importance: "high", // https://developer.android.com/guide/topics/ui/notifiers/notifications#importance + visibility: "public", // https://developer.android.com/training/notify-user/build-notification#lockscreenNotification + sound: "alert_sound", // In the "alert_sound" example, the file should located as resources/raw/alert_sound.mp3 + lights: true, // enable lights for notifications + vibration: true // enable vibration for notifications +}); +``` + +### Version 6.2.0 (26/04/2020) + +IOS 9 support reintroduced. + +### Version 6.1.0 (24/04/2020) + +For Android, some notification properties are only defined programmatically, one of those is channel. +Channel can define the default behavior for notifications on Android 8.0+. +This feature was meant to bring the channel-only configurations importance and visibility: + +```javascript +FCMPlugin.createNotificationChannelAndroid({ + id: "urgent_alert", // required + name: "Urgent Alert", // required + description: "Very urgent message alert", + importance: "high", // https://developer.android.com/guide/topics/ui/notifiers/notifications#importance + visibility: "public", // https://developer.android.com/training/notify-user/build-notification#lockscreenNotification +}); +``` + +:warning: Once a channel is created, it stays unchangeable until the user uninstalls the app. + +To have a notification to use the channel, you have to add to the push notification payload the key `android_channel_id` with the id given to `createNotificationChannelAndroid` (https://firebase.google.com/docs/cloud-messaging/http-server-ref#notification-payload-support) + +### Version 6.0.1 (20/04/2020) + +As a hotfix to avoid incompatibility with cordova-plugin-ionic-webview, the the changes requested for cordova support (https://cordova.apache.org/howto/2020/03/18/wkwebviewonly) will not be automatic applied. + +### Version 6.0.0 (18/04/2020) + +On iOS, first run doesn't automatically request Push permission. + +The permission, as it is still required, may now be requested from javascript at any moment by executing: +```javascript +//FCMPlugin.requestPushPermissionIOS( successCallback(), errorCallback(err) ); +FCMPlugin.requestPushPermissionIOS(); +``` + +## Version 5 +Minor changes omitted. + +### Version 5.1.0 (18/04/2020) + +Replaced `UIWebView` with `WKWebView`, as required by Apple (https://developer.apple.com/documentation/uikit/uiwebview). + +For a smooth upgrade, the changes requested for cordova support (https://cordova.apache.org/howto/2020/03/18/wkwebviewonly) are applied automatically. + +### Version 5.0.0 (16/04/2020) + +For both platforms: +- Not only copies, from application root, the Google Services configuration files on build, but also on install; +- `onFirebaseDataNotificationIOS` removed, as relied on upstream socket connection, which will is deprecated and will be removed in Firebase 7 (https://firebase.google.com/support/release-notes/ios#fcm, announced in February 25, 2020). + +For iOS: +- On install "Remote Notification" is set as a "Background Mode" capacity automatically; +- Firebase now is handled manually, due to complications of auto-swizzling; +- Firebase dependencies are now set to 6.21.0; +- Delayed Firebase registration, by 300ms, to avoid deadlock issue with Watchdog; +- Removed support for iOS 9. + +For Android: +- Demonstrative code, for custom notifications, was removed and its imports of androidx classes to improve compatibility with older cordova plugins. + +## Version 4 +Minor changes omitted. + +### Version 4.6.0 (04/04/2020) + +For the IOS, if app is on the foreground and the app receives a `data` push notification, the data can be retrieved by setting the callback to the new method: `FCMPlugin.onFirebaseDataNotificationIOS` [Deprecated]. + +```javascript +FCMPlugin.onFirebaseDataNotificationIOS( + function(payload) { + console.info("Message id: "+payload.messageID) + console.info("Data parameters: "+payload.appData) + } +); +``` + +This method is specifically implemented on IOS due to specific payload format ([src/FCMPlugin.d.ts](https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated/blob/master/src/FCMPlugin.d.ts)). + + +### Version 4.5.1 (30/03/2020) + +Due to a bug introduced in v4.4.3, the file `platforms/android/app/src/main/res/values/strings.xml` had two tags included on install, tags which, on build, are also included by cordova-android@8.x.x. Hence failing the build process. + +To apply the fix, install the new version and remove these two tags from your values/strings.xml: +* `...` +* `...` + +### Version 4.2.0 (24/02/2020) + +`ANDROID_DEFAULT_NOTIFICATION_ICON` included as a variable. + +To define the default icon for notifications (`com.google.firebase.messaging.default_notification_icon`), install this plugin like: +```bash +cordova plugin add cordova-plugin-fcm-with-dependecy-updated --variable ANDROID_DEFAULT_NOTIFICATION_ICON="@mipmap/notification_icon" +``` + +### Version 4.1.0 (26/10/2019) + +Older notifications can be cleared from notification center. +Works on both IOS and Android. + +```javascript +//FCMPlugin.clearAllNotifications( successCallback(msg), errorCallback(err) ); +FCMPlugin.clearAllNotifications(); +``` + +### Version 4.0.0 (12/10/2019) +The old `FCMPlugin.getToken` is focused on retrieving the FCM Token. +For the IOS, APNS token can now be retrieved by the new method: + +```javascript +FCMPlugin.getAPNSToken( + function(token) { + console.info("Retrieved token: "+token) + }, + function(error) { + console.error(error); + } +); +``` + +On android, it will always return `null`. + +The APNS token, once given, should not change for the same user (as commented on in https://stackoverflow.com/questions/6652242/does-the-apns-device-token-ever-change-once-created). + +Although, contrary to APNS, the FCM tokens do expire, and for this reason, `FCMPlugin.onTokenRefresh` will be called with the new one FCM token. + +## Version 3 +Minor changes omitted. + +### Version 3.2.0 (16/09/2019) + +#### Checking for permissions +Useful for IOS. On android, it will always return `true`. + +```javascript +FCMPlugin.hasPermission(function(doesIt){ + // doesIt === true => yes, push was allowed + // doesIt === false => nope, push will not be available + // doesIt === null => still not answered, recommended checking again later + if(doesIt) { + haveFun(); + } +}); +``` + +## Version 2 +Minor changes omitted. + +### Version 2.1.2 (03/06/2017) +- Tested on Android and iOS using Cordova cli 6.4.0, Cordova android 6.0.0 and Cordova ios 4.3.1 +- Available sdk functions: onTokenRefresh, getToken, subscribeToTopic, unsubscribeFromTopic and onNotification +- 'google-services.json' and 'GoogleService-Info.plist' are added automatically from Cordova project root to platform folders +- Added data payload parameter to check whether the user tapped on the notification or was received while in foreground. +- **Free testing server available for free! https://cordova-plugin-fcm.appspot.com** diff --git a/LICENSE b/LICENSE new file mode 100644 index 000000000..9cf106272 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index fb325d53d..48176829f 100644 --- a/README.md +++ b/README.md @@ -1,156 +1,373 @@ +⚠️ After 3 years of developing and maintaining this plugin, due to the lack of sponsorship from the community and more promising opportunities, this project is now frozen. Anyone can clone and continue the good work. ⚠️ + # Google Firebase Cloud Messaging Cordova Push Plugin > Extremely easy plug&play push notification plugin for Cordova applications with Google Firebase FCM. ->[![paypal](https://www.paypalobjects.com/en_US/i/btn/btn_donateCC_LG.gif)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=VF654BMGUPQTJ) +[![Donate](https://img.shields.io/badge/Donate-Paypal-0a83fc.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=B33LA4QVUGVBW&source=url) +[![npm downloads](https://img.shields.io/npm/dt/cordova-plugin-fcm-with-dependecy-updated.svg)](https://www.npmjs.com/package/cordova-plugin-fcm-with-dependecy-updated) +[![npm per month](https://img.shields.io/npm/dm/cordova-plugin-fcm-with-dependecy-updated.svg)](https://www.npmjs.com/package/cordova-plugin-fcm-with-dependecy-updated) +[![npm version](https://img.shields.io/npm/v/cordova-plugin-fcm-with-dependecy-updated.svg)](https://www.npmjs.com/package/cordova-plugin-fcm-with-dependecy-updated) +[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) +[![GitHub issues](https://img.shields.io/github/issues/andrehtissot/cordova-plugin-fcm-with-dependecy-updated.svg)](https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated/issues) +[![GitHub forks](https://img.shields.io/github/forks/andrehtissot/cordova-plugin-fcm-with-dependecy-updated.svg)](https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated/network) +[![GitHub stars](https://img.shields.io/github/stars/andrehtissot/cordova-plugin-fcm-with-dependecy-updated.svg)](https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated/stargazers) +[![Known Vulnerabilities](https://snyk.io/test/github/andrehtissot/cordova-plugin-fcm-with-dependecy-updated/badge.svg?targetFile=package.json)](https://snyk.io/test/github/andrehtissot/cordova-plugin-fcm-with-dependecy-updated?targetFile=package.json) +[![DeepScan grade](https://deepscan.io/api/teams/3417/projects/5068/branches/39495/badge/grade.svg)](https://deepscan.io/dashboard#view=project&tid=3417&pid=5068&bid=39495) -#### Version 2.1.2 (03/06/2017) -- Tested on Android and iOS using Cordova cli 6.4.0, Cordova android 6.0.0 and Cordova ios 4.3.1 -- Available sdk functions: onTokenRefresh, getToken, subscribeToTopic, unsubscribeFromTopic and onNotification -- 'google-services.json' and 'GoogleService-Info.plist' are added automatically from Cordova project root to platform folders -- Added data payload parameter to check whether the user tapped on the notification or was received while in foreground. -- **Free testing server available for free! https://cordova-plugin-fcm.appspot.com** +[How it works](#how-it-works) | [Installation](#installation) | [Push Payload Configuration](#push-payload-configuration) | [Features](#features) | [Example Apps](#example-apps) | [Companion Plugins](#companion-plugins) | [Changelog](https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated/blob/master/CHANGELOG.md) | [Authorship](#authorship) -## Installation -Make sure you have ‘google-services.json’ for Android or ‘GoogleService-Info.plist’ for iOS in your Cordova project root folder. You don´t need to configure anything else in order to have push notification working for both platforms, everything is magic. -```Bash -cordova plugin add cordova-plugin-fcm +## How it works +Send a push notification to a single device or topic. -``` +- Application is in foreground: + + - The notification data is received in the JavaScript callback without notification bar message (this is the normal behavior of mobile push notifications). + - For Android, to show the notification received on the foreground, it's recommended to use [cordova-plugin-local-notification](https://github.com/katzer/cordova-plugin-local-notifications#readme) as it provides many presentation and interaction features. -#### Firebase configuration files -Get the needed configuration files for Android or iOS from the Firebase Console (see docs: https://firebase.google.com/docs/). +- Application is in background or closed: -#### Android compilation details -Put the downloaded file 'google-services.json' in the Cordova project root folder. + - The device displays the notification message in the device notification bar. + - If the user taps the notification, the application comes to foreground and the notification data is received in the JavaScript callback. + - If the user does not tap the notification but opens the application, nothing happens until the notification is tapped. + +### Push Notifications on iOS -You will need to ensure that you have installed the appropiate Android SDK libraries. +On Android, push notifications don't require any special permission and can be tested from emulators freely. +Unfortunately, Apple is not as nice to work with, requiring: +- The running device to be a real device, no simulators allowed; +- Application has require the `UIBackgroundModes=[remote-notification]` permission (automatically configured by this plugin); +- The user running the application has to manually allow the application to receive push notifications; +- The application must be build with credentials created from a paid account (or team) that is allowed to receive push notifications; +- The build installed has to have come from either Apple Store or TestFlight; Or, be build with a special certificate (https://customersupport.doubledutch.me/hc/en-us/articles/229495568-iOS-How-to-Create-a-Push-Notification-Certificate) -:warning: For Android >5.0 status bar icon, you must include transparent solid color icon with name 'fcm_push_icon.png' in the 'res' folder in the same way you add the other application icons. -If you do not set this resource, then the SDK will use the default icon for your app which may not meet the standards for Android >5.0. +## Installation -#### iOS compilation details -Put the downloaded file 'GoogleService-Info.plist' in the Cordova project root folder. +Make sure you have ‘google-services.json’ for Android and/or ‘GoogleService-Info.plist’ for iOS in your Cordova project root folder. -## Usage +#### Preferences -:warning: It's highly recommended to use REST API to send push notifications because Firebase console does not have all the functionalities. **Pay attention to the payload example in order to use the plugin properly**. -You can also test your notifications with the free testing server: https://cordova-plugin-fcm.appspot.com +|Preference|Default Value|Description| +|---|---|---| +|ANDROID_DEFAULT_NOTIFICATION_ICON|@mipmap/ic_launcher|Default notification icon.| +|ANDROID_FCM_VERSION|21.0.0|Native Firebase Message SDK version.
:warning: Replaced by BoM versioning on Gradle >= 3.4.| +|ANDROID_FIREBASE_BOM_VERSION|26.0.0|[Firebase BoM](https://firebase.google.com/docs/android/learn-more#bom) version.| +|ANDROID_GOOGLE_SERVICES_VERSION|4.3.4|Native Google Services SDK version.| +|ANDROID_GRADLE_TOOLS_VERSION|4.1.0|Gradle tools version.| +|IOS_FIREBASE_MESSAGING_VERSION|~> 7.4.0|Native Firebase Message SDK version| -#### Receiving Token Refresh +#### Cordova -```javascript -//FCMPlugin.onTokenRefresh( onTokenRefreshCallback(token) ); -//Note that this callback will be fired everytime a new token is generated, including the first time. -FCMPlugin.onTokenRefresh(function(token){ - alert( token ); -}); +Default preferences: + +```sh +npm install -g cordova@latest # Version 9 or higher required +npm uninstall @ionic-native/fcm # Ionic support is included and conflicts with @ionic-native's implementation. +cordova plugin add cordova-plugin-fcm-with-dependecy-updated ``` -#### Get token +Complete: -```javascript -//FCMPlugin.getToken( successCallback(token), errorCallback(err) ); -//Keep in mind the function will return null if the token has not been established yet. -FCMPlugin.getToken(function(token){ - alert(token); -}); +```sh +npm install -g cordova@latest # Version 9 or higher required +npm uninstall @ionic-native/fcm # Ionic support is included and conflicts with @ionic-native's implementation. +cordova plugin add cordova-plugin-fcm-with-dependecy-updated \ + --variable ANDROID_DEFAULT_NOTIFICATION_ICON="@mipmap/ic_launcher" \ + --variable ANDROID_FIREBASE_BOM_VERSION="26.0.0" \ + --variable ANDROID_GOOGLE_SERVICES_VERSION="4.3.4" \ + --variable ANDROID_GRADLE_TOOLS_VERSION="4.1.0" \ + --variable IOS_FIREBASE_MESSAGING_VERSION="~> 7.4.0" ``` -#### Subscribe to topic +#### Ionic + +Default preferences: -```javascript -//FCMPlugin.subscribeToTopic( topic, successCallback(msg), errorCallback(err) ); -//All devices are subscribed automatically to 'all' and 'ios' or 'android' topic respectively. -//Must match the following regular expression: "[a-zA-Z0-9-_.~%]{1,900}". -FCMPlugin.subscribeToTopic('topicExample'); +```sh +npm install -g cordova@latest # Version 9 or higher required +npm uninstall @ionic-native/fcm # Ionic support is included and conflicts with @ionic-native's implementation. +ionic cordova plugin add cordova-plugin-fcm-with-dependecy-updated ``` -#### Unsubscribe from topic +Complete: -```javascript -//FCMPlugin.unsubscribeFromTopic( topic, successCallback(msg), errorCallback(err) ); -FCMPlugin.unsubscribeFromTopic('topicExample'); +```sh +npm install -g cordova@latest # Version 9 or higher required +npm uninstall @ionic-native/fcm # Ionic support is included and conflicts with @ionic-native's implementation. +ionic cordova plugin add cordova-plugin-fcm-with-dependecy-updated \ + --variable ANDROID_DEFAULT_NOTIFICATION_ICON="@mipmap/ic_launcher" \ + --variable ANDROID_FIREBASE_BOM_VERSION="26.0.0" \ + --variable ANDROID_GOOGLE_SERVICES_VERSION="4.3.4" \ + --variable ANDROID_GRADLE_TOOLS_VERSION="4.1.0" \ + --variable IOS_FIREBASE_MESSAGING_VERSION="~> 7.4.0" ``` -#### Receiving push notification data +## Push Payload Configuration + +Besides common FCM configuration (https://firebase.google.com/docs/cloud-messaging/ios/certs), the Push payload should contain "notification" and "data" keys and "click_action" equals to "FCM_PLUGIN_ACTIVITY" within "notification". -```javascript -//FCMPlugin.onNotification( onNotificationCallback(data), successCallback(msg), errorCallback(err) ) -//Here you define your application behaviour based on the notification data. -FCMPlugin.onNotification(function(data){ - if(data.wasTapped){ - //Notification was received on device tray and tapped by the user. - alert( JSON.stringify(data) ); - }else{ - //Notification was received in foreground. Maybe the user needs to be notified. - alert( JSON.stringify(data) ); +Structure expected: +```js +{ + ..., + "notification": { + ... + }, + "data": { + ... + }, + "android": { + "notification": { + "click_action": "FCM_PLUGIN_ACTIVITY" } -}); + }, + ..., +} ``` -#### Send notification. Payload example (REST API) -Full documentation: https://firebase.google.com/docs/cloud-messaging/http-server-ref -Free testing server: https://cordova-plugin-fcm.appspot.com -```javascript -//POST: https://fcm.googleapis.com/fcm/send -//HEADER: Content-Type: application/json -//HEADER: Authorization: key=AIzaSy******************* +Example: +```json { + "token": "[FCM token]", "notification":{ "title":"Notification title", "body":"Notification body", "sound":"default", - "click_action":"FCM_PLUGIN_ACTIVITY", - "icon":"fcm_push_icon" }, "data":{ "param1":"value1", "param2":"value2" }, - "to":"/topics/topicExample", - "priority":"high", - "restricted_package_name":"" + "android": { + "notification": { + "icon":"fcm_push_icon", + "click_action": "FCM_PLUGIN_ACTIVITY" + } + } } -//sound: optional field if you want sound with the notification -//click_action: must be present with the specified value for Android -//icon: white icon resource name for Android >5.0 -//data: put any "param":"value" and retreive them in the JavaScript notification callback -//to: device token or /topic/topicExample -//priority: must be set to "high" for delivering notifications on closed iOS apps -//restricted_package_name: optional field if you want to send only to a restricted app package (i.e: com.myapp.test) ``` -## How it works -Send a push notification to a single device or topic. -- 1.a Application is in foreground: - - The notification data is received in the JavaScript callback without notification bar message (this is the normal behaviour of mobile push notifications). -- 1.b Application is in background or closed: - - The device displays the notification message in the device notification bar. - - If the user taps the notification, the application comes to foreground and the notification data is received in the JavaScript callback. - - If the user does not tap the notification but opens the applicacion, nothing happens until the notification is tapped. -## License +## Features + +- [As its own](#as-its-own) + - [FCM.clearAllNotifications()](#fcmclearallnotifications) + - [FCM.createNotificationChannel()](#fcmcreatenotificationchannel) + - [FCM.deleteInstanceId()](#fcmdeleteinstanceid) + - [FCM.getAPNSToken()](#fcmgetapnstoken) + - [FCM.getInitialPushPayload()](#fcmgetinitialpushpayload) + - [FCM.getToken()](#fcmgettoken) + - [FCM.hasPermission()](#fcmhaspermission) + - [FCM.onNotification()](#fcmonnotification) + - [FCM.onTokenRefresh()](#fcmontokenrefresh) + - [**FCM.requestPushPermission()**](#fcmrequestpushpermission) + - [FCM.subscribeToTopic()](#fcmsubscribetotopic) + - [FCM.unsubscribeFromTopic()](#fcmunsubscribefromtopic) + - [FCM.eventTarget](#fcmeventtarget) +- [**With Ionic**](#with-ionic) + - [FCM.onNotification()](#fcmonnotification-1) + - [FCM.onTokenRefresh()](#fcmontokenrefresh-1) + +#### As its own + +The JS functions are now as written bellow and do require Promise support. Which, for Android API 19 support, it can be fulfilled by a polyfill. + +##### FCM.clearAllNotifications() + +Removes existing push notifications from the notifications center. +```typescript +await FCM.clearAllNotifications(); +``` + +##### FCM.createNotificationChannel() + +For Android, some notification properties are only defined programmatically. +Channel can define the default behavior for notifications on Android 8.0+. +Once a channel is created, it stays unchangeable until the user uninstalls the app. +```typescript +await FCM.createNotificationChannel({ + id: "urgent_alert", // required + name: "Urgent Alert", // required + description: "Very urgent message alert", + importance: "high", // https://developer.android.com/guide/topics/ui/notifiers/notifications#importance + visibility: "public", // https://developer.android.com/training/notify-user/build-notification#lockscreenNotification + sound: "alert_sound", // In the "alert_sound" example, the file should located as resources/raw/alert_sound.mp3 + lights: true, // enable lights for notifications + vibration: true // enable vibration for notifications +}); +``` + +##### FCM.deleteInstanceId() + +Deletes the InstanceId, revoking all tokens. +```typescript +await FCM.deleteInstanceId(); ``` -The MIT License -Copyright (c) 2017 Felipe Echanique Torres (felipe.echanique in the gmail.com) +##### FCM.getAPNSToken() -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: +Gets iOS device's current APNS token. +```typescript +const apnsToken: string = await FCM.getAPNSToken(); +``` -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. +##### FCM.getInitialPushPayload() -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. +Retrieves the message that, on tap, opened the app. And `null`, if the app was open normally. +```typescript +const pushPayload: object = await FCM.getInitialPushPayload() ``` + +##### FCM.getToken() + +Gets device's current registration id. +```typescript +const fcmToken: string = await FCM.getToken() +``` + +##### FCM.hasPermission() + +Checking for permissions on iOS. On android, it always returns `true`. +```typescript +const doesIt: boolean = await FCM.hasPermission() +``` + +##### FCM.onNotification() + +Callback firing when receiving new notifications. It serves as a shortcut to listen to eventTarget's "notification" event. +```typescript +const disposable = FCM.onNotification((payload: object) => { + // ... +}) +// ... +disposable.dispose() // To remove listener +``` + +:warning: If the subscription to notification events happens after the notification has been fired, it'll be lost. As it is expected that you'd not always be able to catch the notification payload that the opened the app, the `FCM.getInitialPushPayload()` method was introduced. + +##### FCM.onTokenRefresh() + +Callback firing when receiving a new Firebase token. It serves as a shortcut to listen to eventTarget's "tokenRefresh" event. +```typescript +const disposable = FCM.onTokenRefresh((fcmToken: string) => { + // ... +}) +// ... +disposable.dispose() // To remove listener +``` + +##### FCM.requestPushPermission() + +Request push notification permission on iOS, alerting the user if he/she/they have not yet accepted or denied. +For Android, it'll always return true. +```typescript +const wasPermissionGiven: boolean = await FCM.requestPushPermission({ + ios9Support: { + timeout: 10, // How long it will wait for a decision from the user before returning `false` + interval: 0.3 // How long between each permission verification + } +}) +``` + +:warning: Without this request, the Application won't receive any notification on iOS! +:warning: The user will only have its permition required once, after that time, this call will only return if the permission was given that time. + +##### FCM.subscribeToTopic() + +Subscribes you to a [topic](https://firebase.google.com/docs/notifications/android/console-topics). +```typescript +const topic: string +// ... +await FCM.subscribeToTopic(topic) +``` + +##### FCM.unsubscribeFromTopic() + +Unsubscribes you from a [topic](https://firebase.google.com/docs/notifications/android/console-topics). +```typescript +const topic: string +// ... +await FCM.unsubscribeFromTopic(topic) +``` + +##### FCM.eventTarget + +EventTarget object for native-sourced custom events. Useful for more advanced listening handling. +```typescript +const listener = (data) => { + const payload = data.detail + // ... +} +FCM.eventTarget.addEventListener("notification", listener, false); +// ... +FCM.eventTarget.removeEventListener("notification", listener, false); +``` + +#### With Ionic + +Ionic support was implemented as part of this plugin to allow users to have access to newer features with the type support. It is available in 3 flavors: +- Ionic v5: +```typescript +import { FCM } from "cordova-plugin-fcm-with-dependecy-updated/ionic"; +``` +- Ionic ngx: +```typescript +import { FCM } from "cordova-plugin-fcm-with-dependecy-updated/ionic/ngx"; +``` +- Ionic v4 (also works for Ionic v3): +```typescript +import { FCM } from "cordova-plugin-fcm-with-dependecy-updated/ionic/v4"; +``` + +It brings the same behavior as the native implementation, except for `FCM.onNotification()` and `FCM.onTokenRefresh()`, which gain rxjs' Observable support. + +To avoid confusion, it's suggested to also remove the redundant @ionic-native/fcm package. + +##### FCM.onNotification() + +Event firing when receiving new notifications. +```typescript +this.fcm.onNotification().subscribe((payload: object) => { + // ... +}); +``` + +##### FCM.onTokenRefresh() + +Event firing when receiving a new Firebase token. +```typescript +this.fcm.onTokenRefresh().subscribe((token: string) => { + // ... +}); +``` + +## Example Apps + +### Cordova + +https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated-app-example + +### Ionic v3 + +https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated-ionic-v3-example + +### Ionic v5 + +https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated-ionic-v5-example + +## Companion Plugins + +### Optional standalone FCM Image Support for Cordova iOS + +After a lot of work, the first release of the plugin is out. Which should enable the support, just by installing it. + +Link: https://github.com/andrehtissot/cordova-plugin-fcm-image-support + +### Optional standalone Cocoapods CDN source switcher + +When the environment supports, the cocoapods source is automatically set to the official CDN instead of the slow Github repository. + +Link: https://github.com/andrehtissot/cordova-plugin-cocoapods-cdn + +## Authorship +This started as a fork from https://github.com/fechanique/cordova-plugin-fcm and, gradually, had most of its implementation rewritten and improved, for newer dependency versions support, jitpack and cocoapods support, and new useful features. diff --git a/cordova-plugin-fcm-with-dependecy-updated.podspec b/cordova-plugin-fcm-with-dependecy-updated.podspec new file mode 100644 index 000000000..7cb1925bb --- /dev/null +++ b/cordova-plugin-fcm-with-dependecy-updated.podspec @@ -0,0 +1,145 @@ +# +# Be sure to run `pod spec lint cordova-plugin-fcm-with-dependecy-updated.podspec' to ensure this is a +# valid spec and to remove all comments including this before submitting the spec. +# +# To learn more about Podspec attributes see https://docs.cocoapods.org/specification.html +# To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ +# + +Pod::Spec.new do |spec| + + # ――― Spec Metadata ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # These will help people to find your library, and whilst it + # can feel like a chore to fill in it's definitely to your advantage. The + # summary should be tweet-length, and the description more in depth. + # + + spec.name = "cordova-plugin-fcm-with-dependecy-updated" + spec.version = "7.8.0" + spec.summary = "Google FCM Push Notifications Cordova Plugin" + + # This description is used to generate tags and improve search results. + # * Think: What does it do? Why did you write it? What is the focus? + # * Try to keep it short, snappy and to the point. + # * Write the description between the DESC delimiters below. + # * Finally, don't worry about the indent, CocoaPods strips it! + spec.description = <<-DESC + Extremely easy plug&play push notification plugin for Cordova applications with Google Firebase FCM. +DESC + + spec.homepage = "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated" + # spec.screenshots = "www.example.com/screenshots_1.gif", "www.example.com/screenshots_2.gif" + + + # ――― Spec License ――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # Licensing your code is important. See https://choosealicense.com for more info. + # CocoaPods will detect a license file if there is a named LICENSE* + # Popular ones are 'MIT', 'BSD' and 'Apache License, Version 2.0'. + # + + spec.license = { :type => "MIT", :file => "LICENSE" } + + + # ――― Author Metadata ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # Specify the authors of the library, with email addresses. Email addresses + # of the authors are extracted from the SCM log. E.g. $ git log. CocoaPods also + # accepts just a name if you'd rather not provide an email address. + # + # Specify a social_media_url where others can refer to, for example a twitter + # profile URL. + # + + spec.author = { "André Tissot" => "andrehtissot@gmail.com" } + # Or just: spec.author = "" + # spec.social_media_url = "https://twitter.com/" + + # ――― Platform Specifics ――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # If this Pod runs only on iOS or OS X, then specify the platform and + # the deployment target. You can optionally include the target after the platform. + # + + spec.platform = :ios + # spec.platform = :ios + # spec.platform = :ios, "5.0" + + # When using multiple platforms + # spec.ios.deployment_target = "5.0" + # spec.osx.deployment_target = "10.7" + # spec.watchos.deployment_target = "2.0" + # spec.tvos.deployment_target = "9.0" + + + # ――― Source Location ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # Specify the location from where the source should be retrieved. + # Supports git, hg, bzr, svn and HTTP. + # + + spec.source = { :git => "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated.git", :tag => "v#{spec.version}" } + + + # ――― Source Code ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # CocoaPods is smart about how it includes source code. For source files + # giving a folder will include any swift, h, m, mm, c & cpp files. + # For header files it will include any header in the folder. + # Not including the public_header_files will make all headers public. + # + + # spec.source_files = "Classes", "Classes/**/*.{h,m}" + spec.source_files = "src/ios/*.{h,m}", "src/ios/firebase/*.{h,m}" + spec.exclude_files = "Classes/Exclude" + + # spec.public_header_files = "Classes/**/*.h" + + + # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # A list of resources included with the Pod. These are copied into the + # target bundle with a build phase script. Anything else will be cleaned. + # You can preserve files from being cleaned, please don't preserve + # non-essential files like tests, examples and documentation. + # + + # Reference: https://stackoverflow.com/questions/36797639/including-a-plist-file-with-my-cocoapod + spec.resource_bundle = {"GoogleService" => "src/ios/Assets/*" } + # spec.resource = "icon.png" + # spec.resources = "Resources/*.png" + + # spec.preserve_paths = "FilesToSave", "MoreFilesToSave" + + + # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # Link your library with frameworks, or libraries. Libraries do not include + # the lib prefix of their name. + # + + # spec.frameworks = "AddressBook", "Security", "UIKit" + # spec.framework = "SomeFramework" + # spec.frameworks = "SomeFramework", "AnotherFramework" + + # spec.libraries = "sqlite3", "z", "c++" + # spec.library = "iconv" + # spec.libraries = "iconv", "xml2" + + + # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # + # + # If your library depends on compiler flags you can set them in the xcconfig hash + # where they will only apply to your library. If you depend on other Podspecs + # you can include multiple dependencies to ensure it works. + + # spec.requires_arc = true + + # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } + spec.dependency "Cordova", ">= 3.0.0" + + # TODO: Migrate to use Core cocoapods Firebase dependencies + spec.dependency 'Firebase/Messaging' + +end diff --git a/ionic/FCM.d.ts b/ionic/FCM.d.ts new file mode 100644 index 000000000..e37567904 --- /dev/null +++ b/ionic/FCM.d.ts @@ -0,0 +1,109 @@ +import { Observable } from 'rxjs'; +import { INotificationPayload } from '../typings/INotificationPayload'; +import { IRequestPushPermissionOptions } from '../typings/IRequestPushPermissionOptions'; +import { IChannelConfiguration } from '../typings/IChannelConfiguration'; +/** + * @name FCM + * @description + * Easy plug&play push notification for Google Firebase FCM. + * + * @interfaces + * INotificationPayload + * IChannelConfiguration + * IRequestPushPermissionOptions + */ +export declare class FCMPluginOnIonic { + /** + * Removes existing push notifications from the notifications center + * + * @returns {Promise} Async call to native implementation + */ + clearAllNotifications(): Promise; + /** + * For Android, some notification properties are only defined programmatically. + * Channel can define the default behavior for notifications on Android 8.0+. + * Once a channel is created, it stays unchangeable until the user uninstalls the app. + * + * @param {IChannelConfiguration} channelConfig The parmeters of the new channel + * + * @returns {Promise} Async call to native implementation + */ + createNotificationChannel(channelConfig: IChannelConfiguration): Promise; + /** + * This method deletes the InstanceId, revoking all tokens. + * + * @returns {Promise} Async call to native implementation + */ + deleteInstanceId(): Promise; + /** + * Gets ios device's current APNS token + * + * @returns {Promise} Returns a Promise that resolves with the APNS token + */ + getAPNSToken(): Promise; + /** + * Retrieves the message that, on tap, opened the app + * + * @private + * + * @returns {Promise} Async call to native implementation + */ + getInitialPushPayload(): Promise; + /** + * Gets device's current registration id + * + * @returns {Promise} Returns a Promise that resolves with the registration id token + */ + getToken(): Promise; + /** + * Checking for permissions. + * + * @returns {Promise} Returns a Promise of: + * - true: push was allowed (or platform is android) + * - false: push will not be available + * - null: still not answered, recommended checking again later. + */ + hasPermission(): Promise; + /** + * Event firing when receiving new notifications + * + * @argument {{ once?: boolean }} options once defines if the listener is only trigger once + * @returns {Observable} An object to listen for notification data + */ + onNotification(options?: { + once?: boolean; + }): Observable; + /** + * Event firing when receiving a new Firebase token + * + * @argument {{ once?: boolean }} options once defines if the listener is only trigger once + * @returns {Observable} An object to listen for the token + */ + onTokenRefresh(options?: { + once?: boolean; + }): Observable; + /** + * Request push notification permission, alerting the user if it not have yet decided + * + * @param {IRequestPushPermissionOptions} options Options for push request + * @returns {Promise} Returns a Promise that resolves with the permission status + */ + requestPushPermission(options?: IRequestPushPermissionOptions): Promise; + /** + * Subscribes you to a [topic](https://firebase.google.com/docs/notifications/android/console-topics) + * + * @param {string} topic Topic to be subscribed to + * + * @returns {Promise} Async call to native implementation + */ + subscribeToTopic(topic: string): Promise; + /** + * Unsubscribes you from a [topic](https://firebase.google.com/docs/notifications/android/console-topics) + * + * @param {string} topic Topic to be unsubscribed from + * + * @returns {Promise} Async call to native implementation + */ + unsubscribeFromTopic(topic: string): Promise; +} +export declare const FCM: FCMPluginOnIonic; diff --git a/ionic/FCM.js b/ionic/FCM.js new file mode 100644 index 000000000..820fe3b8e --- /dev/null +++ b/ionic/FCM.js @@ -0,0 +1,51 @@ +import { Subject } from 'rxjs' + +function FCMPluginOnIonic() {} +FCMPluginOnIonic.prototype.clearAllNotifications = function () { + return window.FCM.clearAllNotifications() +} +FCMPluginOnIonic.prototype.createNotificationChannel = function (channelConfig) { + return window.FCM.createNotificationChannel(channelConfig) +} +FCMPluginOnIonic.prototype.deleteInstanceId = function () { + return window.FCM.deleteInstanceId() +} +FCMPluginOnIonic.prototype.getAPNSToken = function () { + return window.FCM.getAPNSToken() +} +FCMPluginOnIonic.prototype.getInitialPushPayload = function () { + return window.FCM.getInitialPushPayload() +} +FCMPluginOnIonic.prototype.getToken = function () { + return window.FCM.getToken() +} +FCMPluginOnIonic.prototype.hasPermission = function () { + return window.FCM.hasPermission() +} +FCMPluginOnIonic.prototype.onNotification = function (options) { + var observable = new Subject() + var handler = function (payload) { + return observable.next(payload) + } + window.FCM.onNotification(handler, options) + return observable +} +FCMPluginOnIonic.prototype.onTokenRefresh = function (options) { + var observable = new Subject() + window.FCM.onTokenRefresh(function (token) { + return observable.next(token) + }, options) + return observable +} +FCMPluginOnIonic.prototype.requestPushPermission = function (options) { + return window.FCM.requestPushPermission(options) +} +FCMPluginOnIonic.prototype.subscribeToTopic = function (topic) { + return window.FCM.subscribeToTopic(topic) +} +FCMPluginOnIonic.prototype.unsubscribeFromTopic = function (topic) { + return window.FCM.unsubscribeFromTopic(topic) +} + +export { FCMPluginOnIonic } +export var FCM = new FCMPluginOnIonic() diff --git a/ionic/ngx/FCM.d.ts b/ionic/ngx/FCM.d.ts new file mode 100644 index 000000000..b48dd761d --- /dev/null +++ b/ionic/ngx/FCM.d.ts @@ -0,0 +1,119 @@ +import { Observable } from 'rxjs'; +import { IChannelConfiguration } from '../../typings/IChannelConfiguration'; +import { INotificationPayload } from '../../typings/INotificationPayload'; +import { IRequestPushPermissionOptions } from '../../typings/IRequestPushPermissionOptions'; +/** + * @name FCM + * @description + * Easy plug&play push notification for Google Firebase FCM. + * + * @interfaces + * INotificationPayload + * IChannelConfiguration + * IRequestPushPermissionOptions + */ +export declare class FCM { + static pluginName: string; + static plugin: string; + static pluginRef: string; + static repo: string; + static platforms: string[]; + static installed: () => boolean; + static getPlugin: () => any; + static getPluginName: () => string; + static getPluginRef: () => string; + static getPluginInstallName: () => string; + static getSupportedPlatforms: () => string[]; + /** + * Removes existing push notifications from the notifications center + * + * @returns {Promise} Async call to native implementation + */ + clearAllNotifications(): Promise; + /** + * For Android, some notification properties are only defined programmatically. + * Channel can define the default behavior for notifications on Android 8.0+. + * Once a channel is created, it stays unchangeable until the user uninstalls the app. + * + * @param {IChannelConfiguration} channelConfig The parmeters of the new channel + * + * @returns {Promise} Async call to native implementation + */ + createNotificationChannel(channelConfig: IChannelConfiguration): Promise; + /** + * This method deletes the InstanceId, revoking all tokens. + * + * @returns {Promise} Async call to native implementation + */ + deleteInstanceId(): Promise; + /** + * Gets ios device's current APNS token + * + * @returns {Promise} Returns a Promise that resolves with the APNS token + */ + getAPNSToken(): Promise; + /** + * Retrieves the message that, on tap, opened the app + * + * @private + * + * @returns {Promise} Async call to native implementation + */ + getInitialPushPayload(): Promise; + /** + * Gets device's current registration id + * + * @returns {Promise} Returns a Promise that resolves with the registration id token + */ + getToken(): Promise; + /** + * Checking for permissions. + * + * @returns {Promise} Returns a Promise of: + * - true: push was allowed (or platform is android) + * - false: push will not be available + * - null: still not answered, recommended checking again later. + */ + hasPermission(): Promise; + /** + * Event firing when receiving new notifications + * + * @argument {{ once?: boolean }} options once defines if the listener is only trigger once + * @returns {Observable} An object to listen for notification data + */ + onNotification(options?: { + once?: boolean; + }): Observable; + /** + * Event firing when receiving a new Firebase token + * + * @argument {{ once?: boolean }} options once defines if the listener is only trigger once + * @returns {Observable} An object to listen for the token + */ + onTokenRefresh(options?: { + once?: boolean; + }): Observable; + /** + * Request push notification permission, alerting the user if it not have yet decided + * + * @param {IRequestPushPermissionOptions} options Options for push request + * @returns {Promise} Returns a Promise that resolves with the permission status + */ + requestPushPermission(options?: IRequestPushPermissionOptions): Promise; + /** + * Subscribes you to a [topic](https://firebase.google.com/docs/notifications/android/console-topics) + * + * @param {string} topic Topic to be subscribed to + * + * @returns {Promise} Async call to native implementation + */ + subscribeToTopic(topic: string): Promise; + /** + * Unsubscribes you from a [topic](https://firebase.google.com/docs/notifications/android/console-topics) + * + * @param {string} topic Topic to be unsubscribed from + * + * @returns {Promise} Async call to native implementation + */ + unsubscribeFromTopic(topic: string): Promise; +} diff --git a/ionic/ngx/FCM.js b/ionic/ngx/FCM.js new file mode 100644 index 000000000..c4161eccc --- /dev/null +++ b/ionic/ngx/FCM.js @@ -0,0 +1,83 @@ +var __decorate = + (this && this.__decorate) || + function (decorators, target, key, desc) { + var c = arguments.length, + r = + c < 3 + ? target + : desc === null + ? (desc = Object.getOwnPropertyDescriptor(target, key)) + : desc, + d + if (typeof Reflect === 'object' && typeof Reflect.decorate === 'function') + r = Reflect.decorate(decorators, target, key, desc) + else + for (var i = decorators.length - 1; i >= 0; i--) + if ((d = decorators[i])) + r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r + return c > 3 && r && Object.defineProperty(target, key, r), r + } +import { Injectable } from '@angular/core' +import { Subject } from 'rxjs' +import { IonicNativePlugin } from '@ionic-native/core' + +function FCM() {} +FCM.prototype.clearAllNotifications = function () { + return window.FCM.clearAllNotifications() +} +FCM.prototype.createNotificationChannel = function (channelConfig) { + return window.FCM.createNotificationChannel(channelConfig) +} +FCM.prototype.deleteInstanceId = function () { + return window.FCM.deleteInstanceId() +} +FCM.prototype.getAPNSToken = function () { + return window.FCM.getAPNSToken() +} +FCM.prototype.getInitialPushPayload = function () { + return window.FCM.getInitialPushPayload() +} +FCM.prototype.getToken = function () { + return window.FCM.getToken() +} +FCM.prototype.hasPermission = function () { + return window.FCM.hasPermission() +} +FCM.prototype.onNotification = function (options) { + var observable = new Subject() + var handler = function (payload) { + return observable.next(payload) + } + window.FCM.onNotification(handler, options) + return observable +} +FCM.prototype.onTokenRefresh = function (options) { + var observable = new Subject() + window.FCM.onTokenRefresh(function (token) { + return observable.next(token) + }, options) + return observable +} +FCM.prototype.requestPushPermission = function (options) { + return window.FCM.requestPushPermission(options) +} +FCM.prototype.subscribeToTopic = function (topic) { + return window.FCM.subscribeToTopic(topic) +} +FCM.prototype.unsubscribeFromTopic = function (topic) { + return window.FCM.unsubscribeFromTopic(topic) +} +FCM.pluginName = 'FCM' +FCM.plugin = 'cordova-plugin-fcm-with-dependecy-updated' +FCM.pluginRef = 'FCM' +FCM.repo = 'https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated' +FCM.platforms = ['Android', 'iOS'] +FCM.installed = IonicNativePlugin.installed +FCM.getPlugin = IonicNativePlugin.getPlugin +FCM.getPluginName = IonicNativePlugin.getPluginName +FCM.getPluginRef = IonicNativePlugin.getPluginRef +FCM.getPluginInstallName = IonicNativePlugin.getPluginInstallName +FCM.getSupportedPlatforms = IonicNativePlugin.getSupportedPlatforms +FCM = __decorate([Injectable()], FCM) + +export { FCM } diff --git a/ionic/ngx/package.json b/ionic/ngx/package.json new file mode 100644 index 000000000..f0723fb0a --- /dev/null +++ b/ionic/ngx/package.json @@ -0,0 +1,41 @@ +{ + "version": "7.8.0", + "name": "cordova-plugin-fcm-with-dependecy-updated", + "cordova_name": "Cordova FCM Push Plugin", + "description": "Google Firebase Cloud Messaging Cordova Push Plugin fork with dependecy updated", + "license": "MIT", + "main": "./FCM.js", + "repo": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated", + "issue": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated/issues", + "author": "André Augusto Tissot", + "repository": { + "type": "git", + "url": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated" + }, + "keywords": [ + "ecosystem:cordova", + "cordova-android", + "cordova-ios", + "notifications", + "push", + "firebase", + "fcm", + "ios", + "android", + "cordova", + "ionic" + ], + "platforms": ["android", "ios"], + "devDependencies": { + "@types/cordova": "0.0.34", + "prettier": "^2.0.5", + "tslint": "^6.1.2", + "tslint-microsoft-contrib": "^6.2.0", + "typescript": "^3.9.3" + }, + "dependencies": { + "@angular/core": "^9.1.9", + "@ionic-native/core": "^5.26.0", + "rxjs": "^6.5.5" + } +} diff --git a/ionic/package.json b/ionic/package.json new file mode 100644 index 000000000..f0723fb0a --- /dev/null +++ b/ionic/package.json @@ -0,0 +1,41 @@ +{ + "version": "7.8.0", + "name": "cordova-plugin-fcm-with-dependecy-updated", + "cordova_name": "Cordova FCM Push Plugin", + "description": "Google Firebase Cloud Messaging Cordova Push Plugin fork with dependecy updated", + "license": "MIT", + "main": "./FCM.js", + "repo": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated", + "issue": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated/issues", + "author": "André Augusto Tissot", + "repository": { + "type": "git", + "url": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated" + }, + "keywords": [ + "ecosystem:cordova", + "cordova-android", + "cordova-ios", + "notifications", + "push", + "firebase", + "fcm", + "ios", + "android", + "cordova", + "ionic" + ], + "platforms": ["android", "ios"], + "devDependencies": { + "@types/cordova": "0.0.34", + "prettier": "^2.0.5", + "tslint": "^6.1.2", + "tslint-microsoft-contrib": "^6.2.0", + "typescript": "^3.9.3" + }, + "dependencies": { + "@angular/core": "^9.1.9", + "@ionic-native/core": "^5.26.0", + "rxjs": "^6.5.5" + } +} diff --git a/ionic/v4/FCM.d.ts b/ionic/v4/FCM.d.ts new file mode 100644 index 000000000..f44330f98 --- /dev/null +++ b/ionic/v4/FCM.d.ts @@ -0,0 +1,108 @@ +import { Observable } from 'rxjs'; +import { IChannelConfiguration } from '../../typings/IChannelConfiguration'; +import { IRequestPushPermissionOptions } from '../../typings/IRequestPushPermissionOptions'; +import { INotificationPayload } from '../../typings/INotificationPayload'; +/** + * @name FCM + * @description + * Easy plug&play push notification for Google Firebase FCM. + * + * @interfaces + * INotificationPayload + * IChannelConfiguration + * IRequestPushPermissionOptions + */ +export declare class FCM { + /** + * Removes existing push notifications from the notifications center + * + * @returns {Promise} Async call to native implementation + */ + clearAllNotifications(): Promise; + /** + * For Android, some notification properties are only defined programmatically. + * Channel can define the default behavior for notifications on Android 8.0+. + * Once a channel is created, it stays unchangeable until the user uninstalls the app. + * + * @param {IChannelConfiguration} channelConfig The parmeters of the new channel + * + * @returns {Promise} Async call to native implementation + */ + createNotificationChannel(channelConfig: IChannelConfiguration): Promise; + /** + * This method deletes the InstanceId, revoking all tokens. + * + * @returns {Promise} Async call to native implementation + */ + deleteInstanceId(): Promise; + /** + * Gets ios device's current APNS token + * + * @returns {Promise} Returns a Promise that resolves with the APNS token + */ + getAPNSToken(): Promise; + /** + * Retrieves the message that, on tap, opened the app + * + * @private + * + * @returns {Promise} Async call to native implementation + */ + getInitialPushPayload(): Promise; + /** + * Gets device's current registration id + * + * @returns {Promise} Returns a Promise that resolves with the registration id token + */ + getToken(): Promise; + /** + * Checking for permissions. + * + * @returns {Promise} Returns a Promise of: + * - true: push was allowed (or platform is android) + * - false: push will not be available + * - null: still not answered, recommended checking again later. + */ + hasPermission(): Promise; + /** + * Event firing when receiving new notifications + * + * @argument {{ once?: boolean }} options once defines if the listener is only trigger once + * @returns {Observable} An object to listen for notification data + */ + onNotification(options?: { + once?: boolean; + }): Observable; + /** + * Event firing when receiving a new Firebase token + * + * @argument {{ once?: boolean }} options once defines if the listener is only trigger once + * @returns {Observable} An object to listen for the token + */ + onTokenRefresh(options?: { + once?: boolean; + }): Observable; + /** + * Request push notification permission, alerting the user if it not have yet decided + * + * @param {IRequestPushPermissionOptions} options Options for push request + * @returns {Promise} Returns a Promise that resolves with the permission status + */ + requestPushPermission(options?: IRequestPushPermissionOptions): Promise; + /** + * Subscribes you to a [topic](https://firebase.google.com/docs/notifications/android/console-topics) + * + * @param {string} topic Topic to be subscribed to + * + * @returns {Promise} Async call to native implementation + */ + subscribeToTopic(topic: string): Promise; + /** + * Unsubscribes you from a [topic](https://firebase.google.com/docs/notifications/android/console-topics) + * + * @param {string} topic Topic to be unsubscribed from + * + * @returns {Promise} Async call to native implementation + */ + unsubscribeFromTopic(topic: string): Promise; +} diff --git a/ionic/v4/FCM.js b/ionic/v4/FCM.js new file mode 100644 index 000000000..0df5351b7 --- /dev/null +++ b/ionic/v4/FCM.js @@ -0,0 +1,84 @@ +var __decorate = + (this && this.__decorate) || + function (decorators, target, key, desc) { + var c = arguments.length, + r = + c < 3 + ? target + : desc === null + ? (desc = Object.getOwnPropertyDescriptor(target, key)) + : desc, + d + if (typeof Reflect === 'object' && typeof Reflect.decorate === 'function') + r = Reflect.decorate(decorators, target, key, desc) + else + for (var i = decorators.length - 1; i >= 0; i--) + if ((d = decorators[i])) + r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r + return c > 3 && r && Object.defineProperty(target, key, r), r + } +import { Injectable } from '@angular/core' +import { Plugin } from '@ionic-native/core' +import { Subject } from 'rxjs' + +function FCM() {} +FCM.prototype.clearAllNotifications = function () { + return window.FCM.clearAllNotifications() +} +FCM.prototype.createNotificationChannel = function (channelConfig) { + return window.FCM.createNotificationChannel(channelConfig) +} +FCM.prototype.deleteInstanceId = function () { + return window.FCM.deleteInstanceId() +} +FCM.prototype.getAPNSToken = function () { + return window.FCM.getAPNSToken() +} +FCM.prototype.getInitialPushPayload = function () { + return window.FCM.getInitialPushPayload() +} +FCM.prototype.getToken = function () { + return window.FCM.getToken() +} +FCM.prototype.hasPermission = function () { + return window.FCM.hasPermission() +} +FCM.prototype.onNotification = function (options) { + var observable = new Subject() + var handler = function (payload) { + return observable.next(payload) + } + window.FCM.onNotification(handler, options) + return observable +} +FCM.prototype.onTokenRefresh = function (options) { + var observable = new Subject() + window.FCM.onTokenRefresh(function (token) { + return observable.next(token) + }, options) + return observable +} +FCM.prototype.requestPushPermission = function (options) { + return window.FCM.requestPushPermission(options) +} +FCM.prototype.subscribeToTopic = function (topic) { + return window.FCM.subscribeToTopic(topic) +} +FCM.prototype.unsubscribeFromTopic = function (topic) { + return window.FCM.unsubscribeFromTopic(topic) +} +FCM = __decorate( + [ + Plugin({ + pluginName: 'FCM', + plugin: 'cordova-plugin-fcm-with-dependecy-updated', + pluginRef: 'FCM', + repo: 'https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated', + platforms: ['Android', 'iOS'] + }), + Injectable() + ], + FCM +) + +export { FCM } diff --git a/ionic/v4/package.json b/ionic/v4/package.json new file mode 100644 index 000000000..f0723fb0a --- /dev/null +++ b/ionic/v4/package.json @@ -0,0 +1,41 @@ +{ + "version": "7.8.0", + "name": "cordova-plugin-fcm-with-dependecy-updated", + "cordova_name": "Cordova FCM Push Plugin", + "description": "Google Firebase Cloud Messaging Cordova Push Plugin fork with dependecy updated", + "license": "MIT", + "main": "./FCM.js", + "repo": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated", + "issue": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated/issues", + "author": "André Augusto Tissot", + "repository": { + "type": "git", + "url": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated" + }, + "keywords": [ + "ecosystem:cordova", + "cordova-android", + "cordova-ios", + "notifications", + "push", + "firebase", + "fcm", + "ios", + "android", + "cordova", + "ionic" + ], + "platforms": ["android", "ios"], + "devDependencies": { + "@types/cordova": "0.0.34", + "prettier": "^2.0.5", + "tslint": "^6.1.2", + "tslint-microsoft-contrib": "^6.2.0", + "typescript": "^3.9.3" + }, + "dependencies": { + "@angular/core": "^9.1.9", + "@ionic-native/core": "^5.26.0", + "rxjs": "^6.5.5" + } +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..e237ef496 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,794 @@ +{ + "name": "cordova-plugin-fcm-with-dependecy-updated", + "version": "7.8.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "version": "7.8.0", + "license": "MIT", + "dependencies": { + "typescript": "^4.1.3" + }, + "devDependencies": { + "prettier": "^2.2.1", + "tslint": "^6.1.3", + "tslint-microsoft-contrib": "^6.2.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "dependencies": { + "@babel/highlight": "^7.10.4" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "node_modules/@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/is-core-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.0.0.tgz", + "integrity": "sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "node_modules/prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/resolve": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", + "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", + "dev": true, + "dependencies": { + "is-core-module": "^2.0.0", + "path-parse": "^1.0.6" + } + }, + "node_modules/semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "node_modules/tslint": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.3", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.13.0", + "tsutils": "^2.29.0" + }, + "bin": { + "tslint": "bin/tslint" + }, + "engines": { + "node": ">=4.8.0" + } + }, + "node_modules/tslint-microsoft-contrib": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tslint-microsoft-contrib/-/tslint-microsoft-contrib-6.2.0.tgz", + "integrity": "sha512-6tfi/2tHqV/3CL77pULBcK+foty11Rr0idRDxKnteTaKm6gWF9qmaCNU17HVssOuwlYNyOmd9Jsmjd+1t3a3qw==", + "dev": true, + "dependencies": { + "tsutils": "^2.27.2 <2.29.0" + } + }, + "node_modules/tslint-microsoft-contrib/node_modules/tsutils": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.28.0.tgz", + "integrity": "sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + } + }, + "node_modules/tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + } + }, + "node_modules/typescript": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", + "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + }, + "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-core-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.0.0.tgz", + "integrity": "sha512-jq1AH6C8MuteOoBPwkxHafmByhL9j5q4OaPGdbuD+ZtQJVzH+i6E3BJDQcBA09k57i2Hh2yQbEG8yObZ0jdlWw==", + "dev": true, + "requires": { + "has": "^1.0.3" + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true + }, + "resolve": { + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.18.1.tgz", + "integrity": "sha512-lDfCPaMKfOJXjy0dPayzPdF1phampNWr3qFCjAu+rw/qbQmr5jWH5xN2hwh9QKfw9E5v4hwV7A+jrCmL8yjjqA==", + "dev": true, + "requires": { + "is-core-module": "^2.0.0", + "path-parse": "^1.0.6" + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + }, + "tslint": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.3", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.13.0", + "tsutils": "^2.29.0" + } + }, + "tslint-microsoft-contrib": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tslint-microsoft-contrib/-/tslint-microsoft-contrib-6.2.0.tgz", + "integrity": "sha512-6tfi/2tHqV/3CL77pULBcK+foty11Rr0idRDxKnteTaKm6gWF9qmaCNU17HVssOuwlYNyOmd9Jsmjd+1t3a3qw==", + "dev": true, + "requires": { + "tsutils": "^2.27.2 <2.29.0" + }, + "dependencies": { + "tsutils": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.28.0.tgz", + "integrity": "sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + } + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + } + }, + "typescript": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.3.tgz", + "integrity": "sha512-B3ZIOf1IKeH2ixgHhj6la6xdwR9QrLC5d1VKeCSY4tvkqhF2eqd9O7txNlS0PO3GrBAFIdr3L1ndNwteUbZLYg==" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/package.json b/package.json index 208fab003..e30105ef8 100644 --- a/package.json +++ b/package.json @@ -1,35 +1,50 @@ { - "version": "2.1.2", - "name": "cordova-plugin-fcm", - "cordova_name": "Cordova FCM Push Plugin", - "description": "Google Firebase Cloud Messaging Cordova Push Plugin", - "license": "MIT", - "repo": "", - "issue": "", - "author": { - "name": "Felipe Echanique Torres", - "email": "felipe.echanique@gmail.com" - }, - "repository": { - "type": "git", - "url": "https://github.com/fechanique/cordova-plugin-fcm" - }, - "keywords": [ - "ecosystem:cordova", - "cordova-android", - "cordova-ios", - "notifications", - "push", - "firebase", - "fcm", - "ios", - "android", - "cordova" - ], - "platforms": [ - "android", - "ios" - ], - "engines": [], - "englishdoc": "" + "version": "7.8.0", + "name": "cordova-plugin-fcm-with-dependecy-updated", + "cordova_name": "Cordova FCM Push Plugin", + "description": "Google Firebase Cloud Messaging Cordova Push Plugin fork with dependecy updated", + "license": "MIT", + "main": "www/FCMPlugin.js", + "typings": "typings/index.d.ts", + "repo": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated", + "issue": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated/issues", + "author": "André Augusto Tissot", + "scripts": { + "build": "npm run build:www;npm run build:ionic", + "build:www": "cd ./src/www;npm run build", + "build:ionic": "cd ./src/ionic;npm run build", + "prettier-src": "find ./src/ -name '*.js' -or -name '*.ts' -or -name '*.tsx' -or -name '*.scss' | grep -v node_modules | xargs npx prettier --write", + "prettier-scripts": "find ./scripts -name '*.js' | xargs npx prettier --write", + "prettier": "npm run prettier-src;npm run prettier-scripts", + "tslint": "npx tslint -p ./src/www --fix;npx tslint -p ./src/ionic --fix" + }, + "repository": { + "type": "git", + "url": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated" + }, + "keywords": [ + "ecosystem:cordova", + "cordova-android", + "cordova-ios", + "notifications", + "push", + "firebase", + "fcm", + "ios", + "android", + "cordova" + ], + "platforms": [ + "android", + "ios" + ], + "englishdoc": "", + "devDependencies": { + "prettier": "^2.2.1", + "tslint": "^6.1.3", + "tslint-microsoft-contrib": "^6.2.0" + }, + "dependencies": { + "typescript": "^4.1.3" + } } diff --git a/plugin.xml b/plugin.xml index 3d6ccae6f..183c1737c 100644 --- a/plugin.xml +++ b/plugin.xml @@ -17,114 +17,119 @@ specific language governing permissions and limitations under the License. --> - - FCMPlugin - Cordova FCM Plugin - Apache 2.0 - cordova, fcm, push, plugin - - - Cordova FCM plugin v2.1.2 installed - For more details visit https://github.com/fechanique/cordova-plugin-fcm - - - - - - - - - - - - + + Cordova FCM Push Plugin + Google Firebase Cloud Messaging Cordova Push Plugin fork with dependecy updated + MIT + notifications, push, firebase, fcm, ios, android, cordova, plugin + + + + + + + + + + + + + + + + + + + - - - + + + - + - + - - - - - + - - - - + + + + + + + + + - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - development + development production - - - - - - + + + remote-notification + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + diff --git a/scripts/.prettierrc b/scripts/.prettierrc new file mode 100644 index 000000000..de023c2ae --- /dev/null +++ b/scripts/.prettierrc @@ -0,0 +1,7 @@ +{ + "semi": true, + "singleQuote": true, + "trailingComma": "none", + "printWidth": 100, + "tabWidth": 4, +} diff --git a/scripts/configuration.js b/scripts/configuration.js new file mode 100644 index 000000000..f3b12ac85 --- /dev/null +++ b/scripts/configuration.js @@ -0,0 +1,44 @@ +'use strict'; + +var helpers = require('./helpers'); + +var APP_NAME = helpers.getValueFromXml( + 'config.xml', + 'name', + 'app name was not found on config.xml' +); +var IOS_DIR = 'platforms/ios'; +var ANDROID_DIR = 'platforms/android'; + +exports.PLATFORM = { + IOS: { + label: 'ios', + dir: IOS_DIR, + googleServiceDestinations: [ + IOS_DIR + '/' + APP_NAME + '/Resources/GoogleService-Info.plist', + IOS_DIR + '/' + APP_NAME + '/Resources/Resources/GoogleService-Info.plist' + ], + googleServiceSources: [ + 'GoogleService-Info.plist', + IOS_DIR + '/www/GoogleService-Info.plist', + 'www/GoogleService-Info.plist' + ] + }, + ANDROID: { + label: 'android', + dir: ANDROID_DIR, + googleServiceDestinations: [ + ANDROID_DIR + '/google-services.json', + ANDROID_DIR + '/app/google-services.json' + ], + googleServiceSources: [ + ANDROID_DIR + '/google-services.json', + ANDROID_DIR + '/assets/www/google-services.json', + 'www/google-services.json', + 'google-services.json' + ], + stringsXmls: [ + ANDROID_DIR + '/app/build/generated/res/google-services/debug/values/values.xml' + ] + } +}; diff --git a/scripts/copy_google_service_files.js b/scripts/copy_google_service_files.js new file mode 100644 index 000000000..2e83a77ca --- /dev/null +++ b/scripts/copy_google_service_files.js @@ -0,0 +1,48 @@ +#!/usr/bin/env node +'use strict'; + +var fs = require('fs'); +var helpers = require('./helpers'); +var configurations = require(`./configuration`); + +var PLATFORM = configurations.PLATFORM; + +function copyGoogleServiceFile(platform) { + var googleServiceContent = helpers.getGoogleServiceContent(platform); + if (!googleServiceContent) { + return; + } + platform.googleServiceDestinations.forEach(function (destinationPath) { + try { + ensureFileDirExistance(destinationPath); + fs.writeFileSync(destinationPath, googleServiceContent); + } catch (error) { + helpers.logError('Error on trying to write ' + destinationPath, error); + return; + } + }); +} + +function ensureFileDirExistance(filePath) { + var dirPath = filePath.substring(0, filePath.lastIndexOf('/')); + ensureDirExistance(dirPath); +} + +function ensureDirExistance(dirPath) { + if (!fs.existsSync(dirPath)) { + dirPath.split('/').reduce(function (currentPath, folder) { + currentPath += folder + '/'; + if (!fs.existsSync(currentPath)) { + fs.mkdirSync(currentPath); + } + return currentPath; + }, ''); + } +} + +if (helpers.directoryExists(PLATFORM.ANDROID.dir)) { + copyGoogleServiceFile(PLATFORM.ANDROID); +} +if (helpers.directoryExists(PLATFORM.IOS.dir)) { + copyGoogleServiceFile(PLATFORM.IOS); +} diff --git a/scripts/fcm_config_files_process.js b/scripts/fcm_config_files_process.js deleted file mode 100644 index 1969efb6e..000000000 --- a/scripts/fcm_config_files_process.js +++ /dev/null @@ -1,130 +0,0 @@ -#!/usr/bin/env node -'use strict'; - -var fs = require('fs'); -var path = require('path'); - -fs.ensureDirSync = function (dir) { - if (!fs.existsSync(dir)) { - dir.split(path.sep).reduce(function (currentPath, folder) { - currentPath += folder + path.sep; - if (!fs.existsSync(currentPath)) { - fs.mkdirSync(currentPath); - } - return currentPath; - }, ''); - } -}; - -var config = fs.readFileSync('config.xml').toString(); -var name = getValue(config, 'name'); - -var IOS_DIR = 'platforms/ios'; -var ANDROID_DIR = 'platforms/android'; - -var PLATFORM = { - IOS: { - dest: [ - IOS_DIR + '/' + name + '/Resources/GoogleService-Info.plist', - IOS_DIR + '/' + name + '/Resources/Resources/GoogleService-Info.plist' - ], - src: [ - 'GoogleService-Info.plist', - IOS_DIR + '/www/GoogleService-Info.plist', - 'www/GoogleService-Info.plist' - ] - }, - ANDROID: { - dest: [ - ANDROID_DIR + '/google-services.json' - ], - src: [ - 'google-services.json', - ANDROID_DIR + '/assets/www/google-services.json', - 'www/google-services.json' - ], - stringsXml: ANDROID_DIR + '/res/values/strings.xml' - } -}; - -// Copy key files to their platform specific folders -if (directoryExists(IOS_DIR)) { - copyKey(PLATFORM.IOS); -} -if (directoryExists(ANDROID_DIR)) { - copyKey(PLATFORM.ANDROID, updateStringsXml) -} - -function updateStringsXml(contents) { - var json = JSON.parse(contents); - var strings = fs.readFileSync(PLATFORM.ANDROID.stringsXml).toString(); - - // strip non-default value - strings = strings.replace(new RegExp('([^\@<]+?)', 'i'), ''); - - // strip non-default value - strings = strings.replace(new RegExp('([^\@<]+?)', 'i'), ''); - - // strip empty lines - strings = strings.replace(new RegExp('(\r\n|\n|\r)[ \t]*(\r\n|\n|\r)', 'gm'), '$1'); - - // replace the default value - strings = strings.replace(new RegExp('([^<]+?)', 'i'), '' + json.client[0].client_info.mobilesdk_app_id + ''); - - // replace the default value - strings = strings.replace(new RegExp('([^<]+?)', 'i'), '' + json.client[0].api_key[0].current_key + ''); - - fs.writeFileSync(PLATFORM.ANDROID.stringsXml, strings); -} - -function copyKey(platform, callback) { - for (var i = 0; i < platform.src.length; i++) { - var file = platform.src[i]; - if (fileExists(file)) { - try { - var contents = fs.readFileSync(file).toString(); - - try { - platform.dest.forEach(function (destinationPath) { - var folder = destinationPath.substring(0, destinationPath.lastIndexOf('/')); - fs.ensureDirSync(folder); - fs.writeFileSync(destinationPath, contents); - }); - } catch (e) { - // skip - } - - callback && callback(contents); - } catch (err) { - console.log(err) - } - - break; - } - } -} - -function getValue(config, name) { - var value = config.match(new RegExp('<' + name + '>(.*?)', 'i')); - if (value && value[1]) { - return value[1] - } else { - return null - } -} - -function fileExists(path) { - try { - return fs.statSync(path).isFile(); - } catch (e) { - return false; - } -} - -function directoryExists(path) { - try { - return fs.statSync(path).isDirectory(); - } catch (e) { - return false; - } -} \ No newline at end of file diff --git a/scripts/helpers.js b/scripts/helpers.js new file mode 100644 index 000000000..07d65bbdd --- /dev/null +++ b/scripts/helpers.js @@ -0,0 +1,92 @@ +'use strict'; + +var fs = require('fs'); + +exports.fileExists = function (path) { + try { + return fs.statSync(path).isFile(); + } catch (e) { + return false; + } +}; + +exports.directoryExists = function (path) { + try { + return fs.statSync(path).isDirectory(); + } catch (e) { + return false; + } +}; + +exports.findExistingFilePath = function (paths) { + for (var i = paths.length - 1; i > -1; i--) { + if (exports.fileExists(paths[i])) { + return paths[i]; + } + } +}; + +exports.logError = function (message, obj) { + if (obj) { + console.error('\x1b[1m\x1b[31m%s\n%O\x1b[0m', message, obj); + } else { + console.error('\x1b[1m\x1b[31m%s\x1b[0m', message); + } +}; + +exports.logWarning = function (message) { + console.warn('\x1b[1m\x1b[33m%s\x1b[0m', message); +}; + +exports.getValueFromXml = function (xmlFilePath, name, errorMessage) { + var config = fs.readFileSync(xmlFilePath).toString(); + var value = config.match(new RegExp('<' + name + '[^>]*>(.*?)', 'i')); + if (value && value[1]) { + return value[1]; + } else { + exports.logWarning(errorMessage); + return null; + } +}; + +exports.getGoogleServiceContent = function (platform) { + var googleServiceSourcePath = exports.findExistingFilePath(platform.googleServiceSources); + if (!googleServiceSourcePath) { + if (platform.label === 'android') { + exports.logWarning('Android-specific google-services.json file not found!'); + } else { + exports.logWarning('iOS-specific GoogleService-Info.plist file not found!'); + } + return null; + } + + try { + return fs.readFileSync(googleServiceSourcePath).toString(); + } catch (error) { + exports.logError('Error on trying to read ' + googleServiceSourcePath, error); + return null; + } +}; + +exports.execute = function (command, args) { + return new Promise(function (resolve, reject) { + try { + const spawn = require('child_process').spawn; + const child = spawn(command, args); + const stdout = []; + child.stdout.on('data', function (buffer) { + const lines = buffer.toString().split('\n'); + for (const line of lines) { + if (line !== '') { + stdout.push(line); + } + } + }); + child.stdout.on('end', function () { + resolve(stdout.join('\n')); + }); + } catch (e) { + reject(e); + } + }); +}; diff --git a/scripts/install_ionic_dependencies.bat b/scripts/install_ionic_dependencies.bat new file mode 100755 index 000000000..5d0a0b74f --- /dev/null +++ b/scripts/install_ionic_dependencies.bat @@ -0,0 +1,6 @@ +cd node_modules +cd cordova-plugin-fcm-with-dependecy-updated +cd scripts +node install_ionic_dependencies.js ionic +node install_ionic_dependencies.js ionic/ngx +node install_ionic_dependencies.js ionic/v4 diff --git a/scripts/install_ionic_dependencies.js b/scripts/install_ionic_dependencies.js new file mode 100644 index 000000000..094988830 --- /dev/null +++ b/scripts/install_ionic_dependencies.js @@ -0,0 +1,67 @@ +const helpers = require('./helpers'); +const DEST_PATH = process.argv[2]; + +const shouldInstallIonicDependencies = function () { + const fs = require('fs'); + const packageFilePath = `${process.cwd()}/../../../package.json`; + if (!helpers.fileExists(packageFilePath)) { + helpers.logWarning('package.json was not found.'); + helpers.logWarning('Ionic dependencies omission cannot be safely skipped.'); + return true; + } + let packageDataString; + try { + packageDataString = fs.readFileSync(packageFilePath); + } catch (e) { + helpers.logWarning('package.json found is unreadable.', e); + helpers.logWarning('Ionic dependencies omission cannot be safely skipped.'); + return true; + } + let packageData; + try { + packageData = JSON.parse(packageDataString); + } catch (e) { + helpers.logWarning('package.json could not be parsed.', e); + helpers.logWarning('Ionic dependencies omission cannot be safely skipped.'); + return true; + } + return !!( + packageData && + packageData.dependencies && + packageData.dependencies['@ionic-native/core'] + ); +}; + +const installIonicDependencies = function () { + const path = require('path'); + const fullDestPath = `${path.dirname(process.cwd())}/${DEST_PATH}`; + try { + process.chdir(fullDestPath); + } catch (error) { + helpers.logError(`Failed change directory to ${fullDestPath}!`, error); + helpers.logError( + `Please run \`cd node_modules/cordova-plugin-fcm-with-dependecy-updated/${DEST_PATH}; npm install\` manually` + ); + return; + } + + const npm = process.platform === 'win32' ? 'npm.cmd' : 'npm'; + helpers + .execute(npm, ['install', '--loglevel', 'error', '--no-progress']) + .catch(function (e) { + helpers.logError('Failed to auto install Ionic dependencies!', e); + helpers.logError( + `Please run \`cd node_modules/cordova-plugin-fcm-with-dependecy-updated/${DEST_PATH}; npm install\` manually` + ); + }) + .then(function (output) { + console.log(`Ionic dependencies installed for ${DEST_PATH}:`); + console.log(output); + }); +}; + +if (shouldInstallIonicDependencies()) { + installIonicDependencies(); +} else { + console.log(`Ionic dependencies install skipped for ${DEST_PATH}`); +} diff --git a/src/android/FCMPlugin.gradle b/src/android/FCMPlugin.gradle index 4ab3a0990..6653cbf96 100644 --- a/src/android/FCMPlugin.gradle +++ b/src/android/FCMPlugin.gradle @@ -1,13 +1,111 @@ buildscript { - repositories { - jcenter() - mavenLocal() + ext { + // Load system enviroment defined values, or, if unavailable, plugin defaults + def ANDROID_GRADLE_TOOLS_VERSION = System.getenv('ANDROID_GRADLE_TOOLS_VERSION') ?: '4.1.0' + try { + // Load configuration from install variables (package.json) two directories above + def PLUGIN_CONFIG = new groovy.json.JsonSlurper().parseText(file("${projectDir}/../../package.json").text).cordova.plugins['cordova-plugin-fcm-with-dependecy-updated'] + ANDROID_GRADLE_TOOLS_VERSION = PLUGIN_CONFIG.ANDROID_GRADLE_TOOLS_VERSION + } catch(Exception e0) { + try { + // Load configuration from install variables (package.json) three directories above + def PLUGIN_CONFIG = new groovy.json.JsonSlurper().parseText(file("${projectDir}/../../../package.json").text).cordova.plugins['cordova-plugin-fcm-with-dependecy-updated'] + ANDROID_GRADLE_TOOLS_VERSION = PLUGIN_CONFIG.ANDROID_GRADLE_TOOLS_VERSION + } catch(Exception e1) { + def WARNING_STYLE = "${(char)27}[33;49"+"m" + def DEFAULT_STYLE = "${(char)27}[39;49"+"m" + print(WARNING_STYLE+"FCMPlugin: Plugin install variables are not accessible, as package.json was unreachable/unreadable."+DEFAULT_STYLE) + } } + // Override config with project values if available + ANDROID_GRADLE_TOOLS_VERSION = rootProject.hasProperty('ANDROID_GRADLE_TOOLS_VERSION') ? rootProject.ext.ANDROID_GRADLE_TOOLS_VERSION : ANDROID_GRADLE_TOOLS_VERSION + ext.gradleNumberVersion = ANDROID_GRADLE_TOOLS_VERSION.substring(0, ANDROID_GRADLE_TOOLS_VERSION.lastIndexOf(".")).toFloat() + } + repositories { + mavenCentral() + jcenter() + mavenLocal() + if (gradleNumberVersion >= 3.4f) { + google() + } + } dependencies { - classpath 'com.android.tools.build:gradle:+' - classpath 'com.google.gms:google-services:3.0.0' + // Load system enviroment defined values, or, if unavailable, plugin defaults + def ANDROID_GOOGLE_SERVICES_VERSION = System.getenv('ANDROID_GOOGLE_SERVICES_VERSION') ?: '4.3.4' + def ANDROID_GRADLE_TOOLS_VERSION = System.getenv('ANDROID_GRADLE_TOOLS_VERSION') ?: '4.1.0' + try { + // Load configuration from install variables (package.json) two directories above + def PLUGIN_CONFIG = new groovy.json.JsonSlurper().parseText(file("${projectDir}/../../package.json").text).cordova.plugins['cordova-plugin-fcm-with-dependecy-updated'] + ANDROID_GOOGLE_SERVICES_VERSION = PLUGIN_CONFIG.ANDROID_GOOGLE_SERVICES_VERSION + ANDROID_GRADLE_TOOLS_VERSION = PLUGIN_CONFIG.ANDROID_GRADLE_TOOLS_VERSION + } catch(Exception e0) { + try { + // Load configuration from install variables (package.json) three directories above + def PLUGIN_CONFIG = new groovy.json.JsonSlurper().parseText(file("${projectDir}/../../../package.json").text).cordova.plugins['cordova-plugin-fcm-with-dependecy-updated'] + ANDROID_GOOGLE_SERVICES_VERSION = PLUGIN_CONFIG.ANDROID_GOOGLE_SERVICES_VERSION + ANDROID_GRADLE_TOOLS_VERSION = PLUGIN_CONFIG.ANDROID_GRADLE_TOOLS_VERSION + } catch(Exception e1) { + def WARNING_STYLE = "${(char)27}[33;49"+"m" + def DEFAULT_STYLE = "${(char)27}[39;49"+"m" + print(WARNING_STYLE+"FCMPlugin: Plugin install variables are not accessible, as package.json was unreachable/unreadable."+DEFAULT_STYLE) + } + } + // Override config with project values if available + ANDROID_GOOGLE_SERVICES_VERSION = rootProject.hasProperty('ANDROID_GOOGLE_SERVICES_VERSION') ? rootProject.ext.ANDROID_GOOGLE_SERVICES_VERSION : ANDROID_GOOGLE_SERVICES_VERSION + ANDROID_GRADLE_TOOLS_VERSION = rootProject.hasProperty('ANDROID_GRADLE_TOOLS_VERSION') ? rootProject.ext.ANDROID_GRADLE_TOOLS_VERSION : ANDROID_GRADLE_TOOLS_VERSION + // Avoid use "GRADLE_VERSION" because jitpack sets this like a environment variable + classpath "com.android.tools.build:gradle:${ANDROID_GRADLE_TOOLS_VERSION}" + classpath "com.google.gms:google-services:${ANDROID_GOOGLE_SERVICES_VERSION}" + } +} +repositories { + mavenCentral() + jcenter() + if (gradleNumberVersion >= 3.4f) { + google() + } +} +dependencies { + // Load system enviroment defined values, or, if unavailable, plugin defaults + def ANDROID_FCM_VERSION = System.getenv('ANDROID_FCM_VERSION') ?: '21.0.0' + def ANDROID_FIREBASE_BOM_VERSION = System.getenv('ANDROID_FIREBASE_BOM_VERSION') ?: '26.0.0' + try { + // Load configuration from install variables (package.json) two directories above + def PLUGIN_CONFIG = new groovy.json.JsonSlurper().parseText(file("${projectDir}/../../package.json").text).cordova.plugins['cordova-plugin-fcm-with-dependecy-updated'] + ANDROID_FCM_VERSION = PLUGIN_CONFIG.ANDROID_FCM_VERSION + ANDROID_FIREBASE_BOM_VERSION = PLUGIN_CONFIG.ANDROID_FIREBASE_BOM_VERSION + } catch(Exception e0) { + try { + // Load configuration from install variables (package.json) three directories above + def PLUGIN_CONFIG = new groovy.json.JsonSlurper().parseText(file("${projectDir}/../../../package.json").text).cordova.plugins['cordova-plugin-fcm-with-dependecy-updated'] + ANDROID_FCM_VERSION = PLUGIN_CONFIG.ANDROID_FCM_VERSION + ANDROID_FIREBASE_BOM_VERSION = PLUGIN_CONFIG.ANDROID_FIREBASE_BOM_VERSION + } catch(Exception e1) { + def WARNING_STYLE = "${(char)27}[33;49"+"m" + def DEFAULT_STYLE = "${(char)27}[39;49"+"m" + print(WARNING_STYLE+"FCMPlugin: Plugin install variables are not accessible, as package.json was unreachable/unreadable."+DEFAULT_STYLE) + } + } + // Override config with project values if available + ANDROID_FCM_VERSION = rootProject.hasProperty('ANDROID_FCM_VERSION') ? rootProject.ext.ANDROID_FCM_VERSION : ANDROID_FCM_VERSION + ANDROID_FIREBASE_BOM_VERSION = rootProject.hasProperty('ANDROID_FIREBASE_BOM_VERSION') ? rootProject.ext.ANDROID_FIREBASE_BOM_VERSION : ANDROID_FIREBASE_BOM_VERSION + if (gradleNumberVersion < 3.4f) { + def WARNING_STYLE = "${(char)27}[33;49"+"m" + def DEFAULT_STYLE = "${(char)27}[39;49"+"m" + print(WARNING_STYLE+"FCMPlugin: Support for Gradle v3.3 or lower is deprecated. Please upgrade to a newer version."+DEFAULT_STYLE) + compile "com.google.firebase:firebase-messaging:${ANDROID_FCM_VERSION}" + } else if (gradleNumberVersion < 5.0f) { + def WARNING_STYLE = "${(char)27}[33;49"+"m" + def DEFAULT_STYLE = "${(char)27}[39;49"+"m" + print(WARNING_STYLE+"FCMPlugin: Support for Gradle v4 or lower is deprecated. Please upgrade to a newer version."+DEFAULT_STYLE) + implementation "com.google.firebase:firebase-messaging:${ANDROID_FCM_VERSION}" + } else { + implementation platform("com.google.firebase:firebase-bom:${ANDROID_FIREBASE_BOM_VERSION}") + implementation "com.google.firebase:firebase-messaging" } } // apply plugin: 'com.google.gms.google-services' // class must be used instead of id(string) to be able to apply plugin from non-root gradle file -apply plugin: com.google.gms.googleservices.GoogleServicesPlugin \ No newline at end of file +if (!project.plugins.hasPlugin('com.google.gms.google-services')) { + apply plugin: com.google.gms.googleservices.GoogleServicesPlugin +} diff --git a/src/android/FCMPlugin.java b/src/android/FCMPlugin.java deleted file mode 100644 index c23b9c762..000000000 --- a/src/android/FCMPlugin.java +++ /dev/null @@ -1,163 +0,0 @@ -package com.gae.scaffolder.plugin; - -import org.apache.cordova.CordovaWebView; -import org.apache.cordova.CallbackContext; -import org.apache.cordova.CordovaPlugin; -import org.apache.cordova.CordovaInterface; -import android.util.Log; - -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; - -import android.os.Bundle; - -import com.google.firebase.messaging.FirebaseMessaging; -import com.google.firebase.iid.FirebaseInstanceId; - -import java.util.Map; - -public class FCMPlugin extends CordovaPlugin { - - private static final String TAG = "FCMPlugin"; - - public static CordovaWebView gWebView; - public static String notificationCallBack = "FCMPlugin.onNotificationReceived"; - public static String tokenRefreshCallBack = "FCMPlugin.onTokenRefreshReceived"; - public static Boolean notificationCallBackReady = false; - public static Map lastPush = null; - - public FCMPlugin() {} - - public void initialize(CordovaInterface cordova, CordovaWebView webView) { - super.initialize(cordova, webView); - gWebView = webView; - Log.d(TAG, "==> FCMPlugin initialize"); - FirebaseMessaging.getInstance().subscribeToTopic("android"); - FirebaseMessaging.getInstance().subscribeToTopic("all"); - } - - public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException { - - Log.d(TAG,"==> FCMPlugin execute: "+ action); - - try{ - // READY // - if (action.equals("ready")) { - // - callbackContext.success(); - } - // GET TOKEN // - else if (action.equals("getToken")) { - cordova.getActivity().runOnUiThread(new Runnable() { - public void run() { - try{ - String token = FirebaseInstanceId.getInstance().getToken(); - callbackContext.success( FirebaseInstanceId.getInstance().getToken() ); - Log.d(TAG,"\tToken: "+ token); - }catch(Exception e){ - Log.d(TAG,"\tError retrieving token"); - } - } - }); - } - // NOTIFICATION CALLBACK REGISTER // - else if (action.equals("registerNotification")) { - notificationCallBackReady = true; - cordova.getActivity().runOnUiThread(new Runnable() { - public void run() { - if(lastPush != null) FCMPlugin.sendPushPayload( lastPush ); - lastPush = null; - callbackContext.success(); - } - }); - } - // UN/SUBSCRIBE TOPICS // - else if (action.equals("subscribeToTopic")) { - cordova.getThreadPool().execute(new Runnable() { - public void run() { - try{ - FirebaseMessaging.getInstance().subscribeToTopic( args.getString(0) ); - callbackContext.success(); - }catch(Exception e){ - callbackContext.error(e.getMessage()); - } - } - }); - } - else if (action.equals("unsubscribeFromTopic")) { - cordova.getThreadPool().execute(new Runnable() { - public void run() { - try{ - FirebaseMessaging.getInstance().unsubscribeFromTopic( args.getString(0) ); - callbackContext.success(); - }catch(Exception e){ - callbackContext.error(e.getMessage()); - } - } - }); - } - else{ - callbackContext.error("Method not found"); - return false; - } - }catch(Exception e){ - Log.d(TAG, "ERROR: onPluginAction: " + e.getMessage()); - callbackContext.error(e.getMessage()); - return false; - } - - //cordova.getThreadPool().execute(new Runnable() { - // public void run() { - // // - // } - //}); - - //cordova.getActivity().runOnUiThread(new Runnable() { - // public void run() { - // // - // } - //}); - return true; - } - - public static void sendPushPayload(Map payload) { - Log.d(TAG, "==> FCMPlugin sendPushPayload"); - Log.d(TAG, "\tnotificationCallBackReady: " + notificationCallBackReady); - Log.d(TAG, "\tgWebView: " + gWebView); - try { - JSONObject jo = new JSONObject(); - for (String key : payload.keySet()) { - jo.put(key, payload.get(key)); - Log.d(TAG, "\tpayload: " + key + " => " + payload.get(key)); - } - String callBack = "javascript:" + notificationCallBack + "(" + jo.toString() + ")"; - if(notificationCallBackReady && gWebView != null){ - Log.d(TAG, "\tSent PUSH to view: " + callBack); - gWebView.sendJavascript(callBack); - }else { - Log.d(TAG, "\tView not ready. SAVED NOTIFICATION: " + callBack); - lastPush = payload; - } - } catch (Exception e) { - Log.d(TAG, "\tERROR sendPushToView. SAVED NOTIFICATION: " + e.getMessage()); - lastPush = payload; - } - } - - public static void sendTokenRefresh(String token) { - Log.d(TAG, "==> FCMPlugin sendRefreshToken"); - try { - String callBack = "javascript:" + tokenRefreshCallBack + "('" + token + "')"; - gWebView.sendJavascript(callBack); - } catch (Exception e) { - Log.d(TAG, "\tERROR sendRefreshToken: " + e.getMessage()); - } - } - - @Override - public void onDestroy() { - gWebView = null; - notificationCallBackReady = false; - } -} diff --git a/src/android/MyFirebaseInstanceIDService.java b/src/android/MyFirebaseInstanceIDService.java deleted file mode 100644 index 8fb3ff123..000000000 --- a/src/android/MyFirebaseInstanceIDService.java +++ /dev/null @@ -1,25 +0,0 @@ -package com.gae.scaffolder.plugin; - -import android.util.Log; - -import com.google.firebase.iid.FirebaseInstanceId; -import com.google.firebase.iid.FirebaseInstanceIdService; - -/** - * Created by Felipe Echanique on 08/06/2016. - */ -public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService { - - private static final String TAG = "FCMPlugin"; - - @Override - public void onTokenRefresh(){ - // Get updated InstanceID token. - String refreshedToken = FirebaseInstanceId.getInstance().getToken(); - Log.d(TAG, "Refreshed token: " + refreshedToken); - FCMPlugin.sendTokenRefresh( refreshedToken ); - - // TODO: Implement this method to send any registration to your app's servers. - //sendRegistrationToServer(refreshedToken); - } -} diff --git a/src/android/MyFirebaseMessagingService.java b/src/android/MyFirebaseMessagingService.java deleted file mode 100644 index 941e7ca75..000000000 --- a/src/android/MyFirebaseMessagingService.java +++ /dev/null @@ -1,85 +0,0 @@ -package com.gae.scaffolder.plugin; - -import android.app.NotificationManager; -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.media.RingtoneManager; -import android.net.Uri; -import android.support.v4.app.NotificationCompat; -import android.util.Log; -import java.util.Map; -import java.util.HashMap; - -import com.google.firebase.messaging.FirebaseMessagingService; -import com.google.firebase.messaging.RemoteMessage; - -/** - * Created by Felipe Echanique on 08/06/2016. - */ -public class MyFirebaseMessagingService extends FirebaseMessagingService { - - private static final String TAG = "FCMPlugin"; - - /** - * Called when message is received. - * - * @param remoteMessage Object representing the message received from Firebase Cloud Messaging. - */ - // [START receive_message] - @Override - public void onMessageReceived(RemoteMessage remoteMessage) { - // TODO(developer): Handle FCM messages here. - // If the application is in the foreground handle both data and notification messages here. - // Also if you intend on generating your own notifications as a result of a received FCM - // message, here is where that should be initiated. See sendNotification method below. - Log.d(TAG, "==> MyFirebaseMessagingService onMessageReceived"); - - if( remoteMessage.getNotification() != null){ - Log.d(TAG, "\tNotification Title: " + remoteMessage.getNotification().getTitle()); - Log.d(TAG, "\tNotification Message: " + remoteMessage.getNotification().getBody()); - } - - Map data = new HashMap(); - data.put("wasTapped", false); - for (String key : remoteMessage.getData().keySet()) { - Object value = remoteMessage.getData().get(key); - Log.d(TAG, "\tKey: " + key + " Value: " + value); - data.put(key, value); - } - - Log.d(TAG, "\tNotification Data: " + data.toString()); - FCMPlugin.sendPushPayload( data ); - //sendNotification(remoteMessage.getNotification().getTitle(), remoteMessage.getNotification().getBody(), remoteMessage.getData()); - } - // [END receive_message] - - /** - * Create and show a simple notification containing the received FCM message. - * - * @param messageBody FCM message body received. - */ - private void sendNotification(String title, String messageBody, Map data) { - Intent intent = new Intent(this, FCMPluginActivity.class); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); - for (String key : data.keySet()) { - intent.putExtra(key, data.get(key).toString()); - } - PendingIntent pendingIntent = PendingIntent.getActivity(this, 0 /* Request code */, intent, - PendingIntent.FLAG_ONE_SHOT); - - Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); - NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this) - .setSmallIcon(getApplicationInfo().icon) - .setContentTitle(title) - .setContentText(messageBody) - .setAutoCancel(true) - .setSound(defaultSoundUri) - .setContentIntent(pendingIntent); - - NotificationManager notificationManager = - (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); - - notificationManager.notify(0 /* ID of notification */, notificationBuilder.build()); - } -} diff --git a/src/android/com/gae/scaffolder/plugin/FCMPlugin.java b/src/android/com/gae/scaffolder/plugin/FCMPlugin.java new file mode 100644 index 000000000..08e335d36 --- /dev/null +++ b/src/android/com/gae/scaffolder/plugin/FCMPlugin.java @@ -0,0 +1,329 @@ +package com.gae.scaffolder.plugin; + +import androidx.core.app.NotificationManagerCompat; +import android.app.NotificationManager; +import android.content.Context; +import android.util.Log; + +import com.gae.scaffolder.plugin.interfaces.*; +import com.google.android.gms.tasks.OnCompleteListener; +import com.google.android.gms.tasks.OnFailureListener; +import com.google.android.gms.tasks.Task; +import com.google.firebase.iid.FirebaseInstanceId; +import com.google.firebase.iid.InstanceIdResult; +import com.google.firebase.messaging.FirebaseMessaging; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Map; + +public class FCMPlugin extends CordovaPlugin { + public static String notificationEventName = "notification"; + public static String tokenRefreshEventName = "tokenRefresh"; + public static Map initialPushPayload; + public static final String TAG = "FCMPlugin"; + private static FCMPlugin instance; + protected Context context; + protected static CallbackContext jsEventBridgeCallbackContext; + + public FCMPlugin() {} + public FCMPlugin(Context context) { + this.context = context; + } + + public static synchronized FCMPlugin getInstance(Context context) { + if (instance == null) { + instance = new FCMPlugin(context); + instance = getPlugin(instance); + } + + return instance; + } + + public static synchronized FCMPlugin getInstance() { + if (instance == null) { + instance = new FCMPlugin(); + instance = getPlugin(instance); + } + + return instance; + } + + public static FCMPlugin getPlugin(FCMPlugin plugin) { + if (plugin.webView != null) { + instance = (FCMPlugin) plugin.webView.getPluginManager().getPlugin(FCMPlugin.class.getName()); + } else { + plugin.initialize(null, null); + instance = plugin; + } + + return instance; + } + + public void initialize(CordovaInterface cordova, CordovaWebView webView) { + super.initialize(cordova, webView); + Log.d(TAG, "==> FCMPlugin initialize"); + + FirebaseMessaging.getInstance().subscribeToTopic("android"); + FirebaseMessaging.getInstance().subscribeToTopic("all"); + } + + public boolean execute(final String action, final JSONArray args, final CallbackContext callbackContext) throws JSONException { + Log.d(TAG, "==> FCMPlugin execute: " + action); + + try { + if (action.equals("ready")) { + callbackContext.success(); + } else if (action.equals("startJsEventBridge")) { + this.jsEventBridgeCallbackContext = callbackContext; + } else if (action.equals("getToken")) { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + getToken(callbackContext); + } + }); + } else if (action.equals("getInitialPushPayload")) { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + getInitialPushPayload(callbackContext); + } + }); + } else if (action.equals("subscribeToTopic")) { + cordova.getThreadPool().execute(new Runnable() { + public void run() { + try { + FirebaseMessaging.getInstance().subscribeToTopic(args.getString(0)); + callbackContext.success(); + } catch (Exception e) { + callbackContext.error(e.getMessage()); + } + } + }); + } else if (action.equals("unsubscribeFromTopic")) { + cordova.getThreadPool().execute(new Runnable() { + public void run() { + try { + FirebaseMessaging.getInstance().unsubscribeFromTopic(args.getString(0)); + callbackContext.success(); + } catch (Exception e) { + callbackContext.error(e.getMessage()); + } + } + }); + } else if (action.equals("clearAllNotifications")) { + cordova.getThreadPool().execute(new Runnable() { + public void run() { + try { + Context context = cordova.getActivity(); + NotificationManager nm = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); + nm.cancelAll(); + callbackContext.success(); + } catch (Exception e) { + callbackContext.error(e.getMessage()); + } + } + }); + } else if (action.equals("createNotificationChannel")) { + cordova.getActivity().runOnUiThread(new Runnable() { + public void run() { + new FCMPluginChannelCreator(getContext()).createNotificationChannel(callbackContext, args); + } + }); + } else if (action.equals("deleteInstanceId")) { + this.deleteInstanceId(callbackContext); + } else if (action.equals("hasPermission")) { + this.hasPermission(callbackContext); + } else { + callbackContext.error("Method not found"); + return false; + } + } catch (Exception e) { + Log.d(TAG, "ERROR: onPluginAction: " + e.getMessage()); + callbackContext.error(e.getMessage()); + return false; + } + + return true; + } + + public void getInitialPushPayload(CallbackContext callback) { + if(initialPushPayload == null) { + Log.d(TAG, "getInitialPushPayload: null"); + callback.success((String) null); + return; + } + Log.d(TAG, "getInitialPushPayload"); + try { + JSONObject jo = new JSONObject(); + for (String key : initialPushPayload.keySet()) { + jo.put(key, initialPushPayload.get(key)); + Log.d(TAG, "\tinitialPushPayload: " + key + " => " + initialPushPayload.get(key)); + } + callback.success(jo); + } catch(Exception error) { + try { + callback.error(exceptionToJson(error)); + } + catch (JSONException jsonErr) { + Log.e(TAG, "Error when parsing json", jsonErr); + } + } + } + + public void getToken(final TokenListeners callback) { + try { + FirebaseInstanceId.getInstance().getInstanceId().addOnCompleteListener(new OnCompleteListener() { + @Override + public void onComplete(Task task) { + if (!task.isSuccessful()) { + Log.w(TAG, "getInstanceId failed", task.getException()); + try { + callback.error(exceptionToJson(task.getException())); + } + catch (JSONException jsonErr) { + Log.e(TAG, "Error when parsing json", jsonErr); + } + return; + } + + // Get new Instance ID token + String newToken = task.getResult().getToken(); + + Log.i(TAG, "\tToken: " + newToken); + callback.success(newToken); + } + }); + + FirebaseInstanceId.getInstance().getInstanceId().addOnFailureListener(new OnFailureListener() { + @Override + public void onFailure(final Exception e) { + try { + Log.e(TAG, "Error retrieving token: ", e); + callback.error(exceptionToJson(e)); + } catch (JSONException jsonErr) { + Log.e(TAG, "Error when parsing json", jsonErr); + } + } + }); + } catch (Exception e) { + Log.w(TAG, "\tError retrieving token", e); + try { + callback.error(exceptionToJson(e)); + } catch(JSONException je) {} + } + } + + private void deleteInstanceId(final CallbackContext callbackContext) { + cordova.getThreadPool().execute(new Runnable() { + public void run() { + try { + FirebaseInstanceId.getInstance().deleteInstanceId(); + callbackContext.success(); + } catch (Exception e) { + callbackContext.error(e.getMessage()); + } + } + }); + } + + private void hasPermission(final CallbackContext callbackContext) { + cordova.getThreadPool().execute(new Runnable() { + public void run() { + try { + NotificationManagerCompat notificationManagerCompat = + NotificationManagerCompat.from(cordova.getActivity().getApplicationContext()); + callbackContext.success(notificationManagerCompat.areNotificationsEnabled() ? 1 : 0); + } catch (Exception e) { + callbackContext.error(e.getMessage()); + } + } + }); + } + + private JSONObject exceptionToJson(final Exception exception) throws JSONException { + return new JSONObject() { + { + put("message", exception.getMessage()); + put("cause", exception.getClass().getName()); + put("stacktrace", exception.getStackTrace().toString()); + } + }; + } + + public void getToken(final CallbackContext callbackContext) { + this.getToken(new TokenListeners() { + @Override + public void success(String message) { + callbackContext.success(message); + } + + @Override + public void error(JSONObject message) { + callbackContext.error(message); + } + }); + } + + private static void dispatchJSEvent(String eventName, String stringifiedJSONValue) throws Exception { + String jsEventData = "[\"" + eventName + "\"," + stringifiedJSONValue + "]"; + PluginResult dataResult = new PluginResult(PluginResult.Status.OK, jsEventData); + dataResult.setKeepCallback(true); + if(FCMPlugin.jsEventBridgeCallbackContext == null) { + Log.d(TAG, "\tUnable to send event due to unreachable bridge context"); + return; + } + FCMPlugin.jsEventBridgeCallbackContext.sendPluginResult(dataResult); + Log.d(TAG, "\tSent event: " + eventName + " with " + stringifiedJSONValue); + } + + public static void setInitialPushPayload(Map payload) { + if(initialPushPayload == null) { + initialPushPayload = payload; + } + } + + public static void sendPushPayload(Map payload) { + Log.d(TAG, "==> FCMPlugin sendPushPayload"); + try { + JSONObject jo = new JSONObject(); + for (String key : payload.keySet()) { + jo.put(key, payload.get(key)); + Log.d(TAG, "\tpayload: " + key + " => " + payload.get(key)); + } + FCMPlugin.dispatchJSEvent(notificationEventName, jo.toString()); + } catch (Exception e) { + Log.d(TAG, "\tERROR sendPushPayload: " + e.getMessage()); + } + } + + public static void sendTokenRefresh(String token) { + Log.d(TAG, "==> FCMPlugin sendTokenRefresh"); + try { + FCMPlugin.dispatchJSEvent(tokenRefreshEventName, "\"" + token + "\""); + } catch (Exception e) { + Log.d(TAG, "\tERROR sendTokenRefresh: " + e.getMessage()); + } + } + + @Override + public void onDestroy() { + initialPushPayload = null; + jsEventBridgeCallbackContext = null; + } + + protected Context getContext() { + context = cordova != null ? cordova.getActivity().getBaseContext() : context; + if (context == null) { + throw new RuntimeException("The Android Context is required. Verify if the 'activity' or 'context' are passed by constructor"); + } + + return context; + } +} diff --git a/src/android/FCMPluginActivity.java b/src/android/com/gae/scaffolder/plugin/FCMPluginActivity.java similarity index 56% rename from src/android/FCMPluginActivity.java rename to src/android/com/gae/scaffolder/plugin/FCMPluginActivity.java index c613f5ee7..35acda3b1 100644 --- a/src/android/FCMPluginActivity.java +++ b/src/android/com/gae/scaffolder/plugin/FCMPluginActivity.java @@ -4,48 +4,48 @@ import android.app.NotificationManager; import android.content.Context; import android.content.Intent; -import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Bundle; -import android.preference.PreferenceManager; -import android.support.v4.content.LocalBroadcastManager; import android.util.Log; -import java.util.Map; import java.util.HashMap; +import java.util.Map; public class FCMPluginActivity extends Activity { private static String TAG = "FCMPlugin"; /* - * this activity will be started if the user touches a notification that we own. + * this activity will be started if the user touches a notification that we own. * We send it's data off to the push plugin for processing. - * If needed, we boot up the main activity to kickstart the application. + * If needed, we boot up the main activity to kickstart the application. * @see android.app.Activity#onCreate(android.os.Bundle) */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - Log.d(TAG, "==> FCMPluginActivity onCreate"); - - Map data = new HashMap(); - if (getIntent().getExtras() != null) { - Log.d(TAG, "==> USER TAPPED NOTFICATION"); - data.put("wasTapped", true); - for (String key : getIntent().getExtras().keySet()) { - String value = getIntent().getExtras().getString(key); - Log.d(TAG, "\tKey: " + key + " Value: " + value); - data.put(key, value); - } - } - - FCMPlugin.sendPushPayload(data); - + Log.d(TAG, "==> FCMPluginActivity onCreate"); + this.sendPushPayload(); finish(); - forceMainActivityReload(); } + private void sendPushPayload() { + Bundle intentExtras = getIntent().getExtras(); + if(intentExtras == null) { + return; + } + Log.d(TAG, "==> USER TAPPED NOTIFICATION"); + Map data = new HashMap(); + data.put("wasTapped", true); + for (String key : intentExtras.keySet()) { + Object value = intentExtras.get(key); + Log.d(TAG, "\tKey: " + key + " Value: " + value); + data.put(key, value); + } + FCMPlugin.setInitialPushPayload(data); + FCMPlugin.sendPushPayload(data); + } + private void forceMainActivityReload() { PackageManager pm = getPackageManager(); Intent launchIntent = pm.getLaunchIntentForPackage(getApplicationContext().getPackageName()); @@ -55,21 +55,21 @@ private void forceMainActivityReload() { @Override protected void onResume() { super.onResume(); - Log.d(TAG, "==> FCMPluginActivity onResume"); + Log.d(TAG, "==> FCMPluginActivity onResume"); final NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.cancelAll(); } - - @Override - public void onStart() { - super.onStart(); - Log.d(TAG, "==> FCMPluginActivity onStart"); - } - - @Override - public void onStop() { - super.onStop(); - Log.d(TAG, "==> FCMPluginActivity onStop"); - } + + @Override + public void onStart() { + super.onStart(); + Log.d(TAG, "==> FCMPluginActivity onStart"); + } + + @Override + public void onStop() { + super.onStop(); + Log.d(TAG, "==> FCMPluginActivity onStop"); + } } \ No newline at end of file diff --git a/src/android/com/gae/scaffolder/plugin/FCMPluginChannelCreator.java b/src/android/com/gae/scaffolder/plugin/FCMPluginChannelCreator.java new file mode 100644 index 000000000..6d025b613 --- /dev/null +++ b/src/android/com/gae/scaffolder/plugin/FCMPluginChannelCreator.java @@ -0,0 +1,134 @@ +package com.gae.scaffolder.plugin; + +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; +import android.media.AudioAttributes; +import android.content.Context; +import android.os.Build; +import android.util.Log; +import android.net.Uri; + +import org.apache.cordova.CallbackContext; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +public class FCMPluginChannelCreator { + private static int INVALID_INT_OPTION = -1000; + private static final String TAG = FCMPlugin.TAG; + private Context context; + + public FCMPluginChannelCreator(Context context) { + this.context = context; + } + + class ChannelConfig { + public String id; + public String name; + public String description; + public int importance; + public int visibility; + public String sound; + public Boolean lights; + public Boolean vibration; + + public ChannelConfig(final JSONObject channelConfigJson) throws JSONException { + this.id = channelConfigJson.getString("id"); + this.name = channelConfigJson.getString("name"); + this.description = channelConfigJson.optString("description"); + this.importance = convertImportanceStringToInt(channelConfigJson.optString("importance")); + this.visibility = convertVisibilityStringToInt(channelConfigJson.optString("visibility")); + this.sound = channelConfigJson.optString("sound"); + this.lights = channelConfigJson.has("lights") + ? channelConfigJson.optBoolean("lights", false) : null; + this.vibration = channelConfigJson.has("vibration") + ? channelConfigJson.optBoolean("vibration", false) : null; + } + + private int convertImportanceStringToInt(String importance) { + switch (importance) { + case "none": + return NotificationManager.IMPORTANCE_NONE; + case "min": + return NotificationManager.IMPORTANCE_MIN; + case "low": + return NotificationManager.IMPORTANCE_LOW; + case "default": + return NotificationManager.IMPORTANCE_DEFAULT; + case "high": + return NotificationManager.IMPORTANCE_HIGH; + default: + return NotificationManager.IMPORTANCE_UNSPECIFIED; + } + } + + private int convertVisibilityStringToInt(String visibility) { + switch (visibility) { + case "public": + return Notification.VISIBILITY_PUBLIC; + case "private": + return Notification.VISIBILITY_PRIVATE; + case "secret": + return Notification.VISIBILITY_SECRET; + default: + return INVALID_INT_OPTION; + } + } + + public JSONObject toJSONObject() throws JSONException { + JSONObject json = new JSONObject(); + json.put("id", this.id); + json.put("name", this.name); + json.put("description", this.description); + json.put("importance", this.importance); + json.put("visibility", this.visibility); + json.put("sound", this.sound); + json.put("lights", this.lights); + json.put("vibration", this.vibration); + return json; + } + } + + public void createNotificationChannel(final CallbackContext callbackContext, final JSONArray args) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) { + callbackContext.success(); + return; + } + try { + Log.d(TAG, "Channel started with "+args.toString()); + ChannelConfig channelConfig = new ChannelConfig(args.getJSONObject(0)); + NotificationChannel channel = new NotificationChannel(channelConfig.id, channelConfig.name, channelConfig.importance); + if(channelConfig.visibility != INVALID_INT_OPTION) { + channel.setLockscreenVisibility(channelConfig.visibility); + } + if(!channelConfig.description.equals("")) { + channel.setDescription(channelConfig.description); + } + if(!channelConfig.sound.equals("")) { + AudioAttributes audioAttributes = new AudioAttributes.Builder() + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE) + .build(); + channel.setSound(Uri.parse("android.resource://" + this.context.getPackageName() + "/raw/" + channelConfig.sound), audioAttributes); + } + if(channelConfig.lights != null) { + channel.enableLights(channelConfig.lights); + } + if(channelConfig.vibration != null) { + channel.enableVibration(channelConfig.vibration); + } + + // Register the channel with the system; you can't change the importance + // or other notification behaviors after this + NotificationManager notificationManager = this.context + .getSystemService(NotificationManager.class); + notificationManager.createNotificationChannel(channel); + callbackContext.success(); + Log.d(TAG, "Channel finished as "+channelConfig.toJSONObject().toString()); + } catch (Exception e) { + Log.w(TAG, "createNotificationChannel: "+e.getMessage()); + callbackContext.error(e.getMessage()); + } + } +} diff --git a/src/android/com/gae/scaffolder/plugin/MyFirebaseMessagingService.java b/src/android/com/gae/scaffolder/plugin/MyFirebaseMessagingService.java new file mode 100644 index 000000000..072d755bf --- /dev/null +++ b/src/android/com/gae/scaffolder/plugin/MyFirebaseMessagingService.java @@ -0,0 +1,64 @@ +package com.gae.scaffolder.plugin; + +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.media.RingtoneManager; +import android.net.Uri; +import android.util.Log; +import java.util.Map; +import java.util.HashMap; + +import com.google.firebase.messaging.FirebaseMessagingService; +import com.google.firebase.messaging.RemoteMessage; + +public class MyFirebaseMessagingService extends FirebaseMessagingService { + + private static final String TAG = "FCMPlugin"; + + @Override + public void onNewToken(String token) { + super.onNewToken(token); + Log.d(TAG, "New token: " + token); + FCMPlugin.sendTokenRefresh(token); + } + + /** + * Called when message is received. + * + * @param remoteMessage Object representing the message received from Firebase Cloud Messaging. + */ + // [START receive_message] + @Override + public void onMessageReceived(RemoteMessage remoteMessage) { + // TODO(developer): Handle FCM messages here. + // If the application is in the foreground handle both data and notification messages here. + // Also if you intend on generating your own notifications as a result of a received FCM + // message, here is where that should be initiated. See sendNotification method below. + Log.d(TAG, "==> MyFirebaseMessagingService onMessageReceived"); + + if(remoteMessage.getNotification() != null){ + Log.d(TAG, "\tNotification Title: " + remoteMessage.getNotification().getTitle()); + Log.d(TAG, "\tNotification Message: " + remoteMessage.getNotification().getBody()); + } + + Map data = new HashMap(); + data.put("wasTapped", false); + + if(remoteMessage.getNotification() != null){ + data.put("title", remoteMessage.getNotification().getTitle()); + data.put("body", remoteMessage.getNotification().getBody()); + } + + for (String key : remoteMessage.getData().keySet()) { + Object value = remoteMessage.getData().get(key); + Log.d(TAG, "\tKey: " + key + " Value: " + value); + data.put(key, value); + } + + Log.d(TAG, "\tNotification Data: " + data.toString()); + FCMPlugin.sendPushPayload(data); + } + // [END receive_message] +} diff --git a/src/android/com/gae/scaffolder/plugin/interfaces/OnFinishedListener.java b/src/android/com/gae/scaffolder/plugin/interfaces/OnFinishedListener.java new file mode 100644 index 000000000..b625d684b --- /dev/null +++ b/src/android/com/gae/scaffolder/plugin/interfaces/OnFinishedListener.java @@ -0,0 +1,5 @@ +package com.gae.scaffolder.plugin.interfaces; + +public interface OnFinishedListener { + void success(TResult result); +} diff --git a/src/android/com/gae/scaffolder/plugin/interfaces/TokenListeners.java b/src/android/com/gae/scaffolder/plugin/interfaces/TokenListeners.java new file mode 100644 index 000000000..ff5b1f2a1 --- /dev/null +++ b/src/android/com/gae/scaffolder/plugin/interfaces/TokenListeners.java @@ -0,0 +1,5 @@ +package com.gae.scaffolder.plugin.interfaces; + +public interface TokenListeners extends OnFinishedListener { + void error(TError message); +} diff --git a/src/ionic/FCM.ts b/src/ionic/FCM.ts new file mode 100644 index 000000000..7a013b67c --- /dev/null +++ b/src/ionic/FCM.ts @@ -0,0 +1,91 @@ +import { Observable, Subject } from 'rxjs' +import { INotificationPayload } from '../../typings/INotificationPayload' +import { FCMPlugin } from '../www/FCMPlugin' +import { IRequestPushPermissionOptions } from '../../typings/IRequestPushPermissionOptions' +import { IChannelConfiguration } from '../../typings/IChannelConfiguration' + +declare namespace window { + export let FCM: FCMPlugin +} + +/** @copyFrom typings/FCMPlugin.d.ts FCMPlugin */ +export class FCMPluginOnIonic { + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin clearAllNotifications */ + public clearAllNotifications(): Promise { + return window.FCM.clearAllNotifications() + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin createNotificationChannel */ + public createNotificationChannel(channelConfig: IChannelConfiguration): Promise { + return window.FCM.createNotificationChannel(channelConfig) + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin deleteInstanceId */ + public deleteInstanceId(): Promise { + return window.FCM.deleteInstanceId() + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin getAPNSToken */ + public getAPNSToken(): Promise { + return window.FCM.getAPNSToken() + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin getInitialPushPayload */ + public getInitialPushPayload(): Promise { + return window.FCM.getInitialPushPayload() + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin getToken */ + public getToken(): Promise { + return window.FCM.getToken() + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin hasPermission */ + public hasPermission(): Promise { + return window.FCM.hasPermission() + } + + /** + * Event firing when receiving new notifications + * + * @argument {{ once?: boolean }} options once defines if the listener is only trigger once + * @returns {Observable} An object to listen for notification data + */ + public onNotification(options?: { once?: boolean }): Observable { + const observable = new Subject() + const handler = (payload: INotificationPayload) => observable.next(payload) + window.FCM.onNotification(handler, options) + + return observable + } + + /** + * Event firing when receiving a new Firebase token + * + * @argument {{ once?: boolean }} options once defines if the listener is only trigger once + * @returns {Observable} An object to listen for the token + */ + public onTokenRefresh(options?: { once?: boolean }): Observable { + const observable = new Subject() + window.FCM.onTokenRefresh((token: string) => observable.next(token), options) + + return observable + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin requestPushPermission */ + public requestPushPermission(options?: IRequestPushPermissionOptions): Promise { + return window.FCM.requestPushPermission(options) + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin subscribeToTopic */ + public subscribeToTopic(topic: string): Promise { + return window.FCM.subscribeToTopic(topic) + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin unsubscribeFromTopic */ + public unsubscribeFromTopic(topic: string): Promise { + return window.FCM.unsubscribeFromTopic(topic) + } +} + +export const FCM = new FCMPluginOnIonic() diff --git a/src/ionic/ngx/FCM.ts b/src/ionic/ngx/FCM.ts new file mode 100644 index 000000000..808003f45 --- /dev/null +++ b/src/ionic/ngx/FCM.ts @@ -0,0 +1,95 @@ +import { Injectable } from '@angular/core' +import { Observable, Subject } from 'rxjs' +import { IonicNativePlugin } from '@ionic-native/core' +import type { FCMPlugin } from '../../www/FCMPlugin' +import type { IChannelConfiguration } from '../../www/IChannelConfiguration' +import type { INotificationPayload } from '../../www/INotificationPayload' +import type { IRequestPushPermissionOptions } from '../../www/IRequestPushPermissionOptions' + +declare namespace window { + export let FCM: FCMPlugin +} + +/** @copyFrom typings/FCMPlugin.d.ts FCMPlugin */ +@Injectable() +export class FCM { + public static pluginName: string = 'FCM' + public static plugin: string = 'cordova-plugin-fcm-with-dependecy-updated' + public static pluginRef: string = 'FCM' + public static repo: string = + 'https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated' + public static platforms: string[] = ['Android', 'iOS'] + public static installed: () => boolean = IonicNativePlugin.installed + public static getPlugin: () => any = IonicNativePlugin.getPlugin + public static getPluginName: () => string = IonicNativePlugin.getPluginName + public static getPluginRef: () => string = IonicNativePlugin.getPluginRef + public static getPluginInstallName: () => string = IonicNativePlugin.getPluginInstallName + public static getSupportedPlatforms: () => string[] = IonicNativePlugin.getSupportedPlatforms + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin clearAllNotifications */ + public clearAllNotifications(): Promise { + return window.FCM.clearAllNotifications() + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin createNotificationChannel */ + public createNotificationChannel(channelConfig: IChannelConfiguration): Promise { + return window.FCM.createNotificationChannel(channelConfig) + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin deleteInstanceId */ + public deleteInstanceId(): Promise { + return window.FCM.deleteInstanceId() + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin getAPNSToken */ + public getAPNSToken(): Promise { + return window.FCM.getAPNSToken() + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin getInitialPushPayload */ + public getInitialPushPayload(): Promise { + return window.FCM.getInitialPushPayload() + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin getToken */ + public getToken(): Promise { + return window.FCM.getToken() + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin hasPermission */ + public hasPermission(): Promise { + return window.FCM.hasPermission() + } + + /** @copyFrom ionic/FCM.d.ts FCMPluginOnIonic onNotification */ + public onNotification(options?: { once?: boolean }): Observable { + const observable = new Subject() + const handler = (payload: INotificationPayload) => observable.next(payload) + window.FCM.onNotification(handler, options) + + return observable + } + + /** @copyFrom ionic/FCM.d.ts FCMPluginOnIonic onTokenRefresh */ + public onTokenRefresh(options?: { once?: boolean }): Observable { + const observable = new Subject() + window.FCM.onTokenRefresh((token: string) => observable.next(token), options) + + return observable + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin requestPushPermission */ + public requestPushPermission(options?: IRequestPushPermissionOptions): Promise { + return window.FCM.requestPushPermission(options) + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin subscribeToTopic */ + public subscribeToTopic(topic: string): Promise { + return window.FCM.subscribeToTopic(topic) + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin unsubscribeFromTopic */ + public unsubscribeFromTopic(topic: string): Promise { + return window.FCM.unsubscribeFromTopic(topic) + } +} diff --git a/src/ionic/package-lock.json b/src/ionic/package-lock.json new file mode 100644 index 000000000..e917d1d32 --- /dev/null +++ b/src/ionic/package-lock.json @@ -0,0 +1,404 @@ +{ + "name": "cordova-plugin-fcm-with-dependecy-updated", + "version": "7.8.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@angular/core": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@angular/core/-/core-11.0.4.tgz", + "integrity": "sha512-860cTMjdCHcvEsHOsTzpg5rThxwVgtnY4yT0SgboWiphrlzX+aNoyN/cCJHxWhmOTRlrl6/+hkeRq95E2BZkKw==", + "requires": { + "tslib": "^2.0.0" + } + }, + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@ionic-native/core": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@ionic-native/core/-/core-5.30.0.tgz", + "integrity": "sha512-UkktFoSOAt/lgsc1nxnwjCul29yD06qHNjyv7/K7JxhqeJrqPBKihnkLu7OTAe52KdFBozRxLKDP6HWcGderqA==", + "requires": { + "@types/cordova": "^0.0.34" + }, + "dependencies": { + "@types/cordova": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz", + "integrity": "sha1-6nrd907Ow9dimCegw54smt3HPQQ=" + } + } + }, + "@types/cordova": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz", + "integrity": "sha1-6nrd907Ow9dimCegw54smt3HPQQ=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "builtin-modules": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz", + "integrity": "sha1-Jw8HbFpywC9bZaR9+Uxf46J4iS8=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "js-yaml": { + "version": "3.14.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", + "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "requires": { + "minimist": "^1.2.5" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "prettier": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.2.1.tgz", + "integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "rxjs": { + "version": "6.6.3", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.6.3.tgz", + "integrity": "sha512-trsQc+xYYXZ3urjOiJOuCOa5N3jAZ3eiSpQB5hIT8zGlL2QfnHLJ2r7GMkBGuIausdJN1OneaI6gQlsqNHHmZQ==", + "requires": { + "tslib": "^1.9.0" + }, + "dependencies": { + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + } + } + }, + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==", + "dev": true + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "tslib": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.3.tgz", + "integrity": "sha512-uZtkfKblCEQtZKBF6EBXVZeQNl82yqtDQdv+eck8u7tdPxjLu2/lp5/uPW+um2tpuxINHWy3GhiccY7QgEaVHQ==" + }, + "tslint": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/tslint/-/tslint-6.1.3.tgz", + "integrity": "sha512-IbR4nkT96EQOvKE2PW/djGz8iGNeJ4rF2mBfiYaR/nvUWYKJhLwimoJKgjIFEIDibBtOevj7BqCRL4oHeWWUCg==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "builtin-modules": "^1.1.1", + "chalk": "^2.3.0", + "commander": "^2.12.1", + "diff": "^4.0.1", + "glob": "^7.1.1", + "js-yaml": "^3.13.1", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.3", + "resolve": "^1.3.2", + "semver": "^5.3.0", + "tslib": "^1.13.0", + "tsutils": "^2.29.0" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + } + } + }, + "tslint-microsoft-contrib": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tslint-microsoft-contrib/-/tslint-microsoft-contrib-6.2.0.tgz", + "integrity": "sha512-6tfi/2tHqV/3CL77pULBcK+foty11Rr0idRDxKnteTaKm6gWF9qmaCNU17HVssOuwlYNyOmd9Jsmjd+1t3a3qw==", + "dev": true, + "requires": { + "tsutils": "^2.27.2 <2.29.0" + }, + "dependencies": { + "tsutils": { + "version": "2.28.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.28.0.tgz", + "integrity": "sha512-bh5nAtW0tuhvOJnx1GLRn5ScraRLICGyJV5wJhtRWOLsxW70Kk5tZtpK3O/hW6LDnqKS9mlUMPZj9fEMJ0gxqA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + } + } + } + } + }, + "tsutils": { + "version": "2.29.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-2.29.0.tgz", + "integrity": "sha512-g5JVHCIJwzfISaXpXE1qvNalca5Jwob6FjI4AoPlqMusJ6ftFE7IkkFoMhVLRgK+4Kx3gkzb8UZK5t5yTTvEmA==", + "dev": true, + "requires": { + "tslib": "^1.8.1" + }, + "dependencies": { + "tslib": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", + "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "dev": true + } + } + }, + "typescript": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz", + "integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==", + "dev": true + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true + } + } +} diff --git a/src/ionic/package.json b/src/ionic/package.json new file mode 100644 index 000000000..a7771b596 --- /dev/null +++ b/src/ionic/package.json @@ -0,0 +1,52 @@ +{ + "version": "7.8.0", + "name": "cordova-plugin-fcm-with-dependecy-updated", + "cordova_name": "Cordova FCM Push Plugin", + "description": "Google Firebase Cloud Messaging Cordova Push Plugin fork with dependecy updated", + "license": "MIT", + "main": "./FCMPluginOnIonic.js", + "repo": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated", + "issue": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated/issues", + "author": "André Augusto Tissot", + "scripts": { + "build": "npm run build:before;npm run build:dts;npm run build:js;npm run build:after", + "build:before": "rm -rf ../../ionic;mkdir -p ../../ionic/ngx;mkdir -p ../../ionic/v4", + "build:js": "./scripts/build.js.sh", + "build:dts": "./scripts/build.dts.sh", + "build:after": "cd ../../ionic;rm -rf ionic;rm -rf www", + "tsc": "npx tsc -p . --noEmit" + }, + "repository": { + "type": "git", + "url": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated" + }, + "keywords": [ + "ecosystem:cordova", + "cordova-android", + "cordova-ios", + "notifications", + "push", + "firebase", + "fcm", + "ios", + "android", + "cordova", + "ionic" + ], + "platforms": [ + "android", + "ios" + ], + "dependencies": { + "@angular/core": "^11.0.4", + "@ionic-native/core": "^5.30.0", + "rxjs": "^6.6.3" + }, + "devDependencies": { + "@types/cordova": "0.0.34", + "prettier": "^2.2.1", + "tslint": "^6.1.3", + "tslint-microsoft-contrib": "^6.2.0", + "typescript": "^4.1.2" + } +} diff --git a/src/ionic/package.toCopy.json b/src/ionic/package.toCopy.json new file mode 100644 index 000000000..f0723fb0a --- /dev/null +++ b/src/ionic/package.toCopy.json @@ -0,0 +1,41 @@ +{ + "version": "7.8.0", + "name": "cordova-plugin-fcm-with-dependecy-updated", + "cordova_name": "Cordova FCM Push Plugin", + "description": "Google Firebase Cloud Messaging Cordova Push Plugin fork with dependecy updated", + "license": "MIT", + "main": "./FCM.js", + "repo": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated", + "issue": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated/issues", + "author": "André Augusto Tissot", + "repository": { + "type": "git", + "url": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated" + }, + "keywords": [ + "ecosystem:cordova", + "cordova-android", + "cordova-ios", + "notifications", + "push", + "firebase", + "fcm", + "ios", + "android", + "cordova", + "ionic" + ], + "platforms": ["android", "ios"], + "devDependencies": { + "@types/cordova": "0.0.34", + "prettier": "^2.0.5", + "tslint": "^6.1.2", + "tslint-microsoft-contrib": "^6.2.0", + "typescript": "^3.9.3" + }, + "dependencies": { + "@angular/core": "^9.1.9", + "@ionic-native/core": "^5.26.0", + "rxjs": "^6.5.5" + } +} diff --git a/src/ionic/scripts/build.dts.sh b/src/ionic/scripts/build.dts.sh new file mode 100755 index 000000000..44a289611 --- /dev/null +++ b/src/ionic/scripts/build.dts.sh @@ -0,0 +1,42 @@ +## Generate .d.ts files +tsc -p . --declaration true --removeComments false + +## Fix root .d.ts files +cd ../../ionic +sed 's/\.\.\/\.\.\/typings\//\.\.\/typings\//' ionic/FCM.d.ts > ionic/FCM.tmp.d.ts +sed 's/\.\.\/www\//\.\.\/typings\//' ionic/FCM.tmp.d.ts > FCM.d.ts +cp ../src/ionic/package.toCopy.json package.json + +## Fix ngx .d.ts files +sed 's/\.\.\/www\//\.\.\/typings\//' ionic/ngx/FCM.d.ts > ngx/FCM.d.ts +cp ../src/ionic/package.toCopy.json ngx/package.json + +## Fix v4 .d.ts files +mv ionic/v4/*.d.ts ./v4/ +sed 's/\.\.\/\.\.\/typings\//\.\.\/typings\//' v4/FCM.d.ts > v4/FCM.tmp.d.ts +mv v4/FCM.tmp.d.ts v4/FCM.d.ts +cp ../src/ionic/package.toCopy.json v4/package.json + +## Replace copyFrom with source value +cd .. +source "src/ionic/scripts/replaceCopyFromOnFile.helper.sh" +replaceCopyFromOnFile ionic/FCM.d.ts +replaceCopyFromOnFile ionic/ngx/FCM.d.ts +replaceCopyFromOnFile ionic/v4/FCM.d.ts + +## Remove empty lines +sed '/^[[:space:]]*$/d' ionic/FCM.d.ts > ionic/FCM.d.ts.tmp +mv ionic/FCM.d.ts.tmp ionic/FCM.d.ts +sed '/^[[:space:]]*$/d' ionic/ngx/FCM.d.ts > ionic/ngx/FCM.d.ts.tmp +mv ionic/ngx/FCM.d.ts.tmp ionic/ngx/FCM.d.ts +sed '/^[[:space:]]*$/d' ionic/v4/FCM.d.ts > ionic/v4/FCM.d.ts.tmp +mv ionic/v4/FCM.d.ts.tmp ionic/v4/FCM.d.ts + +## Simplify imports +simplifyImports() { + filePath="$1" + sed 's/import type /import /g' "$filePath" > "$filePath.tmp" + mv "$filePath.tmp" "$filePath" +} +simplifyImports ionic/v4/FCM.d.ts +simplifyImports ionic/ngx/FCM.d.ts \ No newline at end of file diff --git a/src/ionic/scripts/build.js.sh b/src/ionic/scripts/build.js.sh new file mode 100755 index 000000000..4b96148a5 --- /dev/null +++ b/src/ionic/scripts/build.js.sh @@ -0,0 +1,29 @@ +## Build JS files +tsc -p . --declaration false --removeComments true + +## Move files to the correct directories +cd ../../ionic +mv ionic/*.js . +mv ionic/ngx/*.js ./ngx/ +mv ionic/v4/*.js ./v4/ + +## Simplify main build +sed 's/var FCMPluginOnIonic = (function () {//' FCM.js > FCM.js.tmp +sed 's/}());//' FCM.js.tmp > FCM.js +sed 's/return FCMPluginOnIonic;//' FCM.js > FCM.js.tmp +mv FCM.js.tmp FCM.js +npx prettier FCM.js --write + +## Simplify v4 build +sed 's/var FCM = (function () {//' v4/FCM.js > v4/FCM.js.tmp +sed 's/}());//' v4/FCM.js.tmp > v4/FCM.js +sed 's/return FCM;//' v4/FCM.js > v4/FCM.js.tmp +mv v4/FCM.js.tmp v4/FCM.js +npx prettier v4/FCM.js --write + +## Simplify ngx build +sed 's/var FCM = (function () {//' ngx/FCM.js > ngx/FCM.js.tmp +sed 's/}());//' ngx/FCM.js.tmp > ngx/FCM.js +sed 's/return FCM;//' ngx/FCM.js > ngx/FCM.js.tmp +mv ngx/FCM.js.tmp ngx/FCM.js +npx prettier ngx/FCM.js --write diff --git a/src/ionic/scripts/replaceCopyFromOnFile.helper.sh b/src/ionic/scripts/replaceCopyFromOnFile.helper.sh new file mode 100755 index 000000000..c1e4dd2b8 --- /dev/null +++ b/src/ionic/scripts/replaceCopyFromOnFile.helper.sh @@ -0,0 +1,27 @@ +source "src/ionic/scripts/retriveJSDocFromFileClass.helper.sh" +source "src/ionic/scripts/retriveJSDocFromFileClassMethod.helper.sh" + +replaceCopyFromOnFile() { + filePath="$1" + fileTextOut="" + SAFE_IFS=IFS + while IFS= read -r fileLine; do + if [[ ! "$fileLine" == *"/** @copyFrom "* ]]; then + fileTextOut+="$fileLine\n" + continue + fi + targetFilePath=$(echo "$fileLine" | xargs | cut -d ' ' -f 3) + className=$(echo "$fileLine" | xargs | cut -d ' ' -f 4) + methodName=$(echo "$fileLine" | xargs | cut -d ' ' -f 5) + if [[ "$methodName" == "*/" ]]; then + fileTextOut+=$(retriveJSDocFromFileClass "$targetFilePath" "$className")"\n" + else + fileTextOut+=$(retriveJSDocFromFileClassMethod "$targetFilePath" "$className" "$methodName")"\n" + fi + done < "$filePath" + IFS=SAFE_IFS + echo "$fileTextOut" > "$filePath.tmp" + mv "$filePath.tmp" "$filePath" +} + +export -f replaceCopyFromOnFile \ No newline at end of file diff --git a/src/ionic/scripts/retriveJSDocFromFileClass.helper.sh b/src/ionic/scripts/retriveJSDocFromFileClass.helper.sh new file mode 100755 index 000000000..dac8668e0 --- /dev/null +++ b/src/ionic/scripts/retriveJSDocFromFileClass.helper.sh @@ -0,0 +1,24 @@ +retriveJSDocFromFileClass() { + filePath="$1" + className="$2" + buffer="" + bufferEnabled="" + while IFS= read -r fileLine; do + if [[ "$fileLine" == *"class $className "* ]]; then + echo "$buffer" + fi + if [[ "$fileLine" == *"/**"* ]]; then + bufferEnabled="TRUE" + buffer="" + fi + if [[ ! "$bufferEnabled" ]]; then + continue + fi + buffer+="$fileLine\n" + if [[ "$fileLine" == *"*/"* ]]; then + bufferEnabled="" + fi + done < "$filePath" +} + +export -f retriveJSDocFromFileClass \ No newline at end of file diff --git a/src/ionic/scripts/retriveJSDocFromFileClassMethod.helper.sh b/src/ionic/scripts/retriveJSDocFromFileClassMethod.helper.sh new file mode 100755 index 000000000..7f64bb0c8 --- /dev/null +++ b/src/ionic/scripts/retriveJSDocFromFileClassMethod.helper.sh @@ -0,0 +1,32 @@ +retriveJSDocFromFileClassMethod() { + filePath="$1" + className="$2" + methodName="$3" + classFound="" + buffer="" + bufferEnabled="" + while IFS= read -r fileLine; do + if [[ "$fileLine" == *"class $className "* ]]; then + classFound="TRUE" + fi + if [[ ! "$classFound" ]]; then + continue + fi + if [[ "$fileLine" == *"$methodName"* ]]; then + echo "$buffer" + fi + if [[ "$fileLine" == *"/**"* ]]; then + bufferEnabled="TRUE" + buffer="" + fi + if [[ ! "$bufferEnabled" ]]; then + continue + fi + buffer+="$fileLine\n" + if [[ "$fileLine" == *"*/"* ]]; then + bufferEnabled="" + fi + done < "$filePath" +} + +export -f retriveJSDocFromFileClassMethod \ No newline at end of file diff --git a/src/ionic/tsconfig.json b/src/ionic/tsconfig.json new file mode 100644 index 000000000..b93b455f6 --- /dev/null +++ b/src/ionic/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "alwaysStrict": true, + "baseUrl": ".", + "esModuleInterop": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "lib": ["es2015", "dom"], + "module": "es2015", + "moduleResolution": "node", + "outDir": "../../ionic", + "strictNullChecks": true, + "strictPropertyInitialization": true, + "target": "es5" + }, + "include": ["./**/*.ts"], + "exclude": ["node_modules"] +} diff --git a/src/ionic/v4/FCM.ts b/src/ionic/v4/FCM.ts new file mode 100644 index 000000000..0111307b8 --- /dev/null +++ b/src/ionic/v4/FCM.ts @@ -0,0 +1,89 @@ +import { Injectable } from '@angular/core' +import { Plugin } from '@ionic-native/core' +import { Observable, Subject } from 'rxjs' +import type { FCMPlugin } from '../../../typings/FCMPlugin' +import type { IChannelConfiguration } from '../../../typings/IChannelConfiguration' +import type { IRequestPushPermissionOptions } from '../../../typings/IRequestPushPermissionOptions' +import type { INotificationPayload } from '../../../typings/INotificationPayload' + +declare namespace window { + export let FCM: FCMPlugin +} + +/** @copyFrom typings/FCMPlugin.d.ts FCMPlugin */ +@Plugin({ + pluginName: 'FCM', + plugin: 'cordova-plugin-fcm-with-dependecy-updated', + pluginRef: 'FCM', + repo: 'https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated', + platforms: ['Android', 'iOS'], +}) +@Injectable() +export class FCM { + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin clearAllNotifications */ + public clearAllNotifications(): Promise { + return window.FCM.clearAllNotifications() + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin createNotificationChannel */ + public createNotificationChannel(channelConfig: IChannelConfiguration): Promise { + return window.FCM.createNotificationChannel(channelConfig) + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin deleteInstanceId */ + public deleteInstanceId(): Promise { + return window.FCM.deleteInstanceId() + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin getAPNSToken */ + public getAPNSToken(): Promise { + return window.FCM.getAPNSToken() + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin getInitialPushPayload */ + public getInitialPushPayload(): Promise { + return window.FCM.getInitialPushPayload() + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin getToken */ + public getToken(): Promise { + return window.FCM.getToken() + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin hasPermission */ + public hasPermission(): Promise { + return window.FCM.hasPermission() + } + + /** @copyFrom ionic/FCM.d.ts FCMPluginOnIonic onNotification */ + public onNotification(options?: { once?: boolean }): Observable { + const observable = new Subject() + const handler = (payload: INotificationPayload) => observable.next(payload) + window.FCM.onNotification(handler, options) + + return observable + } + + /** @copyFrom ionic/FCM.d.ts FCMPluginOnIonic onTokenRefresh */ + public onTokenRefresh(options?: { once?: boolean }): Observable { + const observable = new Subject() + window.FCM.onTokenRefresh((token: string) => observable.next(token), options) + + return observable + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin requestPushPermission */ + public requestPushPermission(options?: IRequestPushPermissionOptions): Promise { + return window.FCM.requestPushPermission(options) + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin subscribeToTopic */ + public subscribeToTopic(topic: string): Promise { + return window.FCM.subscribeToTopic(topic) + } + + /** @copyFrom typings/FCMPlugin.d.ts FCMPlugin unsubscribeFromTopic */ + public unsubscribeFromTopic(topic: string): Promise { + return window.FCM.unsubscribeFromTopic(topic) + } +} diff --git a/src/ios/AppDelegate+FCMPlugin.h b/src/ios/AppDelegate+FCMPlugin.h index 418f0e965..d1d991660 100644 --- a/src/ios/AppDelegate+FCMPlugin.h +++ b/src/ios/AppDelegate+FCMPlugin.h @@ -1,11 +1,3 @@ -// -// AppDelegate+FCMPlugin.h -// TestApp -// -// Created by felipe on 12/06/16. -// -// - #import "AppDelegate.h" #import #import @@ -13,5 +5,13 @@ @interface AppDelegate (FCMPlugin) + (NSData*)getLastPush; ++ (NSData*)getInitialPushPayload; ++ (NSString*)getFCMToken; ++ (NSString*)getAPNSToken; ++ (void)deleteInstanceId:(void (^)(NSError *error))handler; ++ (void)setLastPush:(NSData*)push; ++ (void)setInitialPushPayload:(NSData*)payload; ++ (void)requestPushPermission:(void (^)(BOOL yesOrNo, NSError* error))block withOptions:(NSDictionary*)options; ++ (void)hasPushPermission:(void (^)(NSNumber* yesNoOrNil))block; @end diff --git a/src/ios/AppDelegate+FCMPlugin.m b/src/ios/AppDelegate+FCMPlugin.m index 4261b1604..0cef55cf8 100644 --- a/src/ios/AppDelegate+FCMPlugin.m +++ b/src/ios/AppDelegate+FCMPlugin.m @@ -1,309 +1,253 @@ -// -// AppDelegate+FCMPlugin.m -// TestApp -// -// Created by felipe on 12/06/16. -// -// #import "AppDelegate+FCMPlugin.h" #import "FCMPlugin.h" +#import "FCMPluginIOS9Support.h" +#import "FCMNotificationCenterDelegate.h" #import #import -#import "Firebase.h" - -#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 @import UserNotifications; -#endif - -@import FirebaseInstanceID; -@import FirebaseMessaging; +@import Firebase; // Implement UNUserNotificationCenterDelegate to receive display notification via APNS for devices // running iOS 10 and above. Implement FIRMessagingDelegate to receive data message via FCM for // devices running iOS 10 and above. -#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 -@interface AppDelegate () +@interface AppDelegate () @end -#endif - -// Copied from Apple's header in case it is missing in some cases (e.g. pre-Xcode 8 builds). -#ifndef NSFoundationVersionNumber_iOS_9_x_Max -#define NSFoundationVersionNumber_iOS_9_x_Max 1299 -#endif @implementation AppDelegate (MCPlugin) static NSData *lastPush; +static NSData *initialPushPayload; +static NSString *fcmToken; +static NSString *apnsToken; NSString *const kGCMMessageIDKey = @"gcm.message_id"; +FCMNotificationCenterDelegate *notificationCenterDelegate; //Method swizzling -+ (void)load -{ ++ (void)load { Method original = class_getInstanceMethod(self, @selector(application:didFinishLaunchingWithOptions:)); Method custom = class_getInstanceMethod(self, @selector(application:customDidFinishLaunchingWithOptions:)); method_exchangeImplementations(original, custom); } - (BOOL)application:(UIApplication *)application customDidFinishLaunchingWithOptions:(NSDictionary *)launchOptions { - [self application:application customDidFinishLaunchingWithOptions:launchOptions]; NSLog(@"DidFinishLaunchingWithOptions"); - - - // Register for remote notifications. This shows a permission dialog on first run, to - // show the dialog at a more appropriate time move this registration accordingly. - if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_7_1) { - // iOS 7.1 or earlier. Disable the deprecation warnings. -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wdeprecated-declarations" - UIRemoteNotificationType allNotificationTypes = - (UIRemoteNotificationTypeSound | - UIRemoteNotificationTypeAlert | - UIRemoteNotificationTypeBadge); - [application registerForRemoteNotificationTypes:allNotificationTypes]; -#pragma clang diagnostic pop - } else { - // iOS 8 or later - // [START register_for_notifications] - if (floor(NSFoundationVersionNumber) <= NSFoundationVersionNumber_iOS_9_x_Max) { - UIUserNotificationType allNotificationTypes = - (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge); - UIUserNotificationSettings *settings = - [UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil]; - [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; - } else { - // iOS 10 or later -#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - UNAuthorizationOptions authOptions = - UNAuthorizationOptionAlert - | UNAuthorizationOptionSound - | UNAuthorizationOptionBadge; - [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError * _Nullable error) { - }]; - - // For iOS 10 display notification (sent via APNS) - [UNUserNotificationCenter currentNotificationCenter].delegate = self; - // For iOS 10 data message (sent via FCM) - [FIRMessaging messaging].remoteMessageDelegate = self; -#endif - } - - [[UIApplication sharedApplication] registerForRemoteNotifications]; - // [END register_for_notifications] + if ([UNUserNotificationCenter class] != nil) { + // For iOS 10 display notification (sent via APNS) + notificationCenterDelegate = [NSClassFromString(@"FCMNotificationCenterDelegate") alloc]; + [notificationCenterDelegate configureForNotifications]; } + [self performSelector:@selector(configureForNotifications) withObject:self afterDelay:0.3f]; - // [START configure_firebase] - [FIRApp configure]; - // [END configure_firebase] - // Add observer for InstanceID token refresh callback. - [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(tokenRefreshNotification:) - name:kFIRInstanceIDTokenRefreshNotification object:nil]; return YES; } -// [START message_handling] -// Receive displayed notifications for iOS 10 devices. - -// Note on the pragma: When compiling with iOS 10 SDK, include methods that -// handle notifications using notification center. -#if defined(__IPHONE_10_0) && __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 - -// Handle incoming notification messages while app is in the foreground. -- (void)userNotificationCenter:(UNUserNotificationCenter *)center - willPresentNotification:(UNNotification *)notification - withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { - // Print message ID. - NSDictionary *userInfo = notification.request.content.userInfo; - if (userInfo[kGCMMessageIDKey]) { - NSLog(@"Message ID 1: %@", userInfo[kGCMMessageIDKey]); +- (void)configureForNotifications { + if([FIRApp defaultApp] == nil) { + [FIRApp configure]; } - - // Print full message. - NSLog(@"%@", userInfo); - - NSError *error; - NSDictionary *userInfoMutable = [userInfo mutableCopy]; - NSData *jsonData = [NSJSONSerialization dataWithJSONObject:userInfoMutable - options:0 - error:&error]; - [FCMPlugin.fcmPlugin notifyOfMessage:jsonData]; - - // Change this to your preferred presentation option - completionHandler(UNNotificationPresentationOptionNone); + // For iOS message (sent via FCM) + [FIRMessaging messaging].delegate = self; } -// Handle notification messages after display notification is tapped by the user. -- (void)userNotificationCenter:(UNUserNotificationCenter *)center -didReceiveNotificationResponse:(UNNotificationResponse *)response - withCompletionHandler:(void (^)())completionHandler { - NSDictionary *userInfo = response.notification.request.content.userInfo; - if (userInfo[kGCMMessageIDKey]) { - NSLog(@"Message ID 2: %@", userInfo[kGCMMessageIDKey]); ++ (void)requestPushPermission:(void (^)(BOOL yesOrNo, NSError* _Nullable error))block withOptions:(NSDictionary*)options { + if ([UNUserNotificationCenter class] == nil) { + return [FCMPluginIOS9Support requestPushPermission:block withOptions:options]; } - - // Print full message. - NSLog(@"aaa%@", userInfo); - - NSError *error; - NSDictionary *userInfoMutable = [userInfo mutableCopy]; - - - NSLog(@"New method with push callback: %@", userInfo); - - [userInfoMutable setValue:@(YES) forKey:@"wasTapped"]; - NSData *jsonData = [NSJSONSerialization dataWithJSONObject:userInfoMutable - options:0 - error:&error]; - NSLog(@"APP WAS CLOSED DURING PUSH RECEPTION Saved data: %@", jsonData); - lastPush = jsonData; - - - completionHandler(); + UNAuthorizationOptions authOptions = UNAuthorizationOptionAlert | UNAuthorizationOptionSound | UNAuthorizationOptionBadge; + [[UNUserNotificationCenter currentNotificationCenter] requestAuthorizationWithOptions:authOptions completionHandler:^(BOOL granted, NSError* _Nullable error) { + if (granted) { + dispatch_async(dispatch_get_main_queue(), ^{ + [[UIApplication sharedApplication] registerForRemoteNotifications]; + }); + block(YES, error); + return; + } + NSLog(@"User Notification permission denied: %@", error.localizedDescription); + block(NO, error); + }]; } -#endif - -// [START receive_message in background iOS < 10] - -// Include the iOS < 10 methods for handling notifications for when running on iOS < 10. -// As in, even if you compile with iOS 10 SDK, when running on iOS 9 the only way to get -// notifications is the didReceiveRemoteNotification. -- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo -{ - // Short-circuit when actually running iOS 10+, let notification centre methods handle the notification. - if (NSFoundationVersionNumber >= NSFoundationVersionNumber_iOS_9_x_Max) { +- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceTokenData { + [FIRMessaging messaging].APNSToken = deviceTokenData; + NSString *deviceToken; + if (@available(iOS 13, *)) { + deviceToken = [self hexadecimalStringFromData:deviceTokenData]; + } else { + deviceToken = [[[[deviceTokenData description] + stringByReplacingOccurrencesOfString:@"<"withString:@""] + stringByReplacingOccurrencesOfString:@">" withString:@""] + stringByReplacingOccurrencesOfString:@" " withString:@""]; + } + apnsToken = deviceToken; + NSLog(@"Device APNS Token: %@", deviceToken); + if (@available(iOS 10, *)) { return; } + [FCMPluginIOS9Support application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceTokenData]; +} - NSLog(@"Message ID: %@", userInfo[@"gcm.message_id"]); - - NSError *error; - NSDictionary *userInfoMutable = [userInfo mutableCopy]; - - if (application.applicationState != UIApplicationStateActive) { - NSLog(@"New method with push callback: %@", userInfo); - - [userInfoMutable setValue:@(YES) forKey:@"wasTapped"]; - NSData *jsonData = [NSJSONSerialization dataWithJSONObject:userInfoMutable - options:0 - error:&error]; - NSLog(@"APP WAS CLOSED DURING PUSH RECEPTION Saved data: %@", jsonData); - lastPush = jsonData; +- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotifications:(NSError *)error { + NSLog(@"Failed to register for remote notifications: %@", error); + if (@available(iOS 10, *)) { + return; } + [FCMPluginIOS9Support application:application didFailToRegisterForRemoteNotifications:error]; } -// [END receive_message in background] iOS < 10] -// [START receive_message iOS < 10] -- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo -fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler -{ - // Short-circuit when actually running iOS 10+, let notification centre methods handle the notification. - if (NSFoundationVersionNumber >= NSFoundationVersionNumber_iOS_9_x_Max) { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-implementations" +- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { + if (@available(iOS 10, *)) { return; } + [FCMPluginIOS9Support application:application didReceiveRemoteNotification:userInfo]; +} +#pragma clang diagnostic pop - // If you are receiving a notification message while your app is in the background, - // this callback will not be fired till the user taps on the notification launching the application. - // TODO: Handle data of notification - - // Print message ID. - NSLog(@"Message ID: %@", userInfo[@"gcm.message_id"]); - - // Pring full message. - NSLog(@"%@", userInfo); - NSError *error; - - NSDictionary *userInfoMutable = [userInfo mutableCopy]; - - // Has user tapped the notificaiton? - // UIApplicationStateActive - app is currently active - // UIApplicationStateInactive - app is transitioning from background to - // foreground (user taps notification) - - UIApplicationState state = application.applicationState; - if (application.applicationState == UIApplicationStateActive - || application.applicationState == UIApplicationStateInactive) { - [userInfoMutable setValue:@(NO) forKey:@"wasTapped"]; - NSLog(@"app active"); - NSData *jsonData = [NSJSONSerialization dataWithJSONObject:userInfoMutable - options:0 - error:&error]; - [FCMPlugin.fcmPlugin notifyOfMessage:jsonData]; +- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo +fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { + [[FIRMessaging messaging] appDidReceiveMessage:userInfo]; + + if (@available(iOS 10, *)) { + // Print message ID. + NSLog(@"Message ID: %@", userInfo[@"gcm.message_id"]); + + // Pring full message. + NSLog(@"%@", userInfo); + + // If the app is in the background, keep it for later, in case it's not tapped. + if(application.applicationState == UIApplicationStateBackground) { + NSError *error; + NSDictionary *userInfoMutable = [userInfo mutableCopy]; + [userInfoMutable setValue:@(NO) forKey:@"wasTapped"]; + NSLog(@"app active"); + lastPush = [NSJSONSerialization dataWithJSONObject:userInfoMutable options:0 error:&error]; + [AppDelegate setInitialPushPayload:lastPush]; + } else if(application.applicationState == UIApplicationStateInactive) { + NSError *error; + NSDictionary *userInfoMutable = [userInfo mutableCopy]; + [userInfoMutable setValue:@(YES) forKey:@"wasTapped"]; + NSLog(@"app opened by user tap"); + lastPush = [NSJSONSerialization dataWithJSONObject:userInfoMutable options:0 error:&error]; + [AppDelegate setInitialPushPayload:lastPush]; + } - // app is in background + completionHandler(UIBackgroundFetchResultNoData); + return; } - completionHandler(UIBackgroundFetchResultNoData); + [FCMPluginIOS9Support application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; } -// [END receive_message iOS < 10] // [END message_handling] - -// [START refresh_token] -- (void)tokenRefreshNotification:(NSNotification *)notification -{ - // Note that this callback will be fired everytime a new token is generated, including the first - // time. So if you need to retrieve the token as soon as it is available this is where that - // should be done. - NSString *refreshedToken = [[FIRInstanceID instanceID] token]; - NSLog(@"InstanceID token: %@", refreshedToken); - [FCMPlugin.fcmPlugin notifyOfTokenRefresh:refreshedToken]; - // Connect to FCM since connection may have failed when attempted before having a token. +- (void)messaging:(nonnull FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)deviceToken { + NSLog(@"Device FCM Token: %@", deviceToken); + if(deviceToken == nil) { + fcmToken = nil; + [FCMPlugin.fcmPlugin notifyFCMTokenRefresh:nil]; + return; + } + // Notify about received token. + NSDictionary *dataDict = [NSDictionary dictionaryWithObject:deviceToken forKey:@"token"]; + [[NSNotificationCenter defaultCenter] postNotificationName:@"FCMToken" object:nil userInfo:dataDict]; + fcmToken = deviceToken; + [FCMPlugin.fcmPlugin notifyFCMTokenRefresh:deviceToken]; [self connectToFcm]; - - // TODO: If necessary send token to appliation server. } -// [END refresh_token] -// [START connect_to_fcm] -- (void)connectToFcm -{ - +// [BEGIN connect_to_fcm] +- (void)connectToFcm { // Won't connect since there is no token - if (![[FIRInstanceID instanceID] token]) { + if (!fcmToken) { return; } - - // Disconnect previous FCM connection if it exists. - [[FIRMessaging messaging] disconnect]; - - [[FIRMessaging messaging] connectWithCompletion:^(NSError * _Nullable error) { - if (error != nil) { - NSLog(@"Unable to connect to FCM. %@", error); - } else { - NSLog(@"Connected to FCM."); - [[FIRMessaging messaging] subscribeToTopic:@"/topics/ios"]; - [[FIRMessaging messaging] subscribeToTopic:@"/topics/all"]; - } - }]; + [[FIRMessaging messaging] subscribeToTopic:@"ios"]; + [[FIRMessaging messaging] subscribeToTopic:@"all"]; } // [END connect_to_fcm] -- (void)applicationDidBecomeActive:(UIApplication *)application -{ +- (void)applicationDidBecomeActive:(UIApplication *)application { NSLog(@"app become active"); [FCMPlugin.fcmPlugin appEnterForeground]; [self connectToFcm]; } -// [START disconnect_from_fcm] -- (void)applicationDidEnterBackground:(UIApplication *)application -{ +// [BEGIN disconnect_from_fcm] +- (void)applicationDidEnterBackground:(UIApplication *)application { NSLog(@"app entered background"); - [[FIRMessaging messaging] disconnect]; [FCMPlugin.fcmPlugin appEnterBackground]; NSLog(@"Disconnected from FCM"); } // [END disconnect_from_fcm] -+(NSData*)getLastPush -{ ++ (void)setLastPush:(NSData*)push { + lastPush = push; +} + ++ (void)setInitialPushPayload:(NSData*)payload { + if(initialPushPayload == nil) { + initialPushPayload = payload; + } +} + ++ (NSData*)getLastPush { NSData* returnValue = lastPush; lastPush = nil; return returnValue; } ++ (NSData*)getInitialPushPayload { + return initialPushPayload; +} + ++ (NSString*)getFCMToken { + return fcmToken; +} + ++ (NSString*)getAPNSToken { + return apnsToken; +} + ++ (void)deleteInstanceId:(void (^)(NSError *error))handler { + [[FIRInstanceID instanceID] deleteIDWithHandler:handler]; +} + ++ (void)hasPushPermission:(void (^)(NSNumber* yesNoOrNil))block { + if ([UNUserNotificationCenter class] == nil) { + [FCMPluginIOS9Support hasPushPermission:block]; + return; + } + UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; + [center getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings *settings){ + switch (settings.authorizationStatus) { + case UNAuthorizationStatusAuthorized: { + block([NSNumber numberWithBool:YES]); + } + case UNAuthorizationStatusDenied: { + block([NSNumber numberWithBool:NO]); + } + default: { + block(nil); + } + } + }]; +} + +- (NSString *)hexadecimalStringFromData:(NSData *)data { + NSUInteger dataLength = data.length; + if (dataLength == 0) { + return nil; + } + + const unsigned char *dataBuffer = data.bytes; + NSMutableString *hexString = [NSMutableString stringWithCapacity:(dataLength * 2)]; + for (int i = 0; i < dataLength; ++i) { + [hexString appendFormat:@"%02x", dataBuffer[i]]; + } + return [hexString copy]; +} @end diff --git a/src/ios/GoogleService-Info.plist b/src/ios/Assets/GoogleService-Info.plist similarity index 100% rename from src/ios/GoogleService-Info.plist rename to src/ios/Assets/GoogleService-Info.plist diff --git a/src/ios/FCMNotificationCenterDelegate.h b/src/ios/FCMNotificationCenterDelegate.h new file mode 100644 index 000000000..027d3c6c4 --- /dev/null +++ b/src/ios/FCMNotificationCenterDelegate.h @@ -0,0 +1,7 @@ +#import + +@interface FCMNotificationCenterDelegate : NSObject {} + +- (void)configureForNotifications; + +@end diff --git a/src/ios/FCMNotificationCenterDelegate.m b/src/ios/FCMNotificationCenterDelegate.m new file mode 100644 index 000000000..4039fe02c --- /dev/null +++ b/src/ios/FCMNotificationCenterDelegate.m @@ -0,0 +1,117 @@ +#import "AppDelegate+FCMPlugin.h" +#import "FCMPlugin.h" +#import "FCMNotificationCenterDelegate.h" +#import +#import + +@import UserNotifications; + +// Implement UNUserNotificationCenterDelegate to receive display notification via APNS for devices +// running iOS 10 and above. +@interface FCMNotificationCenterDelegate () +@end + +@implementation FCMNotificationCenterDelegate + +NSMutableArray*> *subNotificationCenterDelegates; + +- (void) forceNotificationCenterDelegate:(float)timeout { + [self setNotificationCenterDelegate]; + if(timeout < 0) { + // The job should be done. + return; + } + SEL thisMethodSelector = NSSelectorFromString(@"forceNotificationCenterDelegate:"); + if([self respondsToSelector:thisMethodSelector]) { +// NSLog(@"FCMNotificationCenterDelegate found: %@", [UNUserNotificationCenter currentNotificationCenter].delegate); + float remainingTimeout = timeout - 0.1f; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:thisMethodSelector]]; + [invocation setSelector:thisMethodSelector]; + [invocation setTarget:self]; + [invocation setArgument:&(remainingTimeout) atIndex:2]; + [NSTimer scheduledTimerWithTimeInterval:0.1f invocation:invocation repeats:NO]; + return; + } + NSLog(@"forceNotificationCenterDelegate selector not found in FCMNotificationCenterDelegate"); +} + +- (void)configureForNotifications { + subNotificationCenterDelegates = [[NSMutableArray alloc]initWithCapacity:0]; + [self setNotificationCenterDelegate]; + [self forceNotificationCenterDelegate:10]; +} + +- (void) setNotificationCenterDelegate { + if([UNUserNotificationCenter currentNotificationCenter].delegate == self) { + return; + } + if([UNUserNotificationCenter currentNotificationCenter].delegate != nil) { + [subNotificationCenterDelegates addObject:[UNUserNotificationCenter currentNotificationCenter].delegate]; +// NSLog(@"subNotificationCenterDelegates: %@", subNotificationCenterDelegates); + } + [UNUserNotificationCenter currentNotificationCenter].delegate = self; +} + + +// Handle incoming notification messages while app is in the foreground. +- (void)userNotificationCenter:(UNUserNotificationCenter *)center + willPresentNotification:(UNNotification *)notification + withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { + NSLog(@"FCMNotificationCenterDelegate.willPresentNotification!"); + NSData *jsonData = [self extractJSONData:notification withWasTapped:NO]; + [FCMPlugin.fcmPlugin notifyOfMessage:jsonData]; + __block UNNotificationPresentationOptions notificationPresentationOptions = UNNotificationPresentationOptionNone; + void (^subDelegateCompletionHandler)(UNNotificationPresentationOptions) = ^(UNNotificationPresentationOptions possibleNotificationPresentationOptions) { + if(notificationPresentationOptions < possibleNotificationPresentationOptions) { + notificationPresentationOptions |= possibleNotificationPresentationOptions; + } + }; + SEL thisMethodSelector = NSSelectorFromString(@"userNotificationCenter:willPresentNotification:withCompletionHandler:"); + for (NSObject* subNotificationCenterDelegate in subNotificationCenterDelegates) { + if([subNotificationCenterDelegate respondsToSelector:thisMethodSelector]) { + [subNotificationCenterDelegate userNotificationCenter:center willPresentNotification:notification withCompletionHandler:subDelegateCompletionHandler]; + } + } + completionHandler(notificationPresentationOptions); +} + +// Handle notification messages after display notification is tapped by the user. +- (void)userNotificationCenter:(UNUserNotificationCenter *)center +didReceiveNotificationResponse:(UNNotificationResponse *)response + withCompletionHandler:(void (^)(void))completionHandler { + NSLog(@"FCMNotificationCenterDelegate.didReceiveNotificationResponse!"); + NSData *jsonData = [self extractJSONData:response.notification withWasTapped:YES]; + [AppDelegate setInitialPushPayload:jsonData]; + [FCMPlugin.fcmPlugin notifyOfMessage:jsonData]; + void (^noopCompletionHandler)(void) = ^(){}; + SEL thisMethodSelector = NSSelectorFromString(@"userNotificationCenter:didReceiveNotificationResponse:withCompletionHandler:"); + for (NSObject* subNotificationCenterDelegate in subNotificationCenterDelegates) { + if([subNotificationCenterDelegate respondsToSelector:thisMethodSelector]) { + [subNotificationCenterDelegate userNotificationCenter:center didReceiveNotificationResponse:response withCompletionHandler:noopCompletionHandler]; + } + } + completionHandler(); +} + +- (NSData*)extractJSONData:(UNNotification*)notification + withWasTapped:(BOOL)wasTapped { + UNNotificationContent *content = notification.request.content; + NSLog(@"Push notification received: title=\"%@\" subtitle=\"%@\" body=\"%@\" badge=\"%@\"", + content.title, content.subtitle, content.body, content.badge); + NSLog(@"Push data received: %@", content.userInfo); + NSDictionary *notificationData = [content.userInfo mutableCopy]; + if([notificationData objectForKey:@"wasTapped"] == nil) { [notificationData setValue:@(wasTapped) forKey:@"wasTapped"]; } + if([notificationData objectForKey:@"title"] == nil) { [notificationData setValue:content.title forKey:@"title"]; } + if([notificationData objectForKey:@"subtitle"] == nil) { [notificationData setValue:content.subtitle forKey:@"subtitle"]; } + if([notificationData objectForKey:@"body"] == nil) { [notificationData setValue:content.body forKey:@"body"]; } + if([notificationData objectForKey:@"badge"] == nil) { [notificationData setValue:content.badge forKey:@"badge"]; } + NSError *error; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:notificationData options:0 error:&error]; + if(error != NULL) { + NSLog(@"Error on converting to JSON: %@", notificationData); + return NULL; + } + return jsonData; +} + +@end diff --git a/src/ios/FCMPlugin.h b/src/ios/FCMPlugin.h index be65fdcb7..24e8dc90c 100644 --- a/src/ios/FCMPlugin.h +++ b/src/ios/FCMPlugin.h @@ -1,19 +1,21 @@ #import #import -@interface FCMPlugin : CDVPlugin -{ - //NSString *notificationCallBack; -} +@interface FCMPlugin : CDVPlugin {} + (FCMPlugin *) fcmPlugin; +- (void)notifyFCMTokenRefresh:(NSString*) token; - (void)ready:(CDVInvokedUrlCommand*)command; +- (void)hasPermission:(CDVInvokedUrlCommand*)command; - (void)getToken:(CDVInvokedUrlCommand*)command; +- (void)returnTokenOrRetry:(void (^)(NSString* fcmToken))onSuccess; +- (void)getAPNSToken:(CDVInvokedUrlCommand*)command; +- (void)getInitialPushPayload:(CDVInvokedUrlCommand*)command; +- (void)deleteInstanceId:(CDVInvokedUrlCommand*)command; +- (void)clearAllNotifications:(CDVInvokedUrlCommand *)command; - (void)subscribeToTopic:(CDVInvokedUrlCommand*)command; - (void)unsubscribeFromTopic:(CDVInvokedUrlCommand*)command; -- (void)registerNotification:(CDVInvokedUrlCommand*)command; - (void)notifyOfMessage:(NSData*) payload; -- (void)notifyOfTokenRefresh:(NSString*) token; - (void)appEnterBackground; - (void)appEnterForeground; diff --git a/src/ios/FCMPlugin.m b/src/ios/FCMPlugin.m index e4edb1613..0bb0af76e 100644 --- a/src/ios/FCMPlugin.m +++ b/src/ios/FCMPlugin.m @@ -1,127 +1,224 @@ #include #include - #import "AppDelegate+FCMPlugin.h" - +#import #import +#import #import "FCMPlugin.h" -#import "Firebase.h" +#import @interface FCMPlugin () {} @end @implementation FCMPlugin -static BOOL notificatorReceptorReady = NO; static BOOL appInForeground = YES; -static NSString *notificationCallback = @"FCMPlugin.onNotificationReceived"; -static NSString *tokenRefreshCallback = @"FCMPlugin.onTokenRefreshReceived"; +static NSString *notificationEventName = @"notification"; +static NSString *tokenRefreshCallback = @"tokenRefresh"; +static NSString *jsEventBridgeCallbackId; static FCMPlugin *fcmPluginInstance; -+ (FCMPlugin *) fcmPlugin { - ++ (FCMPlugin *)fcmPlugin { return fcmPluginInstance; } -- (void) ready:(CDVInvokedUrlCommand *)command -{ +- (void)ready:(CDVInvokedUrlCommand *)command { NSLog(@"Cordova view ready"); fcmPluginInstance = self; [self.commandDelegate runInBackground:^{ - CDVPluginResult* pluginResult = nil; pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; - } -// GET TOKEN // -- (void) getToken:(CDVInvokedUrlCommand *)command -{ +- (void)hasPermission:(CDVInvokedUrlCommand *)command { + [self.commandDelegate runInBackground:^{ + [AppDelegate hasPushPermission:^(NSNumber* pushPermission){ + __block CDVPluginResult *commandResult; + if (pushPermission == nil) { + NSLog(@"has push permission: unknown"); + commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else if ([pushPermission boolValue] == YES) { + NSLog(@"has push permission: true"); + commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:YES]; + } else if ([pushPermission boolValue] == NO) { + NSLog(@"has push permission: false"); + commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:NO]; + } + [self.commandDelegate sendPluginResult:commandResult callbackId:command.callbackId]; + }]; + }]; +} + +- (void)startJsEventBridge:(CDVInvokedUrlCommand *)command { + NSLog(@"start Js Event Bridge"); + jsEventBridgeCallbackId = command.callbackId; +} + +- (void)getToken:(CDVInvokedUrlCommand *)command { NSLog(@"get Token"); + [self returnTokenOrRetry:^(NSString* fcmToken){ + CDVPluginResult* pluginResult = nil; + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:fcmToken]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +- (void)returnTokenOrRetry:(void (^)(NSString* fcmToken))onSuccess { + NSString* fcmToken = [AppDelegate getFCMToken]; + if(fcmToken != nil) { + onSuccess(fcmToken); + return; + } + SEL thisMethodSelector = NSSelectorFromString(@"returnTokenOrRetry:"); + NSLog(@"FCMToken unavailable, it'll retry in one second"); + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:thisMethodSelector]]; + [invocation setSelector:thisMethodSelector]; + [invocation setTarget:self]; + [invocation setArgument:&(onSuccess) atIndex:2]; //arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocationion + [NSTimer scheduledTimerWithTimeInterval:1 invocation:invocation repeats:NO]; +} + +- (void)getAPNSToken:(CDVInvokedUrlCommand *)command { + NSLog(@"get APNS Token"); [self.commandDelegate runInBackground:^{ - NSString* token = [[FIRInstanceID instanceID] token]; CDVPluginResult* pluginResult = nil; - pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:token]; + NSString* apnsToken = [AppDelegate getAPNSToken]; + NSLog(@"get APNS Token value: %@", apnsToken); + pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:apnsToken]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; } -// UN/SUBSCRIBE TOPIC // -- (void) subscribeToTopic:(CDVInvokedUrlCommand *)command -{ +- (void)clearAllNotifications:(CDVInvokedUrlCommand *)command { + [self.commandDelegate runInBackground:^{ + NSLog(@"clear all notifications"); + [[UIApplication sharedApplication] setApplicationIconBadgeNumber:1]; + [[UIApplication sharedApplication] setApplicationIconBadgeNumber:0]; + CDVPluginResult *pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +- (void)subscribeToTopic:(CDVInvokedUrlCommand *)command { NSString* topic = [command.arguments objectAtIndex:0]; NSLog(@"subscribe To Topic %@", topic); [self.commandDelegate runInBackground:^{ - if(topic != nil)[[FIRMessaging messaging] subscribeToTopic:[NSString stringWithFormat:@"/topics/%@", topic]]; + if(topic != nil)[[FIRMessaging messaging] subscribeToTopic:topic]; CDVPluginResult* pluginResult = nil; pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:topic]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; } -- (void) unsubscribeFromTopic:(CDVInvokedUrlCommand *)command -{ +- (void)unsubscribeFromTopic:(CDVInvokedUrlCommand *)command { NSString* topic = [command.arguments objectAtIndex:0]; NSLog(@"unsubscribe From Topic %@", topic); [self.commandDelegate runInBackground:^{ - if(topic != nil)[[FIRMessaging messaging] unsubscribeFromTopic:[NSString stringWithFormat:@"/topics/%@", topic]]; + if(topic != nil)[[FIRMessaging messaging] unsubscribeFromTopic:topic]; CDVPluginResult* pluginResult = nil; pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:topic]; [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; }]; } -- (void) registerNotification:(CDVInvokedUrlCommand *)command -{ - NSLog(@"view registered for notifications"); - - notificatorReceptorReady = YES; - NSData* lastPush = [AppDelegate getLastPush]; - if (lastPush != nil) { - [FCMPlugin.fcmPlugin notifyOfMessage:lastPush]; - } - - CDVPluginResult* pluginResult = nil; - pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; - [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; +- (void)requestPushPermission:(CDVInvokedUrlCommand *)command { + [self.commandDelegate runInBackground:^{ + NSNumber* ios9SupportTimeout = [command argumentAtIndex:0 withDefault:[NSNumber numberWithFloat:10]]; + NSNumber* ios9SupportInterval = [command argumentAtIndex:1 withDefault:[NSNumber numberWithFloat:0.3]]; + NSLog(@"requestPushPermission { ios9SupportTimeout:%@ ios9SupportInterval:%@ }", ios9SupportTimeout, ios9SupportInterval); + id objects[] = { ios9SupportTimeout, ios9SupportInterval }; + id keys[] = { @"ios9SupportTimeout", @"ios9SupportInterval" }; + NSDictionary* options = [NSDictionary dictionaryWithObjects:objects forKeys:keys count:2]; + [AppDelegate requestPushPermission:^(BOOL pushPermission, NSError* _Nullable error) { + if(error != nil){ + NSLog(@"push permission request error: %@", error); + __block CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[error description]]; + [self.commandDelegate sendPluginResult:commandResult callbackId:command.callbackId]; + return; + } + NSLog(@"push permission request result: %@", pushPermission ? @"Yes" : @"No"); + __block CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool:pushPermission]; + [self.commandDelegate sendPluginResult:commandResult callbackId:command.callbackId]; + } withOptions:options]; + }]; } --(void) notifyOfMessage:(NSData *)payload -{ - NSString *JSONString = [[NSString alloc] initWithBytes:[payload bytes] length:[payload length] encoding:NSUTF8StringEncoding]; - NSString * notifyJS = [NSString stringWithFormat:@"%@(%@);", notificationCallback, JSONString]; - NSLog(@"stringByEvaluatingJavaScriptFromString %@", notifyJS); - - if ([self.webView respondsToSelector:@selector(stringByEvaluatingJavaScriptFromString:)]) { - [(UIWebView *)self.webView stringByEvaluatingJavaScriptFromString:notifyJS]; - } else { - [self.webViewEngine evaluateJavaScript:notifyJS completionHandler:nil]; - } +- (void)getInitialPushPayload:(CDVInvokedUrlCommand *)command { + NSLog(@"getInitialPushPayload"); + [self.commandDelegate runInBackground:^{ + NSData* dataPayload = [AppDelegate getInitialPushPayload]; + if (dataPayload == nil) { + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:nil]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } + NSString *strUTF8 = [[NSString alloc] initWithData:dataPayload encoding:NSUTF8StringEncoding]; + NSData *dataPayloadUTF8 = [strUTF8 dataUsingEncoding:NSUTF8StringEncoding]; + NSError* error = nil; + NSDictionary *payloadDictionary = [NSJSONSerialization JSONObjectWithData:dataPayloadUTF8 options:0 error:&error]; + if (error) { + NSString* errorMessage = [NSString stringWithFormat:@"%@ => '%@'", [error localizedDescription], strUTF8]; + NSLog(@"getInitialPushPayload error: %@", errorMessage); + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_JSON_EXCEPTION messageAsString:errorMessage]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + return; + } + NSLog(@"getInitialPushPayload value: %@", payloadDictionary); + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:payloadDictionary]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId]; + }]; +} + +- (void)deleteInstanceId:(CDVInvokedUrlCommand *)command { + [self.commandDelegate runInBackground:^{ + [AppDelegate deleteInstanceId:^(NSError *error) { + __block CDVPluginResult *commandResult; + if(error == nil) { + NSLog(@"InstanceID deleted"); + commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK]; + } else { + NSLog(@"InstanceID deletion error: %@", error); + commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:[error description]]; + } + [self.commandDelegate sendPluginResult:commandResult callbackId:command.callbackId]; + }]; + }]; +} + +- (void)notifyOfMessage:(NSData *)payload { + NSLog(@"notifyOfMessage payload: %@", payload); + NSString* JSONString = [[NSString alloc] initWithBytes:[payload bytes] length:[payload length] encoding:NSUTF8StringEncoding]; + [self dispatchJSEvent:notificationEventName withData:JSONString]; +} + +- (void)notifyFCMTokenRefresh:(NSString *)token { + NSLog(@"notifyFCMTokenRefresh token: %@", token); + NSString* jsToken = [NSString stringWithFormat:@"\"%@\"", token]; + [self dispatchJSEvent:tokenRefreshCallback withData:jsToken]; } --(void) notifyOfTokenRefresh:(NSString *)token -{ - NSString * notifyJS = [NSString stringWithFormat:@"%@('%@');", tokenRefreshCallback, token]; - NSLog(@"stringByEvaluatingJavaScriptFromString %@", notifyJS); - - if ([self.webView respondsToSelector:@selector(stringByEvaluatingJavaScriptFromString:)]) { - [(UIWebView *)self.webView stringByEvaluatingJavaScriptFromString:notifyJS]; - } else { - [self.webViewEngine evaluateJavaScript:notifyJS completionHandler:nil]; +- (void)dispatchJSEvent:(NSString *)eventName withData:(NSString *)jsData { + if(jsEventBridgeCallbackId == nil) { + NSLog(@"dispatchJSEvent: Unable to send event due to unreachable bridge context: %@ with %@", eventName, jsData); + return; } + NSLog(@"dispatchJSEvent: %@ with %@", eventName, jsData); + NSString* eventDataTemplate = @"[\"%@\",%@]"; + NSString* eventData = [NSString stringWithFormat:eventDataTemplate, eventName, jsData]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:eventData]; + [pluginResult setKeepCallbackAsBool:TRUE]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:jsEventBridgeCallbackId]; } --(void) appEnterBackground -{ +- (void)appEnterBackground { NSLog(@"Set state background"); appInForeground = NO; } --(void) appEnterForeground -{ +- (void)appEnterForeground { NSLog(@"Set state foreground"); NSData* lastPush = [AppDelegate getLastPush]; if (lastPush != nil) { diff --git a/src/ios/FCMPluginIOS9Support.h b/src/ios/FCMPluginIOS9Support.h new file mode 100644 index 000000000..b768de82e --- /dev/null +++ b/src/ios/FCMPluginIOS9Support.h @@ -0,0 +1,10 @@ +@interface FCMPluginIOS9Support : NSObject {} + ++ (void)requestPushPermission:(void (^)(BOOL yesOrNo, NSError* error))block withOptions:(NSDictionary*)options; ++ (void)hasPushPermission:(void (^)(NSNumber* yesNoOrNil))block; ++ (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo; ++ (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler; ++ (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceTokenData; ++ (void)application:(UIApplication *)application didFailToRegisterForRemoteNotifications:(NSError *)error; + +@end diff --git a/src/ios/FCMPluginIOS9Support.m b/src/ios/FCMPluginIOS9Support.m new file mode 100644 index 000000000..038b009f2 --- /dev/null +++ b/src/ios/FCMPluginIOS9Support.m @@ -0,0 +1,156 @@ +#import +#import +#import "FCMPlugin.h" +#import "FCMPluginIOS9Support.h" +#import "AppDelegate+FCMPlugin.h" + +@interface FCMPluginIOS9Support () {} +@end + +@implementation FCMPluginIOS9Support + +NSString *const hasRequestedPushPermissionPersistenceKey = @"FCMPlugin.iOS9.hasRequestedPushPermission"; +static void (^requestPushPermissionCallback)(BOOL yesOrNo, NSError* _Nullable error); + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" ++ (void)requestPushPermission:(void (^)(BOOL yesOrNo, NSError* _Nullable error))block withOptions:(NSDictionary*)options { + requestPushPermissionCallback = block; + UIUserNotificationType allNotificationTypes = + (UIUserNotificationTypeSound | UIUserNotificationTypeAlert | UIUserNotificationTypeBadge); + UIUserNotificationSettings *settings = + [UIUserNotificationSettings settingsForTypes:allNotificationTypes categories:nil]; + [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; + NSNumber* ios9SupportTimeout = options[@"ios9SupportTimeout"]; + float timeout = [ios9SupportTimeout floatValue]; + NSNumber* ios9SupportInterval = options[@"ios9SupportInterval"]; + float interval = [ios9SupportInterval floatValue]; + dispatch_async(dispatch_get_main_queue(), ^{ + [[UIApplication sharedApplication] registerForRemoteNotifications]; + [self waitForUserDecision:timeout withInterval:interval]; + }); +} +#pragma clang diagnostic pop + ++ (void)callbackRequestPushPermission:(BOOL)yesOrNo { + [self callbackRequestPushPermission:yesOrNo withError:nil]; +} + ++ (void)callbackRequestPushPermission:(BOOL)yesOrNo withError:(NSError* _Nullable)error { + [self setHasRequestedPushPermission:YES]; + if(requestPushPermissionCallback != nil) { + requestPushPermissionCallback(yesOrNo, error); + requestPushPermissionCallback = nil; + } +} + ++ (void)waitForUserDecision:(float)timeout withInterval:(float)interval { + [self hasPushPermission:^(NSNumber* pushPermission){ + if(pushPermission != nil) { + // User has chosen. + [self callbackRequestPushPermission:[pushPermission boolValue]]; + return; + } + if(timeout < 0) { + // We have to speculate that the request was not accepted + [self callbackRequestPushPermission:NO]; + return; + } + SEL thisMethodSelector = NSSelectorFromString(@"waitForUserDecision:withInterval:"); + if(![self respondsToSelector:thisMethodSelector]) { + NSLog(@"waitForUserDecision:withInterval: selector not found in FCMPluginIOS9Support"); + return; + } + float remainingTimeout = timeout - interval; + float givenInterval = interval; + NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[self methodSignatureForSelector:thisMethodSelector]]; + [invocation setSelector:thisMethodSelector]; + [invocation setTarget:self]; + [invocation setArgument:&(remainingTimeout) atIndex:2]; //arguments 0 and 1 are self and _cmd respectively, automatically set by NSInvocationion + [invocation setArgument:&(givenInterval) atIndex:3]; + [NSTimer scheduledTimerWithTimeInterval:interval invocation:invocation repeats:NO]; + }]; +} + ++ (void)setHasRequestedPushPermission:(BOOL)pushPermission { + [[NSUserDefaults standardUserDefaults] + setObject:[NSNumber numberWithBool:pushPermission] forKey:hasRequestedPushPermissionPersistenceKey]; + [[NSUserDefaults standardUserDefaults] synchronize]; +} + ++ (BOOL)getHasRequestedPushPermission { + if ([[NSUserDefaults standardUserDefaults] objectForKey:hasRequestedPushPermissionPersistenceKey]) { + bool hasRequestedPushPermission = [[[NSUserDefaults standardUserDefaults] + objectForKey:hasRequestedPushPermissionPersistenceKey] boolValue]; + return hasRequestedPushPermission; + } + return NO; +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" ++ (void)hasPushPermission:(void (^)(NSNumber* yesNoOrNil))block { + NSString* apnsToken = [AppDelegate getAPNSToken]; + if(apnsToken != nil) { + block([NSNumber numberWithBool:YES]); + return; + } + if ([[UIApplication sharedApplication] isRegisteredForRemoteNotifications]) { + block([NSNumber numberWithBool:YES]); + return; + } + UIUserNotificationSettings *notificationSettings = [[UIApplication sharedApplication] currentUserNotificationSettings]; + BOOL itDoesHaveNotificationSettings = ((int) notificationSettings.types) != 0; + if(itDoesHaveNotificationSettings) { + block([NSNumber numberWithBool:YES]); + } + BOOL alreadyRequested = [self getHasRequestedPushPermission]; + block(alreadyRequested ? [NSNumber numberWithBool:NO] : nil); +} +#pragma clang diagnostic pop + ++ (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { + NSLog(@"Message ID: %@", userInfo[@"gcm.message_id"]); + NSError *error; + NSDictionary *userInfoMutable = [userInfo mutableCopy]; + if (application.applicationState != UIApplicationStateActive) { + NSLog(@"New method with push callback: %@", userInfo); + [userInfoMutable setValue:@(YES) forKey:@"wasTapped"]; + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:userInfoMutable options:0 error:&error]; + NSLog(@"APP WAS CLOSED DURING PUSH RECEPTION Saved data: %@", jsonData); + [AppDelegate setInitialPushPayload:jsonData]; + [AppDelegate setLastPush:jsonData]; + } +} + ++ (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceTokenData { + [self callbackRequestPushPermission:YES]; +} + ++ (void)application:(UIApplication *)application didFailToRegisterForRemoteNotifications:(NSError *)error { + [self callbackRequestPushPermission:NO withError:error]; +} + ++ (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { + NSLog(@"Message ID: %@", userInfo[@"gcm.message_id"]); + NSLog(@"%@", userInfo); + NSError *error; + NSDictionary *userInfoMutable = [userInfo mutableCopy]; + + // Has user tapped the notificaiton? + // UIApplicationStateActive - app is currently active + // UIApplicationStateInactive - app is transitioning from background to + // foreground (user taps notification) + if (application.applicationState == UIApplicationStateActive + || application.applicationState == UIApplicationStateInactive) { + [userInfoMutable setValue:@(NO) forKey:@"wasTapped"]; + NSLog(@"app active"); + NSData *jsonData = [NSJSONSerialization dataWithJSONObject:userInfoMutable + options:0 + error:&error]; + [FCMPlugin.fcmPlugin notifyOfMessage:jsonData]; + } + completionHandler(UIBackgroundFetchResultNoData); +} + +@end diff --git a/src/ios/firebase/Firebase.h b/src/ios/firebase/Firebase.h deleted file mode 100644 index 90798a6a7..000000000 --- a/src/ios/firebase/Firebase.h +++ /dev/null @@ -1,52 +0,0 @@ -#import -#import - -#if !defined(__has_include) - #error "Firebase.h won't import anything if your compiler doesn't support __has_include. Please \ - import the headers individually." -#else - #if __has_include() - #import - #endif - - #if __has_include() - #import - #endif - - #if __has_include() - #import - #endif - - #if __has_include() - #import - #endif - - #if __has_include() - #import - #endif - - #if __has_include() - #import - #endif - - #if __has_include() - #import - #endif - - #if __has_include() - #import - #endif - - #if __has_include() - #import - #endif - - #if __has_include() - #import - #endif - - #if __has_include() - #import - #endif - -#endif // defined(__has_include) diff --git a/src/ios/firebase/FirebaseAnalytics.framework/FirebaseAnalytics b/src/ios/firebase/FirebaseAnalytics.framework/FirebaseAnalytics deleted file mode 100644 index 86b3a4cb2..000000000 Binary files a/src/ios/firebase/FirebaseAnalytics.framework/FirebaseAnalytics and /dev/null differ diff --git a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRAnalytics+AppDelegate.h b/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRAnalytics+AppDelegate.h deleted file mode 100644 index e3ff4c125..000000000 --- a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRAnalytics+AppDelegate.h +++ /dev/null @@ -1,57 +0,0 @@ -#import - -#import "FIRAnalytics.h" - -/** - * Provides App Delegate handlers to be used in your App Delegate. - * - * To save time integrating Firebase Analytics in an application, Firebase Analytics does not - * require delegation implementation from the AppDelegate. Instead this is automatically done by - * Firebase Analytics. Should you choose instead to delegate manually, you can turn off the App - * Delegate Proxy by adding FirebaseAppDelegateProxyEnabled into your app's Info.plist and setting - * it to NO, and adding the methods in this category to corresponding delegation handlers. - * - * To handle Universal Links, you must return YES in - * [UIApplicationDelegate application:didFinishLaunchingWithOptions:]. - */ -@interface FIRAnalytics (AppDelegate) - -/** - * Handles events related to a URL session that are waiting to be processed. - * - * For optimal use of Firebase Analytics, call this method from the - * [UIApplicationDelegate application:handleEventsForBackgroundURLSession:completionHandler] - * method of the app delegate in your app. - * - * @param identifier The identifier of the URL session requiring attention. - * @param completionHandler The completion handler to call when you finish processing the events. - * Calling this completion handler lets the system know that your app's user interface is - * updated and a new snapshot can be taken. - */ -+ (void)handleEventsForBackgroundURLSession:(NSString *)identifier - completionHandler:(void (^)(void))completionHandler; - -/** - * Handles the event when the app is launched by a URL. - * - * Call this method from [UIApplicationDelegate application:openURL:options:] (on iOS 9.0 and - * above), or [UIApplicationDelegate application:openURL:sourceApplication:annotation:] (on iOS 8.x - * and below) in your app. - * - * @param url The URL resource to open. This resource can be a network resource or a file. - */ -+ (void)handleOpenURL:(NSURL *)url; - -/** - * Handles the event when the app receives data associated with user activity that includes a - * Universal Link (on iOS 9.0 and above). - * - * Call this method from [UIApplication continueUserActivity:restorationHandler:] in your app - * delegate (on iOS 9.0 and above). - * - * @param userActivity The activity object containing the data associated with the task the user - * was performing. - */ -+ (void)handleUserActivity:(id)userActivity; - -@end diff --git a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRAnalytics.h b/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRAnalytics.h deleted file mode 100644 index 43ee86e74..000000000 --- a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRAnalytics.h +++ /dev/null @@ -1,71 +0,0 @@ -#import - -#import "FIREventNames.h" -#import "FIRParameterNames.h" -#import "FIRUserPropertyNames.h" - -/// The top level Firebase Analytics singleton that provides methods for logging events and setting -/// user properties. See the developer guides for general -/// information on using Firebase Analytics in your apps. -@interface FIRAnalytics : NSObject - -/// Logs an app event. The event can have up to 25 parameters. Events with the same name must have -/// the same parameters. Up to 500 event names are supported. Using predefined events and/or -/// parameters is recommended for optimal reporting. -/// -/// The following event names are reserved and cannot be used: -///
    -///
  • app_clear_data
  • -///
  • app_remove
  • -///
  • app_update
  • -///
  • error
  • -///
  • first_open
  • -///
  • in_app_purchase
  • -///
  • notification_dismiss
  • -///
  • notification_foreground
  • -///
  • notification_open
  • -///
  • notification_receive
  • -///
  • os_update
  • -///
  • session_start
  • -///
  • user_engagement
  • -///
-/// -/// @param name The name of the event. Should contain 1 to 32 alphanumeric characters or -/// underscores. The name must start with an alphabetic character. Some event names are -/// reserved. See FIREventNames.h for the list of reserved event names. The "firebase_" prefix -/// is reserved and should not be used. Note that event names are case-sensitive and that -/// logging two events whose names differ only in case will result in two distinct events. -/// @param parameters The dictionary of event parameters. Passing nil indicates that the event has -/// no parameters. Parameter names can be up to 24 characters long and must start with an -/// alphabetic character and contain only alphanumeric characters and underscores. Only NSString -/// and NSNumber (signed 64-bit integer and 64-bit floating-point number) parameter types are -/// supported. NSString parameter values can be up to 36 characters long. The "firebase_" prefix -/// is reserved and should not be used for parameter names. -+ (void)logEventWithName:(nonnull NSString *)name - parameters:(nullable NSDictionary *)parameters; - -/// Sets a user property to a given value. Up to 25 user property names are supported. Once set, -/// user property values persist throughout the app lifecycle and across sessions. -/// -/// The following user property names are reserved and cannot be used: -///
    -///
  • first_open_time
  • -///
  • last_deep_link_referrer
  • -///
  • user_id
  • -///
-/// -/// @param value The value of the user property. Values can be up to 36 characters long. Setting the -/// value to nil removes the user property. -/// @param name The name of the user property to set. Should contain 1 to 24 alphanumeric characters -/// or underscores and must start with an alphabetic character. The "firebase_" prefix is -/// reserved and should not be used for user property names. -+ (void)setUserPropertyString:(nullable NSString *)value forName:(nonnull NSString *)name; - -/// Sets the user ID property. This feature must be used in accordance with -/// Google's Privacy Policy -/// -/// @param userID The user ID to ascribe to the user of this app on this device, which must be -/// non-empty and no more than 36 characters long. Setting userID to nil removes the user ID. -+ (void)setUserID:(nullable NSString *)userID; - -@end diff --git a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRAnalyticsConfiguration.h b/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRAnalyticsConfiguration.h deleted file mode 100644 index dc227a4c3..000000000 --- a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRAnalyticsConfiguration.h +++ /dev/null @@ -1 +0,0 @@ -#import diff --git a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRApp.h b/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRApp.h deleted file mode 100644 index de24da17d..000000000 --- a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRApp.h +++ /dev/null @@ -1 +0,0 @@ -#import diff --git a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRConfiguration.h b/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRConfiguration.h deleted file mode 100644 index be2ff7bff..000000000 --- a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRConfiguration.h +++ /dev/null @@ -1 +0,0 @@ -#import diff --git a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIREventNames.h b/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIREventNames.h deleted file mode 100644 index f68967b73..000000000 --- a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIREventNames.h +++ /dev/null @@ -1,321 +0,0 @@ -/// @file FIREventNames.h -/// -/// Predefined event names. -/// -/// An Event is an important occurrence in your app that you want to measure. You can report up to -/// 500 different types of Events per app and you can associate up to 25 unique parameters with each -/// Event type. Some common events are suggested below, but you may also choose to specify custom -/// Event types that are associated with your specific app. Each event type is identified by a -/// unique name. Event names can be up to 32 characters long, may only contain alphanumeric -/// characters and underscores ("_"), and must start with an alphabetic character. The "firebase_" -/// prefix is reserved and should not be used. - -/// Add Payment Info event. This event signifies that a user has submitted their payment information -/// to your app. -static NSString *const kFIREventAddPaymentInfo = @"add_payment_info"; - -/// E-Commerce Add To Cart event. This event signifies that an item was added to a cart for -/// purchase. Add this event to a funnel with kFIREventEcommercePurchase to gauge the effectiveness -/// of your checkout process. Note: If you supply the {@link kFIRParameterValue} parameter, you must -/// also supply the {@link kFIRParameterCurrency} parameter so that revenue metrics can be computed -/// accurately. Params: -/// -///
    -///
  • {@link kFIRParameterQuantity} (signed 64-bit integer as NSNumber)
  • -///
  • {@link kFIRParameterItemID} (NSString)
  • -///
  • {@link kFIRParameterItemName} (NSString)
  • -///
  • {@link kFIRParameterItemCategory} (NSString)
  • -///
  • {@link kFIRParameterItemLocationID} (NSString) (optional)
  • -///
  • {@link kFIRParameterPrice} (double as NSNumber) (optional)
  • -///
  • {@link kFIRParameterCurrency} (NSString) (optional)
  • -///
  • {@link kFIRParameterValue} (double as NSNumber) (optional)
  • -///
  • {@link kFIRParameterOrigin} (NSString) (optional)
  • -///
  • {@link kFIRParameterDestination} (NSString) (optional)
  • -///
  • {@link kFIRParameterStartDate} (NSString) (optional)
  • -///
  • {@link kFIRParameterEndDate} (NSString) (optional)
  • -///
-static NSString *const kFIREventAddToCart = @"add_to_cart"; - -/// E-Commerce Add To Wishlist event. This event signifies that an item was added to a wishlist. -/// Use this event to identify popular gift items in your app. Note: If you supply the -/// {@link kFIRParameterValue} parameter, you must also supply the {@link kFIRParameterCurrency} -/// parameter so that revenue metrics can be computed accurately. Params: -/// -///
    -///
  • {@link kFIRParameterQuantity} (signed 64-bit integer as NSNumber)
  • -///
  • {@link kFIRParameterItemID} (NSString)
  • -///
  • {@link kFIRParameterItemName} (NSString)
  • -///
  • {@link kFIRParameterItemCategory} (NSString)
  • -///
  • {@link kFIRParameterItemLocationID} (NSString) (optional)
  • -///
  • {@link kFIRParameterPrice} (double as NSNumber) (optional)
  • -///
  • {@link kFIRParameterCurrency} (NSString) (optional)
  • -///
  • {@link kFIRParameterValue} (double as NSNumber) (optional)
  • -///
-static NSString *const kFIREventAddToWishlist = @"add_to_wishlist"; - -/// App Open event. By logging this event when an App is moved to the foreground, developers can -/// understand how often users leave and return during the course of a Session. Although Sessions -/// are automatically reported, this event can provide further clarification around the continuous -/// engagement of app-users. -static NSString *const kFIREventAppOpen = @"app_open"; - -/// E-Commerce Begin Checkout event. This event signifies that a user has begun the process of -/// checking out. Add this event to a funnel with your kFIREventEcommercePurchase event to gauge the -/// effectiveness of your checkout process. Note: If you supply the {@link kFIRParameterValue} -/// parameter, you must also supply the {@link kFIRParameterCurrency} parameter so that revenue -/// metrics can be computed accurately. Params: -/// -///
    -///
  • {@link kFIRParameterValue} (double as NSNumber) (optional)
  • -///
  • {@link kFIRParameterCurrency} (NSString) (optional)
  • -///
  • {@link kFIRParameterTransactionID} (NSString) (optional)
  • -///
  • {@link kFIRParameterStartDate} (NSString) (optional)
  • -///
  • {@link kFIRParameterEndDate} (NSString) (optional)
  • -///
  • {@link kFIRParameterNumberOfNights} (signed 64-bit integer as NSNumber) (optional) for -/// hotel bookings
  • -///
  • {@link kFIRParameterNumberOfRooms} (signed 64-bit integer as NSNumber) (optional) for -/// hotel bookings
  • -///
  • {@link kFIRParameterNumberOfPassengers} (signed 64-bit integer as NSNumber) (optional) -/// for travel bookings
  • -///
  • {@link kFIRParameterOrigin} (NSString) (optional)
  • -///
  • {@link kFIRParameterDestination} (NSString) (optional)
  • -///
  • {@link kFIRParameterTravelClass} (NSString) (optional) for travel bookings
  • -///
-static NSString *const kFIREventBeginCheckout = @"begin_checkout"; - -/// Earn Virtual Currency event. This event tracks the awarding of virtual currency in your app. Log -/// this along with {@link kFIREventSpendVirtualCurrency} to better understand your virtual economy. -/// Params: -/// -///
    -///
  • {@link kFIRParameterVirtualCurrencyName} (NSString)
  • -///
  • {@link kFIRParameterValue} (signed 64-bit integer or double as NSNumber)
  • -///
-static NSString *const kFIREventEarnVirtualCurrency = @"earn_virtual_currency"; - -/// E-Commerce Purchase event. This event signifies that an item was purchased by a user. Note: -/// This is different from the in-app purchase event, which is reported automatically for App -/// Store-based apps. Note: If you supply the {@link kFIRParameterValue} parameter, you must also -/// supply the {@link kFIRParameterCurrency} parameter so that revenue metrics can be computed -/// accurately. Params: -/// -///
    -///
  • {@link kFIRParameterCurrency} (NSString) (optional)
  • -///
  • {@link kFIRParameterValue} (double as NSNumber) (optional)
  • -///
  • {@link kFIRParameterTransactionID} (NSString) (optional)
  • -///
  • {@link kFIRParameterTax} (double as NSNumber) (optional)
  • -///
  • {@link kFIRParameterShipping} (double as NSNumber) (optional)
  • -///
  • {@link kFIRParameterCoupon} (NSString) (optional)
  • -///
  • {@link kFIRParameterLocation} (NSString) (optional)
  • -///
  • {@link kFIRParameterStartDate} (NSString) (optional)
  • -///
  • {@link kFIRParameterEndDate} (NSString) (optional)
  • -///
  • {@link kFIRParameterNumberOfNights} (signed 64-bit integer as NSNumber) (optional) for -/// hotel bookings
  • -///
  • {@link kFIRParameterNumberOfRooms} (signed 64-bit integer as NSNumber) (optional) for -/// hotel bookings
  • -///
  • {@link kFIRParameterNumberOfPassengers} (signed 64-bit integer as NSNumber) (optional) -/// for travel bookings
  • -///
  • {@link kFIRParameterOrigin} (NSString) (optional)
  • -///
  • {@link kFIRParameterDestination} (NSString) (optional)
  • -///
  • {@link kFIRParameterTravelClass} (NSString) (optional) for travel bookings
  • -///
-static NSString *const kFIREventEcommercePurchase = @"ecommerce_purchase"; - -/// Generate Lead event. Log this event when a lead has been generated in the app to understand the -/// efficacy of your install and re-engagement campaigns. Note: If you supply the -/// {@link kFIRParameterValue} parameter, you must also supply the {@link kFIRParameterCurrency} -/// parameter so that revenue metrics can be computed accurately. Params: -/// -///
    -///
  • {@link kFIRParameterCurrency} (NSString) (optional)
  • -///
  • {@link kFIRParameterValue} (double as NSNumber) (optional)
  • -///
-static NSString *const kFIREventGenerateLead = @"generate_lead"; - -/// Join Group event. Log this event when a user joins a group such as a guild, team or family. Use -/// this event to analyze how popular certain groups or social features are in your app. Params: -/// -///
    -///
  • {@link kFIRParameterGroupID} (NSString)
  • -///
-static NSString *const kFIREventJoinGroup = @"join_group"; - -/// Level Up event. This event signifies that a player has leveled up in your gaming app. It can -/// help you gauge the level distribution of your userbase and help you identify certain levels that -/// are difficult to pass. Params: -/// -///
    -///
  • {@link kFIRParameterLevel} (signed 64-bit integer as NSNumber)
  • -///
  • {@link kFIRParameterCharacter} (NSString) (optional)
  • -///
-static NSString *const kFIREventLevelUp = @"level_up"; - -/// Login event. Apps with a login feature can report this event to signify that a user has logged -/// in. -static NSString *const kFIREventLogin = @"login"; - -/// Post Score event. Log this event when the user posts a score in your gaming app. This event can -/// help you understand how users are actually performing in your game and it can help you correlate -/// high scores with certain audiences or behaviors. Params: -/// -///
    -///
  • {@link kFIRParameterScore} (signed 64-bit integer as NSNumber)
  • -///
  • {@link kFIRParameterLevel} (signed 64-bit integer as NSNumber) (optional)
  • -///
  • {@link kFIRParameterCharacter} (NSString) (optional)
  • -///
-static NSString *const kFIREventPostScore = @"post_score"; - -/// Present Offer event. This event signifies that the app has presented a purchase offer to a user. -/// Add this event to a funnel with the kFIREventAddToCart and kFIREventEcommercePurchase to gauge -/// your conversion process. Note: If you supply the {@link kFIRParameterValue} parameter, you must -/// also supply the {@link kFIRParameterCurrency} parameter so that revenue metrics can be computed -/// accurately. Params: -/// -///
    -///
  • {@link kFIRParameterQuantity} (signed 64-bit integer as NSNumber)
  • -///
  • {@link kFIRParameterItemID} (NSString)
  • -///
  • {@link kFIRParameterItemName} (NSString)
  • -///
  • {@link kFIRParameterItemCategory} (NSString)
  • -///
  • {@link kFIRParameterItemLocationID} (NSString) (optional)
  • -///
  • {@link kFIRParameterPrice} (double as NSNumber) (optional)
  • -///
  • {@link kFIRParameterCurrency} (NSString) (optional)
  • -///
  • {@link kFIRParameterValue} (double as NSNumber) (optional)
  • -///
-static NSString *const kFIREventPresentOffer = @"present_offer"; - -/// E-Commerce Purchase Refund event. This event signifies that an item purchase was refunded. -/// Note: If you supply the {@link kFIRParameterValue} parameter, you must also supply the -/// {@link kFIRParameterCurrency} parameter so that revenue metrics can be computed accurately. -/// Params: -/// -///
    -///
  • {@link kFIRParameterCurrency} (NSString) (optional)
  • -///
  • {@link kFIRParameterValue} (double as NSNumber) (optional)
  • -///
  • {@link kFIRParameterTransactionID} (NSString) (optional)
  • -///
-static NSString *const kFIREventPurchaseRefund = @"purchase_refund"; - -/// Search event. Apps that support search features can use this event to contextualize search -/// operations by supplying the appropriate, corresponding parameters. This event can help you -/// identify the most popular content in your app. Params: -/// -///
    -///
  • {@link kFIRParameterSearchTerm} (NSString)
  • -///
  • {@link kFIRParameterStartDate} (NSString) (optional)
  • -///
  • {@link kFIRParameterEndDate} (NSString) (optional)
  • -///
  • {@link kFIRParameterNumberOfNights} (signed 64-bit integer as NSNumber) (optional) for -/// hotel bookings
  • -///
  • {@link kFIRParameterNumberOfRooms} (signed 64-bit integer as NSNumber) (optional) for -/// hotel bookings
  • -///
  • {@link kFIRParameterNumberOfPassengers} (signed 64-bit integer as NSNumber) (optional) -/// for travel bookings
  • -///
  • {@link kFIRParameterOrigin} (NSString) (optional)
  • -///
  • {@link kFIRParameterDestination} (NSString) (optional)
  • -///
  • {@link kFIRParameterTravelClass} (NSString) (optional) for travel bookings
  • -///
-static NSString *const kFIREventSearch = @"search"; - -/// Select Content event. This general purpose event signifies that a user has selected some content -/// of a certain type in an app. The content can be any object in your app. This event can help you -/// identify popular content and categories of content in your app. Params: -/// -///
    -///
  • {@link kFIRParameterContentType} (NSString)
  • -///
  • {@link kFIRParameterItemID} (NSString)
  • -///
-static NSString *const kFIREventSelectContent = @"select_content"; - -/// Share event. Apps with social features can log the Share event to identify the most viral -/// content. Params: -/// -///
    -///
  • {@link kFIRParameterContentType} (NSString)
  • -///
  • {@link kFIRParameterItemID} (NSString)
  • -///
-static NSString *const kFIREventShare = @"share"; - -/// Sign Up event. This event indicates that a user has signed up for an account in your app. The -/// parameter signifies the method by which the user signed up. Use this event to understand the -/// different behaviors between logged in and logged out users. Params: -/// -///
    -///
  • {@link kFIRParameterSignUpMethod} (NSString)
  • -///
-static NSString *const kFIREventSignUp = @"sign_up"; - -/// Spend Virtual Currency event. This event tracks the sale of virtual goods in your app and can -/// help you identify which virtual goods are the most popular objects of purchase. Params: -/// -///
    -///
  • {@link kFIRParameterItemName} (NSString)
  • -///
  • {@link kFIRParameterVirtualCurrencyName} (NSString)
  • -///
  • {@link kFIRParameterValue} (signed 64-bit integer or double as NSNumber)
  • -///
-static NSString *const kFIREventSpendVirtualCurrency = @"spend_virtual_currency"; - -/// Tutorial Begin event. This event signifies the start of the on-boarding process in your app. Use -/// this in a funnel with kFIREventTutorialComplete to understand how many users complete this -/// process and move on to the full app experience. -static NSString *const kFIREventTutorialBegin = @"tutorial_begin"; - -/// Tutorial End event. Use this event to signify the user's completion of your app's on-boarding -/// process. Add this to a funnel with kFIREventTutorialBegin to gauge the completion rate of your -/// on-boarding process. -static NSString *const kFIREventTutorialComplete = @"tutorial_complete"; - -/// Unlock Achievement event. Log this event when the user has unlocked an achievement in your -/// game. Since achievements generally represent the breadth of a gaming experience, this event can -/// help you understand how many users are experiencing all that your game has to offer. Params: -/// -///
    -///
  • {@link kFIRParameterAchievementID} (NSString)
  • -///
-static NSString *const kFIREventUnlockAchievement = @"unlock_achievement"; - -/// View Item event. This event signifies that some content was shown to the user. This content may -/// be a product, a webpage or just a simple image or text. Use the appropriate parameters to -/// contextualize the event. Use this event to discover the most popular items viewed in your app. -/// Note: If you supply the {@link kFIRParameterValue} parameter, you must also supply the -/// {@link kFIRParameterCurrency} parameter so that revenue metrics can be computed accurately. -/// Params: -/// -///
    -///
  • {@link kFIRParameterItemID} (NSString)
  • -///
  • {@link kFIRParameterItemName} (NSString)
  • -///
  • {@link kFIRParameterItemCategory} (NSString)
  • -///
  • {@link kFIRParameterItemLocationID} (NSString) (optional)
  • -///
  • {@link kFIRParameterPrice} (double as NSNumber) (optional)
  • -///
  • {@link kFIRParameterQuantity} (signed 64-bit integer as NSNumber) (optional)
  • -///
  • {@link kFIRParameterCurrency} (NSString) (optional)
  • -///
  • {@link kFIRParameterValue} (double as NSNumber) (optional)
  • -///
  • {@link kFIRParameterStartDate} (NSString) (optional)
  • -///
  • {@link kFIRParameterEndDate} (NSString) (optional)
  • -///
  • {@link kFIRParameterFlightNumber} (NSString) (optional) for travel bookings
  • -///
  • {@link kFIRParameterNumberOfPassengers} (signed 64-bit integer as NSNumber) (optional) -/// for travel bookings
  • -///
  • {@link kFIRParameterNumberOfNights} (signed 64-bit integer as NSNumber) (optional) for -/// travel bookings
  • -///
  • {@link kFIRParameterNumberOfRooms} (signed 64-bit integer as NSNumber) (optional) for -/// travel bookings
  • -///
  • {@link kFIRParameterOrigin} (NSString) (optional)
  • -///
  • {@link kFIRParameterDestination} (NSString) (optional)
  • -///
  • {@link kFIRParameterSearchTerm} (NSString) (optional) for travel bookings
  • -///
  • {@link kFIRParameterTravelClass} (NSString) (optional) for travel bookings
  • -///
-static NSString *const kFIREventViewItem = @"view_item"; - -/// View Item List event. Log this event when the user has been presented with a list of items of a -/// certain category. Params: -/// -///
    -///
  • {@link kFIRParameterItemCategory} (NSString)
  • -///
-static NSString *const kFIREventViewItemList = @"view_item_list"; - -/// View Search Results event. Log this event when the user has been presented with the results of a -/// search. Params: -/// -///
    -///
  • {@link kFIRParameterSearchTerm} (NSString)
  • -///
-static NSString *const kFIREventViewSearchResults = @"view_search_results"; diff --git a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIROptions.h b/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIROptions.h deleted file mode 100644 index 126824b02..000000000 --- a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIROptions.h +++ /dev/null @@ -1 +0,0 @@ -#import diff --git a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRParameterNames.h b/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRParameterNames.h deleted file mode 100644 index 42c0c5e21..000000000 --- a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRParameterNames.h +++ /dev/null @@ -1,304 +0,0 @@ -/// @file FIRParameterNames.h -/// -/// Predefined event parameter names. -/// -/// Params supply information that contextualize Events. You can associate up to 25 unique Params -/// with each Event type. Some Params are suggested below for certain common Events, but you are -/// not limited to these. You may supply extra Params for suggested Events or custom Params for -/// Custom events. Param names can be up to 24 characters long, may only contain alphanumeric -/// characters and underscores ("_"), and must start with an alphabetic character. Param values can -/// be up to 36 characters long. The "firebase_" prefix is reserved and should not be used. - -/// Game achievement ID (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterAchievementID : @"10_matches_won",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterAchievementID = @"achievement_id"; - -/// Character used in game (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterCharacter : @"beat_boss",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterCharacter = @"character"; - -/// Type of content selected (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterContentType : @"news article",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterContentType = @"content_type"; - -/// Coupon code for a purchasable item (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterCoupon : @"zz123",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterCoupon = @"coupon"; - -/// Purchase currency in 3-letter -/// ISO_4217 format (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterCurrency : @"USD",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterCurrency = @"currency"; - -/// Flight or Travel destination (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterDestination : @"Mountain View, CA",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterDestination = @"destination"; - -/// The arrival date, check-out date or rental end date for the item. This should be in -/// YYYY-MM-DD format (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterEndDate : @"2015-09-14",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterEndDate = @"end_date"; - -/// Flight number for travel events (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterFlightNumber : @"ZZ800",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterFlightNumber = @"flight_number"; - -/// Group/clan/guild ID (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterGroupID : @"g1",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterGroupID = @"group_id"; - -/// Item category (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterItemCategory : @"t-shirts",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterItemCategory = @"item_category"; - -/// Item ID (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterItemID : @"p7654",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterItemID = @"item_id"; - -/// The Google Place ID (NSString) that -/// corresponds to the associated item. Alternatively, you can supply your own custom Location ID. -///
-///     NSDictionary *params = @{
-///       kFIRParameterItemLocationID : @"ChIJiyj437sx3YAR9kUWC8QkLzQ",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterItemLocationID = @"item_location_id"; - -/// Item name (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterItemName : @"abc",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterItemName = @"item_name"; - -/// Level in game (signed 64-bit integer as NSNumber). -///
-///     NSDictionary *params = @{
-///       kFIRParameterLevel : @(42),
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterLevel = @"level"; - -/// Location (NSString). The Google Place ID -/// that corresponds to the associated event. Alternatively, you can supply your own custom -/// Location ID. -///
-///     NSDictionary *params = @{
-///       kFIRParameterLocation : @"ChIJiyj437sx3YAR9kUWC8QkLzQ",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterLocation = @"location"; - -/// Number of nights staying at hotel (signed 64-bit integer as NSNumber). -///
-///     NSDictionary *params = @{
-///       kFIRParameterNumberOfNights : @(3),
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterNumberOfNights = @"number_of_nights"; - -/// Number of passengers traveling (signed 64-bit integer as NSNumber). -///
-///     NSDictionary *params = @{
-///       kFIRParameterNumberOfPassengers : @(11),
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterNumberOfPassengers = @"number_of_passengers"; - -/// Number of rooms for travel events (signed 64-bit integer as NSNumber). -///
-///     NSDictionary *params = @{
-///       kFIRParameterNumberOfRooms : @(2),
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterNumberOfRooms = @"number_of_rooms"; - -/// Flight or Travel origin (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterOrigin : @"Mountain View, CA",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterOrigin = @"origin"; - -/// Purchase price (double as NSNumber). -///
-///     NSDictionary *params = @{
-///       kFIRParameterPrice : @(1.0),
-///       kFIRParameterCurrency : @"USD",  // e.g. $1.00 USD
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterPrice = @"price"; - -/// Purchase quantity (signed 64-bit integer as NSNumber). -///
-///     NSDictionary *params = @{
-///       kFIRParameterQuantity : @(1),
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterQuantity = @"quantity"; - -/// Score in game (signed 64-bit integer as NSNumber). -///
-///     NSDictionary *params = @{
-///       kFIRParameterScore : @(4200),
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterScore = @"score"; - -/// The search string/keywords used (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterSearchTerm : @"periodic table",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterSearchTerm = @"search_term"; - -/// Shipping cost (double as NSNumber). -///
-///     NSDictionary *params = @{
-///       kFIRParameterShipping : @(9.50),
-///       kFIRParameterCurrency : @"USD",  // e.g. $9.50 USD
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterShipping = @"shipping"; - -/// Sign up method (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterSignUpMethod : @"google",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterSignUpMethod = @"sign_up_method"; - -/// The departure date, check-in date or rental start date for the item. This should be in -/// YYYY-MM-DD format (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterStartDate : @"2015-09-14",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterStartDate = @"start_date"; - -/// Tax amount (double as NSNumber). -///
-///     NSDictionary *params = @{
-///       kFIRParameterTax : @(1.0),
-///       kFIRParameterCurrency : @"USD",  // e.g. $1.00 USD
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterTax = @"tax"; - -/// A single ID for a ecommerce group transaction (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterTransactionID : @"ab7236dd9823",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterTransactionID = @"transaction_id"; - -/// Travel class (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterTravelClass : @"business",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterTravelClass = @"travel_class"; - -/// A context-specific numeric value which is accumulated automatically for each event type. This is -/// a general purpose parameter that is useful for accumulating a key metric that pertains to an -/// event. Examples include revenue, distance, time and points. Value should be specified as signed -/// 64-bit integer or double as NSNumber. Notes: Currency-related values should be supplied using -/// double as NSNumber and must be accompanied by a {@link kFIRParameterCurrency} parameter. The -/// valid range of accumulated values is [-9,223,372,036,854.77, 9,223,372,036,854.77]. -///
-///     NSDictionary *params = @{
-///       kFIRParameterValue : @(3.99),
-///       kFIRParameterCurrency : @"USD",  // e.g. $3.99 USD
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterValue = @"value"; - -/// Name of virtual currency type (NSString). -///
-///     NSDictionary *params = @{
-///       kFIRParameterVirtualCurrencyName : @"virtual_currency_name",
-///       // ...
-///     };
-/// 
-static NSString *const kFIRParameterVirtualCurrencyName = @"virtual_currency_name"; diff --git a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRUserPropertyNames.h b/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRUserPropertyNames.h deleted file mode 100644 index 54cf1c201..000000000 --- a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FIRUserPropertyNames.h +++ /dev/null @@ -1,13 +0,0 @@ -/// @file FIRUserPropertyNames.h -/// -/// Predefined user property names. -/// -/// A UserProperty is an attribute that describes the app-user. By supplying UserProperties, you can -/// later analyze different behaviors of various segments of your userbase. You may supply up to 25 -/// unique UserProperties per app, and you can use the name and value of your choosing for each one. -/// UserProperty names can be up to 24 characters long, may only contain alphanumeric characters and -/// underscores ("_"), and must start with an alphabetic character. UserProperty values can be up to -/// 36 characters long. The "firebase_" prefix is reserved and should not be used. - -/// The method used to sign in. For example, "google", "facebook" or "twitter". -static NSString *const kFIRUserPropertySignUpMethod = @"sign_up_method"; diff --git a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FirebaseAnalytics.h b/src/ios/firebase/FirebaseAnalytics.framework/Headers/FirebaseAnalytics.h deleted file mode 100644 index 02667b303..000000000 --- a/src/ios/firebase/FirebaseAnalytics.framework/Headers/FirebaseAnalytics.h +++ /dev/null @@ -1,11 +0,0 @@ -// Generated umbrella header for FirebaseAnalytics. - -#import "FIRAnalytics+AppDelegate.h" -#import "FIRAnalytics.h" -#import "FIRAnalyticsConfiguration.h" -#import "FIRApp.h" -#import "FIRConfiguration.h" -#import "FIREventNames.h" -#import "FIROptions.h" -#import "FIRParameterNames.h" -#import "FIRUserPropertyNames.h" diff --git a/src/ios/firebase/FirebaseAnalytics.framework/Modules/module.modulemap b/src/ios/firebase/FirebaseAnalytics.framework/Modules/module.modulemap deleted file mode 100644 index e54b5eaae..000000000 --- a/src/ios/firebase/FirebaseAnalytics.framework/Modules/module.modulemap +++ /dev/null @@ -1,23 +0,0 @@ -framework module FirebaseAnalytics { - - export * - - umbrella header "FirebaseAnalytics.h" - - header "FIRAnalytics+AppDelegate.h" - header "FIRAnalytics.h" - header "FIRAnalyticsConfiguration.h" - header "FIRApp.h" - header "FIRConfiguration.h" - header "FIREventNames.h" - header "FIROptions.h" - header "FIRParameterNames.h" - header "FIRUserPropertyNames.h" - - link framework "AddressBook" - link framework "StoreKit" - - link "c++" - link "sqlite3" - link "z" -} diff --git a/src/ios/firebase/FirebaseCore.framework/FirebaseCore b/src/ios/firebase/FirebaseCore.framework/FirebaseCore deleted file mode 100644 index 5f3dcecff..000000000 Binary files a/src/ios/firebase/FirebaseCore.framework/FirebaseCore and /dev/null differ diff --git a/src/ios/firebase/FirebaseCore.framework/Headers/FIRAnalyticsConfiguration.h b/src/ios/firebase/FirebaseCore.framework/Headers/FIRAnalyticsConfiguration.h deleted file mode 100644 index 667d5a4be..000000000 --- a/src/ios/firebase/FirebaseCore.framework/Headers/FIRAnalyticsConfiguration.h +++ /dev/null @@ -1,38 +0,0 @@ -#import - -/** - * This class provides configuration fields for Firebase Analytics. - */ -@interface FIRAnalyticsConfiguration : NSObject - -/** - * Returns the shared instance of FIRAnalyticsConfiguration. - */ -+ (FIRAnalyticsConfiguration *)sharedInstance; - -/** - * Sets the minimum engagement time in seconds required to start a new session. The default value - * is 10 seconds. - */ -- (void)setMinimumSessionInterval:(NSTimeInterval)minimumSessionInterval; - -/** - * Sets the interval of inactivity in seconds that terminates the current session. The default - * value is 1800 seconds (30 minutes). - */ -- (void)setSessionTimeoutInterval:(NSTimeInterval)sessionTimeoutInterval; - -/** - * Sets whether analytics collection is enabled for this app on this device. This setting is - * persisted across app sessions. By default it is enabled. - */ -- (void)setAnalyticsCollectionEnabled:(BOOL)analyticsCollectionEnabled; - -/** - * Deprecated. Sets whether measurement and reporting are enabled for this app on this device. By - * default they are enabled. - */ -- (void)setIsEnabled:(BOOL)isEnabled - DEPRECATED_MSG_ATTRIBUTE("Use setAnalyticsCollectionEnabled: instead."); - -@end diff --git a/src/ios/firebase/FirebaseCore.framework/Headers/FIRApp.h b/src/ios/firebase/FirebaseCore.framework/Headers/FIRApp.h deleted file mode 100644 index 45b238898..000000000 --- a/src/ios/firebase/FirebaseCore.framework/Headers/FIRApp.h +++ /dev/null @@ -1,94 +0,0 @@ -#import -#import - -@class FIROptions; - -NS_ASSUME_NONNULL_BEGIN - -typedef void (^FIRAppVoidBoolCallback)(BOOL success); - -/** - * The entry point of Firebase SDKs. - * - * Initialize and configure FIRApp using [FIRApp configure]; - * Or other customized ways as shown below. - * - * The logging system has two modes: default mode and debug mode. In default mode, only logs with - * log level Notice, Warning and Error will be sent to device. In debug mode, all logs will be sent - * to device. The log levels that Firebase uses are consistent with the ASL log levels. - * - * Enable debug mode by passing the -FIRDebugEnabled argument to the application. You can add this - * argument in the application's Xcode scheme. When debug mode is enabled via -FIRDebugEnabled, - * further executions of the application will also be in debug mode. In order to return to default - * mode, you must explicitly disable the debug mode with the application argument -FIRDebugDisabled. - */ -@interface FIRApp : NSObject - -/** - * Configures a default Firebase app. Raises an exception if any configuration step fails. The - * default app is named "__FIRAPP_DEFAULT". This method should be called after the app is launched - * and before using Firebase services. This method is thread safe. - */ -+ (void)configure; - -/** - * Configures the default Firebase app with the provided options. The default app is named - * "__FIRAPP_DEFAULT". Raises an exception if any configuration step fails. This method is thread - * safe. - * - * @param options The Firebase application options used to configure the service. - */ -+ (void)configureWithOptions:(FIROptions *)options; - -/** - * Configures a Firebase app with the given name and options. Raises an exception if any - * configuration step fails. This method is thread safe. - * - * @param name The application's name given by the developer. The name should should only contain - Letters, Numbers and Underscore. - * @param options The Firebase application options used to configure the services. - */ -+ (void)configureWithName:(NSString *)name options:(FIROptions *)options; - -/** - * Returns the default app, or nil if the default app does not exist. - */ -+ (nullable FIRApp *)defaultApp NS_SWIFT_NAME(defaultApp()); - -/** - * Returns a previously created FIRApp instance with the given name, or nil if no such app exists. - * This method is thread safe. - */ -+ (nullable FIRApp *)appNamed:(NSString *)name; - -/** - * Returns the set of all extant FIRApp instances, or nil if there is no FIRApp instance. This - * method is thread safe. - */ -+ (nullable NSDictionary *)allApps; - -/** - * Cleans up the current FIRApp, freeing associated data and returning its name to the pool for - * future use. This method is thread safe in class level. - */ -- (void)deleteApp:(FIRAppVoidBoolCallback)completion; - -/** - * FIRFirebaseApp instances should not be initialized directly. Call |FIRApp configure|, or - * |FIRApp configureWithOptions:|, or |FIRApp configureWithNames:options| directly. - */ -- (nullable instancetype)init NS_UNAVAILABLE; - -/** - * Gets the name of this app. - */ -@property(nonatomic, copy, readonly) NSString *name; - -/** - * Gets the options for this app. - */ -@property(nonatomic, readonly) FIROptions *options; - -@end - -NS_ASSUME_NONNULL_END diff --git a/src/ios/firebase/FirebaseCore.framework/Headers/FIRConfiguration.h b/src/ios/firebase/FirebaseCore.framework/Headers/FIRConfiguration.h deleted file mode 100644 index e85ea8bc8..000000000 --- a/src/ios/firebase/FirebaseCore.framework/Headers/FIRConfiguration.h +++ /dev/null @@ -1,33 +0,0 @@ -#import - -#import "FIRAnalyticsConfiguration.h" - -/** - * The log levels used by FIRConfiguration. - */ -typedef NS_ENUM(NSInteger, FIRLogLevel) { - kFIRLogLevelError __deprecated = 0, - kFIRLogLevelWarning __deprecated, - kFIRLogLevelInfo __deprecated, - kFIRLogLevelDebug __deprecated, - kFIRLogLevelAssert __deprecated, - kFIRLogLevelMax __deprecated = kFIRLogLevelAssert -} DEPRECATED_MSG_ATTRIBUTE( - "Use -FIRDebugEnabled and -FIRDebugDisabled. See FIRApp.h for more details."); - -/** - * This interface provides global level properties that the developer can tweak, and the singleton - * of the Firebase Analytics configuration class. - */ -@interface FIRConfiguration : NSObject - -+ (FIRConfiguration *)sharedInstance; - -// The configuration class for Firebase Analytics. -@property(nonatomic, readwrite) FIRAnalyticsConfiguration *analyticsConfiguration; - -// Global log level. Defaults to kFIRLogLevelError. -@property(nonatomic, readwrite, assign) FIRLogLevel logLevel DEPRECATED_MSG_ATTRIBUTE( - "Use -FIRDebugEnabled and -FIRDebugDisabled. See FIRApp.h for more details."); - -@end diff --git a/src/ios/firebase/FirebaseCore.framework/Headers/FIROptions.h b/src/ios/firebase/FirebaseCore.framework/Headers/FIROptions.h deleted file mode 100644 index 5ab20c69b..000000000 --- a/src/ios/firebase/FirebaseCore.framework/Headers/FIROptions.h +++ /dev/null @@ -1,87 +0,0 @@ -#import - -/** - * This class provides constant fields of Google APIs. - */ -@interface FIROptions : NSObject - -/** - * Returns the default options. - */ -+ (FIROptions *)defaultOptions; - -/** - * An iOS API key used for authenticating requests from your app, e.g. - * @"AIzaSyDdVgKwhZl0sTTTLZ7iTmt1r3N2cJLnaDk", used to identify your app to Google servers. - */ -@property(nonatomic, readonly, copy) NSString *APIKey; - -/** - * The OAuth2 client ID for iOS application used to authenticate Google users, for example - * @"12345.apps.googleusercontent.com", used for signing in with Google. - */ -@property(nonatomic, readonly, copy) NSString *clientID; - -/** - * The tracking ID for Google Analytics, e.g. @"UA-12345678-1", used to configure Google Analytics. - */ -@property(nonatomic, readonly, copy) NSString *trackingID; - -/** - * The Project Number from the Google Developer's console, for example @"012345678901", used to - * configure Google Cloud Messaging. - */ -@property(nonatomic, readonly, copy) NSString *GCMSenderID; - -/** - * The Android client ID used in Google AppInvite when an iOS app has its Android version, for - * example @"12345.apps.googleusercontent.com". - */ -@property(nonatomic, readonly, copy) NSString *androidClientID; - -/** - * The Google App ID that is used to uniquely identify an instance of an app. - */ -@property(nonatomic, readonly, copy) NSString *googleAppID; - -/** - * The database root URL, e.g. @"http://abc-xyz-123.firebaseio.com". - */ -@property(nonatomic, readonly, copy) NSString *databaseURL; - -/** - * The URL scheme used to set up Durable Deep Link service. - */ -@property(nonatomic, readwrite, copy) NSString *deepLinkURLScheme; - -/** - * The Google Cloud Storage bucket name, e.g. @"abc-xyz-123.storage.firebase.com". - */ -@property(nonatomic, readonly, copy) NSString *storageBucket; - -/** - * Initializes a customized instance of FIROptions with keys. googleAppID, bundleID and GCMSenderID - * are required. Other keys may required for configuring specific services. - */ -- (instancetype)initWithGoogleAppID:(NSString *)googleAppID - bundleID:(NSString *)bundleID - GCMSenderID:(NSString *)GCMSenderID - APIKey:(NSString *)APIKey - clientID:(NSString *)clientID - trackingID:(NSString *)trackingID - androidClientID:(NSString *)androidClientID - databaseURL:(NSString *)databaseURL - storageBucket:(NSString *)storageBucket - deepLinkURLScheme:(NSString *)deepLinkURLScheme; - -/** - * Initializes a customized instance of FIROptions from the file at the given plist file path. - * For example, - * NSString *filePath = - * [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"]; - * FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath]; - * Returns nil if the plist file does not exist or is invalid. - */ -- (instancetype)initWithContentsOfFile:(NSString *)plistPath; - -@end diff --git a/src/ios/firebase/FirebaseCore.framework/Headers/FirebaseCore.h b/src/ios/firebase/FirebaseCore.framework/Headers/FirebaseCore.h deleted file mode 100644 index d144758d7..000000000 --- a/src/ios/firebase/FirebaseCore.framework/Headers/FirebaseCore.h +++ /dev/null @@ -1,6 +0,0 @@ -// Generated umbrella header for FirebaseCore. - -#import "FIRAnalyticsConfiguration.h" -#import "FIRApp.h" -#import "FIRConfiguration.h" -#import "FIROptions.h" diff --git a/src/ios/firebase/FirebaseCore.framework/Modules/module.modulemap b/src/ios/firebase/FirebaseCore.framework/Modules/module.modulemap deleted file mode 100644 index 82d14eb6d..000000000 --- a/src/ios/firebase/FirebaseCore.framework/Modules/module.modulemap +++ /dev/null @@ -1,15 +0,0 @@ -framework module FirebaseCore { - - export * - - umbrella header "FirebaseCore.h" - - header "FIRAnalyticsConfiguration.h" - header "FIRApp.h" - header "FIRConfiguration.h" - header "FIROptions.h" - - link framework "SystemConfiguration" - - link "c++" -} diff --git a/src/ios/firebase/FirebaseInstanceID.framework/FirebaseInstanceID b/src/ios/firebase/FirebaseInstanceID.framework/FirebaseInstanceID deleted file mode 100644 index 5d67d055e..000000000 Binary files a/src/ios/firebase/FirebaseInstanceID.framework/FirebaseInstanceID and /dev/null differ diff --git a/src/ios/firebase/FirebaseInstanceID.framework/Headers/FIRInstanceID.h b/src/ios/firebase/FirebaseInstanceID.framework/Headers/FIRInstanceID.h deleted file mode 100644 index 717e290aa..000000000 --- a/src/ios/firebase/FirebaseInstanceID.framework/Headers/FIRInstanceID.h +++ /dev/null @@ -1,245 +0,0 @@ -#import - -/** - * @memberof FIRInstanceID - * - * The scope to be used when fetching/deleting a token for Firebase Messaging. - */ -FOUNDATION_EXPORT NSString * __nonnull const kFIRInstanceIDScopeFirebaseMessaging; - -/** - * Called when the system determines that tokens need to be refreshed. - * This method is also called if Instance ID has been reset in which - * case, tokens and FCM topic subscriptions also need to be refreshed. - * - * Instance ID service will throttle the refresh event across all devices - * to control the rate of token updates on application servers. - */ -FOUNDATION_EXPORT NSString * __nonnull const kFIRInstanceIDTokenRefreshNotification; - -/** - * @related FIRInstanceID - * - * The completion handler invoked when the InstanceID token returns. If - * the call fails we return the appropriate `error code` as described below. - * - * @param token The valid token as returned by InstanceID backend. - * - * @param error The error describing why generating a new token - * failed. See the error codes below for a more detailed - * description. - */ -typedef void(^FIRInstanceIDTokenHandler)( NSString * __nullable token, NSError * __nullable error); - - -/** - * @related FIRInstanceID - * - * The completion handler invoked when the InstanceID `deleteToken` returns. If - * the call fails we return the appropriate `error code` as described below - * - * @param error The error describing why deleting the token failed. - * See the error codes below for a more detailed description. - */ -typedef void(^FIRInstanceIDDeleteTokenHandler)(NSError * __nullable error); - -/** - * @related FIRInstanceID - * - * The completion handler invoked when the app identity is created. If the - * identity wasn't created for some reason we return the appropriate error code. - * - * @param identity A valid identity for the app instance, nil if there was an error - * while creating an identity. - * @param error The error if fetching the identity fails else nil. - */ -typedef void(^FIRInstanceIDHandler)(NSString * __nullable identity, NSError * __nullable error); - -/** - * @related FIRInstanceID - * - * The completion handler invoked when the app identity and all the tokens associated - * with it are deleted. Returns a valid error object in case of failure else nil. - * - * @param error The error if deleting the identity and all the tokens associated with - * it fails else nil. - */ -typedef void(^FIRInstanceIDDeleteHandler)(NSError * __nullable error); - -/** - * @enum FIRInstanceIDError - */ -typedef NS_ENUM(NSUInteger, FIRInstanceIDError) { - // Http related errors. - - /// Unknown error. - FIRInstanceIDErrorUnknown = 0, - - /// Auth Error -- GCM couldn't validate request from this client. - FIRInstanceIDErrorAuthentication = 1, - - /// NoAccess -- InstanceID service cannot be accessed. - FIRInstanceIDErrorNoAccess = 2, - - /// Timeout -- Request to InstanceID backend timed out. - FIRInstanceIDErrorTimeout = 3, - - /// Network -- No network available to reach the servers. - FIRInstanceIDErrorNetwork = 4, - - /// OperationInProgress -- Another similar operation in progress, - /// bailing this one. - FIRInstanceIDErrorOperationInProgress = 5, - - /// InvalidRequest -- Some parameters of the request were invalid. - FIRInstanceIDErrorInvalidRequest = 7, -}; - -/** - * The APNS token type for the app. If the token type is set to `UNKNOWN` - * InstanceID will implicitly try to figure out what the actual token type - * is from the provisioning profile. - */ -typedef NS_ENUM(NSInteger, FIRInstanceIDAPNSTokenType) { - /// Unknown token type. - FIRInstanceIDAPNSTokenTypeUnknown, - /// Sandbox token type. - FIRInstanceIDAPNSTokenTypeSandbox, - /// Production token type. - FIRInstanceIDAPNSTokenTypeProd, -}; - -/** - * Instance ID provides a unique identifier for each app instance and a mechanism - * to authenticate and authorize actions (for example, sending a GCM message). - * - * Instance ID is long lived but, may be reset if the device is not used for - * a long time or the Instance ID service detects a problem. - * If Instance ID is reset, the app will be notified with a `com.firebase.iid.token-refresh` - * notification. - * - * If the Instance ID has become invalid, the app can request a new one and - * send it to the app server. - * To prove ownership of Instance ID and to allow servers to access data or - * services associated with the app, call - * `[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler]`. - */ -@interface FIRInstanceID : NSObject - -/** - * FIRInstanceID. - * - * @return A shared instance of FIRInstanceID. - */ -+ (nonnull instancetype)instanceID NS_SWIFT_NAME(instanceID()); - -/** - * Unavailable. Use +instanceID instead. - */ -- (nonnull instancetype)init __attribute__((unavailable("Use +instanceID instead."))); - -/** - * Set APNS token for the application. This APNS token will be used to register - * with Firebase Messaging using `token` or - * `tokenWithAuthorizedEntity:scope:options:handler`. If the token type is set to - * `FIRInstanceIDAPNSTokenTypeUnknown` InstanceID will read the provisioning profile - * to find out the token type. - * - * @param token The APNS token for the application. - * @param type The APNS token type for the above token. - */ -- (void)setAPNSToken:(nonnull NSData *)token - type:(FIRInstanceIDAPNSTokenType)type; - -#pragma mark - Tokens - -/** - * Returns a Firebase Messaging scoped token for the firebase app. - * - * @return Null Returns null if the device has not yet been registerd with - * Firebase Message else returns a valid token. - */ -- (nullable NSString *)token; - -/** - * Returns a token that authorizes an Entity (example: cloud service) to perform - * an action on behalf of the application identified by Instance ID. - * - * This is similar to an OAuth2 token except, it applies to the - * application instance instead of a user. - * - * This is an asynchronous call. If the token fetching fails for some reason - * we invoke the completion callback with nil `token` and the appropriate - * error. - * - * Note, you can only have one `token` or `deleteToken` call for a given - * authorizedEntity and scope at any point of time. Making another such call with the - * same authorizedEntity and scope before the last one finishes will result in an - * error with code `OperationInProgress`. - * - * @see FIRInstanceID deleteTokenWithAuthorizedEntity:scope:handler: - * - * @param authorizedEntity Entity authorized by the token. - * @param scope Action authorized for authorizedEntity. - * @param options The extra options to be sent with your token request. The - * value for the `apns_token` should be the NSData object - * passed to UIApplication's - * `didRegisterForRemoteNotificationsWithDeviceToken` method. - * All other keys and values in the options dict need to be - * instances of NSString or else they will be discarded. Bundle - * keys starting with 'GCM.' and 'GOOGLE.' are reserved. - * @param handler The callback handler which is invoked when the token is - * successfully fetched. In case of success a valid `token` and - * `nil` error are returned. In case of any error the `token` - * is nil and a valid `error` is returned. The valid error - * codes have been documented above. - */ -- (void)tokenWithAuthorizedEntity:(nonnull NSString *)authorizedEntity - scope:(nonnull NSString *)scope - options:(nullable NSDictionary *)options - handler:(nonnull FIRInstanceIDTokenHandler)handler; - -/** - * Revokes access to a scope (action) for an entity previously - * authorized by `[FIRInstanceID tokenWithAuthorizedEntity:scope:options:handler]`. - * - * This is an asynchronous call. Call this on the main thread since InstanceID lib - * is not thread safe. In case token deletion fails for some reason we invoke the - * `handler` callback passed in with the appropriate error code. - * - * Note, you can only have one `token` or `deleteToken` call for a given - * authorizedEntity and scope at a point of time. Making another such call with the - * same authorizedEntity and scope before the last one finishes will result in an error - * with code `OperationInProgress`. - * - * @param authorizedEntity Entity that must no longer have access. - * @param scope Action that entity is no longer authorized to perform. - * @param handler The handler that is invoked once the unsubscribe call ends. - * In case of error an appropriate error object is returned - * else error is nil. - */ -- (void)deleteTokenWithAuthorizedEntity:(nonnull NSString *)authorizedEntity - scope:(nonnull NSString *)scope - handler:(nonnull FIRInstanceIDDeleteTokenHandler)handler; - -#pragma mark - Identity - -/** - * Asynchronously fetch a stable identifier that uniquely identifies the app - * instance. If the identifier has been revoked or has expired, this method will - * return a new identifier. - * - * - * @param handler The handler to invoke once the identifier has been fetched. - * In case of error an appropriate error object is returned else - * a valid identifier is returned and a valid identifier for the - * application instance. - */ -- (void)getIDWithHandler:(nonnull FIRInstanceIDHandler)handler; - -/** - * Resets Instance ID and revokes all tokens. - */ -- (void)deleteIDWithHandler:(nonnull FIRInstanceIDDeleteHandler)handler; - -@end diff --git a/src/ios/firebase/FirebaseInstanceID.framework/Headers/FirebaseInstanceID.h b/src/ios/firebase/FirebaseInstanceID.framework/Headers/FirebaseInstanceID.h deleted file mode 100644 index 053ec2b1c..000000000 --- a/src/ios/firebase/FirebaseInstanceID.framework/Headers/FirebaseInstanceID.h +++ /dev/null @@ -1 +0,0 @@ -#import "FIRInstanceID.h" diff --git a/src/ios/firebase/FirebaseInstanceID.framework/Modules/module.modulemap b/src/ios/firebase/FirebaseInstanceID.framework/Modules/module.modulemap deleted file mode 100644 index b4a5b5e39..000000000 --- a/src/ios/firebase/FirebaseInstanceID.framework/Modules/module.modulemap +++ /dev/null @@ -1,8 +0,0 @@ -framework module FirebaseInstanceID { - - export * - - umbrella header "FirebaseInstanceID.h" - - header "FIRInstanceID.h" -} diff --git a/src/ios/firebase/FirebaseMessaging.framework/FirebaseMessaging b/src/ios/firebase/FirebaseMessaging.framework/FirebaseMessaging deleted file mode 100644 index de753660e..000000000 Binary files a/src/ios/firebase/FirebaseMessaging.framework/FirebaseMessaging and /dev/null differ diff --git a/src/ios/firebase/FirebaseMessaging.framework/Headers/FIRMessaging.h b/src/ios/firebase/FirebaseMessaging.framework/Headers/FIRMessaging.h deleted file mode 100644 index 04ea927ff..000000000 --- a/src/ios/firebase/FirebaseMessaging.framework/Headers/FIRMessaging.h +++ /dev/null @@ -1,236 +0,0 @@ -#import - -/** - * The completion handler invoked once the data connection with FIRMessaging is - * established. The data connection is used to send a continous stream of - * data and all the FIRMessaging data notifications arrive through this connection. - * Once the connection is established we invoke the callback with `nil` error. - * Correspondingly if we get an error while trying to establish a connection - * we invoke the handler with an appropriate error object and do an - * exponential backoff to try and connect again unless successful. - * - * @param error The error object if any describing why the data connection - * to FIRMessaging failed. - */ -typedef void(^FIRMessagingConnectCompletion)(NSError * __nullable error); - -/** - * Notification sent when the upstream message has been delivered - * successfully to the server. The notification object will be the messageID - * of the successfully delivered message. - */ -FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingSendSuccessNotification; - -/** - * Notification sent when the upstream message was failed to be sent to the - * server. The notification object will be the messageID of the failed - * message. The userInfo dictionary will contain the relevant error - * information for the failure. - */ -FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingSendErrorNotification; - -/** - * Notification sent when the Firebase messaging server deletes pending - * messages due to exceeded storage limits. This may occur, for example, when - * the device cannot be reached for an extended period of time. - * - * It is recommended to retrieve any missing messages directly from the - * server. - */ -FOUNDATION_EXPORT NSString * __nonnull const FIRMessagingMessagesDeletedNotification; - -/** - * @enum FIRMessagingError - */ -typedef NS_ENUM(NSUInteger, FIRMessagingError) { - // Unknown error. - FIRMessagingErrorUnknown = 0, - - // Auth Error -- FIRMessaging couldn't validate request from this client. - FIRMessagingErrorAuthentication = 1, - - // NoAccess -- InstanceID service cannot be accessed. - FIRMessagingErrorNoAccess = 2, - - // Timeout -- Request to InstanceID backend timed out. - FIRMessagingErrorTimeout = 3, - - // Network -- No network available to reach the servers. - FIRMessagingErrorNetwork = 4, - - // OperationInProgress -- Another similar operation in progress, - // bailing this one. - FIRMessagingErrorOperationInProgress = 5, - - // InvalidRequest -- Some parameters of the request were invalid. - FIRMessagingErrorInvalidRequest = 7, -}; - -/// Status for the downstream message received by the app. -typedef NS_ENUM(NSInteger, FIRMessagingMessageStatus) { - FIRMessagingMessageStatusUnknown, - /// New downstream message received by the app. - FIRMessagingMessageStatusNew, -}; - -/// Information about a downstream message received by the app. -@interface FIRMessagingMessageInfo : NSObject - -@property(nonatomic, readonly, assign) FIRMessagingMessageStatus status; - -@end - -/** - * A remote data message received by the app via FCM (not just the APNs interface). - * - * This is only for devices running iOS 10 or above. To support devices running iOS 9 or below, use - * the local and remote notifications handlers defined in UIApplicationDelegate protocol. - */ -@interface FIRMessagingRemoteMessage : NSObject - -/// The downstream message received by the application. -@property(nonatomic, readonly, strong, nonnull) NSDictionary *appData; - -@end - -/** - * A protocol to receive data message via FCM for devices running iOS 10 or above. - * - * To support devices running iOS 9 or below, use the local and remote notifications handlers - * defined in UIApplicationDelegate protocol. - */ -@protocol FIRMessagingDelegate - -/// The callback to handle data message received via FCM for devices running iOS 10 or above. -- (void)applicationReceivedRemoteMessage:(nonnull FIRMessagingRemoteMessage *)remoteMessage; - -@end - -/** - * Firebase Messaging enables apps to communicate with their app servers - * using simple messages. - * - * To send or receive messages, the app must get a - * registration token from GGLInstanceID, which authorizes an - * app server to send messages to an app instance. Pass your sender ID and - * `kGGLInstanceIDScopeFIRMessaging` as parameters to the method. - * - * A sender ID is a project number created when you configure your API project. - * It is labeled "Project Number" in the Google Developers Console. - * - * In order to receive FIRMessaging messages, declare application:didReceiveRemoteNotification: - * - * Client apps can send upstream messages back to the app server using the XMPP-based - * Cloud Connection Server, - * - */ -@interface FIRMessaging : NSObject - -/** - * Delegate to handle remote data messages received via FCM for devices running iOS 10 or above. - */ -@property(nonatomic, weak, nullable) id remoteMessageDelegate; - -/** - * FIRMessaging - * - * @return An instance of FIRMessaging. - */ -+ (nonnull instancetype)messaging NS_SWIFT_NAME(messaging()); - -/** - * Unavailable. Use +messaging instead. - */ -- (nonnull instancetype)init __attribute__((unavailable("Use +messaging instead."))); - -#pragma mark - Connect - -/** - * Create a FIRMessaging data connection which will be used to send the data notifications - * send by your server. It will also be used to send ACKS and other messages based - * on the FIRMessaging ACKS and other messages based on the FIRMessaging protocol. - * - * Use the `disconnect` method to disconnect the connection. - * - * @see FIRMessagingService disconnect - * - * @param handler The handler to be invoked once the connection is established. - * If the connection fails we invoke the handler with an - * appropriate error code letting you know why it failed. At - * the same time, FIRMessaging performs exponential backoff to retry - * establishing a connection and invoke the handler when successful. - */ -- (void)connectWithCompletion:(nonnull FIRMessagingConnectCompletion)handler; - -/** - * Disconnect the current FIRMessaging data connection. This stops any attempts to - * connect to FIRMessaging. Calling this on an already disconnected client is a no-op. - * - * Call this before `teardown` when your app is going to the background. - * Since the FIRMessaging connection won't be allowed to live when in background it is - * prudent to close the connection. - */ -- (void)disconnect; - -#pragma mark - Topics - -/** - * Asynchronously subscribes to a topic. - * - * @param topic The name of the topic, for example @"sports". - */ -- (void)subscribeToTopic:(nonnull NSString *)topic; - -/** - * Asynchronously unsubscribe to a topic. - * - * @param topic The name of the topic, for example @"sports". - */ -- (void)unsubscribeFromTopic:(nonnull NSString *)topic; - -#pragma mark - Upstream - -/** - * Sends an upstream ("device to cloud") message. - * - * The message will be queued if we don't have an active connection. - * You can only use the upstream feature if your GCM implementation - * uses the XMPP-based Cloud Connection Server. - * - * @param message Key/Value pairs to be sent. Values must be String, any - * other type will be ignored. - * @param to A string identifying the receiver of the message. For GCM - * project IDs the value is `SENDER_ID@gcm.googleapis.com`. - * @param messageID The ID of the message. This is generated by the application. It - * must be unique for each message generated by this application. - * It allows error callbacks and debugging, to uniquely identify - * each message. - * @param ttl The time to live for the message. In case we aren't able to - * send the message before the TTL expires we will send you a - * callback. If 0, we'll attempt to send immediately and return - * an error if we're not connected. Otherwise, the message will - * be queued. As for server-side messages, we don't return an error - * if the message has been dropped because of TTL; this can happen - * on the server side, and it would require extra communication. - */ -- (void)sendMessage:(nonnull NSDictionary *)message - to:(nonnull NSString *)receiver - withMessageID:(nonnull NSString *)messageID - timeToLive:(int64_t)ttl; - -#pragma mark - Analytics - -/** - * Call this when the app received a downstream message. Used to track message - * delivery and analytics for messages. You don't need to call this if you - * don't set the `FIRMessagingAutoSetupEnabled` flag in your Info.plist. In the - * latter case the library will call this implicitly to track relevant - * messages. - * - * @param message The downstream message received by the application. - * - * @return Information about the downstream message. - */ -- (nonnull FIRMessagingMessageInfo *)appDidReceiveMessage:(nonnull NSDictionary *)message; - -@end diff --git a/src/ios/firebase/FirebaseMessaging.framework/Headers/FirebaseMessaging.h b/src/ios/firebase/FirebaseMessaging.framework/Headers/FirebaseMessaging.h deleted file mode 100644 index ef49e7ff7..000000000 --- a/src/ios/firebase/FirebaseMessaging.framework/Headers/FirebaseMessaging.h +++ /dev/null @@ -1 +0,0 @@ -#import "FIRMessaging.h" diff --git a/src/ios/firebase/FirebaseMessaging.framework/Modules/module.modulemap b/src/ios/firebase/FirebaseMessaging.framework/Modules/module.modulemap deleted file mode 100644 index a390f119d..000000000 --- a/src/ios/firebase/FirebaseMessaging.framework/Modules/module.modulemap +++ /dev/null @@ -1,13 +0,0 @@ -framework module FirebaseMessaging { - - export * - - umbrella header "FirebaseMessaging.h" - - header "FIRMessaging.h" - - link framework "AddressBook" - link framework "SystemConfiguration" - - link "sqlite3" -} diff --git a/src/ios/firebase/GoogleIPhoneUtilities.framework/GoogleIPhoneUtilities b/src/ios/firebase/GoogleIPhoneUtilities.framework/GoogleIPhoneUtilities deleted file mode 100644 index 7ab2cf260..000000000 Binary files a/src/ios/firebase/GoogleIPhoneUtilities.framework/GoogleIPhoneUtilities and /dev/null differ diff --git a/src/ios/firebase/GoogleInterchangeUtilities.framework/GoogleInterchangeUtilities b/src/ios/firebase/GoogleInterchangeUtilities.framework/GoogleInterchangeUtilities deleted file mode 100644 index de4042474..000000000 Binary files a/src/ios/firebase/GoogleInterchangeUtilities.framework/GoogleInterchangeUtilities and /dev/null differ diff --git a/src/ios/firebase/GoogleSymbolUtilities.framework/GoogleSymbolUtilities b/src/ios/firebase/GoogleSymbolUtilities.framework/GoogleSymbolUtilities deleted file mode 100644 index 408a00262..000000000 Binary files a/src/ios/firebase/GoogleSymbolUtilities.framework/GoogleSymbolUtilities and /dev/null differ diff --git a/src/ios/firebase/GoogleUtilities.framework/GoogleUtilities b/src/ios/firebase/GoogleUtilities.framework/GoogleUtilities deleted file mode 100644 index 0fd17b65b..000000000 Binary files a/src/ios/firebase/GoogleUtilities.framework/GoogleUtilities and /dev/null differ diff --git a/src/www/FCMPlugin.ts b/src/www/FCMPlugin.ts new file mode 100644 index 000000000..c97049534 --- /dev/null +++ b/src/www/FCMPlugin.ts @@ -0,0 +1,194 @@ +import type { IChannelConfiguration } from './IChannelConfiguration' +import type { IRequestPushPermissionOptions } from './IRequestPushPermissionOptions' +import type { INotificationPayload } from './INotificationPayload' +import type { IDisposable } from './IDisposable' +import { execAsPromise } from './execAsPromise' +import { asDisposableListener } from './eventAsDisposable' +import { bridgeNativeEvents } from './bridgeNativeEvents' +declare var window: { + cordova: { + platformId: string + } +} + +/** + * @name FCM + * @description + * Easy plug&play push notification for Google Firebase FCM. + * + * @interfaces + * INotificationPayload + * IChannelConfiguration + * IRequestPushPermissionOptions + */ +export class FCMPlugin { + /** + * EventTarget for native-sourced custom events. + * + * @event notification + * @type {INotificationPayload} + * + * @event tokenRefresh + * @type {string} + * + */ + public readonly eventTarget: EventTarget + + constructor() { + // EventTarget is not fully supported on iOS and older Android + this.eventTarget = document.createElement('div') + execAsPromise('ready') + .catch((error: Error) => console.log('FCM: Ready error: ', error)) + .then(() => { + console.log('FCM: Ready!') + bridgeNativeEvents(this.eventTarget) + }) + console.log('FCM: has been created') + } + + /** + * Removes existing push notifications from the notifications center + * + * @returns {Promise} Async call to native implementation + */ + public clearAllNotifications(): Promise { + return execAsPromise('clearAllNotifications') + } + + /** + * For Android, some notification properties are only defined programmatically. + * Channel can define the default behavior for notifications on Android 8.0+. + * Once a channel is created, it stays unchangeable until the user uninstalls the app. + * + * @param {IChannelConfiguration} channelConfig The parmeters of the new channel + * + * @returns {Promise} Async call to native implementation + */ + public createNotificationChannel(channelConfig: IChannelConfiguration): Promise { + if (window.cordova.platformId !== 'android') { + return Promise.resolve() + } + + return execAsPromise('createNotificationChannel', [channelConfig]) + } + + /** + * This method deletes the InstanceId, revoking all tokens. + * + * @returns {Promise} Async call to native implementation + */ + public deleteInstanceId(): Promise { + return execAsPromise('deleteInstanceId') + } + + /** + * Gets ios device's current APNS token + * + * @returns {Promise} Returns a Promise that resolves with the APNS token + */ + public getAPNSToken(): Promise { + return window.cordova.platformId !== 'ios' + ? Promise.resolve('') + : execAsPromise('getAPNSToken') + } + + /** + * Retrieves the message that, on tap, opened the app + * + * @private + * + * @returns {Promise} Async call to native implementation + */ + public getInitialPushPayload(): Promise { + return execAsPromise('getInitialPushPayload') + } + + /** + * Gets device's current registration id + * + * @returns {Promise} Returns a Promise that resolves with the registration id token + */ + public getToken(): Promise { + return execAsPromise('getToken') + } + + /** + * Checking for permissions. + * + * @returns {Promise} Returns a Promise of: + * - true: push was allowed (or platform is android) + * - false: push will not be available + * - null: still not answered, recommended checking again later. + */ + public hasPermission(): Promise { + return window.cordova.platformId === 'ios' + ? execAsPromise('hasPermission') + : execAsPromise('hasPermission').then((value) => !!value) + } + + /** + * Callback firing when receiving new notifications + * + * @argument {(payload: INotificationPayload) => void} callback function to be called when event is triggered + * @argument {{ once?: boolean }} options once defines if the listener is only trigger once + * @returns {IDisposable} object of which can request the listener's disposal + */ + public onNotification( + callback: (payload: INotificationPayload) => void, + options?: { once?: boolean } + ): IDisposable { + return asDisposableListener(this.eventTarget, 'notification', callback, options) + } + + /** + * Callback firing when receiving a new Firebase token + * + * @argument {(token: string) => void} callback function to be called when event is triggered + * @argument {{ once?: boolean }} options once defines if the listener is only trigger once + * @returns {IDisposable} object of which can request the listener's disposal + */ + public onTokenRefresh( + callback: (token: string) => void, + options?: { once?: boolean } + ): IDisposable { + return asDisposableListener(this.eventTarget, 'tokenRefresh', callback, options) + } + + /** + * Request push notification permission, alerting the user if it not have yet decided + * + * @param {IRequestPushPermissionOptions} options Options for push request + * @returns {Promise} Returns a Promise that resolves with the permission status + */ + public requestPushPermission(options?: IRequestPushPermissionOptions): Promise { + if (window.cordova.platformId !== 'ios') { + return Promise.resolve(true) + } + const ios9SupportTimeout = options?.ios9Support?.timeout ?? 10 + const ios9SupportInterval = options?.ios9Support?.interval ?? 0.3 + + return execAsPromise('requestPushPermission', [ios9SupportTimeout, ios9SupportInterval]) + } + + /** + * Subscribes you to a [topic](https://firebase.google.com/docs/notifications/android/console-topics) + * + * @param {string} topic Topic to be subscribed to + * + * @returns {Promise} Async call to native implementation + */ + public subscribeToTopic(topic: string): Promise { + return execAsPromise('subscribeToTopic', [topic]) + } + + /** + * Unsubscribes you from a [topic](https://firebase.google.com/docs/notifications/android/console-topics) + * + * @param {string} topic Topic to be unsubscribed from + * + * @returns {Promise} Async call to native implementation + */ + public unsubscribeFromTopic(topic: string): Promise { + return execAsPromise('unsubscribeFromTopic', [topic]) + } +} diff --git a/src/www/IChannelConfiguration.d.ts b/src/www/IChannelConfiguration.d.ts new file mode 100644 index 000000000..1962da6d1 --- /dev/null +++ b/src/www/IChannelConfiguration.d.ts @@ -0,0 +1,37 @@ +export interface IChannelConfiguration { + /** + * Channel id, used in the android_channel_id push payload key + */ + id: string + /** + * Channel name, visible for the user + */ + name: string + /** + * Channel description, visible for the user + */ + description?: string + /** + * Importance for notifications of this channel + * https://developer.android.com/guide/topics/ui/notifiers/notifications#importance + */ + importance?: 'none' | 'min' | 'low' | 'default' | 'high' + /** + * Visibility for notifications of this channel + * https://developer.android.com/training/notify-user/build-notification#lockscreenNotification + */ + visibility?: 'public' | 'private' | 'secret' + /** + * Default sound resource for notifications of this channel + * The file should located as resources/raw/[resource name].mp3 + */ + sound?: string + /** + * Enable lights for notifications of this channel + */ + lights?: boolean + /** + * Enable vibration for notifications of this channel + */ + vibration?: boolean +} diff --git a/src/www/IDisposable.d.ts b/src/www/IDisposable.d.ts new file mode 100644 index 000000000..9ab8767bd --- /dev/null +++ b/src/www/IDisposable.d.ts @@ -0,0 +1,8 @@ +export interface IDisposable { + /** + * Method of which disposes listener + * + * @returns {void} + */ + dispose(): void +} diff --git a/src/www/INotificationPayload.d.ts b/src/www/INotificationPayload.d.ts new file mode 100644 index 000000000..28398750a --- /dev/null +++ b/src/www/INotificationPayload.d.ts @@ -0,0 +1,10 @@ +export interface INotificationPayload { + /** + * Determines whether the notification was tapped or not + */ + wasTapped: boolean + /** + * FCM notification data hash item + */ + [others: string]: any +} diff --git a/src/www/IRequestPushPermissionOptions.d.ts b/src/www/IRequestPushPermissionOptions.d.ts new file mode 100644 index 000000000..544cbd3fa --- /dev/null +++ b/src/www/IRequestPushPermissionOptions.d.ts @@ -0,0 +1,20 @@ +export interface IRequestPushPermissionOptions { + /** + * Options exclusive for iOS 9 support + */ + ios9Support?: { + /** + * How long it will wait for a decision from the user before returning `false` + * + * @default 10 + */ + timeout?: number + + /** + * How long between each permission verification + * + * @default 0.3 + */ + interval?: number + } +} diff --git a/src/www/bridgeNativeEvents.ts b/src/www/bridgeNativeEvents.ts new file mode 100644 index 000000000..56284268c --- /dev/null +++ b/src/www/bridgeNativeEvents.ts @@ -0,0 +1,25 @@ +declare var window: { + cordova: { + exec: Function + } +} + +/** + * This is a simple helper to Promisify the calls to cordova + * + * @param {eventTarget} EventTarget EventTarget for native-sourced custom events. + * + * @returns {void} + */ +export const bridgeNativeEvents = (eventTarget: EventTarget): void => { + const onError = (error: Error) => console.log('FCM: Error listening to native events', error) + const onEvent = (data: string) => { + try { + const [eventName, eventData] = JSON.parse(data) + eventTarget.dispatchEvent(new CustomEvent(eventName, { detail: eventData })) + } catch (error) { + console.log('FCM: Error parsing native event data', error) + } + } + window.cordova.exec(onEvent, onError, 'FCMPlugin', 'startJsEventBridge', []) +} diff --git a/src/www/eventAsDisposable.ts b/src/www/eventAsDisposable.ts new file mode 100644 index 000000000..15a1c3ea9 --- /dev/null +++ b/src/www/eventAsDisposable.ts @@ -0,0 +1,25 @@ +import type { IDisposable } from './IDisposable' + +/** + * This is a simple helper to wrapp the event handler into a IDisposable + * + * @param {EventTarget} eventTarget EventTarget for native-sourced custom events + * @param {string} eventName Event name to listen to + * @param {(data: any) => void} callback Event handler + * @param {{ once?: boolean }} options once defines if the listener is only trigger once + * @returns {IDisposable} object of which can request the listener's disposal + */ +export const asDisposableListener = ( + eventTarget: EventTarget, + eventName: string, + callback: (data: R) => void, + options: { once?: boolean } = {} +): IDisposable => { + const { once } = options + const handler = (event: CustomEvent) => callback(event.detail) + eventTarget.addEventListener(eventName, handler, { passive: true, once }) + + return { + dispose: () => eventTarget.removeEventListener(eventName, handler), + } +} diff --git a/src/www/execAsPromise.ts b/src/www/execAsPromise.ts new file mode 100644 index 000000000..0eca38289 --- /dev/null +++ b/src/www/execAsPromise.ts @@ -0,0 +1,20 @@ +declare var window: { + cordova: { + exec: Function + } +} + +/** + * This is a simple helper to Promisify the calls to cordova + * + * @param {string} command The native cordova implementation command + * @param {unknown[]} args The native cordova implementation expected arguments + * + * @returns {Promise} Returns from the async native call the type expected + */ +export const execAsPromise = (command: string, args: unknown[] = []): Promise => + new Promise( + (resolve: (value: R | PromiseLike) => void, reject: (reason?: any) => void) => { + window.cordova.exec(resolve, reject, 'FCMPlugin', command, args) + } + ) diff --git a/src/www/index.ts b/src/www/index.ts new file mode 100644 index 000000000..d2b2d878b --- /dev/null +++ b/src/www/index.ts @@ -0,0 +1,13 @@ +export type { IChannelConfiguration } from './IChannelConfiguration' +export type { IRequestPushPermissionOptions } from './IRequestPushPermissionOptions' +export type { INotificationPayload } from './INotificationPayload' +export type { IDisposable } from './IDisposable' +import { FCMPlugin } from './FCMPlugin' + +interface Window { + FCM: FCMPlugin +} + +export const FCM = new FCMPlugin() +export { FCMPlugin } +export default FCM diff --git a/src/www/package-lock.json b/src/www/package-lock.json new file mode 100644 index 000000000..938b99504 --- /dev/null +++ b/src/www/package-lock.json @@ -0,0 +1,149 @@ +{ + "name": "cordova-plugin-fcm-with-dependecy-updated", + "version": "7.8.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@types/cordova": { + "version": "0.0.34", + "resolved": "https://registry.npmjs.org/@types/cordova/-/cordova-0.0.34.tgz", + "integrity": "sha1-6nrd907Ow9dimCegw54smt3HPQQ=", + "dev": true + }, + "@types/estree": { + "version": "0.0.44", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.44.tgz", + "integrity": "sha512-iaIVzr+w2ZJ5HkidlZ3EJM8VTZb2MJLCjw3V+505yVts0gRC4UMvjw0d1HPtGqI/HQC/KdsYtayfzl+AXY2R8g==", + "dev": true + }, + "@types/node": { + "version": "14.14.12", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.14.12.tgz", + "integrity": "sha512-ASH8OPHMNlkdjrEdmoILmzFfsJICvhBsFfAum4aKZ/9U4B6M6tTmTPh+f3ttWdD74CEGV5XvXWkbyfSdXaTd7g==", + "dev": true + }, + "@types/resolve": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-0.0.8.tgz", + "integrity": "sha512-auApPaJf3NPfe18hSoJkp8EbZzer2ISk7o8mCC3M9he/a04+gbMF97NkpD2S8riMGvm4BMRI59/SZQSaLTKpsQ==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "builtin-modules": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", + "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", + "dev": true + }, + "estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "dev": true + }, + "is-reference": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.0.tgz", + "integrity": "sha512-ZVxq+5TkOx6GQdnoMm2aRdCKADdcrOWXLGzGT+vIA8DMpqEJaRk5AL1bS80zJ2bjHunVmjdzfCt0e4BymIEqKQ==", + "dev": true, + "requires": { + "@types/estree": "0.0.44" + } + }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "rollup": { + "version": "2.34.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.34.2.tgz", + "integrity": "sha512-mvtQLqu3cNeoctS+kZ09iOPxrc1P1/Bt1z15enuQ5feyKOdM3MJAVFjjsygurDpSWn530xB4AlA83TWIzRstXA==", + "dev": true, + "requires": { + "fsevents": "~2.1.2" + } + }, + "rollup-plugin-commonjs": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-commonjs/-/rollup-plugin-commonjs-10.1.0.tgz", + "integrity": "sha512-jlXbjZSQg8EIeAAvepNwhJj++qJWNJw1Cl0YnOqKtP5Djx+fFGkp3WRh+W0ASCaFG5w1jhmzDxgu3SJuVxPF4Q==", + "dev": true, + "requires": { + "estree-walker": "^0.6.1", + "is-reference": "^1.1.2", + "magic-string": "^0.25.2", + "resolve": "^1.11.0", + "rollup-pluginutils": "^2.8.1" + } + }, + "rollup-plugin-node-resolve": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-resolve/-/rollup-plugin-node-resolve-5.2.0.tgz", + "integrity": "sha512-jUlyaDXts7TW2CqQ4GaO5VJ4PwwaV8VUGA7+km3n6k6xtOEacf61u0VXwN80phY/evMcaS+9eIeJ9MOyDxt5Zw==", + "dev": true, + "requires": { + "@types/resolve": "0.0.8", + "builtin-modules": "^3.1.0", + "is-module": "^1.0.0", + "resolve": "^1.11.1", + "rollup-pluginutils": "^2.8.1" + } + }, + "rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "requires": { + "estree-walker": "^0.6.1" + } + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "typescript": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.1.2.tgz", + "integrity": "sha512-thGloWsGH3SOxv1SoY7QojKi0tc+8FnOmiarEGMbd/lar7QOEd3hvlx3Fp5y6FlDUGl9L+pd4n2e+oToGMmhRQ==", + "dev": true + } + } +} diff --git a/src/www/package.json b/src/www/package.json new file mode 100644 index 000000000..82ff04dcd --- /dev/null +++ b/src/www/package.json @@ -0,0 +1,50 @@ +{ + "version": "7.8.0", + "name": "cordova-plugin-fcm-with-dependecy-updated", + "cordova_name": "Cordova FCM Push Plugin", + "description": "Google Firebase Cloud Messaging Cordova Push Plugin fork with dependecy updated", + "license": "MIT", + "main": "www/FCMPlugin.js", + "typings": "typings/index.d.ts", + "repo": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated", + "issue": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated/issues", + "author": "André Augusto Tissot", + "scripts": { + "build": "npm run build:before;npm run build:dts;npm run build:js;npm run build:after", + "build:before": "rm -rf ../../www;mkdir -p ../../www", + "build:js": "npx tsc -p .;npx rollup -c rollup.config.js;npm run build:js:rm-exports;npm run build:js:add-default-export", + "build:js:rm-exports": "cd ../../www;cat FCMPlugin.js | grep -v exports > FCMPlugin.tmp.js;mv FCMPlugin.tmp.js FCMPlugin.js", + "build:js:add-default-export": "cd ../../www;echo 'module.exports = FCM;' >> FCMPlugin.js", + "build:dts": "./scripts/build.dts.sh", + "build:after": "rm -rf ../../www/tmp", + "tsc": "npx tsc -p . --noEmit" + }, + "repository": { + "type": "git", + "url": "https://github.com/andrehtissot/cordova-plugin-fcm-with-dependecy-updated" + }, + "keywords": [ + "ecosystem:cordova", + "cordova-android", + "cordova-ios", + "notifications", + "push", + "firebase", + "fcm", + "ios", + "android", + "cordova" + ], + "platforms": [ + "android", + "ios" + ], + "englishdoc": "", + "devDependencies": { + "@types/cordova": "0.0.34", + "rollup": "^2.34.2", + "rollup-plugin-commonjs": "^10.1.0", + "rollup-plugin-node-resolve": "^5.2.0", + "typescript": "^4.1.2" + } +} diff --git a/src/www/rollup.config.js b/src/www/rollup.config.js new file mode 100644 index 000000000..50789b02f --- /dev/null +++ b/src/www/rollup.config.js @@ -0,0 +1,19 @@ +const nodeResolve = require('rollup-plugin-node-resolve') +const commonjs = require('rollup-plugin-commonjs') + +export default { + input: '../../www/tmp/index.js', + output: { + file: '../../www/FCMPlugin.js', + format: 'cjs', + name: 'FCM', + globals: {} + }, + plugins: [ + commonjs({ + include: 'node_modules/**' + }), + nodeResolve({ browser: true }) + ], + external: [] +} diff --git a/src/www/scripts/build.dts.sh b/src/www/scripts/build.dts.sh new file mode 100755 index 000000000..87e3342a0 --- /dev/null +++ b/src/www/scripts/build.dts.sh @@ -0,0 +1,14 @@ +## Generate .d.ts files +cp ./*.d.ts ../../typings +tsc -p . --declaration true --declarationDir ../../typings --removeComments false + +## Simplify imports +simplifyImports() { + importOrExport="$1" + filePath="$2" + sed "s/$importOrExport type /$importOrExport /g" "$filePath" > "$filePath.tmp" + mv "$filePath.tmp" "$filePath" +} +simplifyImports import ../../typings/FCMPlugin.d.ts +simplifyImports import ../../typings/eventAsDisposable.d.ts +simplifyImports export ../../typings/index.d.ts \ No newline at end of file diff --git a/src/www/tsconfig.json b/src/www/tsconfig.json new file mode 100644 index 000000000..df6be3a96 --- /dev/null +++ b/src/www/tsconfig.json @@ -0,0 +1,21 @@ +{ + "compilerOptions": { + "alwaysStrict": true, + "baseUrl": ".", + "esModuleInterop": true, + "lib": ["dom", "es5", "es2015.promise"], + "module": "es2015", + "outDir": "../../www/tmp", + "noImplicitAny": true, + "noImplicitThis": true, + "removeComments": true, + "moduleResolution": "node", + "resolveJsonModule": true, + "sourceMap": false, + "strictNullChecks": true, + "strictPropertyInitialization": true, + "target": "es5" + }, + "include": ["./*.ts"], + "exclude": ["node_modules"] +} diff --git a/tslint.json b/tslint.json new file mode 100644 index 000000000..b7ea434f1 --- /dev/null +++ b/tslint.json @@ -0,0 +1,36 @@ +{ + "defaultSeverity": "warning", + "extends": ["tslint:recommended"], + "rules": { + "semicolon": [true, "never"], + "arrow-parens": [true, "always"], + "typedef": [ + true, + "call-signature", + "parameter", + "arrow-parameter", + "property-declaration", + "member-variable-declaration" + ], + "align": [true, "elements", "members", "parameters", "statements"], + "trailing-comma": [ + true, + { + "multiline": { + "objects": "always", + "arrays": "always", + "functions": "never", + "typeLiterals": "ignore" + }, + "esSpecCompliant": true + } + ], + "newline-before-return": true, + "no-relative-imports": false, + "no-inner-html": false, + "prefer-type-cast": false, + "no-console": [true, ["warning", "error"]], + "no-namespace": false + }, + "rulesDirectory": ["node_modules/tslint-microsoft-contrib"] +} diff --git a/typings/FCMPlugin.d.ts b/typings/FCMPlugin.d.ts new file mode 100644 index 000000000..390c04b2a --- /dev/null +++ b/typings/FCMPlugin.d.ts @@ -0,0 +1,122 @@ +import { IChannelConfiguration } from './IChannelConfiguration'; +import { IRequestPushPermissionOptions } from './IRequestPushPermissionOptions'; +import { INotificationPayload } from './INotificationPayload'; +import { IDisposable } from './IDisposable'; +/** + * @name FCM + * @description + * Easy plug&play push notification for Google Firebase FCM. + * + * @interfaces + * INotificationPayload + * IChannelConfiguration + * IRequestPushPermissionOptions + */ +export declare class FCMPlugin { + /** + * EventTarget for native-sourced custom events. + * + * @event notification + * @type {INotificationPayload} + * + * @event tokenRefresh + * @type {string} + * + */ + readonly eventTarget: EventTarget; + constructor(); + /** + * Removes existing push notifications from the notifications center + * + * @returns {Promise} Async call to native implementation + */ + clearAllNotifications(): Promise; + /** + * For Android, some notification properties are only defined programmatically. + * Channel can define the default behavior for notifications on Android 8.0+. + * Once a channel is created, it stays unchangeable until the user uninstalls the app. + * + * @param {IChannelConfiguration} channelConfig The parmeters of the new channel + * + * @returns {Promise} Async call to native implementation + */ + createNotificationChannel(channelConfig: IChannelConfiguration): Promise; + /** + * This method deletes the InstanceId, revoking all tokens. + * + * @returns {Promise} Async call to native implementation + */ + deleteInstanceId(): Promise; + /** + * Gets ios device's current APNS token + * + * @returns {Promise} Returns a Promise that resolves with the APNS token + */ + getAPNSToken(): Promise; + /** + * Retrieves the message that, on tap, opened the app + * + * @private + * + * @returns {Promise} Async call to native implementation + */ + getInitialPushPayload(): Promise; + /** + * Gets device's current registration id + * + * @returns {Promise} Returns a Promise that resolves with the registration id token + */ + getToken(): Promise; + /** + * Checking for permissions. + * + * @returns {Promise} Returns a Promise of: + * - true: push was allowed (or platform is android) + * - false: push will not be available + * - null: still not answered, recommended checking again later. + */ + hasPermission(): Promise; + /** + * Callback firing when receiving new notifications + * + * @argument {(payload: INotificationPayload) => void} callback function to be called when event is triggered + * @argument {{ once?: boolean }} options once defines if the listener is only trigger once + * @returns {IDisposable} object of which can request the listener's disposal + */ + onNotification(callback: (payload: INotificationPayload) => void, options?: { + once?: boolean; + }): IDisposable; + /** + * Callback firing when receiving a new Firebase token + * + * @argument {(token: string) => void} callback function to be called when event is triggered + * @argument {{ once?: boolean }} options once defines if the listener is only trigger once + * @returns {IDisposable} object of which can request the listener's disposal + */ + onTokenRefresh(callback: (token: string) => void, options?: { + once?: boolean; + }): IDisposable; + /** + * Request push notification permission, alerting the user if it not have yet decided + * + * @param {IRequestPushPermissionOptions} options Options for push request + * @returns {Promise} Returns a Promise that resolves with the permission status + */ + requestPushPermission(options?: IRequestPushPermissionOptions): Promise; + /** + * Subscribes you to a [topic](https://firebase.google.com/docs/notifications/android/console-topics) + * + * @param {string} topic Topic to be subscribed to + * + * @returns {Promise} Async call to native implementation + */ + subscribeToTopic(topic: string): Promise; + /** + * Unsubscribes you from a [topic](https://firebase.google.com/docs/notifications/android/console-topics) + * + * @param {string} topic Topic to be unsubscribed from + * + * @returns {Promise} Async call to native implementation + */ + unsubscribeFromTopic(topic: string): Promise; +} diff --git a/typings/IChannelConfiguration.d.ts b/typings/IChannelConfiguration.d.ts new file mode 100644 index 000000000..1962da6d1 --- /dev/null +++ b/typings/IChannelConfiguration.d.ts @@ -0,0 +1,37 @@ +export interface IChannelConfiguration { + /** + * Channel id, used in the android_channel_id push payload key + */ + id: string + /** + * Channel name, visible for the user + */ + name: string + /** + * Channel description, visible for the user + */ + description?: string + /** + * Importance for notifications of this channel + * https://developer.android.com/guide/topics/ui/notifiers/notifications#importance + */ + importance?: 'none' | 'min' | 'low' | 'default' | 'high' + /** + * Visibility for notifications of this channel + * https://developer.android.com/training/notify-user/build-notification#lockscreenNotification + */ + visibility?: 'public' | 'private' | 'secret' + /** + * Default sound resource for notifications of this channel + * The file should located as resources/raw/[resource name].mp3 + */ + sound?: string + /** + * Enable lights for notifications of this channel + */ + lights?: boolean + /** + * Enable vibration for notifications of this channel + */ + vibration?: boolean +} diff --git a/typings/IDisposable.d.ts b/typings/IDisposable.d.ts new file mode 100644 index 000000000..9ab8767bd --- /dev/null +++ b/typings/IDisposable.d.ts @@ -0,0 +1,8 @@ +export interface IDisposable { + /** + * Method of which disposes listener + * + * @returns {void} + */ + dispose(): void +} diff --git a/typings/INotificationPayload.d.ts b/typings/INotificationPayload.d.ts new file mode 100644 index 000000000..28398750a --- /dev/null +++ b/typings/INotificationPayload.d.ts @@ -0,0 +1,10 @@ +export interface INotificationPayload { + /** + * Determines whether the notification was tapped or not + */ + wasTapped: boolean + /** + * FCM notification data hash item + */ + [others: string]: any +} diff --git a/typings/IRequestPushPermissionOptions.d.ts b/typings/IRequestPushPermissionOptions.d.ts new file mode 100644 index 000000000..544cbd3fa --- /dev/null +++ b/typings/IRequestPushPermissionOptions.d.ts @@ -0,0 +1,20 @@ +export interface IRequestPushPermissionOptions { + /** + * Options exclusive for iOS 9 support + */ + ios9Support?: { + /** + * How long it will wait for a decision from the user before returning `false` + * + * @default 10 + */ + timeout?: number + + /** + * How long between each permission verification + * + * @default 0.3 + */ + interval?: number + } +} diff --git a/typings/bridgeNativeEvents.d.ts b/typings/bridgeNativeEvents.d.ts new file mode 100644 index 000000000..56a9589d5 --- /dev/null +++ b/typings/bridgeNativeEvents.d.ts @@ -0,0 +1,8 @@ +/** + * This is a simple helper to Promisify the calls to cordova + * + * @param {eventTarget} EventTarget EventTarget for native-sourced custom events. + * + * @returns {void} + */ +export declare const bridgeNativeEvents: (eventTarget: EventTarget) => void; diff --git a/typings/eventAsDisposable.d.ts b/typings/eventAsDisposable.d.ts new file mode 100644 index 000000000..1e56068fb --- /dev/null +++ b/typings/eventAsDisposable.d.ts @@ -0,0 +1,13 @@ +import { IDisposable } from './IDisposable'; +/** + * This is a simple helper to wrapp the event handler into a IDisposable + * + * @param {EventTarget} eventTarget EventTarget for native-sourced custom events + * @param {string} eventName Event name to listen to + * @param {(data: any) => void} callback Event handler + * @param {{ once?: boolean }} options once defines if the listener is only trigger once + * @returns {IDisposable} object of which can request the listener's disposal + */ +export declare const asDisposableListener: (eventTarget: EventTarget, eventName: string, callback: (data: R) => void, options?: { + once?: boolean | undefined; +}) => IDisposable; diff --git a/typings/execAsPromise.d.ts b/typings/execAsPromise.d.ts new file mode 100644 index 000000000..037224907 --- /dev/null +++ b/typings/execAsPromise.d.ts @@ -0,0 +1,9 @@ +/** + * This is a simple helper to Promisify the calls to cordova + * + * @param {string} command The native cordova implementation command + * @param {unknown[]} args The native cordova implementation expected arguments + * + * @returns {Promise} Returns from the async native call the type expected + */ +export declare const execAsPromise: (command: string, args?: unknown[]) => Promise; diff --git a/typings/index.d.ts b/typings/index.d.ts new file mode 100644 index 000000000..e8c34d65b --- /dev/null +++ b/typings/index.d.ts @@ -0,0 +1,8 @@ +export { IChannelConfiguration } from './IChannelConfiguration'; +export { IRequestPushPermissionOptions } from './IRequestPushPermissionOptions'; +export { INotificationPayload } from './INotificationPayload'; +export { IDisposable } from './IDisposable'; +import { FCMPlugin } from './FCMPlugin'; +export declare const FCM: FCMPlugin; +export { FCMPlugin }; +export default FCM; diff --git a/www/FCMPlugin.js b/www/FCMPlugin.js index 149ad5303..a921cf406 100644 --- a/www/FCMPlugin.js +++ b/www/FCMPlugin.js @@ -1,47 +1,101 @@ -var exec = require('cordova/exec'); +'use strict'; -function FCMPlugin() { - console.log("FCMPlugin.js: is created"); -} -// SUBSCRIBE TO TOPIC // -FCMPlugin.prototype.subscribeToTopic = function( topic, success, error ){ - exec(success, error, "FCMPlugin", 'subscribeToTopic', [topic]); -} -// UNSUBSCRIBE FROM TOPIC // -FCMPlugin.prototype.unsubscribeFromTopic = function( topic, success, error ){ - exec(success, error, "FCMPlugin", 'unsubscribeFromTopic', [topic]); -} -// NOTIFICATION CALLBACK // -FCMPlugin.prototype.onNotification = function( callback, success, error ){ - FCMPlugin.prototype.onNotificationReceived = callback; - exec(success, error, "FCMPlugin", 'registerNotification',[]); -} -// TOKEN REFRESH CALLBACK // -FCMPlugin.prototype.onTokenRefresh = function( callback ){ - FCMPlugin.prototype.onTokenRefreshReceived = callback; -} -// GET TOKEN // -FCMPlugin.prototype.getToken = function( success, error ){ - exec(success, error, "FCMPlugin", 'getToken', []); -} +var execAsPromise = function (command, args) { + if (args === void 0) { args = []; } + return new Promise(function (resolve, reject) { + window.cordova.exec(resolve, reject, 'FCMPlugin', command, args); + }); +}; -// DEFAULT NOTIFICATION CALLBACK // -FCMPlugin.prototype.onNotificationReceived = function(payload){ - console.log("Received push notification") - console.log(payload) -} -// DEFAULT TOKEN REFRESH CALLBACK // -FCMPlugin.prototype.onTokenRefreshReceived = function(token){ - console.log("Received token refresh") - console.log(token) -} -// FIRE READY // -exec(function(result){ console.log("FCMPlugin Ready OK") }, function(result){ console.log("FCMPlugin Ready ERROR") }, "FCMPlugin",'ready',[]); +var asDisposableListener = function (eventTarget, eventName, callback, options) { + if (options === void 0) { options = {}; } + var once = options.once; + var handler = function (event) { return callback(event.detail); }; + eventTarget.addEventListener(eventName, handler, { passive: true, once: once }); + return { + dispose: function () { return eventTarget.removeEventListener(eventName, handler); }, + }; +}; +var bridgeNativeEvents = function (eventTarget) { + var onError = function (error) { return console.log('FCM: Error listening to native events', error); }; + var onEvent = function (data) { + try { + var _a = JSON.parse(data), eventName = _a[0], eventData = _a[1]; + eventTarget.dispatchEvent(new CustomEvent(eventName, { detail: eventData })); + } + catch (error) { + console.log('FCM: Error parsing native event data', error); + } + }; + window.cordova.exec(onEvent, onError, 'FCMPlugin', 'startJsEventBridge', []); +}; +var FCMPlugin = (function () { + function FCMPlugin() { + var _this = this; + this.eventTarget = document.createElement('div'); + execAsPromise('ready') + .catch(function (error) { return console.log('FCM: Ready error: ', error); }) + .then(function () { + console.log('FCM: Ready!'); + bridgeNativeEvents(_this.eventTarget); + }); + console.log('FCM: has been created'); + } + FCMPlugin.prototype.clearAllNotifications = function () { + return execAsPromise('clearAllNotifications'); + }; + FCMPlugin.prototype.createNotificationChannel = function (channelConfig) { + if (window.cordova.platformId !== 'android') { + return Promise.resolve(); + } + return execAsPromise('createNotificationChannel', [channelConfig]); + }; + FCMPlugin.prototype.deleteInstanceId = function () { + return execAsPromise('deleteInstanceId'); + }; + FCMPlugin.prototype.getAPNSToken = function () { + return window.cordova.platformId !== 'ios' + ? Promise.resolve('') + : execAsPromise('getAPNSToken'); + }; + FCMPlugin.prototype.getInitialPushPayload = function () { + return execAsPromise('getInitialPushPayload'); + }; + FCMPlugin.prototype.getToken = function () { + return execAsPromise('getToken'); + }; + FCMPlugin.prototype.hasPermission = function () { + return window.cordova.platformId === 'ios' + ? execAsPromise('hasPermission') + : execAsPromise('hasPermission').then(function (value) { return !!value; }); + }; + FCMPlugin.prototype.onNotification = function (callback, options) { + return asDisposableListener(this.eventTarget, 'notification', callback, options); + }; + FCMPlugin.prototype.onTokenRefresh = function (callback, options) { + return asDisposableListener(this.eventTarget, 'tokenRefresh', callback, options); + }; + FCMPlugin.prototype.requestPushPermission = function (options) { + var _a, _b, _c, _d; + if (window.cordova.platformId !== 'ios') { + return Promise.resolve(true); + } + var ios9SupportTimeout = (_b = (_a = options === null || options === void 0 ? void 0 : options.ios9Support) === null || _a === void 0 ? void 0 : _a.timeout) !== null && _b !== void 0 ? _b : 10; + var ios9SupportInterval = (_d = (_c = options === null || options === void 0 ? void 0 : options.ios9Support) === null || _c === void 0 ? void 0 : _c.interval) !== null && _d !== void 0 ? _d : 0.3; + return execAsPromise('requestPushPermission', [ios9SupportTimeout, ios9SupportInterval]); + }; + FCMPlugin.prototype.subscribeToTopic = function (topic) { + return execAsPromise('subscribeToTopic', [topic]); + }; + FCMPlugin.prototype.unsubscribeFromTopic = function (topic) { + return execAsPromise('unsubscribeFromTopic', [topic]); + }; + return FCMPlugin; +}()); +var FCM = new FCMPlugin(); - -var fcmPlugin = new FCMPlugin(); -module.exports = fcmPlugin; +module.exports = FCM;