├── .babelrc
├── .editorconfig
├── .eslintignore
├── .eslintrc.yml
├── .github
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE
│ ├── BUG_REPORT.md
│ └── FEATURE_REQUEST.md
├── PULL_REQUEST_TEMPLATE.md
├── config.yml
├── no-response.yml
├── stale.yml
└── workflows
│ ├── ci.yml
│ └── lock-threads.yml
├── .gitignore
├── .npmignore
├── CHANGELOG.md
├── MIT-LICENSE
├── README.md
├── bower.json
├── docs
├── API.md
├── EXAMPLES.md
├── INSTALLATION.md
├── ISSUES.md
├── PAYLOAD.md
├── PHONEGAP_BUILD.md
├── PLATFORM_SUPPORT.md
└── TYPESCRIPT.md
├── example
└── server
│ ├── pushADM.js
│ ├── pushAPNS.rb
│ ├── pushAzure.js
│ └── pushGCM.rb
├── hooks
└── browser
│ └── updateManifest.js
├── package-lock.json
├── package.json
├── plugin.xml
├── spec
├── helper
│ └── cordova.js
├── index.spec.js
└── unit.json
├── src
├── android
│ └── com
│ │ └── adobe
│ │ └── phonegap
│ │ └── push
│ │ ├── BackgroundActionButtonHandler.kt
│ │ ├── BackgroundHandlerActivity.kt
│ │ ├── FCMService.kt
│ │ ├── PushConstants.kt
│ │ ├── PushDismissedHandler.kt
│ │ ├── PushHandlerActivity.kt
│ │ └── PushPlugin.kt
├── browser
│ ├── ServiceWorker.js
│ └── manifest.json
├── ios
│ ├── AppDelegate+PushPlugin.h
│ ├── AppDelegate+PushPlugin.m
│ ├── PushPlugin.h
│ ├── PushPlugin.m
│ ├── PushPluginConstants.h
│ ├── PushPluginConstants.m
│ ├── PushPluginFCM.h
│ ├── PushPluginFCM.m
│ ├── PushPluginSettings.h
│ └── PushPluginSettings.m
└── js
│ └── push.js
├── types
└── index.d.ts
└── www
├── browser
└── push.js
└── push.js
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [
3 | "@babel/preset-env"
4 | ],
5 | "plugins": [
6 | ["add-header-comment", {
7 | "files": {
8 | "src/js/push.js": {
9 | "header": [
10 | "This file has been generated by Babel.\n",
11 | "DO NOT EDIT IT DIRECTLY\n",
12 | "Edit the JS source file src/js/push.js"
13 | ]
14 | }
15 | }
16 | }]
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | www/push.js
2 |
--------------------------------------------------------------------------------
/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | root: true
2 | extends: '@cordova/eslint-config/browser'
3 | parser: '@babel/eslint-parser'
4 | globals:
5 | cordova: true
6 | rules:
7 | indent: ["error", 2]
8 | no-var: 0
9 |
10 | overrides:
11 | - files: [spec/**/*.js, example/server/**/*.js]
12 | extends: '@cordova/eslint-config/node-tests'
13 | rules:
14 | indent: ["error", 2]
15 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | We love pull requests from everyone.
4 |
5 | ## How to Contribute
6 |
7 | There are multiple ways to contribute:
8 |
9 | * Submit Issue Tickets (Bugs Reports)
10 | * Improve Documentation
11 | * Contribute Code
12 |
13 | ## Setup for Contributing Code
14 |
15 | Before we can start submitting code contributions, we setup locally a workspace where we can work on the code.
16 |
17 | 1. [Fork](https://help.github.com/articles/fork-a-repo/) this repo by following the GitHub's "[Fork a repo](https://help.github.com/articles/fork-a-repo/)" quickstart guide.
18 | 2. [Clone](https://help.github.com/articles/cloning-a-repository/) the newly forked repo locally.
19 |
20 | ```bash
21 | git clone git@github.com:your-username/phonegap-plugin-push.git
22 | ```
23 |
24 | ## Make First Codde Contribution
25 |
26 | 1. Create a new branch where you will make your feature or bugfix changes, with a link to the original repo:
27 |
28 | ```bash
29 | git checkout -b my-awesome-new-feature
30 | git push --set-upstream origin my-awesome-new-feature
31 | git remote add upstream https://github.com/havesource/cordova-plugin-push.git
32 | ```
33 |
34 | 2. Install project dependencies
35 |
36 | ```bash
37 | npm install
38 | ```
39 |
40 | 3. Make sure that the tests are pass before and after making any changes:
41 |
42 | ```bash
43 | npm test
44 | ```
45 |
46 | 4. After making changes and tests are passing, commit the changes:
47 |
48 | ```bash
49 | git commit -m "feat(android): my new cool feature"
50 | ```
51 |
52 | ❗ Make sure to follow the [Commit Message and PR Title Semantics](#Commit-Message-and-PR-Title-Semantics)
53 |
54 | 5. Push your commits to your fork:
55 |
56 | ```bash
57 | git push origin
58 | ```
59 |
60 | 6. [Submit a pull request](https://help.github.com/articles/creating-a-pull-request/).
61 |
62 | ## Commit Message and PR Title Semantics
63 |
64 | When writting a commit message or title to a PR, please make sure to follow the convention described below to help use understand what is being reviewed. The semantics will be validated automatticly by a semantic checker.
65 |
66 | ### `Types`
67 |
68 | There are various `types` of commits that can be choosed from. The following `types`, described below, should be used when writting a commit message to help us understand the type of changes we are reviewing.
69 |
70 | * `ci:` - When change are made to the CI configuration files.
71 | * `chore:` - When changes do not modify source or test files. E.g. Wpdating a dependency that does not require code changes.
72 | * `docs:` - When making documentation only changes.
73 | * `feat:` - When adding a new features. E.g. adding a new parameter to perform a new task.
74 | * `fix:` - When making a bug fix.
75 | * `refactor:` - When making code change that does not fix or add new features.
76 | * `perf:` - When making code changes to improve performance.
77 | * `revert:` - When reverting a previous commit.
78 | * `style:` - When making formatting changes that does not affect the code. E.g. commas, semi-colons, whitespaces, indentioned, etc.
79 | * `test:` - Adding missing or correcting existing tests.
80 |
81 |
82 | ### `Scope`
83 |
84 | `Scope` can also be applied to the commit messages to provide more insight to where the changes are made. For example: if a change is being made only to the `android` source code, you can use `android` as the scope.
85 |
86 | Example: "**feat(android): added support for abc**"
87 |
88 | ## Update with Upstream
89 |
90 | Periodically your cloned repo's branch and PR may become out-of-dated with upstream's master. You should make sure your branch is up-to-date with the original repo:
91 |
92 | ```bash
93 | git fetch upstream
94 | git merge upstream/master
95 | ```
96 |
97 | ## After Submitting a PR
98 |
99 | At this point you're waiting on us. We do our best to keep on top of all the pull requests. We may suggest some changes, improvements or alternatives.
100 |
101 | Some things that will increase the chance that your pull request is accepted:
102 |
103 | * Write tests
104 | * Write a good commit message
105 | * Make sure the PR merges cleanly with the latest master.
106 | * Describe your feature/bugfix and why it's needed/important in the pull request description.
107 | * Link your PR with the associated issue ticket is present
108 |
109 | ## Editor Config
110 |
111 | The project uses [.editorconfig](https://editorconfig.org/) to define the coding style of each file. We recommend that you install the Editor Config extension for your preferred IDE. Consistency is key.
112 |
113 | ## ESLint
114 |
115 | The project uses [.eslint](https://eslint.org/) to define the JavaScript coding conventions. Most editors now have a ESLint add-on to provide on-save or on-edit linting.
116 |
117 | We look forward to your contributions!
118 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/BUG_REPORT.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🐛 Bug Report
3 | about: If something isn't working as expected.
4 |
5 | ---
6 |
7 | # Bug Report
8 |
9 | ## Expected Behaviour
10 |
11 | ## Actual Behaviour
12 |
13 | ## Reproduce Scenario (including but not limited to)
14 |
15 | ### Steps to Reproduce
16 |
17 | ### Platform and Version (eg. Android 5.0 or iOS 9.2.1)
18 |
19 | ### (Android) Device Vendor (e.g. Samsung, HTC, Sony...)
20 |
21 | ### `cordova info` Printout
22 |
23 |
34 |
35 | ### Sample Push Data Payload
36 |
37 | ### Sample Code that illustrates the problem
38 |
39 | ### Logs taken while reproducing problem
40 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/FEATURE_REQUEST.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: 🚀 Feature Request
3 | about: A suggestion for a new functionality
4 |
5 | ---
6 |
7 | # Feature Request
8 |
9 | ## Motivation Behind Feature
10 |
11 |
12 |
13 | ## Feature Description
14 |
15 |
16 |
17 | ## Alternatives or Workarounds
18 |
19 |
20 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Description
4 |
5 |
6 | ## Related Issue
7 |
8 |
9 |
10 |
11 |
12 | ## Motivation and Context
13 |
14 |
15 | ## How Has This Been Tested?
16 |
17 |
18 |
19 |
20 | ## Screenshots (if appropriate):
21 |
22 | ## Types of changes
23 |
24 | - [ ] Bug fix (non-breaking change which fixes an issue)
25 | - [ ] New feature (non-breaking change which adds functionality)
26 | - [ ] Breaking change (fix or feature that would cause existing functionality to change)
27 |
28 | ## Checklist:
29 |
30 |
31 | - [ ] My code follows the code style of this project.
32 | - [ ] My change requires a change to the documentation.
33 | - [ ] I have updated the documentation accordingly.
34 | - [ ] I have read the **CONTRIBUTING** document.
35 | - [ ] I have added tests to cover my changes.
36 | - [ ] All new and existing tests passed.
37 |
--------------------------------------------------------------------------------
/.github/config.yml:
--------------------------------------------------------------------------------
1 | # The "request-info" app is used to automatically alert users when they have created a
2 | # new issues that does not have any description. We will reassure them that we need information
3 | # to be able to help them. It will also say they should follow the issue template that was
4 | # pre-provided when creating the issue.
5 |
6 | requestInfoReplyComment: >
7 | Thank you for your submission, however, with the lack of information, we are unable
8 | to continue at this time.
9 |
10 | We would appreciate it if you could provide us with more information about this
11 | submission so that we could provide better support to this issue/pr.
12 |
13 | Therefore, please edit this issue accordingly by using our pre-provided template or
14 | close and create a new one and make sure to provide all the required information.
15 |
16 | This submission will be automatically closed later if no information is provided.
17 |
18 | # Titles can also be checked against for lack of descriptiveness. (MUST BE ALL LOWERCASE)
19 | # requestInfoDefaultTitles:
20 | # - update readme.md
21 | # - updates
22 |
23 | # Append the info-needed label issues and prs with insufficient information.
24 | requestInfoLabelToAdd: info-needed
25 |
26 | # Issues and PR requires more information than what is provided in the templates
27 | # This check will will fail if the body matches the provided template
28 | checkIssueTemplate: true
29 | checkPullRequestTemplate: true
30 |
31 | # Only warn about insufficient information on these events types
32 | # Keys must be lowercase. Valid values are 'issue' and 'pullRequest'
33 | requestInfoOn:
34 | pullRequest: true
35 | issue: true
36 |
--------------------------------------------------------------------------------
/.github/no-response.yml:
--------------------------------------------------------------------------------
1 | # The "no-response" app is used to automatically close issues that have not received a response
2 | # from the author. This app is paired with the "request-info" app which manages the warning
3 | # and label pinning.
4 |
5 | # Notice: This app does not support PRs. While the "request-info" app still warns users in PRs,
6 | # PRs will need to be managed manually for the closing.
7 |
8 | # The number of days of inactivity before an issue is closed for lack of response
9 | daysUntilClose: 4
10 |
11 | # The pinned label requiring a response
12 | responseRequiredLabel: info-needed
13 |
14 | # Comment to post when closing an issue for lack of response. `false` to disable
15 | closeComment: >
16 | This issue has been automatically closed because there has been no response
17 | to our request for more information from the original author. With only the
18 | information that is currently in the issue, we don't have enough information
19 | to take action. Please reach out if you have or find the answers we need so
20 | that we can investigate further.
21 |
--------------------------------------------------------------------------------
/.github/stale.yml:
--------------------------------------------------------------------------------
1 | # The "Stale" app is used to automatically close stale issues and pr which accumulates over time.
2 |
3 | # The number of days of inactivity before an issue or pr is flagged as stale with the stale tag.
4 | daysUntilStale: 60
5 |
6 | # The number of days of inactivity before a stale issue is closed
7 | daysUntilClose: 14
8 |
9 | # The issues and pr that contains any of these labels will never be flagged as stale
10 | exemptLabels:
11 | - pinned
12 | - security
13 |
14 | # The label which is appened to mark an issue or pr as stale
15 | staleLabel: wontfix
16 |
17 | # Below is the stale bot's comment to warn users when a issue or pr becomes stale.
18 | markComment: >
19 | This has been automatically marked as stale due to the lack of activity. It will be closed if no further activity occurs.
20 |
21 | If you believe this is still valid, please provide an update to reassure that it is needed and all open questions were answered.
22 | When verifying, please use the master branch as this may have already been resolved.
23 |
24 | Thank you for your contributions.
25 |
26 | # Comment to post when closing a stale issue. Set to `false` to disable
27 | closeComment: false
28 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | # Licensed to the Apache Software Foundation (ASF) under one
2 | # or more contributor license agreements. See the NOTICE file
3 | # distributed with this work for additional information
4 | # regarding copyright ownership. The ASF licenses this file
5 | # to you under the Apache License, Version 2.0 (the
6 | # "License"); you may not use this file except in compliance
7 | # with the License. You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing,
12 | # software distributed under the License is distributed on an
13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 | # KIND, either express or implied. See the License for the
15 | # specific language governing permissions and limitations
16 | # under the License.
17 |
18 | name: Node CI
19 |
20 | on: [push, pull_request]
21 |
22 | jobs:
23 | # Node Tests
24 | nodeTest:
25 | name: Nodes Test on ${{ matrix.os }}
26 |
27 | runs-on: ${{ matrix.os }}
28 |
29 | strategy:
30 | matrix:
31 | os: [ubuntu-latest, windows-latest, macos-latest]
32 |
33 | steps:
34 | - uses: actions/checkout@v4
35 |
36 | - name: Environment Setup - Node
37 | uses: actions/setup-node@v3
38 | with:
39 | node-version: 18.x
40 |
41 | - name: Environment Information
42 | run: |
43 | node --version
44 | npm --version
45 |
46 | - name: npm install and test
47 | run: npm cit
48 | env:
49 | CI: true
50 |
51 | # Add Coverage
52 | # - uses: codecov/codecov-action@v1
53 | # with:
54 | # fail_ci_if_error: true
55 |
--------------------------------------------------------------------------------
/.github/workflows/lock-threads.yml:
--------------------------------------------------------------------------------
1 | name: 'Lock Threads'
2 |
3 | on:
4 | schedule:
5 | - cron: '0 0 * * *'
6 | workflow_dispatch:
7 |
8 | permissions:
9 | issues: write
10 | pull-requests: write
11 | discussions: write
12 |
13 | concurrency:
14 | group: lock-threads
15 |
16 | jobs:
17 | action:
18 | runs-on: ubuntu-latest
19 | steps:
20 | - uses: dessant/lock-threads@v5
21 | with:
22 | issue-inactive-days: '21'
23 | issue-comment: >
24 | This issue has been automatically locked since there
25 | has not been any recent activity after it was closed.
26 | Please open a new issue for related bugs.
27 | issue-lock-reason: 'resolved'
28 | pr-inactive-days: '21'
29 | pr-comment: >
30 | This pull request has been automatically locked since there
31 | has not been any recent activity after it was closed.
32 | Please open a new issue for related bugs.
33 | pr-lock-reason: 'resolved'
34 | discussion-inactive-days: '21'
35 | discussion-comment: >
36 | This discussion has been automatically locked since there
37 | has not been any recent activity after it was closed.
38 | process-only: 'issues, prs, discussions'
39 | log-output: false
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .idea
3 | .DS_Store
4 | npm-debug.log
5 | .tern-project
6 | node_modules/
7 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | .*
2 | docs
3 | example
4 | spec
5 | bower.json
6 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | ## 5.0.5
4 |
5 | **Fixes:**
6 |
7 | - fix(ios): correctly set `foreground` flag when `forceShow` is enabled (#338)
8 |
9 | ## 5.0.4
10 |
11 | - Updated the plugin version in `plugin.xml`. (It was missed in the last three patches :D)
12 |
13 | ## 5.0.3
14 |
15 | **Important Note:**
16 |
17 | This patch release fixes the implementation of the `forceShow` option on iOS to align with Android's behavior.The original goal was to provide identical functionality, but the iOS implementation included unintended platform-specific behavior. While this might feel like a breaking change from version 5.0.0 (2024/11/21), it is classified as a patch release because it corrects a bug where notifications were processed without user interaction and did not align with Android's implementation. This update eliminates the need for developers to handle platform-specific cases, ensuring consistency with Android.
18 |
19 | **Fixes:**
20 |
21 | - fix(ios): dont trigger `notificationReceived` on app reload (#333)
22 | - fix(ios): `forceShow` to display toast but trigger event until tapped (#332)
23 |
24 | ## 5.0.2
25 |
26 | **Fixes:**
27 |
28 | - fix(android): add missing `import androidx.core.app.ActivityCompat` (#331)
29 |
30 | ## 5.0.1
31 |
32 | **Fixes:**
33 |
34 | - fix(ios): revert accidental change to category "`callback`" (#328)
35 | - fix(ios): update reset flags placement in `notificationReceived` (#329)
36 | - fix(ios): request new permissions based on ios config changes (#326)
37 | - fix(android): prevent permission dialog appearing when already denied (#325)
38 | - fix(ios): exclude configure if `FIRApp` is already configured (#321)
39 | - fix(ios): reset flags & store message after processing notification received (#319)
40 | - fix(ios): make sure `notificationMessage` is immutable (#317)
41 |
42 | **Others:**
43 |
44 | - chore(*): typings - update comments & iOS `forceShow` option (#327)
45 | - ci: replace lock app with lock-threads workflow
46 |
47 | ## 5.0.0
48 |
49 | **Breaking:**
50 |
51 | - chore(android)!: remove before compile hook script (#307)
52 | - fix(ios)!: duplicate notification presentation on iOS 18.0 (#303)
53 | - feat(ios)!: move `AppDelegate` logic to `PushPlugin.m` (#300)
54 | - feat(ios)!: bump `firebase@10.24.0` (#294)
55 | - feat(ios)!: extract FCM related logic to `PushPluginFCM` (#293)
56 | - chore(ios)!: remove unused & deprecated code & styling formatting (#291)
57 |
58 | **Features:**
59 |
60 | - feat(ios): extract settings into `PushPluginSettings` (#292)
61 | - feat(ios): implement `forceShow` (#276)
62 |
63 | **Fixes:**
64 |
65 | - fix(android): `getCircleBitmap` not displaying image (#311)
66 | - fix(*): add `clearNotification` to typescript definitions (#309)
67 | - fix(ios): on notification event payload & stop background task (#301)
68 | - fix(ios): run `setApplicationIconBadgeNumber` on main thread (#302)
69 | - fix(android): clipped notification text and expand arrow (#286)
70 | - fix(ios): add missing `critical` to typings (#271)
71 |
72 | **Others:**
73 |
74 | - chore: bump plugin.xml to 5.0.0 (#310)
75 | - chore: revert back to `AppDelegate` (#306)
76 | - chore(ios): update all NSLog to include `[PushPlugin]` prefix (#290)
77 | - doc: update link to iOS payload keys (#312)
78 | - doc: formatting and including 5.0.0 (#308)
79 |
80 | ## 4.0.0
81 |
82 | **Breaking:**
83 |
84 | - feat(android)!: bump platform requirement cordova-android>=12.0.0 (#243)
85 | - feat!(ios): update Firebase Messaging to ~> 8.1.1 (#152)
86 | - fix(windows)!: remove deprecated platform (#245)
87 |
88 | **Features:**
89 |
90 | - feat(android): bump gradle plugin kotlin to 1.7.21 (#246)
91 | - feat(android): bump GradlePluginGoogleServicesVersion to 4.3.15 (match w/ Cordova-Android@12.x) (#244)
92 | - feat(android): support targetSdkVersion >= 31 (Android 12) (#185)
93 |
94 | **Fixes:**
95 |
96 | - fix(ios): callback not called when foreground is true #101 (#102)
97 | - fix(android): deprecated warning Html.fromHtml (#230)
98 | - fix(android): Ask for POST_NOTIFICATIONS permission if necessary (#238)
99 | - fix(android): PushHandlerActivity permissions regression (#183)
100 |
101 | **Others:**
102 |
103 | - dep: resolve audit with rebuilt package-lock & rebuilt push.js with new packages (#248)
104 | - dep(npm): bump all devDependencies (#241)
105 | - ci: bump github action workflow and dependencies (#242)
106 |
107 | ## 3.0.1
108 |
109 | **Fixes:**
110 |
111 | * fix(android): add service exported flags w/ intent-filter (#173)
112 | * fix(android): processPushBundle flipped returned value (#160)
113 | * fix: doc typo (#159)
114 |
115 | **Chores:**
116 |
117 | * chore: correct Bower metadata to MIT license (#166)
118 | * chore(npm): rebuilt package-lock.json & fix audit
119 |
120 | **Docs:**
121 |
122 | * doc: add 2.x and 3.x to PLATFORM_SUPPORT.md
123 |
124 | ## 3.0.0
125 |
126 | **Overview:**
127 |
128 | In this major release, there are many breaking changes to the Android platform. The primary changes in this release are dropping Android Support Library for AndroidX, updating the Google Services Gradle Plugin requirement, refactoring the code to Kotlin, and upgrading all other third-party libraries. See below for more details.
129 |
130 | ---
131 |
132 | **Requirement Changes:**
133 |
134 | * Cordova-Android: 9.x
135 |
136 | ---
137 |
138 | **Libraries & Dependencie Updates and Changes:**
139 |
140 | * Bumped `me.leolin:ShortcutBadger@1.1.22`
141 | * Bumped `Google Services Gradle Plugin@4.3.8`
142 | * Migrated from **Android Support Library** to **AndroidX**
143 | * Added `androidx.core:core@1.6.+`
144 |
145 | ---
146 |
147 | **Breaking:**
148 |
149 | * feat!(android): migrate source code to kotlin w/ refactors (#123)
150 | * kotlin@1.5.20
151 | * add before_compile hookscript for cordova-android 9.x support
152 | * converted all java files to kotlin
153 | * updted target-dir path for kotlin files
154 | * refactored with improved null checks
155 | * removed some duplicated code
156 | * updated logging (more logging & better tagging)¥
157 | * feat!(android): bump platform requirements (#122)
158 | * feat!(android): bump me.leolin:ShortcutBadger@1.1.22 (#121)
159 | * feat!(android): bump Google Services Gradle Plugin@4.3.8 (#120)
160 | * feat!(android): Migrate to AndroidX (#119)
161 | * feat!(android): migrate ASL to AndroidX
162 | * feat!(android): swap framework support-v13 w/ androidx.core
163 | * feat!(android): force AndroidXEnabled to true
164 | * feat!(android): bump androidx.core:core@1.6.+
165 | * doc(android): add androidx core version
166 |
167 | **Fixes:**
168 |
169 | * fix(docs): update TS type import to new package name (#109)
170 |
171 | **Chores:**
172 |
173 | * chore: rebuilt package-lock.json (#131)
174 | * chore: bump npm dev dependencies (#132) (#133)
175 | * chore(npm): bump devDep, rebuild package-lock & fix audit (#110)
176 | * chore(npm): bump devDep @cordova/eslint-config@^4.0.0 w/ fixes (#144)
177 |
178 | **CI:**
179 |
180 | * ci: remove old travis configs (#128)
181 | * ci: add codacy badge (#129)
182 | * ci: add gh-actions badge to readme (#130)
183 |
184 | **Docs:**
185 |
186 | * doc: fixed minor typo (#98)
187 |
188 | ## 2.0.0
189 |
190 | **Overview:**
191 |
192 | This release contains breaking changes. One of these needed changes resolved issues of restoring the plugin from npm.
193 |
194 | With this breaking change, the `havesource-cordova-plugin-push` package name is no longer used. Please completely uninstall the old version before installing the new version. This will ensure that the correct package name `@havesource/cordova-plugin-push` is used.
195 |
196 | There is also an update to the installation requirements:
197 |
198 | | | Version |
199 | | - | - |
200 | | Cordova CLI | 10.0.0 |
201 | | Cordova Android | 8.0.0 |
202 | | **Cordova iOS** | **6.0.0** |
203 | | CocoaPods | 1.8.0 |
204 |
205 | **Breaking:**
206 |
207 | * breaking(ios): requirement bump [#80](https://github.com/havesource/cordova-plugin-push/pull/80)
208 | * breaking: fixed incorrect initial cordova-cli requirement [79333b2](https://github.com/havesource/cordova-plugin-push/commit/79333b25e1ff68fea377be499da91528c82fa21f)
209 |
210 | **Feature:**
211 |
212 | * feat(ios): force `cocoapods` cdn [#48](https://github.com/havesource/cordova-plugin-push/pull/48)
213 | * feat(ios): support `firebase/messaging` dep version override [#47](https://github.com/havesource/cordova-plugin-push/pull/47)
214 |
215 | **Chore:**
216 |
217 | * chore(`npm`): rebuilt `package-lock.json` [67e4e4b](https://github.com/havesource/cordova-plugin-push/commit/67e4e4ba185511e60b4d85cae882c41dae1c9cc0)
218 | * chore(`android`): remove duplicate code [#81](https://github.com/havesource/cordova-plugin-push/pull/81)
219 | * chore: bump dev dependencies [#79](https://github.com/havesource/cordova-plugin-push/pull/79)
220 |
221 | **CI & Docs:**
222 |
223 | * ci(gh-actions): bump dependencies [#78](https://github.com/havesource/cordova-plugin-push/pull/78)
224 |
225 |
226 | ## 1.0.0
227 |
228 | **Breaking:**
229 |
230 | * breaking(android): bump fcm@18.+ [#19](https://github.com/havesource/cordova-plugin-push/pull/19)
231 | * breaking(android): drop phonegap-plugin-multidex dependency [#21](https://github.com/havesource/cordova-plugin-push/pull/21)
232 | * breaking(android): move clearAllNotifications to destroy from pause [#13](https://github.com/havesource/cordova-plugin-push/pull/13)
233 |
234 | **Feature:**
235 |
236 | * feat(android): notification data pass [#31](https://github.com/havesource/cordova-plugin-push/pull/31)
237 | * feat(ios): support critical alert notifications [#12](https://github.com/havesource/cordova-plugin-push/pull/12)
238 | * feat(ios): increase firebase framework to 6.32.2 [#42](https://github.com/havesource/cordova-plugin-push/pull/42)
239 | * feat: remove cordova-support-google-services dependency [#8](https://github.com/havesource/cordova-plugin-push/pull/8)
240 |
241 | **Fix:**
242 |
243 | * fix(android): missing channel description crash [#53](https://github.com/havesource/cordova-plugin-push/pull/53)
244 | * fix(android): Use app name for default channel [#49](https://github.com/havesource/cordova-plugin-push/pull/49)
245 | * fix(android): enable lights when lightColor is set [#15](https://github.com/havesource/cordova-plugin-push/pull/15)
246 | * fix(browser): corrected path to manifest file. [#32](https://github.com/havesource/cordova-plugin-push/pull/32)
247 |
248 | **Chore:**
249 |
250 | * chore(android): set requirement >= 8.0.0 [#52](https://github.com/havesource/cordova-plugin-push/pull/52)
251 | * chore(android): cleanup & format [#26](https://github.com/havesource/cordova-plugin-push/pull/26)
252 | * chore(android): bump com.android.support:support-v13:28.0.0 [#20](https://github.com/havesource/cordova-plugin-push/pull/20)
253 | * chore(ios): use latest firebase library [#24](https://github.com/havesource/cordova-plugin-push/pull/24)
254 | * chore(npm): rebuilt package-lock.json [#55](https://github.com/havesource/cordova-plugin-push/pull/55)
255 | * chore(npm): properly configure for scope package [#33](https://github.com/havesource/cordova-plugin-push/pull/33)
256 | * chore(type-definition): Update PushNotificationStatic [#14](https://github.com/havesource/cordova-plugin-push/pull/14)
257 | * chore(github-pages): remove config [#4](https://github.com/havesource/cordova-plugin-push/pull/4)
258 | * chore: update ticket management [#27](https://github.com/havesource/cordova-plugin-push/pull/27)
259 | * chore: add missing build of push.js [#22](https://github.com/havesource/cordova-plugin-push/pull/22)
260 | * chore: match plugin.xml version w/ package.json [#10](https://github.com/havesource/cordova-plugin-push/pull/10)
261 | * chore: update xml namespace [#9](https://github.com/havesource/cordova-plugin-push/pull/9)
262 | * chore: update version requirements [#7](https://github.com/havesource/cordova-plugin-push/pull/7)
263 | * chore: update npm & git ignore list [#6](https://github.com/havesource/cordova-plugin-push/pull/6)
264 | * chore: update plugin package [#1](https://github.com/havesource/cordova-plugin-push/pull/1)
265 | * chore: remove unused dependencies [#2](https://github.com/havesource/cordova-plugin-push/pull/2)
266 |
267 | **Refactor & Style:**
268 |
269 | * refactor(eslint): update dependencies w/ fixes [#3](https://github.com/havesource/cordova-plugin-push/pull/3)
270 | * style(md): format with md all in one (vscode) [#11](https://github.com/havesource/cordova-plugin-push/pull/11)
271 |
272 | **CI & Docs:**
273 |
274 | * ci(gh-actions): add workflow [#23](https://github.com/havesource/cordova-plugin-push/pull/23)
275 | * ci: update travis configs [#5](https://github.com/havesource/cordova-plugin-push/pull/5)
276 | * doc(android): enable & set notification light with lightColor [#54](https://github.com/havesource/cordova-plugin-push/pull/54)
277 | * doc: cleanup docs [#51](https://github.com/havesource/cordova-plugin-push/pull/51)
278 | * doc: update various markdown docs [#28](https://github.com/havesource/cordova-plugin-push/pull/28)
279 |
280 | ## Previous Change Log
281 |
282 | Since this repo is a fork from the original Adobe PhoneGap push plugin, you can continue to read the previous changelog here:
283 |
284 | https://github.com/havesource/cordova-plugin-push/blob/phonegap-2.3.0/CHANGELOG.md
285 |
--------------------------------------------------------------------------------
/MIT-LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2012-2017 Adobe Systems
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining
4 | a copy of this software and associated documentation files (the
5 | "Software"), to deal in the Software without restriction, including
6 | without limitation the rights to use, copy, modify, merge, publish,
7 | distribute, sublicense, and/or sell copies of the Software, and to
8 | permit persons to whom the Software is furnished to do so, subject to
9 | the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be
12 | included in all copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Cordova Plugin Push
2 |
3 | [](https://github.com/havesource/cordova-plugin-push/actions/workflows/ci.yml) [](https://app.codacy.com/gh/havesource/cordova-plugin-push?utm_source=github.com&utm_medium=referral&utm_content=havesource/cordova-plugin-push&utm_campaign=Badge_Grade_Settings)
4 |
5 | > Register and receive push notifications
6 |
7 | # What is this?
8 |
9 | This plugin offers support to receive and handle native push notifications with a **single unified API**.
10 |
11 | This does not mean you will be able to send a single push message and have it arrive on devices running different operating systems. By default Android uses FCM and iOS uses APNS and their payloads are significantly different. Even if you are using FCM for both Android and iOS there are differences in the payload required for the plugin to work correctly. For Android **always** put your push payload in the `data` section of the push notification. For more information on why that is the case read [Notification vs Data Payload](https://github.com/havesource/cordova-plugin-push/blob/master/docs/PAYLOAD.md#notification-vs-data-payloads). For iOS follow the regular [FCM documentation](https://firebase.google.com/docs/cloud-messaging/http-server-ref).
12 |
13 | This plugin does not provide a way to determine which platform you are running on. The best way to do that is use the `device.platform` property provided by [cordova-plugin-device](https://github.com/apache/cordova-plugin-device).
14 |
15 | * [Reporting Issues](docs/ISSUES.md)
16 | * [Installation](docs/INSTALLATION.md)
17 | * [API reference](docs/API.md)
18 | * [Typescript support](docs/TYPESCRIPT.md)
19 | * [Examples](docs/EXAMPLES.md)
20 | * [Platform support](docs/PLATFORM_SUPPORT.md)
21 | * [Cloud build support (PG Build, IntelXDK)](docs/PHONEGAP_BUILD.md)
22 | * [Push notification payload details](docs/PAYLOAD.md)
23 | * [Contributing](.github/CONTRIBUTING.md)
24 | * [License (MIT)](MIT-LICENSE)
25 |
26 | # Do you like tutorial? You get tutorial!
27 |
28 | * [PhoneGap Day US Push Workshop 2016 (using node-gcm)](http://macdonst.github.io/push-workshop/)
29 |
30 | # Thanks to all our contributors
31 |
32 | [
](https://github.com/10ko)[
](https://github.com/TVolly)[
](https://github.com/waptaxi)[
](https://github.com/viktormuller)[
](https://github.com/devgeeks)[
](https://github.com/rastafan)
33 |
34 | [
](https://github.com/mdoelker)[
](https://github.com/markeeftb)[
](https://github.com/malwatte)[
](https://github.com/madebycm)[
](https://github.com/kelvinhokk)[
](https://github.com/keab42)
35 |
36 | [
](https://github.com/jomarocas)[
](https://github.com/giuseppelt)[
](https://github.com/ericb)[
](https://github.com/eKazim)[
](https://github.com/clementcontet)[
](https://github.com/yaswanthsvist)
37 |
38 | [
](https://github.com/Vabs28)[
](https://github.com/TillaTheHun0)[
](https://github.com/tomasvarg)[
](https://github.com/tobmaster)[
](https://github.com/ThiagoBueno)[
](https://github.com/szh)
39 |
40 | [
](https://github.com/SharUpOff)[
](https://github.com/smorstabilini)[
](https://github.com/fesor)[
](https://github.com/GreyDekart)[
](https://github.com/sebastiansier)[
](https://github.com/olastor)
41 |
42 | [
](https://github.com/tanansatpal)[
](https://github.com/SandroGrzicic)[
](https://github.com/xorxor)[
](https://github.com/rubenstolk)[
](https://github.com/roel-sluper)[
](https://github.com/pataar)
43 |
44 | [
](https://github.com/peteonrails)[
](https://github.com/pjalbuquerque)[
](https://github.com/NitroGhost)[
](https://github.com/matrosov-nikita)[
](https://github.com/Mikejo5000)[
](https://github.com/michellarcari)
45 |
46 | [
](https://github.com/adamschachne)[
](https://github.com/alharding)[
](https://github.com/albertleao)[
](https://github.com/gotev)[
](https://github.com/Alex-Sessler)[
](https://github.com/ben-8409)
47 |
48 | [
](https://github.com/bmwertman)[
](https://github.com/bmatto)[
](https://github.com/countcain)[
](https://github.com/CookieCookson)[
](https://github.com/cdorner)[
](https://github.com/colene)
49 |
50 | [
](https://github.com/cfsnyder)[
](https://github.com/cmalard)[
](https://github.com/dansumption)[
](https://github.com/dannywillems)[
](https://github.com/DrMoriarty)[
](https://github.com/eladmoshe)
51 |
52 | [
](https://github.com/mlabarca)[
](https://github.com/bromeostasis)[
](https://github.com/filmaj)[
](https://github.com/geo242)[
](https://github.com/gbenvenuti)[
](https://github.com/polyn0m)
53 |
54 | [
](https://github.com/jacquesdev)[
](https://github.com/janpio)[
](https://github.com/jakari)[
](https://github.com/purplecabbage)[
](https://github.com/theaccordance)[
](https://github.com/jonas-m-)
55 |
56 | [
](https://github.com/Chuckytuh)[
](https://github.com/leonardobazico)[
](https://github.com/loslislo-lshift)[
](https://github.com/luka5)[
](https://github.com/mac89)[
](https://github.com/markokeeffe)
57 |
58 | [
](https://github.com/mbektchiev)[
](https://github.com/goya)[
](https://github.com/slorber)[
](https://github.com/daserge)[
](https://github.com/smdvdsn)[
](https://github.com/ryanluker)
59 |
60 | [
](https://github.com/russellbeattie)[
](https://github.com/rjmunro)[
](https://github.com/hanicker)[
](https://github.com/mwbrooks)[
](https://github.com/LightZam)[
](https://github.com/laagland)
61 |
62 | [
](https://github.com/cuatl)[
](https://github.com/gianpaj)[
](https://github.com/EdMcBane)[
](https://github.com/chriswiggins)[
](https://github.com/barryvdh)[
](https://github.com/armno)
63 |
64 | [
](https://github.com/archananaik)[
](https://github.com/jakub-g)[
](https://github.com/shazron)[
](https://github.com/sclement41)[
](https://github.com/hung-doan)[
](https://github.com/BBosman)
65 |
66 | [
](https://github.com/giordanocardillo)[
](https://github.com/mikepsinn)[
](https://github.com/AdriVanHoudt)[
](https://github.com/alexislg2)[
](https://github.com/jcesarmobile)[
](https://github.com/nadyaA)
67 |
68 | [
](https://github.com/jdhiro)[
](https://github.com/edewit)[
](https://github.com/wildabeast)[
](https://github.com/mkuklis)[
](https://github.com/ashconnell)[
](https://github.com/zwacky)
69 |
70 | [
](https://github.com/rakatyal)[
](https://github.com/jtbdevelopment)[
](https://github.com/EddyVerbruggen)[
](https://github.com/fredgalvao)[
](https://github.com/bobeast)[
](https://github.com/macdonst)
71 |
72 | [
](https://github.com/larrybahr)
73 |
--------------------------------------------------------------------------------
/bower.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "phonegap-plugin-push",
3 | "description": "bower package for server side javascript",
4 | "main": "www/push.js",
5 | "authors": [
6 | "Adobe PhoneGap Team"
7 | ],
8 | "license": "MIT",
9 | "keywords": [
10 | "ecosystem:cordova",
11 | "ecosystem:phonegap",
12 | "cordova-ios",
13 | "cordova-android",
14 | "cordova-browser"
15 | ],
16 | "homepage": "https://github.com/phonegap/phonegap-plugin-push",
17 | "moduleType": [],
18 | "ignore": [
19 | "**/.*",
20 | "node_modules",
21 | "bower_components",
22 | "test",
23 | "tests"
24 | ]
25 | }
26 |
--------------------------------------------------------------------------------
/docs/EXAMPLES.md:
--------------------------------------------------------------------------------
1 | # Creating a Project From a Template
2 |
3 | If you want to get started with a sample project you can create a new project from the example template.
4 |
5 | ```
6 | phonegap create my-app --template phonegap-template-push
7 | ```
8 |
9 | ## Quick Example
10 |
11 | ```javascript
12 | const push = PushNotification.init({
13 | android: {
14 | },
15 | browser: {
16 | pushServiceURL: 'http://push.api.phonegap.com/v1/push'
17 | },
18 | ios: {
19 | alert: "true",
20 | badge: "true",
21 | sound: "true"
22 | }
23 | });
24 |
25 | push.on('registration', (data) => {
26 | // data.registrationId
27 | });
28 |
29 | push.on('notification', (data) => {
30 | // data.message,
31 | // data.title,
32 | // data.count,
33 | // data.sound,
34 | // data.image,
35 | // data.additionalData
36 | });
37 |
38 | push.on('error', (e) => {
39 | // e.message
40 | });
41 | ```
42 |
--------------------------------------------------------------------------------
/docs/INSTALLATION.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | - [Installation](#installation)
4 | - [Installation Requirements](#installation-requirements)
5 | - [Cordova-Android 9.x Specifics](#cordova-android-9x-specifics)
6 | - [Android details](#android-details)
7 | - [Co-existing with Facebook Plugin](#co-existing-with-facebook-plugin)
8 | - [Co-existing with plugins that use Firebase](#co-existing-with-plugins-that-use-firebase)
9 | - [Common errors](#common-errors)
10 | - [Multidex](#multidex)
11 | - [More than one library with package name 'com.google.android.gms'](#more-than-one-library-with-package-name-comgoogleandroidgms)
12 | - [Browser details](#browser-details)
13 | - [Browser Support](#browser-support)
14 | - [iOS details](#ios-details)
15 | - [System \& Cordova Requirements](#system--cordova-requirements)
16 | - [Bitcode](#bitcode)
17 | - [CocoaPods](#cocoapods)
18 | - [Common CocoaPod Installation issues](#common-cocoapod-installation-issues)
19 | - [Library not found for -lPods-Appname](#library-not-found-for--lpods-appname)
20 | - [Library not found for -lGoogleToolboxForMac](#library-not-found-for--lgoogletoolboxformac)
21 | - [Additional Resources](#additional-resources)
22 |
23 | ## Installation Requirements
24 |
25 | | Plugin version | Cordova CLI | Cordova Android | Cordova iOS | CocoaPods |
26 | | -------------- | ----------- | --------------- | ----------- | --------- |
27 | | 1.0.0 | 10.0.0 | 8.0.0 | 5.1.1 | 1.8.0 |
28 | | 2.0.0 | 10.0.0 | 8.0.0 | 6.0.0 | 1.8.0 |
29 | | 3.0.0 | 10.0.0 | 9.0.0 | 6.0.0 | 1.8.0 |
30 | | 4.0.0 | 10.0.0 | 12.0.0 | 6.0.0 | 1.8.0 |
31 | | 5.0.0 | 10.0.0 | 12.0.0 | 6.0.0 | 1.8.0 |
32 |
33 | To install from the command line:
34 |
35 | ```bash
36 | cordova plugin add @havesource/cordova-plugin-push
37 | ```
38 |
39 | It is also possible to install via repo url directly (unstable)
40 |
41 | or
42 |
43 | ```bash
44 | cordova plugin add github:havesource/cordova-plugin-push
45 | ```
46 |
47 | To configure the `SENDER_ID`, place your `google-services.json` (Android) and/or `GoogleService-Info.plist` in the root folder of your project and then add the following lines into your config.xml.
48 |
49 | In the `platform` tag for Android add the following `resource-file` tag if you are using `cordova-android` 8.0 or greater:
50 |
51 | E.g.
52 |
53 | ```xml
54 |
55 |
56 |
57 | ```
58 |
59 | By default, on iOS, the plugin will register with APNS. If you want to use FCM on iOS, in the `platform` tag for iOS add the following `resource-file` tag:
60 |
61 | ```xml
62 |
63 |
64 |
65 | ```
66 |
67 | > Note: if you are using Ionic you may need to specify the SENDER_ID variable in your package.json.
68 |
69 | ```json
70 | "cordovaPlugins": [
71 | {
72 | "locator": "phonegap-plugin-push"
73 | }
74 | ]
75 | ```
76 |
77 | > Note: You need to specify the SENDER_ID variable in your config.xml if you plan on installing/restoring plugins using the prepare method. The prepare method will skip installing the plugin otherwise.
78 |
79 | ```xml
80 |
81 | ```
82 |
83 | ### Cordova-Android 9.x Specifics
84 |
85 | **Using AndroidX Library:**
86 |
87 | As of version **3.0.0**, this plugin has migrated from the Android Support Library to AndroidX. Since Cordova-Android 9.x does not use the AndroidX library by default, you will need to install the [`cordova-plugin-androidx-adapter`](https://www.npmjs.com/package/cordova-plugin-androidx-adapter) plugin.
88 |
89 | This plugin will migrate any Android Support Library references to AndroidX.
90 |
91 | Please note that this will also migrate references of other plugins.
92 |
93 | If you are using **Cordova Android 8.x** please continue reading in the **Cordova Android 8.x Specifics** section.
94 |
95 | ## Android details
96 |
97 | ### Co-existing with Facebook Plugin
98 |
99 | There are a number of Cordova Facebook Plugins available but the one that we recommend is [Jeduan's fork](https://github.com/jeduan/cordova-plugin-facebook4) of the original Wizcorp plugin. It is setup to use Gradle/Maven and the latest Facebook SDK properly.
100 |
101 | To add to your app:
102 |
103 | ```bash
104 | cordova plugin add cordova-plugin-facebook4 --variable APP_ID="App ID" --variable APP_NAME="App Name"
105 | ```
106 |
107 | ### Co-existing with plugins that use Firebase
108 |
109 | Problems may arise when push plugin is used along plugins that implement Firebase functionality (e.g. `cordova-plugin-firebase-analytics`). Both plugins include a version of the FCM libraries.
110 |
111 | To make the two work together, you need to migrate your GCM project from Google console to Firebase console:
112 |
113 | 1. In Firebase console - [import your existing GCM project](https://firebase.google.com/support/guides/google-android#migrate_your_console_project), don't create a new one.
114 | 2. Set your `FCM_VERSION` variable to match the version used in the other plugin. In case of Cordova, your `package.json` contains something like this:
115 |
116 | ```json
117 | {
118 | "cordova": {
119 | "plugins": {
120 | "@havesource/cordova-plugin-push": {
121 | "ANDROIDX_CORE_VERSION": "1.6.+",
122 | "FCM_VERSION": "18.+"
123 | }
124 | },
125 | "platforms": []
126 | }
127 | }
128 | ```
129 |
130 | _Note:_ No changes on the back-end side are needed: [even though recommended](https://developers.google.com/cloud-messaging/android/android-migrate-fcm#update_server_endpoints), it isn't yet required and sending messages through GCM gateway should work just fine.
131 |
132 | _Note:_ The `FCM_VERSION` must be greater than or equal to 17.1.0 and less than or equal to 18.0.0.
133 |
134 | ### Common errors
135 |
136 | #### Multidex
137 |
138 | If you have an issue compiling the app and you're getting an error similar to this (`com.android.dex.DexException: Multiple dex files define`):
139 |
140 | ```
141 | UNEXPECTED TOP-LEVEL EXCEPTION:
142 | com.android.dex.DexException: Multiple dex files define Landroid/support/annotation/AnimRes;
143 | at com.android.dx.merge.DexMerger.readSortableTypes(DexMerger.java:596)
144 | at com.android.dx.merge.DexMerger.getSortedTypes(DexMerger.java:554)
145 | at com.android.dx.merge.DexMerger.mergeClassDefs(DexMerger.java:535)
146 | at com.android.dx.merge.DexMerger.mergeDexes(DexMerger.java:171)
147 | at com.android.dx.merge.DexMerger.merge(DexMerger.java:189)
148 | at com.android.dx.command.dexer.Main.mergeLibraryDexBuffers(Main.java:502)
149 | at com.android.dx.command.dexer.Main.runMonoDex(Main.java:334)
150 | at com.android.dx.command.dexer.Main.run(Main.java:277)
151 | at com.android.dx.command.dexer.Main.main(Main.java:245)
152 | at com.android.dx.command.Main.main(Main.java:106)
153 | ```
154 |
155 | Then at least one other plugin you have installed is using an outdated way to declare dependencies such as `android-support` or `play-services-gcm`.
156 | This causes gradle to fail, and you'll need to identify which plugin is causing it and request an update to the plugin author, so that it uses the proper way to declare dependencies for cordova.
157 | See [this for the reference on the cordova plugin specification](https://cordova.apache.org/docs/en/5.4.0/plugin_ref/spec.html#link-18), it'll be usefull to mention it when creating an issue or requesting that plugin to be updated.
158 |
159 | Common plugins to suffer from this outdated dependency management are plugins related to _facebook_, _google+_, _notifications_, _crosswalk_ and _google maps_.
160 |
161 | #### More than one library with package name 'com.google.android.gms'
162 |
163 | When some other packages include `cordova-google-play-services` as a dependency, such as is the case with the `cordova-admob` and `cordova-plugin-analytics` plugins, it is impossible to also add the `@havesource/cordova-plugin-push`, for the following error will rise during the build process:
164 |
165 | ```
166 | :processDebugResources FAILED
167 | FAILURE: Build failed with an exception.
168 |
169 | What went wrong: Execution failed for task ':processDebugResources'. > Error: more than one library with package name 'com.google.android.gms'
170 | ```
171 |
172 | Those plugins should be using gradle to include the Google Play Services package but instead they include the play services jar directly or via a plugin dependency. So all of that is bad news. These plugins should be updated to use gradle. Please raise issues on those plugins as the change is not hard to make.
173 |
174 | In fact there is a PR open to do just that appfeel/analytics-google#11 for cordova-plugin-analytics. You should bug the team at appfeel to merge that PR.
175 |
176 | Alternatively, switch to another plugin that provides the same functionality but uses gradle:
177 |
178 | [https://github.com/danwilson/google-analytics-plugin](https://github.com/danwilson/google-analytics-plugin)
179 | [https://github.com/cmackay/google-analytics-plugin](https://github.com/cmackay/google-analytics-plugin)
180 |
181 | ## Browser details
182 |
183 | For the time being, push support on the browser is not supported. The original plugin used the PhoneGap push server which may no longer be active.
184 |
185 | ### Browser Support
186 |
187 | Chrome 49+
188 | Firefox 46+
189 |
190 | ## iOS details
191 |
192 | ### System & Cordova Requirements
193 |
194 | **System:**
195 |
196 | - `Xcode`: `11.0` or greater.
197 | - `CocoaPods`: `1.8.0` or greater. Preferably `1.9.x`
198 | - `Ruby`: `2.0.0` or greater.
199 |
200 | **Cordova:**
201 |
202 | - `cordova-cli`: `10.0.0` or greater.
203 | - `cordova-ios`: `6.0.0` or greater. Preferably `6.1.x`
204 |
205 | ### Bitcode
206 |
207 | If you are running into a problem where the linker is complaining about bit code. For instance:
208 |
209 | ```log
210 | ld: '' does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. for architecture arm64 clang: error: linker command failed with exit code 1 (use -v to see invocation)
211 | ```
212 |
213 | You have two options. The first is to [disable bitcode as per this StackOverflow answer](http://stackoverflow.com/a/32466484/41679) or [upgrade to cordova-ios 6.0.0 or greater](https://cordova.apache.org/announcements/2020/06/01/cordova-ios-release-6.0.0.html).
214 |
215 | ```bash
216 | cordova platform rm ios
217 | cordova platform add ios@6.0.0
218 | ```
219 |
220 | ### CocoaPods
221 |
222 | To install CocoaPods, please follow the installation instructions [here](https://guides.cocoapods.org/using/getting-started). Since version `1.8.0` and greater, the pod repo no longer needs to be setup or fetched. Pods specs will be fetched directly from the **CocoaPods CDN**.
223 |
224 | If you are upgrading from an older version, it might be best to uninstall first the older version and remove the `~/.cocoapods/` directory.
225 |
226 | This plugin uses the [Firebase/Messaging](https://cocoapods.org/pods/Firebase) library.
227 |
228 | #### Common CocoaPod Installation issues
229 |
230 | If you are attempting to install this plugin and you run into this error:
231 |
232 | ```log
233 | Installing "@havesource/cordova-plugin-push" for ios
234 | Failed to install '@havesource/cordova-plugin-push':Error: pod: Command failed with exit code 1
235 | at ChildProcess.whenDone (/Users/smacdona/code/push151/platforms/ios/cordova/node_modules/cordova-common/src/superspawn.js:169:23)
236 | at emitTwo (events.js:87:13)
237 | at ChildProcess.emit (events.js:172:7)
238 | at maybeClose (internal/child_process.js:818:16)
239 | at Process.ChildProcess._handle.onexit (internal/child_process.js:211:5)
240 | Error: pod: Command failed with exit code 1
241 | ```
242 |
243 | Please try to add the plugin again, with the `--verbose` flag. The above error is generic and can actually be caused by a number of reasons. The `--verbose` flag should help display the exact cause of the install failure.
244 |
245 | One of the most common reason is that it is trying to fetch the podspec from the CocoaPods repo and the repo is out-of-date. It recommended to use CocoaPods CDN over the repo. If your using an older version of CocoaPods, it is recommend to upgrade with a fresh installation.
246 |
247 | With a fresh installations, you should have one repo source which can be checked with the `pod repo` command.
248 |
249 | ```log
250 | $ pod repo
251 |
252 | trunk
253 | - Type: CDN
254 | - URL: https://cdn.cocoapods.org/
255 | - Path: /Users/home/.cocoapods/repos/trunk
256 |
257 | 1 repo
258 | ```
259 |
260 | ##### Library not found for -lPods-Appname
261 |
262 | If you open the app in Xcode and you get an error like:
263 |
264 | ```log
265 | ld: library not found for -lPods-Appname
266 | clang: error: linker command failed with exit code 1
267 | ```
268 |
269 | Then you are opening the .xcodeproj file when you should be opening the .xcworkspace file.
270 |
271 | ##### Library not found for -lGoogleToolboxForMac
272 |
273 | Trying to build for iOS using the latest cocoapods (1.9.3) but failed with the following error (from terminal running cordova build ios):
274 |
275 | ```log
276 | ld: library not found for -lGoogleToolboxForMac
277 | ```
278 |
279 | Workarounds are to add the platform first and install the plugins later, or to manually run pod install on projectName/platforms/ios.
280 |
281 | Another workaround is to go to build phases in your project at Link Binary Libraries and add `libPods-PROJECTNAME.a` and `libGoogleToolboxForMac.a`
282 |
283 | ## Additional Resources
284 |
285 | The push plugin enables you to play sounds and display different icons during push (Android only). These additional resources need to be added to your projects `platforms` directory in order for them to be included into your final application binary.
286 |
287 | You can now use the `resource-file` tag to deliver the image and sound files to your application. For example if you wanted to include an extra image file for only your Android build you would add the `resource-file` tag to your android `platform` tag:
288 |
289 | ```xml
290 |
291 |
292 |
293 | ```
294 |
295 | or if you wanted to include a sound file for iOS:
296 |
297 | ```xml
298 |
299 |
300 |
301 | ```
302 |
--------------------------------------------------------------------------------
/docs/ISSUES.md:
--------------------------------------------------------------------------------
1 | # ISSUES
2 |
3 | - [ISSUES](#issues)
4 | - [Read the docs](#read-the-docs)
5 | - [Search the issues](#search-the-issues)
6 | - [Opening a new issue](#opening-a-new-issue)
7 | - [Provide details](#provide-details)
8 | - [An example issue](#an-example-issue)
9 | - [Voting on an issue](#voting-on-an-issue)
10 |
11 | The following tips are for users of this plugin who want to get help.
12 |
13 | ## Read the docs
14 |
15 | I'll be the first to admit that the docs are not perfect but start here at the [README](https://github.com/phonegap/phonegap-plugin-push/blob/master/README.md) to see if your problem is documented. If it isn't continue on but if you do get an answer then consider sending a documentation pull request.
16 |
17 | ## Search the issues
18 |
19 | Your question may have already been answered. Make sure you search at least the repo's [issues](https://github.com/phonegap/phonegap-plugin-push/issues) before you create a new one.
20 |
21 | ## Opening a new issue
22 |
23 | If you have searched the issues and haven't found anything that resembles your problem then follow these guidelines in creating a new issue.
24 |
25 | ### Provide details
26 |
27 | Give as many details as possible. Issues without many details will be more difficult to debug and will encounter delays.
28 |
29 | Select a concise, informative title for the issue. Here's a good article on writing [subject lines](https://www.nngroup.com/articles/microcontent-how-to-write-headlines-page-titles-and-subject-lines/).
30 |
31 | Include the following at a minimum:
32 | _ what version number of plugin are you using?
33 | - which platform and version you are testing on? iOS 9.0, Android 5.0, etc.
34 | - a detailed description of your problem. Including:
35 | - steps to reproduce
36 | - expected result
37 | - actual result
38 | - how you are sending the push data to the device, including an example payload
39 |
40 | You may also want to include:
41 | - some sample code that illustrates the problem.
42 | - logs taken while the problem was reproduced.
43 | - screenshots!
44 |
45 | If the code or logs are huge, let's say over 20 lines please think about using a web service like [Gist](https://gist.github.com/) or [Pastebin](http://pastebin.com/).
46 |
47 | ### An example issue
48 |
49 | **The wrong way**
50 |
51 | *Title:* This plugin does not work for me
52 |
53 | *Details:* Please fix quickly as my business depends on this plugin.
54 |
55 | **The right way**
56 |
57 | *Title:* Registration event never received on Samsung Galaxy S running Android 2.3
58 |
59 | *Details:* I'm using version 1.5.2 of this plugin on my Samsung Galaxy S5 device which runs Android 4.4. I never receiving the `registration` event in my application when I expect it to return a value I can send to my push service.
60 |
61 | You can see the code I'm using in this gist: [https://gist.github.com/macdonst/191f74ac75b6802c047d](https://gist.github.com/macdonst/191f74ac75b6802c047d)
62 |
63 | And an output of the logs when trying to run the app are in this gist: [https://gist.github.com/macdonst/47549150c299080c455c](https://gist.github.com/macdonst/47549150c299080c455c)
64 |
65 | Please point me in the right direction.
66 |
67 | *Response:*
68 |
69 | Thanks for the detailed logs and example code by looking them over I'm sure of what your problem is. If you look at line [334](https://gist.github.com/macdonst/47549150c299080c455c#file-logcat-txt-L334) of your logcat you will see that it complains that:
70 |
71 | ```
72 | I/chromium(11669): [INFO:CONSOLE(54)] "Uncaught ReferenceError: PushNotification is not defined", source: file:///android_asset/www/js/index.js (54)
73 | ```
74 |
75 | This leads me to line [4](https://gist.github.com/macdonst/191f74ac75b6802c047d#file-app-js-L4) of your code where you are initializing push before you get the `deviceready` event. Like all Cordova API's you have to wait until you receive the `deviceready` event before you initialize Push.
76 |
77 | Check out [https://github.com/phonegap/phonegap-plugin-push/blob/20f489a90cf519f962fd957700f92115f142594b/example/www/js/index.js](https://github.com/phonegap/phonegap-plugin-push/blob/20f489a90cf519f962fd957700f92115f142594b/example/www/js/index.js) for an example of how to wait for `deviceready`.
78 |
79 | ## Voting on an issue
80 |
81 | Did you know you can vote on issues in the phonegap-plugin-push repository? If you install the [ZenHub](https://chrome.google.com/webstore/detail/zenhub-for-github/ogcgkffhplmphkaahpmffcafajaocjbd) Chrome Extension you will be able to +1 issues to indicate how popular they are to the community. It's a way better way for the contributors to keep track of important issues.
82 |
--------------------------------------------------------------------------------
/docs/PHONEGAP_BUILD.md:
--------------------------------------------------------------------------------
1 | # Cloud Build Services
2 |
3 | - [Cloud Build Services](#cloud-build-services)
4 | - [PhoneGap Build Support](#phonegap-build-support)
5 | - [Including the plugin](#including-the-plugin)
6 | - [Adding resources](#adding-resources)
7 | - [Android](#android)
8 | - [IntelXDK Support](#intelxdk-support)
9 | - [Ionic Cloud Build](#ionic-cloud-build)
10 |
11 | ## PhoneGap Build Support
12 |
13 | ### Including the plugin
14 |
15 | Including this plugin in a project that is built by PhoneGap Build is as easy as adding (replacing `123456789` with your own, that is):
16 |
17 | ```xml
18 |
19 |
20 |
21 |
22 | ```
23 |
24 | into your app's `config.xml` file. PhoneGap Build will pick up the latest version of phonegap-plugin-push published on npm. If you want to specify a particular version of the plugin you can add the `spec` attribute to the `plugin` tag.
25 |
26 | ```xml
27 |
28 |
29 | ```
30 |
31 | Note: version 1.3.0 of this plugin begins to use Gradle to install the Android Support Framework. Support for Gradle has recently been added to PhoneGap Build. Please read [this blog post](http://phonegap.com/blog/2015/09/28/android-using-gradle/) for more information.
32 |
33 | ### Adding resources
34 |
35 | Because PhoneGap Build does not support running hooks if you want to include custom image or sounds you will need to use a _beta_ feature to include these files.
36 |
37 | #### Android
38 |
39 | To add custom files, create a directory called `locales/android/` in the root of your PGB application zip / repo, and place your resource files there. The contents will be copied into the Android `res/` directory, and any nested sub-directory structures will persist. Here's an example of how these files will be compiled into your APK:
40 |
41 | ```
42 | /locales/android/drawables/logo.png --> /res/drawables/logo.png
43 | /locales/android/raw/beep.mp3 --> /res/raw/beep.mp3
44 | /locales/android/values-fr/strings.xml --> /res/values-fr/strings.xml
45 | ```
46 |
47 | Existing directories will be merged, but at this time any individual files you include will overwrite their target if it exists.
48 |
49 | ## IntelXDK Support
50 |
51 | 1. Do pre-requisite setup on [the iOS Provisioning Portal](https://developer.apple.com/account/ios/identifier/bundle). Refer to [this guide](https://www.raywenderlich.com/123862/push-notifications-tutorial) or Apple docs for detailed steps.
52 | a. make a new App ID (you'll need to set this in Intel XDK config later)
53 | b. enable push notifications
54 | c. iOS Distribution cert: create (if needed), download and install (if needed), export as a .p12 (set and remember the password as you'll need this to import into Intel XDK later)
55 | **NOTE**: Intel XDK does not support Development certs, so you MUST use your Distribution cert.
56 | d. Make an AdHoc Provisioning Profile using your App ID from (1a) and your cert from (1c). Make sure your test device is enabled. Download and save with a name you will recognize. (you'll need to add this to your Intel XDK project later)
57 | e. make a push cert, download it, install it, export it to .p12, convert it to .pem (this is for the push server that will send the notification - you'll need this later to test your Intel XDK app)
58 |
59 | 2. In Intel XDK, make a new Cordova CLI 5.4.1 project using the HTML5+Cordova Blank Template, then replace the contents of www with [the contents of www from the PhoneGap Push Template](https://github.com/phonegap/phonegap-template-push/tree/master/template_src/www).
60 |
61 | 3. Delete www/config.xml (optional? Intel XDK does not use config.xml)
62 |
63 | 4. Intel XDK Project Settings
64 | a. set the iOS App ID to match the App ID from (1a)
65 | b. (if needed) import your .p12 from (1c) - Account Settings->Developer Certificates->iOS, then select it as the Developer Certificate for the project
66 | c. Select "adhoc" for Provisioning Profile
67 | d. copy your provisioning profile from (1d) into www/, then click "Ad hoc Provisioning Profile" and select the profile
68 | e. Add the latest version of phonegap-plugin-push as a "Third-Party Plugin" (at time of testing this was 1.6.4)
69 | f. **After the plugin is added, you will need to edit plugins/phonegap-plugin-push/plugin.xml**. Intel XDK 3357 does not support plugins with gradle references, so the gradle reference must be commented out (this will prevent this version of the plugin from working for Android but is needed for the iOS build to succeed):
70 | ``
71 | A future version of Intel XDK will support gradle references.
72 |
73 | 5. XDK Build Tab
74 | a. Enable iOS build (click the checkmark)
75 | b. Unlock your iOS certificate (click the lock and enter the password from (1c))
76 | c. click Start Builds
77 | d. once the build completes, download and install the app
78 |
79 | 6. connect test device by USB and open XCode Devices window (probably could also use Safari Web Inspector + Cordova Console plugin) - start the app and a log message should be written into the console that looks like "Push Plugin register success: \"
80 |
81 | 7. exit the app (close with home button then swipe it off the multitask view)
82 |
83 | 8. The angle brackets and everything between (from (5)) is the device token - copy it into a text file
84 |
85 | 9. Add the device token to your server and send a push notification
86 | a. I used [phonegap-plugin-push/example/server/pushAPNS.rb](https://github.com/phonegap/phonegap-plugin-push/blob/master/example/server/pushAPNS.rb) for this
87 | b. APNS.host = 'gateway.push.apple.com'
88 | c. APNS.pem = 'PGPush926Prod.pem' #path to your pem file from (1e)
89 | d. device_token = '\' #the device token from (7)
90 | e. edit the alert message and badge number
91 | f. you probably need to install the required gem (`gem install pushmeup`)
92 | g. send the notification (`ruby pushAPNS.rb`)
93 |
94 | 10. See notification on device!
95 |
96 | ## Ionic Cloud Build
97 |
98 | Users have reported issues with Ionic Cloud Build. Apparently there are some differences in the way variables are handled. If your app has an issue where the `PushNotification` object can't be found try the following.
99 |
100 | 1. Remove the inclusion of `phonegap-plugin-push` from config.xml. That is delete lines that look like this:
101 |
102 | ```xml
103 |
104 |
105 |
106 | ```
107 |
108 | 2. Add the following lines into `package.json` in the `cordovaPlugins` array.
109 |
110 | ```json
111 | {
112 | "variables": {
113 | "SENDER_ID": "xxx"
114 | },
115 | "locator": "phonegap-plugin-push"
116 | }
117 | ```
118 |
--------------------------------------------------------------------------------
/docs/PLATFORM_SUPPORT.md:
--------------------------------------------------------------------------------
1 | # Supported Platforms
2 |
3 | ## Version 5.x.x
4 |
5 | - Cordova CLI (10.0.0 or higher)
6 | - Cordova Android (12.0.0 or higher)
7 | - Browser
8 | - Cordova iOS (6.0.0 or higher)
9 |
10 | ## Version 4.x.x
11 |
12 | - Cordova CLI (10.0.0 or higher)
13 | - Cordova Android (12.0.0 or higher)
14 | - Browser
15 | - Cordova iOS (6.0.0 or higher)
16 |
17 | ## Version 3.x.x
18 |
19 | - Cordova CLI (10.0.0 or higher)
20 | - Cordova Android (9.0.0 or higher)
21 | - Browser
22 | - Cordova iOS (6.0.0 or higher)
23 | - Windows Universal (not Windows Phone 8)
24 |
25 | ## Version 2.x.x
26 |
27 | - Cordova CLI (7.1.0 or higher)
28 | - Cordova Android (7.1.0 or higher)
29 | - Browser
30 | - Cordova iOS (4.5.0 or higher)
31 | - Windows Universal (not Windows Phone 8)
32 |
33 | ## Version 1.9.x
34 |
35 | - Cordova CLI (6.4.0 or higher)
36 | - Cordova Android (6.0.0 or higher)
37 | - Browser
38 | - Cordova iOS (4.3.0 or higher)
39 | - Windows Universal (not Windows Phone 8)
40 |
41 | ## Version 1.8.x
42 |
43 | - Cordova CLI (3.6.3 or higher)
44 | - Cordova Android (4.0.0 or higher)
45 | - Browser
46 | - Cordova iOS (4.1.0 or higher)
47 | - Windows Universal (not Windows Phone 8)
48 |
--------------------------------------------------------------------------------
/docs/TYPESCRIPT.md:
--------------------------------------------------------------------------------
1 | # Typescript definitions
2 |
3 | For those of you who use Typescript, we're glad to say that we provide the complete definition file along with our package.
4 |
5 | ## Example usage
6 |
7 | All objects will be understood as having a defined type, including init options and eventHandler parameters.
8 | All available attributes and properties will have autocomplete support and type checkings.
9 |
10 | ```typescript
11 | import '@havesource/cordova-plugin-push/types';
12 |
13 | const push = PushNotification.init({
14 | android: {
15 | },
16 | ios: {
17 | alert: "true",
18 | badge: true,
19 | sound: 'false'
20 | }
21 | });
22 |
23 | push.on('registration', (data) => {
24 | console.log(data.registrationId);
25 | });
26 |
27 | push.on('notification', (data) => {
28 | console.log(data.message);
29 | console.log(data.title);
30 | console.log(data.count);
31 | console.log(data.sound);
32 | console.log(data.image);
33 | console.log(data.additionalData);
34 | });
35 |
36 | push.on('error', (e) => {
37 | console.log(e.message);
38 | });
39 | ```
40 |
41 | If you have custom attributes being sent from the server on the payload, you can define them on a custom interface extending the standard one:
42 |
43 | ```typescript
44 | module my.custom {
45 | export interface NotificationEventResponse extends PhonegapPluginPush.NotificationEventResponse {
46 | additionalData: NotificationEventAdditionalData;
47 | }
48 |
49 | export interface NotificationEventAdditionalData extends PhonegapPluginPush.NotificationEventAdditionalData {
50 | bacon?: boolean;
51 | }
52 | }
53 |
54 | push.on('notification', (data: my.custom.NotificationEventResponse) => {
55 | //standard attributes
56 | console.log(data.message);
57 | console.log(data.title);
58 | console.log(data.count);
59 | console.log(data.sound);
60 | console.log(data.image);
61 | console.log(data.additionalData);
62 |
63 | //custom attributes
64 | console.log(data.additionalData.bacon);
65 | });
66 | ```
67 |
--------------------------------------------------------------------------------
/example/server/pushADM.js:
--------------------------------------------------------------------------------
1 | // Client ID and Client Secret received from ADM
2 | // For more info, see: https://developer.amazon.com/public/apis/engage/device-messaging/tech-docs/02-obtaining-adm-credentials
3 | const CLIENT_ID = 'amzn1.application-oa2-client.8e838f6629554e26ae3f43a6c663cd60';
4 | const CLIENT_SECRET = '0af96083320f5d70dc4f358cc783ac65a22e78b297ba257df34d5f723f24543f';
5 |
6 | // Registration ID, received on device after it registers with ADM server
7 | const REGISTRATION_IDS = ['amzn1.adm-registration.v2.Y29tLmFtYXpvbi5EZXZpY2VNZXNzYWdpbmcuUmVnaXN0cmF0aW9uSWRFbmNyeXB0aW9uS2V5ITEhOE9rZ2h5TXlhVEFFczg2ejNWL3JMcmhTa255Uk5BclhBbE1XMFZzcnU1aFF6cTlvdU5FbVEwclZmdk5oTFBVRXVDN1luQlRSNnRVRUViREdQSlBvSzRNaXVRRUlyUy9NYWZCYS9VWTJUaGZwb3ZVTHhlRTM0MGhvampBK01hVktsMEhxakdmQStOSXRjUXBTQUhNU1NlVVVUVkFreVRhRTBCYktaQ2ZkUFdqSmIwcHgzRDhMQnllVXdxQ2EwdHNXRmFVNklYL0U4UXovcHg0K3Jjb25VbVFLRUVVOFVabnh4RDhjYmtIcHd1ZThiekorbGtzR2taMG95cC92Y3NtZytrcTRPNjhXUUpiZEk3QzFvQThBRTFWWXM2NHkyMjdYVGV5RlhhMWNHS0k9IW5GNEJMSXNleC9xbWpHSU52NnczY0E9PQ'];
8 |
9 | // Message payload to be sent to client
10 | const payload = {
11 | data: {
12 | message: 'PushPlugin works!!',
13 | sound: 'beep.wav',
14 | url: 'http://www.amazon.com',
15 | timeStamp: new Date().toISOString(),
16 | foo: 'baz'
17 | },
18 | consolidationKey: 'my app',
19 | expiresAfter: 3600
20 | };
21 |
22 | //* ********************************
23 |
24 | const https = require('https');
25 | const querystring = require('querystring');
26 |
27 | if (CLIENT_ID === '' || CLIENT_SECRET === '' || REGISTRATION_IDS.length === 0) {
28 | console.log('******************\nSetup Error: \nYou need to edit the pushADM.js file and enter your ADM credentials and device registration ID(s).\n******************');
29 | process.exit(1);
30 | }
31 |
32 | // Get access token from server, and use it to post message to device
33 | getAccessToken(function (accessToken) {
34 | for (let i = 0; i < REGISTRATION_IDS.length; i++) {
35 | const registrationID = REGISTRATION_IDS[i];
36 |
37 | postMessage(accessToken, registrationID, payload);
38 | }
39 | });
40 |
41 | // Query OAuth server for access token
42 | // For more info, see: https://developer.amazon.com/public/apis/engage/device-messaging/tech-docs/05-requesting-an-access-token
43 |
44 | function getAccessToken (callback) {
45 | console.log('Requesting access token from server...');
46 |
47 | const credentials = {
48 | scope: 'messaging:push',
49 | grant_type: 'client_credentials',
50 | client_id: CLIENT_ID,
51 | client_secret: CLIENT_SECRET
52 | };
53 |
54 | const post_data = querystring.stringify(credentials);
55 |
56 | const post_options = {
57 | host: 'api.amazon.com',
58 | port: '443',
59 | path: '/auth/O2/token',
60 | method: 'POST',
61 | headers: {
62 | 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
63 | }
64 | };
65 |
66 | const req = https.request(post_options, function (res) {
67 | let data = '';
68 |
69 | res.on('data', function (chunk) {
70 | data += chunk;
71 | });
72 |
73 | res.on('end', function () {
74 | console.log('\nAccess token response:', data);
75 | const accessToken = JSON.parse(data).access_token;
76 | callback(accessToken);
77 | });
78 | });
79 |
80 | req.on('error', function (e) {
81 | console.log('\nProblem with access token request: ', e.message);
82 | });
83 |
84 | req.write(post_data);
85 | req.end();
86 | }
87 |
88 | // Post message payload to ADM server
89 | // For more info, see: https://developer.amazon.com/public/apis/engage/device-messaging/tech-docs/06-sending-a-message
90 |
91 | function postMessage (accessToken, registrationID, payload) {
92 | if (accessToken === undefined || registrationID === undefined || payload === undefined) {
93 | return;
94 | }
95 |
96 | console.log('\nSending message...');
97 |
98 | const post_data = JSON.stringify(payload);
99 |
100 | const api_path = '/messaging/registrations/' + registrationID + '/messages';
101 |
102 | const post_options = {
103 | host: 'api.amazon.com',
104 | port: '443',
105 | path: api_path,
106 | method: 'POST',
107 | headers: {
108 | Authorization: 'Bearer ' + accessToken,
109 | 'X-Amzn-Type-Version': 'com.amazon.device.messaging.ADMMessage@1.0',
110 | 'X-Amzn-Accept-Type': 'com.amazon.device.messaging.ADMSendResult@1.0',
111 | 'Content-Type': 'application/json',
112 | Accept: 'application/json'
113 | }
114 | };
115 |
116 | const req = https.request(post_options, function (res) {
117 | let data = '';
118 |
119 | res.on('data', function (chunk) {
120 | data += chunk;
121 | });
122 |
123 | res.on('end', function () {
124 | console.log('\nSend message response: ', data);
125 | });
126 | });
127 |
128 | req.on('error', function (e) {
129 | console.log('\nProblem with send message request: ', e.message);
130 | });
131 |
132 | req.write(post_data);
133 | req.end();
134 | }
135 |
--------------------------------------------------------------------------------
/example/server/pushAPNS.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'pushmeup'
3 |
4 |
5 | APNS.host = 'gateway.sandbox.push.apple.com'
6 | APNS.port = 2195
7 | APNS.pem = ''
8 | APNS.pass = ''
9 |
10 | device_token = ''
11 | # APNS.send_notification(device_token, 'Hello iPhone!' )
12 | APNS.send_notification(device_token, :alert => 'PushPlugin works!!', :badge => 1, :sound => 'beep.wav')
13 |
--------------------------------------------------------------------------------
/example/server/pushAzure.js:
--------------------------------------------------------------------------------
1 | /*
2 | ** Sample Table Definition - this supports the Azure Mobile Apps
3 | ** TodoItem product
4 | ** https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-cordova-get-started/
5 | */
6 | const azureMobileApps = require('azure-mobile-apps');
7 | const logger = require('azure-mobile-apps/src/logger');
8 |
9 | // Create a new table definition
10 | const table = azureMobileApps.table();
11 |
12 | // In the TodoItem product, sends a push notification
13 | // when a new item inserted into the table.
14 | table.insert(function (context) {
15 | // For more information about the Notification Hubs JavaScript SDK,
16 | // see http://aka.ms/nodejshubs
17 | logger.info('Running TodoItem.insert');
18 |
19 | // Define the push notification template payload.
20 | // Requires template specified in the client app. See
21 | // https://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-cordova-get-started-push/
22 | const payload = '{"message": "' + context.item.text + '" }';
23 |
24 | // Execute the insert. The insert returns the results as a Promise,
25 | // Do the push as a post-execute action within the promise flow.
26 | return context.execute()
27 | .then(function (results) {
28 | // Only do the push if configured
29 | if (context.push) {
30 | context.push.send(null, payload, function (error) {
31 | if (error) {
32 | logger.error('Error while sending push notification: ', error);
33 | } else {
34 | logger.info('Push notification sent successfully!');
35 | }
36 | });
37 | }
38 | // Don't forget to return the results from the context.execute()
39 | return results;
40 | })
41 | .catch(function (error) {
42 | logger.error('Error while running context.execute: ', error);
43 | });
44 | });
45 |
46 | module.exports = table;
47 |
--------------------------------------------------------------------------------
/example/server/pushGCM.rb:
--------------------------------------------------------------------------------
1 | require 'rubygems'
2 | require 'pushmeup'
3 | GCM.host = 'https://android.googleapis.com/gcm/send'
4 | GCM.format = :json
5 | GCM.key = "API_KEY_GOES_HERE"
6 | destination = ["REGISTRATION_ID_GOES_HERE"]
7 | data = {:message => "PhoneGap Build rocks!", :msgcnt => "1", :soundname => "beep.wav"}
8 |
9 | GCM.send_notification( destination, data)
10 |
--------------------------------------------------------------------------------
/hooks/browser/updateManifest.js:
--------------------------------------------------------------------------------
1 | module.exports = function (context) {
2 | console.log('Updating manifest.json with push properties…');
3 | var path = require('path');
4 | var fs = require('fs');
5 |
6 | var platformProjPath = path.join(
7 | context.opts.projectRoot,
8 | 'platforms/browser'
9 | );
10 |
11 | if (!fs.existsSync(platformProjPath)) {
12 | platformProjPath = context.opts.projectRoot;
13 | }
14 |
15 | var platformManifestJson = path.join(platformProjPath, 'www/manifest.json');
16 |
17 | if (!fs.existsSync(platformManifestJson)) {
18 | return;
19 | }
20 |
21 | fs.readFile(platformManifestJson, 'utf8', function (err, platformJson) {
22 | if (err) throw err; // we'll not consider error handling for now
23 | var platformManifest = JSON.parse(platformJson);
24 |
25 | var pluginManifestPath = path.join(
26 | context.opts.projectRoot,
27 | 'plugins/@havesource/cordova-plugin-push/src/browser/manifest.json'
28 | );
29 |
30 | fs.readFile(pluginManifestPath, 'utf8', function (err, pluginJson) {
31 | if (err) throw err; // we'll not consider error handling for now
32 | var pluginManifest = JSON.parse(pluginJson);
33 |
34 | platformManifest.gcm_sender_id = pluginManifest.gcm_sender_id;
35 |
36 | fs.writeFile(
37 | platformManifestJson,
38 | JSON.stringify(platformManifest),
39 | function (err) {
40 | if (err) {
41 | return console.log(err);
42 | }
43 |
44 | console.log('Manifest updated with push sender ID');
45 | }
46 | );
47 | });
48 | });
49 | };
50 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@havesource/cordova-plugin-push",
3 | "version": "5.0.6-dev.0",
4 | "description": "Register and receive push notifications.",
5 | "scripts": {
6 | "build": "babel src/js --out-dir www",
7 | "build:watch": "nodemon -w ./src/js -e js -x npm run build",
8 | "lint": "eslint .",
9 | "lint:fix": "npm run lint -- --fix",
10 | "jasmine": "jasmine --config=spec/unit.json",
11 | "precommit-msg": "echo 'Pre-commit checks...' && exit 0",
12 | "test": "npm run build && npm run lint && npm run jasmine"
13 | },
14 | "repository": "github:havesource/cordova-plugin-push",
15 | "keywords": [
16 | "ecosystem:cordova",
17 | "cordova-ios",
18 | "cordova-android",
19 | "cordova-browser"
20 | ],
21 | "author": "Erisu",
22 | "license": "MIT",
23 | "bugs": "https://github.com/havesource/cordova-plugin-push/issues",
24 | "homepage": "https://github.com/havesource/cordova-plugin-push#readme",
25 | "devDependencies": {
26 | "@babel/cli": "^7.22.10",
27 | "@babel/core": "^7.22.11",
28 | "@babel/eslint-parser": "^7.22.11",
29 | "@babel/preset-env": "^7.22.14",
30 | "@cordova/eslint-config": "^5.0.0",
31 | "babel-plugin-add-header-comment": "^1.0.3",
32 | "fs-extra": "^10.1.0",
33 | "jasmine": "^5.1.0",
34 | "nodemon": "^3.0.1",
35 | "nopt": "^5.0.0",
36 | "xml2js": "^0.6.2"
37 | },
38 | "engines": {
39 | "cordovaDependencies": {
40 | "5.0.0": {
41 | "cordova": ">=10.0.0",
42 | "cordova-android": ">=12.0.0",
43 | "cordova-ios": ">=6.0.0"
44 | },
45 | "4.0.0": {
46 | "cordova": ">=10.0.0",
47 | "cordova-android": ">=12.0.0",
48 | "cordova-ios": ">=6.0.0"
49 | },
50 | "3.0.0": {
51 | "cordova": ">=10.0.0",
52 | "cordova-android": ">=9.0.0",
53 | "cordova-ios": ">=6.0.0"
54 | },
55 | "2.0.0": {
56 | "cordova": ">=10.0.0",
57 | "cordova-android": ">=8.0.0",
58 | "cordova-ios": ">=6.0.0"
59 | },
60 | "1.0.0": {
61 | "cordova": ">=10.0.0",
62 | "cordova-android": ">=8.0.0",
63 | "cordova-ios": ">=5.1.1"
64 | }
65 | }
66 | },
67 | "types": "./types/index.d.ts",
68 | "cordova": {
69 | "id": "@havesource/cordova-plugin-push",
70 | "platforms": [
71 | "ios",
72 | "android",
73 | "browser"
74 | ]
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | Cordova Push Plugin
7 | Enable receiving push notifications on Android and iOS devices. Android uses Firebase Cloud Messaging. iOS uses Apple APNS Notifications.
8 |
9 | MIT
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 | remote-notification
106 |
107 |
108 |
109 |
110 | development
111 |
112 |
113 |
114 | production
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/spec/helper/cordova.js:
--------------------------------------------------------------------------------
1 | /* global cordova:true */
2 |
3 | /*!
4 | * Module dependencies.
5 | */
6 |
7 | /**
8 | * cordova.js for node.
9 | *
10 | * Think of this as cordova-node, which would be simliar to cordova-android
11 | * or cordova-browser. The purpose of this module is to enable testing
12 | * of a plugin's JavaScript interface.
13 | *
14 | * When this module is first required, it will insert a global cordova
15 | * instance, which can hijack cordova-specific commands within the pluin's
16 | * implementation.
17 | *
18 | * Remember to require this module before the plugin that you want to test.
19 | *
20 | * Example:
21 | *
22 | * var cordova = require('./helper/cordova'),
23 | * myPlugin = require('../www/myPlugin');
24 | */
25 |
26 | module.exports = global.cordova = cordova = {
27 |
28 | /**
29 | * cordova.require Mock.
30 | *
31 | * Hijacks all cordova.requires. By default, it returns an empty function.
32 | * You can define your own implementation of each required module before
33 | * or after it has been required.
34 | *
35 | * See `cordova.required` to learn how to add your own module implemtnation.
36 | */
37 |
38 | require: function (moduleId) {
39 | // define a default function if it doesn't exist
40 | if (!cordova.required[moduleId]) {
41 | cordova.required[moduleId] = function () {};
42 | }
43 | // create a new module mapping between the module Id and cordova.required.
44 | return new ModuleMap(moduleId);
45 | },
46 |
47 | /**
48 | * Cordova module implementations.
49 | *
50 | * A key-value hash, where the key is the module such as 'cordova/exec'
51 | * and the value is the function or object returned.
52 | *
53 | * For example:
54 | *
55 | * var exec = require('cordova/exec');
56 | *
57 | * Will map to:
58 | *
59 | * cordova.required['cordova/exec'];
60 | */
61 |
62 | required: {
63 | // populated at runtime
64 | }
65 | };
66 |
67 | /**
68 | * Module Mapper.
69 | *
70 | * Returns a function that when executed will lookup the implementation
71 | * in cordova.required[id].
72 | *
73 | * @param {String} moduleId is the module name/path, such as 'cordova/exec'
74 | * @return {Function}.
75 | */
76 |
77 | function ModuleMap (moduleId) {
78 | return function () {
79 | // lookup and execute the module's mock implementation, passing
80 | // in any parameters that were provided.
81 | return cordova.required[moduleId].apply(this, arguments);
82 | };
83 | }
84 |
--------------------------------------------------------------------------------
/spec/index.spec.js:
--------------------------------------------------------------------------------
1 | /* globals require */
2 |
3 | /*!
4 | * Module dependencies.
5 | */
6 |
7 | const cordova = require('./helper/cordova');
8 | const PushNotification = require('../www/push');
9 | let execSpy;
10 | let execWin;
11 | let options;
12 |
13 | /*!
14 | * Specification.
15 | */
16 |
17 | describe('phonegap-plugin-push', () => {
18 | beforeEach(() => {
19 | options = { android: {}, ios: {} };
20 | execWin = jasmine.createSpy();
21 | execSpy = spyOn(cordova.required, 'cordova/exec').and.callFake(execWin);
22 | });
23 |
24 | describe('PushNotification', () => {
25 | it('should exist', () => {
26 | expect(PushNotification).toBeDefined();
27 | expect(typeof PushNotification === 'object').toBe(true);
28 | });
29 |
30 | it('should contain a init function', () => {
31 | expect(PushNotification.init).toBeDefined();
32 | expect(typeof PushNotification.init === 'function').toBe(true);
33 | });
34 |
35 | it('should contain a hasPermission function', () => {
36 | expect(PushNotification.hasPermission).toBeDefined();
37 | expect(typeof PushNotification.hasPermission === 'function').toBe(true);
38 | });
39 |
40 | it('should contain a createChannel function', () => {
41 | expect(PushNotification.createChannel).toBeDefined();
42 | expect(typeof PushNotification.createChannel === 'function').toBe(true);
43 | });
44 |
45 | it('should contain a deleteChannel function', () => {
46 | expect(PushNotification.deleteChannel).toBeDefined();
47 | expect(typeof PushNotification.deleteChannel === 'function').toBe(true);
48 | });
49 |
50 | it('should contain a listChannels function', () => {
51 | expect(PushNotification.listChannels).toBeDefined();
52 | expect(typeof PushNotification.listChannels === 'function').toBe(true);
53 | });
54 |
55 | it('should contain a unregister function', () => {
56 | const push = PushNotification.init({});
57 | expect(push.unregister).toBeDefined();
58 | expect(typeof push.unregister === 'function').toBe(true);
59 | });
60 |
61 | it('should contain a getApplicationIconBadgeNumber function', () => {
62 | const push = PushNotification.init({});
63 | expect(push.getApplicationIconBadgeNumber).toBeDefined();
64 | expect(typeof push.getApplicationIconBadgeNumber === 'function').toBe(true);
65 | });
66 |
67 | it('should contain a setApplicationIconBadgeNumber function', () => {
68 | const push = PushNotification.init({});
69 | expect(push.setApplicationIconBadgeNumber).toBeDefined();
70 | expect(typeof push.setApplicationIconBadgeNumber === 'function').toBe(true);
71 | });
72 |
73 | it('should contain a clearAllNotifications function', () => {
74 | const push = PushNotification.init({});
75 | expect(push.clearAllNotifications).toBeDefined();
76 | expect(typeof push.clearAllNotifications === 'function').toBe(true);
77 | });
78 |
79 | it('should contain a clearNotification function', () => {
80 | const push = PushNotification.init({});
81 | expect(push.clearNotification).toBeDefined();
82 | expect(typeof push.clearNotification === 'function').toBe(true);
83 | });
84 |
85 | it('should contain a subscribe function', () => {
86 | const push = PushNotification.init({});
87 | expect(push.subscribe).toBeDefined();
88 | expect(typeof push.subscribe === 'function').toBe(true);
89 | });
90 |
91 | it('should contain a unsubscribe function', () => {
92 | const push = PushNotification.init({});
93 | expect(push.unsubscribe).toBeDefined();
94 | expect(typeof push.unsubscribe === 'function').toBe(true);
95 | });
96 | });
97 |
98 | describe('PushNotification instance', () => {
99 | describe('cordova.exec', () => {
100 | it('should call cordova.exec on next process tick', (done) => {
101 | PushNotification.init(options);
102 | setTimeout(() => {
103 | expect(execSpy).toHaveBeenCalledWith(
104 | jasmine.any(Function),
105 | jasmine.any(Function),
106 | 'PushNotification',
107 | 'init',
108 | jasmine.any(Object)
109 | );
110 | done();
111 | }, 100);
112 | });
113 | });
114 |
115 | describe('on "registration" event', () => {
116 | it('should be emitted with an argument', (done) => {
117 | execSpy.and.callFake((win, fail, service, id, args) => {
118 | win({ registrationId: 1 });
119 | });
120 | const push = PushNotification.init(options);
121 | push.on('registration', (data) => {
122 | expect(data.registrationId).toEqual(1);
123 | done();
124 | });
125 | });
126 | });
127 |
128 | describe('on "notification" event', () => {
129 | beforeEach(() => {
130 | execSpy.and.callFake((win, fail, service, id, args) => {
131 | win({
132 | message: 'Message',
133 | title: 'Title',
134 | count: 1,
135 | sound: 'beep',
136 | image: 'Image',
137 | additionalData: {}
138 | });
139 | });
140 | });
141 |
142 | it('should be emitted on success', (done) => {
143 | const push = PushNotification.init(options);
144 | push.on('notification', (data) => {
145 | done();
146 | });
147 | });
148 |
149 | it('should provide the data.message argument', (done) => {
150 | const push = PushNotification.init(options);
151 | push.on('notification', (data) => {
152 | expect(data.message).toEqual('Message');
153 | done();
154 | });
155 | });
156 |
157 | it('should provide the data.title argument', (done) => {
158 | const push = PushNotification.init(options);
159 | push.on('notification', (data) => {
160 | expect(data.title).toEqual('Title');
161 | done();
162 | });
163 | });
164 |
165 | it('should provide the data.count argument', (done) => {
166 | const push = PushNotification.init(options);
167 | push.on('notification', (data) => {
168 | expect(data.count).toEqual(1);
169 | done();
170 | });
171 | });
172 |
173 | it('should provide the data.sound argument', (done) => {
174 | const push = PushNotification.init(options);
175 | push.on('notification', (data) => {
176 | expect(data.sound).toEqual('beep');
177 | done();
178 | });
179 | });
180 |
181 | it('should provide the data.image argument', (done) => {
182 | const push = PushNotification.init(options);
183 | push.on('notification', (data) => {
184 | expect(data.image).toEqual('Image');
185 | done();
186 | });
187 | });
188 |
189 | it('should provide the data.additionalData argument', (done) => {
190 | const push = PushNotification.init(options);
191 | push.on('notification', (data) => {
192 | expect(data.additionalData).toEqual({});
193 | done();
194 | });
195 | });
196 | });
197 |
198 | describe('on "error" event', () => {
199 | it('should be emitted with an Error', (done) => {
200 | execSpy.and.callFake((win, fail, service, id, args) => {
201 | fail('something went wrong');
202 | });
203 | const push = PushNotification.init(options);
204 | push.on('error', (e) => {
205 | expect(e).toEqual(jasmine.any(Error));
206 | expect(e.message).toEqual('something went wrong');
207 | done();
208 | });
209 | });
210 | });
211 |
212 | describe('off "notification" event', () => {
213 | it('should exist and be registered a callback handle', (done) => {
214 | const push = PushNotification.init(options);
215 | const eventHandler = () => {};
216 |
217 | push.on('notification', eventHandler);
218 |
219 | push.off('notification', eventHandler);
220 |
221 | expect(push.handlers.notification.indexOf(eventHandler)).toEqual(-1);
222 | done();
223 | });
224 | });
225 |
226 | describe('off "registration" event', () => {
227 | it('should exist and be registered a callback handle', (done) => {
228 | const push = PushNotification.init(options);
229 | const eventHandler = () => {};
230 |
231 | push.on('registration', eventHandler);
232 |
233 | push.off('registration', eventHandler);
234 |
235 | expect(push.handlers.registration.indexOf(eventHandler)).toEqual(-1);
236 | done();
237 | });
238 | });
239 |
240 | describe('off "error" event', () => {
241 | it('should exist and be registered a callback handle', (done) => {
242 | const push = PushNotification.init(options);
243 | const eventHandler = () => {};
244 |
245 | push.on('error', eventHandler);
246 | push.off('error', eventHandler);
247 |
248 | expect(push.handlers.error.indexOf(eventHandler)).toEqual(-1);
249 | done();
250 | });
251 | });
252 |
253 | describe('unregister method', () => {
254 | it('should clear "registration" event handlers', (done) => {
255 | const push = PushNotification.init(options);
256 | const eventHandler = () => {};
257 |
258 | expect(push.handlers.registration.length).toEqual(0);
259 |
260 | push.on('registration', eventHandler);
261 |
262 | expect(push.handlers.registration.length).toEqual(1);
263 | expect(push.handlers.registration.indexOf(eventHandler)).toBeGreaterThan(-1);
264 |
265 | execSpy.and.callFake((win, fail, service, id, args) => {
266 | win();
267 | });
268 | push.unregister(() => {
269 | expect(push.handlers.registration.length).toEqual(0);
270 | expect(push.handlers.registration.indexOf(eventHandler)).toEqual(-1);
271 | done();
272 | });
273 | });
274 |
275 | it('should clear "notification" event handlers', (done) => {
276 | const push = PushNotification.init(options);
277 | const eventHandler = () => {};
278 |
279 | expect(push.handlers.notification.length).toEqual(0);
280 |
281 | push.on('notification', eventHandler);
282 |
283 | expect(push.handlers.notification.length).toEqual(1);
284 | expect(push.handlers.notification.indexOf(eventHandler)).toBeGreaterThan(-1);
285 |
286 | execSpy.and.callFake((win, fail, service, id, args) => {
287 | win();
288 | });
289 | push.unregister(() => {
290 | expect(push.handlers.notification.length).toEqual(0);
291 | expect(push.handlers.notification.indexOf(eventHandler)).toEqual(-1);
292 | done();
293 | });
294 | });
295 |
296 | it('should clear "error" event handlers', (done) => {
297 | const push = PushNotification.init(options);
298 | const eventHandler = () => {};
299 |
300 | expect(push.handlers.error.length).toEqual(0);
301 |
302 | push.on('error', eventHandler);
303 |
304 | expect(push.handlers.error.length).toEqual(1);
305 | expect(push.handlers.error.indexOf(eventHandler)).toBeGreaterThan(-1);
306 |
307 | execSpy.and.callFake((win, fail, service, id, args) => {
308 | win();
309 | });
310 | push.unregister(() => {
311 | expect(push.handlers.error.length).toEqual(0);
312 | expect(push.handlers.error.indexOf(eventHandler)).toEqual(-1);
313 | done();
314 | });
315 | });
316 | });
317 |
318 | describe('unregister topics method', () => {
319 | it('should not clear "registration" event handlers', (done) => {
320 | const push = PushNotification.init(options);
321 | const eventHandler = () => {};
322 |
323 | expect(push.handlers.registration.length).toEqual(0);
324 |
325 | push.on('registration', eventHandler);
326 |
327 | expect(push.handlers.registration.length).toEqual(1);
328 | expect(push.handlers.registration.indexOf(eventHandler)).toBeGreaterThan(-1);
329 |
330 | execSpy.and.callFake((win, fail, service, id, args) => {
331 | win();
332 | });
333 | push.unregister(
334 | () => {
335 | expect(push.handlers.registration.length).toEqual(1);
336 | expect(push.handlers.registration.indexOf(eventHandler)).toBeGreaterThan(-1);
337 | done();
338 | },
339 | () => {},
340 | ['foo', 'bar']
341 | );
342 | });
343 |
344 | it('should not clear "notification" event handlers', (done) => {
345 | const push = PushNotification.init(options);
346 | const eventHandler = () => {};
347 |
348 | expect(push.handlers.notification.length).toEqual(0);
349 |
350 | push.on('notification', eventHandler);
351 |
352 | expect(push.handlers.notification.length).toEqual(1);
353 | expect(push.handlers.notification.indexOf(eventHandler)).toBeGreaterThan(-1);
354 |
355 | execSpy.and.callFake((win, fail, service, id, args) => {
356 | win();
357 | });
358 | push.unregister(
359 | () => {
360 | expect(push.handlers.notification.length).toEqual(1);
361 | expect(push.handlers.notification.indexOf(eventHandler)).toBeGreaterThan(-1);
362 | done();
363 | },
364 | () => {},
365 | ['foo', 'bar']
366 | );
367 | });
368 |
369 | it('should not clear "error" event handlers', (done) => {
370 | const push = PushNotification.init(options);
371 | const eventHandler = () => {};
372 |
373 | expect(push.handlers.error.length).toEqual(0);
374 |
375 | push.on('error', eventHandler);
376 |
377 | expect(push.handlers.error.length).toEqual(1);
378 | expect(push.handlers.error.indexOf(eventHandler)).toBeGreaterThan(-1);
379 |
380 | execSpy.and.callFake((win, fail, service, id, args) => {
381 | win();
382 | });
383 | push.unregister(
384 | () => {
385 | expect(push.handlers.error.length).toEqual(1);
386 | expect(push.handlers.error.indexOf(eventHandler)).toBeGreaterThan(-1);
387 | done();
388 | },
389 | () => {},
390 | ['foo', 'bar']
391 | );
392 | });
393 | });
394 |
395 | describe('subscribe topic method', () => {
396 | describe('cordova.exec', () => {
397 | it('should call cordova.exec on next process tick', (done) => {
398 | const push = PushNotification.init(options);
399 | push.subscribe('foo', () => {}, () => {});
400 | setTimeout(() => {
401 | expect(execSpy).toHaveBeenCalledWith(
402 | jasmine.any(Function),
403 | jasmine.any(Function),
404 | 'PushNotification',
405 | 'subscribe',
406 | jasmine.any(Object)
407 | );
408 | done();
409 | }, 100);
410 | });
411 | });
412 | });
413 |
414 | describe('unsubscribe topic method', () => {
415 | describe('cordova.exec', () => {
416 | it('should call cordova.exec on next process tick', (done) => {
417 | const push = PushNotification.init(options);
418 | push.unsubscribe('foo', () => {}, () => {});
419 | setTimeout(() => {
420 | expect(execSpy).toHaveBeenCalledWith(
421 | jasmine.any(Function),
422 | jasmine.any(Function),
423 | 'PushNotification',
424 | 'unsubscribe',
425 | jasmine.any(Object)
426 | );
427 | done();
428 | }, 100);
429 | });
430 | });
431 | });
432 |
433 | describe('clear notification method', () => {
434 | describe('cordova.exec', () => {
435 | it('should call cordova.exec on next process tick using number argument', (done) => {
436 | const push = PushNotification.init(options);
437 | push.clearNotification(() => {}, () => {}, 145);
438 | setTimeout(() => {
439 | expect(execSpy).toHaveBeenCalledWith(
440 | jasmine.any(Function),
441 | jasmine.any(Function),
442 | 'PushNotification',
443 | 'clearNotification',
444 | [145]
445 | );
446 | done();
447 | }, 100);
448 | });
449 |
450 | it('should call cordova.exec on next process tick using string argument', (done) => {
451 | const push = PushNotification.init(options);
452 | push.clearNotification(() => {}, () => {}, '145');
453 | setTimeout(() => {
454 | expect(execSpy).toHaveBeenCalledWith(
455 | jasmine.any(Function),
456 | jasmine.any(Function),
457 | 'PushNotification',
458 | 'clearNotification',
459 | [145]
460 | );
461 | done();
462 | }, 100);
463 | });
464 | });
465 | });
466 | });
467 | });
468 |
--------------------------------------------------------------------------------
/spec/unit.json:
--------------------------------------------------------------------------------
1 | {
2 | "spec_dir": "spec",
3 | "spec_files": [
4 | "**/*[sS]pec.js"
5 | ],
6 | "stopSpecOnExpectationFailure": false,
7 | "random": false
8 | }
--------------------------------------------------------------------------------
/src/android/com/adobe/phonegap/push/BackgroundActionButtonHandler.kt:
--------------------------------------------------------------------------------
1 | package com.adobe.phonegap.push
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.NotificationManager
5 | import android.content.BroadcastReceiver
6 | import android.content.Context
7 | import android.content.Intent
8 | import android.util.Log
9 | import androidx.core.app.RemoteInput
10 |
11 | /**
12 | * Background Action Button Handler
13 | */
14 | @Suppress("HardCodedStringLiteral")
15 | @SuppressLint("LongLogTag", "LogConditional")
16 | class BackgroundActionButtonHandler : BroadcastReceiver() {
17 | companion object {
18 | private const val TAG: String = "${PushPlugin.PREFIX_TAG} (BackgroundActionButtonHandler)"
19 | }
20 |
21 | /**
22 | * @param context
23 | * @param intent
24 | */
25 | override fun onReceive(context: Context, intent: Intent) {
26 | val notId = intent.getIntExtra(PushConstants.NOT_ID, 0)
27 | Log.d(TAG, "Not ID: $notId")
28 |
29 | val notificationManager =
30 | context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
31 | notificationManager.cancel(FCMService.getAppName(context), notId)
32 |
33 | intent.extras?.let { extras ->
34 | Log.d(TAG, "Intent Extras: $extras")
35 | extras.getBundle(PushConstants.PUSH_BUNDLE)?.apply {
36 | putBoolean(PushConstants.FOREGROUND, false)
37 | putBoolean(PushConstants.COLDSTART, false)
38 | putString(
39 | PushConstants.ACTION_CALLBACK,
40 | extras.getString(PushConstants.CALLBACK)
41 | )
42 |
43 | RemoteInput.getResultsFromIntent(intent)?.let { remoteInputResults ->
44 | val results = remoteInputResults.getCharSequence(PushConstants.INLINE_REPLY).toString()
45 | Log.d(TAG, "Inline Reply: $results")
46 |
47 | putString(PushConstants.INLINE_REPLY, results)
48 | }
49 | }
50 |
51 | PushPlugin.sendExtras(extras)
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/android/com/adobe/phonegap/push/BackgroundHandlerActivity.kt:
--------------------------------------------------------------------------------
1 | package com.adobe.phonegap.push
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.Activity
5 | import android.app.NotificationManager
6 | import android.content.Intent
7 | import android.os.Bundle
8 | import android.util.Log
9 | import androidx.core.app.RemoteInput
10 |
11 | /**
12 | * Background Handler Activity
13 | */
14 | @Suppress("HardCodedStringLiteral")
15 | @SuppressLint("LongLogTag", "LogConditional")
16 | class BackgroundHandlerActivity : Activity() {
17 | companion object {
18 | private const val TAG: String = "${PushPlugin.PREFIX_TAG} (BackgroundHandlerActivity)"
19 | }
20 |
21 | /**
22 | * This activity will be started if the user touches a notification that we own.
23 | * We send it's data off to the push plugin for processing.
24 | * If needed, we boot up the main activity to kickstart the application.
25 | *
26 | * @param savedInstanceState
27 | *
28 | * @see android.app.Activity#onCreate(android.os.Bundle)
29 | */
30 | public override fun onCreate(savedInstanceState: Bundle?) {
31 | super.onCreate(savedInstanceState)
32 |
33 | Log.v(TAG, "onCreate")
34 |
35 | intent.extras?.let { extras ->
36 | val notId = extras.getInt(PushConstants.NOT_ID, 0)
37 | val callback = extras.getString(PushConstants.CALLBACK)
38 | val startOnBackground = extras.getBoolean(PushConstants.START_IN_BACKGROUND, false)
39 | val dismissed = extras.getBoolean(PushConstants.DISMISSED, false)
40 |
41 | Log.d(TAG, "Not ID: $notId")
42 | Log.d(TAG, "Callback: $callback")
43 | Log.d(TAG, "Start In Background: $startOnBackground")
44 | Log.d(TAG, "Dismissed: $dismissed")
45 |
46 | FCMService().setNotification(notId, "")
47 |
48 | if (!startOnBackground) {
49 | val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
50 | notificationManager.cancel(FCMService.getAppName(this), notId)
51 | }
52 |
53 | processPushBundle()
54 | finish()
55 |
56 | if (!dismissed) {
57 | // Tap the notification, app should start.
58 | if (!PushPlugin.isActive) {
59 | forceMainActivityReload(false)
60 | } else {
61 | forceMainActivityReload(true)
62 | }
63 | }
64 | }
65 | }
66 |
67 | private fun processPushBundle() {
68 | /*
69 | * Takes the pushBundle extras from the intent,
70 | * and sends it through to the PushPlugin for processing.
71 | */
72 | intent.extras?.let { extras ->
73 | var originalExtras = extras.getBundle(PushConstants.PUSH_BUNDLE)
74 |
75 | if (originalExtras == null) {
76 | originalExtras = extras
77 | originalExtras.remove(PushConstants.FROM)
78 | originalExtras.remove(PushConstants.MESSAGE_ID)
79 | originalExtras.remove(PushConstants.COLLAPSE_KEY)
80 | }
81 |
82 | originalExtras.putBoolean(PushConstants.FOREGROUND, false)
83 | originalExtras.putBoolean(PushConstants.COLDSTART, !PushPlugin.isActive)
84 | originalExtras.putBoolean(PushConstants.DISMISSED, extras.getBoolean(PushConstants.DISMISSED))
85 | originalExtras.putString(
86 | PushConstants.ACTION_CALLBACK,
87 | extras.getString(PushConstants.CALLBACK)
88 | )
89 | originalExtras.remove(PushConstants.NO_CACHE)
90 |
91 | RemoteInput.getResultsFromIntent(intent)?.apply {
92 | val reply = getCharSequence(PushConstants.INLINE_REPLY).toString()
93 | Log.d(TAG, "Inline Reply: $reply")
94 |
95 | originalExtras.putString(PushConstants.INLINE_REPLY, reply)
96 | }
97 |
98 | PushPlugin.sendExtras(originalExtras)
99 | }
100 | }
101 |
102 | private fun forceMainActivityReload(startOnBackground: Boolean) {
103 | /*
104 | * Forces the main activity to re-launch if it's unloaded.
105 | */
106 | val launchIntent = packageManager.getLaunchIntentForPackage(applicationContext.packageName)
107 |
108 | intent.extras?.let { extras ->
109 | launchIntent?.apply {
110 | extras.getBundle(PushConstants.PUSH_BUNDLE)?.let { originalExtras ->
111 | putExtras(originalExtras)
112 | }
113 |
114 | addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
115 | addFlags(Intent.FLAG_FROM_BACKGROUND)
116 | putExtra(PushConstants.START_IN_BACKGROUND, startOnBackground)
117 | }
118 | }
119 |
120 | startActivity(launchIntent)
121 | }
122 |
123 | /**
124 | *
125 | */
126 | override fun onResume() {
127 | super.onResume()
128 |
129 | val notificationManager = this.getSystemService(NOTIFICATION_SERVICE) as NotificationManager
130 | notificationManager.cancelAll()
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/src/android/com/adobe/phonegap/push/PushConstants.kt:
--------------------------------------------------------------------------------
1 | package com.adobe.phonegap.push
2 |
3 | /**
4 | * Push Constants
5 | *
6 | * @todo add docs about each constant.
7 | */
8 | @Suppress("HardCodedStringLiteral")
9 | object PushConstants {
10 | const val COM_ADOBE_PHONEGAP_PUSH: String = "com.adobe.phonegap.push"
11 | const val REGISTRATION_ID: String = "registrationId"
12 | const val REGISTRATION_TYPE: String = "registrationType"
13 | const val FOREGROUND: String = "foreground"
14 | const val TITLE: String = "title"
15 | const val NOT_ID: String = "notId"
16 | const val PUSH_BUNDLE: String = "pushBundle"
17 | const val ICON: String = "icon"
18 | const val ICON_COLOR: String = "iconColor"
19 | const val SOUND: String = "sound"
20 | const val SOUND_DEFAULT: String = "default"
21 | const val SOUND_RINGTONE: String = "ringtone"
22 | const val VIBRATE: String = "vibrate"
23 | const val ACTIONS: String = "actions"
24 | const val CALLBACK: String = "callback"
25 | const val ACTION_CALLBACK: String = "actionCallback"
26 | const val DRAWABLE: String = "drawable"
27 | const val MSGCNT: String = "msgcnt"
28 | const val VIBRATION_PATTERN: String = "vibrationPattern"
29 | const val STYLE: String = "style"
30 | const val SUMMARY_TEXT: String = "summaryText"
31 | const val PICTURE: String = "picture"
32 | const val GCM_N: String = "gcm.n."
33 | const val GCM_NOTIFICATION: String = "gcm.notification"
34 | const val GCM_NOTIFICATION_BODY: String = "gcm.notification.body"
35 | const val UA_PREFIX: String = "com.urbanairship.push"
36 | const val PARSE_COM_DATA: String = "data"
37 | const val ALERT: String = "alert"
38 | const val MESSAGE: String = "message"
39 | const val BODY: String = "body"
40 | const val SOUNDNAME: String = "soundname"
41 | const val COLOR: String = "color"
42 | const val LED_COLOR: String = "ledColor"
43 | const val PRIORITY: String = "priority"
44 | const val IMAGE: String = "image"
45 | const val STYLE_INBOX: String = "inbox"
46 | const val STYLE_PICTURE: String = "picture"
47 | const val STYLE_TEXT: String = "text"
48 | const val BADGE: String = "badge"
49 | const val INITIALIZE: String = "init"
50 | const val SUBSCRIBE: String = "subscribe"
51 | const val UNSUBSCRIBE: String = "unsubscribe"
52 | const val UNREGISTER: String = "unregister"
53 | const val EXIT: String = "exit"
54 | const val FINISH: String = "finish"
55 | const val HAS_PERMISSION: String = "hasPermission"
56 | const val ANDROID: String = "android"
57 | const val SENDER_ID: String = "senderID"
58 | const val CLEAR_BADGE: String = "clearBadge"
59 | const val CLEAR_NOTIFICATIONS: String = "clearNotifications"
60 | const val COLDSTART: String = "coldstart"
61 | const val ADDITIONAL_DATA: String = "additionalData"
62 | const val COUNT: String = "count"
63 | const val FROM: String = "from"
64 | const val COLLAPSE_KEY: String = "collapse_key"
65 | const val FORCE_SHOW: String = "forceShow"
66 | const val FCM: String = "FCM"
67 | const val CONTENT_AVAILABLE: String = "content-available"
68 | const val TOPICS: String = "topics"
69 | const val SET_APPLICATION_ICON_BADGE_NUMBER: String = "setApplicationIconBadgeNumber"
70 | const val GET_APPLICATION_ICON_BADGE_NUMBER: String = "getApplicationIconBadgeNumber"
71 | const val CLEAR_ALL_NOTIFICATIONS: String = "clearAllNotifications"
72 | const val VISIBILITY: String = "visibility"
73 | const val INLINE_REPLY: String = "inlineReply"
74 | const val INLINE_REPLY_LABEL: String = "replyLabel"
75 | const val LOC_KEY: String = "locKey"
76 | const val LOC_DATA: String = "locData"
77 | const val TWILIO_BODY: String = "twi_body"
78 | const val TWILIO_TITLE: String = "twi_title"
79 | const val TWILIO_SOUND: String = "twi_sound"
80 | const val AWS_PINPOINT_BODY: String = "pinpoint.notification.body"
81 | const val AWS_PINPOINT_PICTURE: String = "pinpoint.notification.imageUrl"
82 | const val AWS_PINPOINT_PREFIX: String = "pinpoint.notification"
83 | const val MP_MESSAGE: String = "mp_message"
84 | const val START_IN_BACKGROUND: String = "cdvStartInBackground"
85 | const val FORCE_START: String = "force-start"
86 | const val MESSAGE_KEY: String = "messageKey"
87 | const val TITLE_KEY: String = "titleKey"
88 | const val NO_CACHE: String = "no-cache"
89 | const val DISMISSED: String = "dismissed"
90 | const val IMAGE_TYPE: String = "image-type"
91 | const val IMAGE_TYPE_SQUARE: String = "square"
92 | const val IMAGE_TYPE_CIRCLE: String = "circle"
93 | const val SUBJECT: String = "subject"
94 | const val GOOGLE_APP_ID: String = "google_app_id"
95 | const val GCM_DEFAULT_SENDER_ID: String = "gcm_defaultSenderId"
96 | const val PUSH_DISMISSED: String = "push_dismissed"
97 | const val DEFAULT_CHANNEL_ID: String = "PushPluginChannel"
98 | const val CHANNELS: String = "channels"
99 | const val CHANNEL_ID: String = "id"
100 | const val CHANNEL_DESCRIPTION: String = "description"
101 | const val CHANNEL_IMPORTANCE: String = "importance"
102 | const val CHANNEL_LIGHT_COLOR: String = "lightColor"
103 | const val CHANNEL_VIBRATION: String = "vibration"
104 | const val ANDROID_CHANNEL_ID: String = "android_channel_id"
105 | const val CHANNEL_STATE: String = "state"
106 | const val CREATE_CHANNEL: String = "createChannel"
107 | const val DELETE_CHANNEL: String = "deleteChannel"
108 | const val ONGOING: String = "ongoing"
109 | const val LIST_CHANNELS: String = "listChannels"
110 | const val CLEAR_NOTIFICATION: String = "clearNotification"
111 | const val MESSAGE_ID: String = "google.message_id"
112 | const val IS_ENABLED: String = "isEnabled"
113 | }
114 |
--------------------------------------------------------------------------------
/src/android/com/adobe/phonegap/push/PushDismissedHandler.kt:
--------------------------------------------------------------------------------
1 | package com.adobe.phonegap.push
2 |
3 | import android.annotation.SuppressLint
4 | import android.content.BroadcastReceiver
5 | import android.content.Context
6 | import android.content.Intent
7 | import android.util.Log
8 |
9 | /**
10 | *
11 | */
12 | @Suppress("HardCodedStringLiteral")
13 | @SuppressLint("LongLogTag")
14 | class PushDismissedHandler : BroadcastReceiver() {
15 | companion object {
16 | private const val TAG: String = "${PushPlugin.PREFIX_TAG} (PushDismissedHandler)"
17 | }
18 |
19 | /**
20 | * @param context
21 | * @param intent
22 | */
23 | override fun onReceive(context: Context, intent: Intent) {
24 | if (intent.action == PushConstants.PUSH_DISMISSED) {
25 | val notID = intent.getIntExtra(PushConstants.NOT_ID, 0)
26 | Log.d(TAG, "not id = $notID")
27 | FCMService().setNotification(notID, "")
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/android/com/adobe/phonegap/push/PushHandlerActivity.kt:
--------------------------------------------------------------------------------
1 | package com.adobe.phonegap.push
2 |
3 | import android.annotation.SuppressLint
4 | import android.app.Activity
5 | import android.app.NotificationManager
6 | import android.content.Intent
7 | import android.os.Build
8 | import android.os.Bundle
9 | import android.util.Log
10 | import androidx.core.app.RemoteInput
11 |
12 | /**
13 | * Push Handler Activity
14 | */
15 | @Suppress("HardCodedStringLiteral")
16 | @SuppressLint("LongLogTag", "LogConditional")
17 | class PushHandlerActivity : Activity() {
18 | companion object {
19 | private const val TAG: String = "${PushPlugin.PREFIX_TAG} (PushHandlerActivity)"
20 | }
21 |
22 | /**
23 | * this activity will be started if the user touches a notification that we own.
24 | * We send it's data off to the push plugin for processing.
25 | * If needed, we boot up the main activity to kickstart the application.
26 | *
27 | * @param savedInstanceState
28 | *
29 | * @see android.app.Activity#onCreate(android.os.Bundle)
30 | */
31 | public override fun onCreate(savedInstanceState: Bundle?) {
32 | super.onCreate(savedInstanceState)
33 | Log.v(TAG, "onCreate")
34 |
35 | intent.extras?.let { extras ->
36 | val notId = extras.getInt(PushConstants.NOT_ID, 0)
37 | val callback = extras.getString(PushConstants.CALLBACK)
38 | var foreground = extras.getBoolean(PushConstants.FOREGROUND, true)
39 | val startOnBackground = extras.getBoolean(PushConstants.START_IN_BACKGROUND, false)
40 | val dismissed = extras.getBoolean(PushConstants.DISMISSED, false)
41 |
42 | FCMService().setNotification(notId, "")
43 |
44 | if (!startOnBackground) {
45 | val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
46 | notificationManager.cancel(FCMService.getAppName(this), notId)
47 | }
48 |
49 | val notHaveInlineReply = processPushBundle()
50 |
51 | if (notHaveInlineReply && Build.VERSION.SDK_INT < Build.VERSION_CODES.N && !startOnBackground) {
52 | foreground = true
53 | }
54 |
55 | Log.d(TAG, "Not ID: $notId")
56 | Log.d(TAG, "Callback: $callback")
57 | Log.d(TAG, "Foreground: $foreground")
58 | Log.d(TAG, "Start On Background: $startOnBackground")
59 | Log.d(TAG, "Dismissed: $dismissed")
60 |
61 | finish()
62 |
63 | if (!dismissed) {
64 | Log.d(TAG, "Is Push Plugin Active: ${PushPlugin.isActive}")
65 |
66 | if (!PushPlugin.isActive && foreground && notHaveInlineReply) {
67 | Log.d(TAG, "Force Main Activity Reload: Start on Background = False")
68 | forceMainActivityReload(false)
69 | } else if (startOnBackground) {
70 | Log.d(TAG, "Force Main Activity Reload: Start on Background = True")
71 | forceMainActivityReload(true)
72 | } else {
73 | Log.d(TAG, "Don't Want Main Activity")
74 | }
75 | }
76 | }
77 | }
78 |
79 | private fun processPushBundle(): Boolean {
80 | /*
81 | * Takes the pushBundle extras from the intent,
82 | * and sends it through to the PushPlugin for processing.
83 | */
84 | return intent.extras?.let { extras ->
85 | var notHaveInlineReply = true
86 |
87 | extras.getBundle(PushConstants.PUSH_BUNDLE)?.apply {
88 | putBoolean(PushConstants.FOREGROUND, false)
89 | putBoolean(PushConstants.COLDSTART, !PushPlugin.isActive)
90 | putBoolean(PushConstants.DISMISSED, extras.getBoolean(PushConstants.DISMISSED))
91 | putString(
92 | PushConstants.ACTION_CALLBACK,
93 | extras.getString(PushConstants.CALLBACK)
94 | )
95 | remove(PushConstants.NO_CACHE)
96 |
97 | RemoteInput.getResultsFromIntent(intent)?.let { results ->
98 | val reply = results.getCharSequence(PushConstants.INLINE_REPLY).toString()
99 | Log.d(TAG, "Inline Reply: $reply")
100 |
101 | putString(PushConstants.INLINE_REPLY, reply)
102 | notHaveInlineReply = false
103 | }
104 |
105 | PushPlugin.sendExtras(this)
106 | }
107 |
108 | return notHaveInlineReply
109 | } ?: true
110 | }
111 |
112 | private fun forceMainActivityReload(startOnBackground: Boolean) {
113 | /*
114 | * Forces the main activity to re-launch if it's unloaded.
115 | */
116 | val launchIntent = packageManager.getLaunchIntentForPackage(applicationContext.packageName)
117 |
118 | intent.extras?.let { extras ->
119 | launchIntent?.apply {
120 | extras.getBundle(PushConstants.PUSH_BUNDLE)?.apply {
121 | putExtras(this)
122 | }
123 |
124 | addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
125 | addFlags(Intent.FLAG_FROM_BACKGROUND)
126 | putExtra(PushConstants.START_IN_BACKGROUND, startOnBackground)
127 | }
128 | }
129 |
130 | startActivity(launchIntent)
131 | }
132 |
133 | /**
134 | * On Resuming of Activity
135 | */
136 | override fun onResume() {
137 | super.onResume()
138 |
139 | val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
140 | notificationManager.cancelAll()
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/browser/ServiceWorker.js:
--------------------------------------------------------------------------------
1 | var messageChannel;
2 |
3 | self.addEventListener('install', function (event) {
4 | self.skipWaiting();
5 | });
6 |
7 | self.addEventListener('push', function (event) {
8 | // parse incoming message
9 | var obj = {};
10 | var pushData = {
11 | image: 'https://avatars1.githubusercontent.com/u/60365?v=3&s=200',
12 | additionalData: {}
13 | };
14 | if (event.data) {
15 | obj = event.data.json();
16 | }
17 |
18 | console.log(obj);
19 |
20 | // convert to push plugin API
21 | for (var key in obj) {
22 | if (key === 'title') {
23 | pushData.title = obj[key];
24 | } else if (key === 'message' || key === 'body') {
25 | pushData.message = obj[key];
26 | } else if (key === 'count' || key === 'msgcnt' || key === 'badge') {
27 | pushData.count = obj[key];
28 | } else if (key === 'sound' || key === 'soundname') {
29 | pushData.sound = obj[key];
30 | } else if (key === 'image') {
31 | pushData.image = obj[key];
32 | } else {
33 | pushData.additionalData[key] = obj[key];
34 | }
35 | }
36 |
37 | event.waitUntil(
38 | self.registration.showNotification(pushData.title, {
39 | body: pushData.message,
40 | icon: pushData.image,
41 | tag: 'simple-push-demo-notification-tag'
42 | })
43 | );
44 |
45 | messageChannel.ports[0].postMessage(pushData);
46 | });
47 |
48 | self.addEventListener('message', function (event) {
49 | messageChannel = event;
50 | });
51 |
--------------------------------------------------------------------------------
/src/browser/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Push Demo",
3 | "gcm_sender_id": "996231231186"
4 | }
5 |
--------------------------------------------------------------------------------
/src/ios/AppDelegate+PushPlugin.h:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate+PushPlugin.h
3 | //
4 | // Created by Robert Easterday on 10/26/12.
5 | //
6 |
7 | #import "AppDelegate.h"
8 |
9 | @import UserNotifications;
10 |
11 | @interface AppDelegate (PushPlugin)
12 |
13 | - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken;
14 | - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error;
15 | - (void)application:(UIApplication *)application
16 | didReceiveRemoteNotification:(NSDictionary *)userInfo
17 | fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler;
18 |
19 | @end
20 |
--------------------------------------------------------------------------------
/src/ios/AppDelegate+PushPlugin.m:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate+PushPlugin.m
3 | //
4 | // Created by Robert Easterday on 10/26/12.
5 | //
6 |
7 | #import "AppDelegate+PushPlugin.h"
8 | #import "PushPlugin.h"
9 | #import "PushPluginConstants.h"
10 | #import
11 |
12 | @implementation AppDelegate (PushPlugin)
13 |
14 | // its dangerous to override a method from within a category.
15 | // Instead we will use method swizzling. we set this up in the load call.
16 | + (void)load {
17 | static dispatch_once_t onceToken;
18 | dispatch_once(&onceToken, ^{
19 | Class class = [self class];
20 |
21 | SEL originalSelector = @selector(init);
22 | SEL swizzledSelector = @selector(pushPluginSwizzledInit);
23 |
24 | Method original = class_getInstanceMethod(class, originalSelector);
25 | Method swizzled = class_getInstanceMethod(class, swizzledSelector);
26 |
27 | BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzled), method_getTypeEncoding(swizzled));
28 |
29 | if (didAddMethod) {
30 | class_replaceMethod(class, swizzledSelector, method_getImplementation(original), method_getTypeEncoding(original));
31 | } else {
32 | method_exchangeImplementations(original, swizzled);
33 | }
34 | });
35 | }
36 |
37 | - (AppDelegate *)pushPluginSwizzledInit {
38 | UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter];
39 | center.delegate = self;
40 | // This actually calls the original init method over in AppDelegate. Equivilent to calling super
41 | // on an overrided method, this is not recursive, although it appears that way. neat huh?
42 | return [self pushPluginSwizzledInit];
43 | }
44 |
45 | - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
46 | [NSNotificationCenter.defaultCenter postNotificationName:PluginDidRegisterForRemoteNotificationsWithDeviceToken object:deviceToken];
47 | }
48 |
49 | - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
50 | [NSNotificationCenter.defaultCenter postNotificationName:PluginDidFailToRegisterForRemoteNotificationsWithError object:error];
51 | }
52 |
53 | - (void)application:(UIApplication *)application
54 | didReceiveRemoteNotification:(NSDictionary *)userInfo
55 | fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
56 | NSDictionary *notificationInfo = @{@"userInfo" : userInfo, @"completionHandler" : completionHandler};
57 | [NSNotificationCenter.defaultCenter postNotificationName:PluginDidReceiveRemoteNotification object:nil userInfo:notificationInfo];
58 | }
59 |
60 | - (void)userNotificationCenter:(UNUserNotificationCenter *)center
61 | willPresentNotification:(UNNotification *)notification
62 | withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
63 | NSDictionary *notificationInfo = @{@"notification" : notification, @"completionHandler" : completionHandler};
64 | [NSNotificationCenter.defaultCenter postNotificationName:PluginWillPresentNotification object:nil userInfo:notificationInfo];
65 | }
66 |
67 | - (void)userNotificationCenter:(UNUserNotificationCenter *)center
68 | didReceiveNotificationResponse:(UNNotificationResponse *)response
69 | withCompletionHandler:(void (^)(void))completionHandler {
70 | NSDictionary *notificationInfo = @{@"response" : response, @"completionHandler" : completionHandler};
71 | [NSNotificationCenter.defaultCenter postNotificationName:PluginDidReceiveNotificationResponse object:nil userInfo:notificationInfo];
72 | }
73 |
74 | @end
75 |
--------------------------------------------------------------------------------
/src/ios/PushPlugin.h:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2009-2011 Urban Airship Inc. All rights reserved.
3 |
4 | Redistribution and use in source and binary forms, with or without
5 | modification, are permitted provided that the following conditions are met:
6 |
7 | 1. Redistributions of source code must retain the above copyright notice, this
8 | list of conditions and the following disclaimer.
9 |
10 | 2. Redistributions in binaryform must reproduce the above copyright notice,
11 | this list of conditions and the following disclaimer in the documentation
12 | and/or other materials provided withthe distribution.
13 |
14 | THIS SOFTWARE IS PROVIDED BY THE URBAN AIRSHIP INC``AS IS'' AND ANY EXPRESS OR
15 | IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
16 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
17 | EVENT SHALL URBAN AIRSHIP INC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
18 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
19 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
20 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
22 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
23 | ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 | */
25 |
26 | @import Foundation;
27 | @import UserNotifications;
28 | #import
29 | #import
30 | #import
31 |
32 | @interface PushPlugin : CDVPlugin
33 |
34 | @property (nonatomic, copy) NSString *callbackId;
35 |
36 | - (void)init:(CDVInvokedUrlCommand*)command;
37 | - (void)unregister:(CDVInvokedUrlCommand*)command;
38 | - (void)subscribe:(CDVInvokedUrlCommand*)command;
39 | - (void)unsubscribe:(CDVInvokedUrlCommand*)command;
40 | - (void)clearNotification:(CDVInvokedUrlCommand*)command;
41 |
42 | // VoIP Features
43 | - (void)pushRegistry:(PKPushRegistry *)registry didUpdatePushCredentials:(PKPushCredentials *)credentials forType:(NSString *)type;
44 | - (void)pushRegistry:(PKPushRegistry *)registry didReceiveIncomingPushWithPayload:(PKPushPayload *)payload forType:(NSString *)type;
45 |
46 | @end
47 |
--------------------------------------------------------------------------------
/src/ios/PushPluginConstants.h:
--------------------------------------------------------------------------------
1 | //
2 | // PushPluginConstants.h
3 | // App
4 | //
5 | // Created by Erisu on 2024/11/15.
6 | //
7 |
8 | #import
9 |
10 | extern NSString * const PluginDidRegisterForRemoteNotificationsWithDeviceToken;
11 | extern NSString * const PluginDidFailToRegisterForRemoteNotificationsWithError;
12 | extern NSString * const PluginDidReceiveRemoteNotification;
13 | extern NSString * const PluginWillPresentNotification;
14 | extern NSString * const PluginDidReceiveNotificationResponse;
15 |
--------------------------------------------------------------------------------
/src/ios/PushPluginConstants.m:
--------------------------------------------------------------------------------
1 | //
2 | // PushPluginConstants.m
3 | // App
4 | //
5 | // Created by Erisu on 2024/11/15.
6 | //
7 |
8 | #import "PushPluginConstants.h"
9 |
10 | NSString * const PluginDidRegisterForRemoteNotificationsWithDeviceToken = @"CordovaPluginPush_DidRegisterForRemoteNotificationsWithDeviceToken";
11 | NSString * const PluginDidFailToRegisterForRemoteNotificationsWithError = @"CordovaPluginPush_DidFailToRegisterForRemoteNotificationsWithError";
12 | NSString * const PluginDidReceiveRemoteNotification = @"CordovaPluginPush_DidReceiveRemoteNotification";
13 | NSString * const PluginWillPresentNotification = @"CordovaPluginPush_WillPresentNotification";
14 | NSString * const PluginDidReceiveNotificationResponse = @"CordovaPluginPush_DidReceiveNotificationResponse";
15 |
--------------------------------------------------------------------------------
/src/ios/PushPluginFCM.h:
--------------------------------------------------------------------------------
1 | #import
2 | #import
3 |
4 | @import Firebase;
5 |
6 | NS_ASSUME_NONNULL_BEGIN
7 |
8 | @interface PushPluginFCM : NSObject
9 |
10 | @property (nonatomic, assign) BOOL isFCMEnabled;
11 | @property (nonatomic, strong) NSString *callbackId;
12 |
13 | - (instancetype)initWithGoogleServicePlist;
14 |
15 | - (void)configure:(id )commandDelegate;
16 | - (void)configureTokens:(NSData *)token;
17 |
18 | - (void)subscribeToTopic:(NSString *)topic;
19 |
20 | - (void)unsubscribeFromTopic:(NSString *)topic;
21 | - (void)unsubscribeFromTopics:(NSArray *)topics;
22 |
23 | @end
24 |
25 | NS_ASSUME_NONNULL_END
26 |
--------------------------------------------------------------------------------
/src/ios/PushPluginFCM.m:
--------------------------------------------------------------------------------
1 | #import "PushPluginFCM.h"
2 | #import "PushPluginSettings.h"
3 |
4 | @interface PushPluginFCM ()
5 |
6 | @property (nonatomic, assign) BOOL isFCMRefreshTokenObserverAttached;
7 | @property (nonatomic, weak) id commandDelegate;
8 |
9 | @end
10 |
11 | @implementation PushPluginFCM
12 |
13 | - (instancetype)initWithGoogleServicePlist {
14 | self = [super init];
15 | if (self) {
16 | NSString *googleServicePath = [[NSBundle mainBundle] pathForResource:@"GoogleService-Info" ofType:@"plist"];
17 | NSDictionary *googleServicePlist = [[NSDictionary alloc] initWithContentsOfFile:googleServicePath];
18 |
19 | if (googleServicePlist != nil) {
20 | NSString *fcmSenderId = [googleServicePlist objectForKey:@"GCM_SENDER_ID"];
21 | BOOL isGcmEnabled = [[googleServicePlist valueForKey:@"IS_GCM_ENABLED"] boolValue];
22 |
23 | if (isGcmEnabled && fcmSenderId != nil) {
24 | self.isFCMEnabled = YES;
25 | NSLog(@"[PushPlugin] FCM is enabled and Sender ID is available.");
26 | } else {
27 | self.isFCMEnabled = NO;
28 | NSLog(@"[PushPlugin] FCM is not enabled or Sender ID is missing.");
29 | }
30 | } else {
31 | self.isFCMEnabled = NO;
32 | NSLog(@"[PushPlugin] Could not locate GoogleService-Info.plist.");
33 | }
34 | }
35 | return self;
36 | }
37 |
38 | - (void)configure:(id )commandDelegate {
39 | NSLog(@"[PushPlugin] Configuring Firebase App for FCM");
40 | if(![FIRApp defaultApp]) {
41 | [FIRApp configure];
42 | }
43 |
44 | self.commandDelegate = commandDelegate;
45 | }
46 |
47 | - (void)configureTokens:(NSData *)token {
48 | NSLog(@"[PushPlugin] Setting APNS Token for Firebase App");
49 | [[FIRMessaging messaging] setAPNSToken:token];
50 |
51 | [self setFCMTokenWithCompletion];
52 | }
53 |
54 | - (void)setRefreshedFCMToken {
55 | NSLog(@"[PushPlugin] FIR has triggered a token refresh.");
56 | [self setFCMTokenWithCompletion];
57 | }
58 |
59 | - (void)setFCMTokenWithCompletion {
60 | #if TARGET_IPHONE_SIMULATOR
61 | NSLog(@"[PushPlugin] Detected simulator. Will register an FCM token but pushing to simulator is not possible.");
62 | #endif
63 |
64 | __weak __typeof(self) weakSelf = self;
65 | [[FIRMessaging messaging] tokenWithCompletion:^(NSString *token, NSError *error) {
66 | if (error != nil) {
67 | NSLog(@"[PushPlugin] Error getting FCM registration token: %@", error);
68 | } else {
69 | NSLog(@"[PushPlugin] FCM registration token: %@", token);
70 | [weakSelf subscribeToTopics:[PushPluginSettings sharedInstance].fcmTopics];
71 | [weakSelf sendRegistrationPluginResult:token];
72 | [weakSelf attachFCMTokenRefreshObserver];
73 | }
74 | }];
75 | }
76 |
77 | - (void)sendRegistrationPluginResult:(NSString *)token {
78 | NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:2];
79 | [message setObject:token forKey:@"registrationId"];
80 | [message setObject:@"FCM" forKey:@"registrationType"];
81 |
82 | // Send result to trigger 'registration' event but keep callback
83 | CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:message];
84 | [pluginResult setKeepCallbackAsBool:YES];
85 | [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId];
86 | }
87 |
88 | - (void)attachFCMTokenRefreshObserver {
89 | if (self.isFCMRefreshTokenObserverAttached) {
90 | NSLog(@"[PushPlugin] FCM token refresh observer was already attached.");
91 | return;
92 | }
93 |
94 | NSLog(@"[PushPlugin] Attaching FCM token refresh observer.");
95 | [[NSNotificationCenter defaultCenter] addObserver:self
96 | selector:@selector(setRefreshedFCMToken)
97 | name:FIRMessagingRegistrationTokenRefreshedNotification
98 | object:nil];
99 |
100 | self.isFCMRefreshTokenObserverAttached = YES;
101 | }
102 |
103 | - (void)subscribeToTopics:(NSArray *)topics {
104 | if(!topics) {
105 | NSLog(@"[PushPlugin] There are no topics to subscribe to.");
106 | return;
107 | }
108 |
109 | for (NSString *topic in topics) {
110 | [self subscribeToTopic:topic];
111 | }
112 | }
113 |
114 | - (void)subscribeToTopic:(NSString *)topic {
115 | if (!topic) {
116 | NSLog(@"[PushPlugin] There is no topic to subscribe to.");
117 | return;
118 | }
119 |
120 | NSLog(@"[PushPlugin] Subscribing to topic: %@", topic);
121 | [[FIRMessaging messaging] subscribeToTopic:topic];
122 | NSLog(@"[PushPlugin] Successfully subscribed to topic %@", topic);
123 | }
124 |
125 | - (void)unsubscribeFromTopics:(NSArray *)topics {
126 | if(!topics) {
127 | NSLog(@"[PushPlugin] There are no topics to unsubscribe from.");
128 | return;
129 | }
130 |
131 | for (NSString *topic in topics) {
132 | [self unsubscribeFromTopic:topic];
133 | }
134 | }
135 |
136 | - (void)unsubscribeFromTopic:(NSString *)topic {
137 | if (!topic) {
138 | NSLog(@"[PushPlugin] There is no topic to unsubscribe from.");
139 | return;
140 | }
141 |
142 | NSLog(@"[PushPlugin] Unsubscribing from topic: %@", topic);
143 | [[FIRMessaging messaging] unsubscribeFromTopic:topic];
144 | NSLog(@"[PushPlugin] Successfully unsubscribed from topic %@", topic);
145 | }
146 |
147 | @end
148 |
--------------------------------------------------------------------------------
/src/ios/PushPluginSettings.h:
--------------------------------------------------------------------------------
1 | //
2 | // PushPluginSettings.h
3 | // cordovaTest
4 | //
5 | // Created by Erisu on 2024/09/14.
6 | //
7 |
8 | #import
9 | #import
10 |
11 | @interface PushPluginSettings : NSObject
12 |
13 | @property (nonatomic, readonly) BOOL badgeEnabled;
14 | @property (nonatomic, readonly) BOOL soundEnabled;
15 | @property (nonatomic, readonly) BOOL alertEnabled;
16 | @property (nonatomic, readonly) BOOL criticalEnabled;
17 | @property (nonatomic, readonly) BOOL clearBadgeEnabled;
18 | @property (nonatomic, readonly) BOOL forceShowEnabled;
19 | @property (nonatomic, readonly) BOOL voipEnabled;
20 |
21 | @property (nonatomic, readonly, strong) NSArray *fcmTopics;
22 | @property (nonatomic, readonly, strong) NSSet *categories;
23 |
24 | + (instancetype)sharedInstance;
25 |
26 | - (void)updateSettingsWithOptions:(NSDictionary *)options;
27 |
28 | @end
29 |
--------------------------------------------------------------------------------
/src/ios/PushPluginSettings.m:
--------------------------------------------------------------------------------
1 | //
2 | // PushPluginSettings.m
3 | // cordovaTest
4 | //
5 | // Created by Erisu on 2024/09/14.
6 | //
7 |
8 | #import "PushPluginSettings.h"
9 |
10 | @interface PushPluginSettings ()
11 |
12 | @property (nonatomic, strong) NSMutableDictionary *settingsDictionary;
13 |
14 | @end
15 |
16 | @implementation PushPluginSettings
17 |
18 | + (instancetype)sharedInstance {
19 | static PushPluginSettings *sharedInstance = nil;
20 | static dispatch_once_t onceToken;
21 | dispatch_once(&onceToken, ^{
22 | sharedInstance = [[self alloc] initWithDefaults];
23 | });
24 | return sharedInstance;
25 | }
26 |
27 | - (instancetype)initWithDefaults {
28 | self = [super init];
29 | if (self) {
30 | self.settingsDictionary = [@{
31 | @"badge" : @(NO),
32 | @"sound" : @(NO),
33 | @"alert" : @(NO),
34 | @"critical" : @(NO),
35 | @"clearBadge" : @(NO),
36 | @"forceShow" : @(NO),
37 | @"voip" : @(NO),
38 | @"fcmTopics" : @[],
39 | @"categories" : [NSSet set]
40 | } mutableCopy];
41 | }
42 | return self;
43 | }
44 |
45 | - (void)updateSettingsWithOptions:(NSDictionary *)options {
46 | for (NSString *key in options) {
47 | if ([self.settingsDictionary objectForKey:key]) {
48 | // Overrides the default setting if defined and apply the correct formatting based on the key.
49 | if ([key isEqualToString:@"fcmTopics"]) {
50 | self.settingsDictionary[key] = [self parseArrayOption:key fromOptions:options withDefault:nil];
51 | } else if ([key isEqualToString:@"categories"]) {
52 | self.settingsDictionary[key] = [self parseCategoriesFromOptions:options[key]];
53 | } else {
54 | self.settingsDictionary[key] = @([self parseOption:key fromOptions:options withDefault:NO]);
55 | }
56 | } else {
57 | NSLog(@"[PushPlugin] Settings: Invalid option key: %@", key);
58 | }
59 | }
60 |
61 | NSLog(@"[PushPlugin] Settings: %@", self.settingsDictionary);
62 | }
63 |
64 | - (BOOL)parseOption:(NSString *)key fromOptions:(NSDictionary *)options withDefault:(BOOL)defaultValue {
65 | id option = [options objectForKey:key];
66 | if ([option isKindOfClass:[NSString class]]) {
67 | return [option isEqualToString:@"true"];
68 | }
69 | if ([option respondsToSelector:@selector(boolValue)]) {
70 | return [option boolValue];
71 | }
72 | return defaultValue;
73 | }
74 |
75 | - (NSArray *)parseArrayOption:(NSString *)key fromOptions:(NSDictionary *)options withDefault:(NSArray *)defaultValue {
76 | id option = [options objectForKey:key];
77 | if ([option isKindOfClass:[NSArray class]]) {
78 | return (NSArray *)option;
79 | }
80 | return defaultValue;
81 | }
82 |
83 | - (NSSet *)parseCategoriesFromOptions:(NSDictionary *)categoryOptions {
84 | NSMutableSet *categoriesSet = [[NSMutableSet alloc] init];
85 | if (categoryOptions != nil && [categoryOptions isKindOfClass:[NSDictionary class]]) {
86 | for (id key in categoryOptions) {
87 | NSDictionary *category = [categoryOptions objectForKey:key];
88 | UNNotificationCategory *notificationCategory = [self createCategoryFromDictionary:category withIdentifier:key];
89 | if (notificationCategory) {
90 | [categoriesSet addObject:notificationCategory];
91 | }
92 | }
93 | }
94 | return categoriesSet;
95 | }
96 |
97 | - (UNNotificationCategory *)createCategoryFromDictionary:(NSDictionary *)category withIdentifier:(NSString *)identifier {
98 | NSMutableArray *actions = [[NSMutableArray alloc] init];
99 |
100 | UNNotificationAction *yesAction = [self createActionFromDictionary:[category objectForKey:@"yes"]];
101 | if (yesAction)
102 | [actions addObject:yesAction];
103 |
104 | UNNotificationAction *noAction = [self createActionFromDictionary:[category objectForKey:@"no"]];
105 | if (noAction)
106 | [actions addObject:noAction];
107 |
108 | UNNotificationAction *maybeAction = [self createActionFromDictionary:[category objectForKey:@"maybe"]];
109 | if (maybeAction)
110 | [actions addObject:maybeAction];
111 |
112 | return [UNNotificationCategory categoryWithIdentifier:identifier actions:actions intentIdentifiers:@[] options:UNNotificationCategoryOptionNone];
113 | }
114 |
115 | - (UNNotificationAction *)createActionFromDictionary:(NSDictionary *)dictionary {
116 | if (![dictionary isKindOfClass:[NSDictionary class]]) {
117 | return nil;
118 | }
119 |
120 | NSString *identifier = [dictionary objectForKey:@"callback"];
121 | NSString *title = [dictionary objectForKey:@"title"];
122 |
123 | if (!title || !identifier) {
124 | return nil;
125 | }
126 |
127 | UNNotificationActionOptions options = UNNotificationActionOptionNone;
128 | id foreground = [dictionary objectForKey:@"foreground"];
129 | if (foreground != nil && (([foreground isKindOfClass:[NSString class]] && [foreground isEqualToString:@"true"]) || [foreground boolValue])) {
130 | options |= UNNotificationActionOptionForeground;
131 | }
132 |
133 | id destructive = [dictionary objectForKey:@"destructive"];
134 | if (destructive != nil && (([destructive isKindOfClass:[NSString class]] && [destructive isEqualToString:@"true"]) || [destructive boolValue])) {
135 | options |= UNNotificationActionOptionDestructive;
136 | }
137 |
138 | return [UNNotificationAction actionWithIdentifier:identifier title:title options:options];
139 | }
140 |
141 | #pragma mark - Getters for individual settings
142 |
143 | - (BOOL)badgeEnabled {
144 | return [self.settingsDictionary[@"badge"] boolValue];
145 | }
146 |
147 | - (BOOL)soundEnabled {
148 | return [self.settingsDictionary[@"sound"] boolValue];
149 | }
150 |
151 | - (BOOL)alertEnabled {
152 | return [self.settingsDictionary[@"alert"] boolValue];
153 | }
154 |
155 | - (BOOL)criticalEnabled {
156 | return [self.settingsDictionary[@"critical"] boolValue];
157 | }
158 |
159 | - (BOOL)clearBadgeEnabled {
160 | return [self.settingsDictionary[@"clearBadge"] boolValue];
161 | }
162 |
163 | - (BOOL)forceShowEnabled {
164 | return [self.settingsDictionary[@"forceShow"] boolValue];
165 | }
166 |
167 | - (BOOL)voipEnabled {
168 | return [self.settingsDictionary[@"voip"] boolValue];
169 | }
170 |
171 | - (NSArray *)fcmTopics {
172 | return self.settingsDictionary[@"fcmTopics"];
173 | }
174 |
175 | - (NSSet *)categories {
176 | return self.settingsDictionary[@"categories"];
177 | }
178 |
179 | @end
180 |
--------------------------------------------------------------------------------
/src/js/push.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Module dependencies.
3 | */
4 |
5 | const exec = cordova.require('cordova/exec');
6 |
7 | class PushNotification {
8 | /**
9 | * PushNotification constructor.
10 | *
11 | * @param {Object} options to initiate Push Notifications.
12 | * @return {PushNotification} instance that can be monitored and cancelled.
13 | */
14 | constructor (options) {
15 | this.handlers = {
16 | registration: [],
17 | notification: [],
18 | error: []
19 | };
20 |
21 | // require options parameter
22 | if (typeof options === 'undefined') {
23 | throw new Error('The options argument is required.');
24 | }
25 |
26 | // store the options to this object instance
27 | this.options = options;
28 |
29 | // triggered on registration and notification
30 | const success = (result) => {
31 | if (result && typeof result.registrationId !== 'undefined') {
32 | this.emit('registration', result);
33 | } else if (
34 | result &&
35 | result.additionalData &&
36 | typeof result.additionalData.actionCallback !== 'undefined'
37 | ) {
38 | this.emit(result.additionalData.actionCallback, result);
39 | } else if (result) {
40 | this.emit('notification', result);
41 | }
42 | };
43 |
44 | // triggered on error
45 | const fail = (msg) => {
46 | const e = typeof msg === 'string' ? new Error(msg) : msg;
47 | this.emit('error', e);
48 | };
49 |
50 | // wait at least one process tick to allow event subscriptions
51 | setTimeout(() => {
52 | exec(success, fail, 'PushNotification', 'init', [options]);
53 | }, 10);
54 | }
55 |
56 | /**
57 | * Unregister from push notifications
58 | */
59 | unregister (successCallback, errorCallback = () => {}, options) {
60 | if (typeof errorCallback !== 'function') {
61 | console.log('PushNotification.unregister failure: failure parameter not a function');
62 | return;
63 | }
64 |
65 | if (typeof successCallback !== 'function') {
66 | console.log(
67 | 'PushNotification.unregister failure: success callback parameter must be a function'
68 | );
69 | return;
70 | }
71 |
72 | const cleanHandlersAndPassThrough = () => {
73 | if (!options) {
74 | this.handlers = {
75 | registration: [],
76 | notification: [],
77 | error: []
78 | };
79 | }
80 | successCallback();
81 | };
82 |
83 | exec(cleanHandlersAndPassThrough, errorCallback, 'PushNotification', 'unregister', [options]);
84 | }
85 |
86 | /**
87 | * subscribe to a topic
88 | * @param {String} topic topic to subscribe
89 | * @param {Function} successCallback success callback
90 | * @param {Function} errorCallback error callback
91 | * @return {void}
92 | */
93 | subscribe (topic, successCallback, errorCallback = () => {}) {
94 | if (typeof errorCallback !== 'function') {
95 | console.log('PushNotification.subscribe failure: failure parameter not a function');
96 | return;
97 | }
98 |
99 | if (typeof successCallback !== 'function') {
100 | console.log(
101 | 'PushNotification.subscribe failure: success callback parameter must be a function'
102 | );
103 | return;
104 | }
105 |
106 | exec(successCallback, errorCallback, 'PushNotification', 'subscribe', [topic]);
107 | }
108 |
109 | /**
110 | * unsubscribe to a topic
111 | * @param {String} topic topic to unsubscribe
112 | * @param {Function} successCallback success callback
113 | * @param {Function} errorCallback error callback
114 | * @return {void}
115 | */
116 | unsubscribe (topic, successCallback, errorCallback = () => {}) {
117 | if (typeof errorCallback !== 'function') {
118 | console.log('PushNotification.unsubscribe failure: failure parameter not a function');
119 | return;
120 | }
121 |
122 | if (typeof successCallback !== 'function') {
123 | console.log(
124 | 'PushNotification.unsubscribe failure: success callback parameter must be a function'
125 | );
126 | return;
127 | }
128 |
129 | exec(successCallback, errorCallback, 'PushNotification', 'unsubscribe', [topic]);
130 | }
131 |
132 | /**
133 | * Call this to set the application icon badge
134 | */
135 | setApplicationIconBadgeNumber (successCallback, errorCallback = () => {}, badge) {
136 | if (typeof errorCallback !== 'function') {
137 | console.log(
138 | 'PushNotification.setApplicationIconBadgeNumber failure: failure ' +
139 | 'parameter not a function'
140 | );
141 | return;
142 | }
143 |
144 | if (typeof successCallback !== 'function') {
145 | console.log(
146 | 'PushNotification.setApplicationIconBadgeNumber failure: success ' +
147 | 'callback parameter must be a function'
148 | );
149 | return;
150 | }
151 |
152 | exec(successCallback, errorCallback, 'PushNotification', 'setApplicationIconBadgeNumber', [
153 | { badge }
154 | ]);
155 | }
156 |
157 | /**
158 | * Get the application icon badge
159 | */
160 |
161 | getApplicationIconBadgeNumber (successCallback, errorCallback = () => {}) {
162 | if (typeof errorCallback !== 'function') {
163 | console.log(
164 | 'PushNotification.getApplicationIconBadgeNumber failure: failure ' +
165 | 'parameter not a function'
166 | );
167 | return;
168 | }
169 |
170 | if (typeof successCallback !== 'function') {
171 | console.log(
172 | 'PushNotification.getApplicationIconBadgeNumber failure: success ' +
173 | 'callback parameter must be a function'
174 | );
175 | return;
176 | }
177 |
178 | exec(successCallback, errorCallback, 'PushNotification', 'getApplicationIconBadgeNumber', []);
179 | }
180 |
181 | /**
182 | * Clear all notifications
183 | */
184 |
185 | clearAllNotifications (successCallback = () => {}, errorCallback = () => {}) {
186 | if (typeof errorCallback !== 'function') {
187 | console.log(
188 | 'PushNotification.clearAllNotifications failure: failure parameter not a function'
189 | );
190 | return;
191 | }
192 |
193 | if (typeof successCallback !== 'function') {
194 | console.log(
195 | 'PushNotification.clearAllNotifications failure: success callback ' +
196 | 'parameter must be a function'
197 | );
198 | return;
199 | }
200 |
201 | exec(successCallback, errorCallback, 'PushNotification', 'clearAllNotifications', []);
202 | }
203 |
204 | /**
205 | * Clears notifications that have the ID specified.
206 | * @param {Function} [successCallback] Callback function to be called on success.
207 | * @param {Function} [errorCallback] Callback function to be called when an error is encountered.
208 | * @param {Number} id ID of the notification to be removed.
209 | */
210 | clearNotification (successCallback = () => {}, errorCallback = () => {}, id) {
211 | const idNumber = parseInt(id, 10);
212 | if (Number.isNaN(idNumber) || idNumber > Number.MAX_SAFE_INTEGER || idNumber < 0) {
213 | console.log(
214 | 'PushNotification.clearNotification failure: id parameter must' +
215 | 'be a valid integer.'
216 | );
217 | return;
218 | }
219 |
220 | exec(successCallback, errorCallback, 'PushNotification', 'clearNotification',
221 | [idNumber]);
222 | }
223 |
224 | /**
225 | * Listen for an event.
226 | *
227 | * The following events are supported:
228 | *
229 | * - registration
230 | * - notification
231 | * - error
232 | *
233 | * @param {String} eventName to subscribe to.
234 | * @param {Function} callback triggered on the event.
235 | */
236 |
237 | on (eventName, callback) {
238 | if (!Object.prototype.hasOwnProperty.call(this.handlers, eventName)) {
239 | this.handlers[eventName] = [];
240 | }
241 | this.handlers[eventName].push(callback);
242 | }
243 |
244 | /**
245 | * Remove event listener.
246 | *
247 | * @param {String} eventName to match subscription.
248 | * @param {Function} handle function associated with event.
249 | */
250 |
251 | off (eventName, handle) {
252 | if (Object.prototype.hasOwnProperty.call(this.handlers, eventName)) {
253 | const handleIndex = this.handlers[eventName].indexOf(handle);
254 | if (handleIndex >= 0) {
255 | this.handlers[eventName].splice(handleIndex, 1);
256 | }
257 | }
258 | }
259 |
260 | /**
261 | * Emit an event.
262 | *
263 | * This is intended for internal use only.
264 | *
265 | * @param {String} eventName is the event to trigger.
266 | * @param {*} all arguments are passed to the event listeners.
267 | *
268 | * @return {Boolean} is true when the event is triggered otherwise false.
269 | */
270 |
271 | emit (...args) {
272 | const eventName = args.shift();
273 |
274 | if (!Object.prototype.hasOwnProperty.call(this.handlers, eventName)) {
275 | return false;
276 | }
277 |
278 | for (let i = 0, { length } = this.handlers[eventName]; i < length; i += 1) {
279 | const callback = this.handlers[eventName][i];
280 | if (typeof callback === 'function') {
281 | callback(...args); // eslint-disable-line n/no-callback-literal
282 | } else {
283 | console.log(`event handler: ${eventName} must be a function`);
284 | }
285 | }
286 |
287 | return true;
288 | }
289 |
290 | finish (successCallback = () => {}, errorCallback = () => {}, id = 'handler') {
291 | if (typeof successCallback !== 'function') {
292 | console.log('finish failure: success callback parameter must be a function');
293 | return;
294 | }
295 |
296 | if (typeof errorCallback !== 'function') {
297 | console.log('finish failure: failure parameter not a function');
298 | return;
299 | }
300 |
301 | exec(successCallback, errorCallback, 'PushNotification', 'finish', [id]);
302 | }
303 | }
304 |
305 | /*!
306 | * Push Notification Plugin.
307 | */
308 |
309 | module.exports = {
310 | /**
311 | * Register for Push Notifications.
312 | *
313 | * This method will instantiate a new copy of the PushNotification object
314 | * and start the registration process.
315 | *
316 | * @param {Object} options
317 | * @return {PushNotification} instance
318 | */
319 |
320 | init: (options) => new PushNotification(options),
321 |
322 | hasPermission: (successCallback, errorCallback) => {
323 | exec(successCallback, errorCallback, 'PushNotification', 'hasPermission', []);
324 | },
325 |
326 | createChannel: (successCallback, errorCallback, channel) => {
327 | exec(successCallback, errorCallback, 'PushNotification', 'createChannel', [channel]);
328 | },
329 |
330 | deleteChannel: (successCallback, errorCallback, channelId) => {
331 | exec(successCallback, errorCallback, 'PushNotification', 'deleteChannel', [channelId]);
332 | },
333 |
334 | listChannels: (successCallback, errorCallback) => {
335 | exec(successCallback, errorCallback, 'PushNotification', 'listChannels', []);
336 | },
337 |
338 | /**
339 | * PushNotification Object.
340 | *
341 | * Expose the PushNotification object for direct use
342 | * and testing. Typically, you should use the
343 | * .init helper method.
344 | */
345 | PushNotification
346 | };
347 |
--------------------------------------------------------------------------------
/types/index.d.ts:
--------------------------------------------------------------------------------
1 | // Type definitions for phonegap-plugin-push
2 | // Project: https://github.com/havesource/cordova-plugin-push
3 | // Definitions by: Frederico Galvão
4 | // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
5 |
6 | declare namespace PhonegapPluginPush {
7 | type EventResponse = RegistrationEventResponse | NotificationEventResponse | Error
8 |
9 | interface PushNotification {
10 | /**
11 | * The event registration will be triggered on each successful registration with the 3rd party push service.
12 | * @param event
13 | * @param callback
14 | */
15 | on(event: "registration", callback: (response: RegistrationEventResponse) => any): void
16 | /**
17 | * The event notification will be triggered each time a push notification is received by a 3rd party push service on the device.
18 | * @param event
19 | * @param callback
20 | */
21 | on(event: "notification", callback: (response: NotificationEventResponse) => any): void
22 | /**
23 | * The event error will trigger when an internal error occurs and the cache is aborted.
24 | * @param event
25 | * @param callback
26 | */
27 | on(event: "error", callback: (response: Error) => any): void
28 | /**
29 | *
30 | * @param event Name of the event to listen to. See below(above) for all the event names.
31 | * @param callback is called when the event is triggered.
32 | * @param event
33 | * @param callback
34 | */
35 | on(event: string, callback: (response: EventResponse) => any): void
36 |
37 | off(event: "registration", callback: (response: RegistrationEventResponse) => any): void
38 | off(event: "notification", callback: (response: NotificationEventResponse) => any): void
39 | off(event: "error", callback: (response: Error) => any): void
40 | /**
41 | * As stated in the example, you will have to store your event handler if you are planning to remove it.
42 | * @param event Name of the event type. The possible event names are the same as for the push.on function.
43 | * @param callback handle to the function to get removed.
44 | * @param event
45 | * @param callback
46 | */
47 | off(event: string, callback: (response: EventResponse) => any): void
48 |
49 | /**
50 | * The unregister method is used when the application no longer wants to receive push notifications.
51 | * Beware that this cleans up all event handlers previously registered,
52 | * so you will need to re-register them if you want them to function again without an application reload.
53 | * @param successHandler
54 | * @param errorHandler
55 | * @param topics
56 | */
57 | unregister(successHandler: () => any, errorHandler?: () => any, topics?: string[]): void
58 |
59 | /**
60 | * The subscribe method is used when the application wants to subscribe a new topic to receive push notifications.
61 | * @param topic Topic to subscribe to.
62 | * @param successHandler Is called when the api successfully unregisters.
63 | * @param errorHandler Is called when the api encounters an error while unregistering.
64 | */
65 | subscribe(topic: string, successHandler: () => any, errorHandler: () => any): void;
66 |
67 | /**
68 | * The unsubscribe method is used when the application no longer wants to receive push notifications
69 | * from a specific topic but continue to receive other push messages.
70 | * @param topic Topic to unsubscribe from.
71 | * @param successHandler Is called when the api successfully unregisters.
72 | * @param errorHandler Is called when the api encounters an error while unregistering.
73 | */
74 | unsubscribe(topic: string, successHandler: () => any, errorHandler: () => any): void;
75 |
76 | /*TODO according to js source code, "errorHandler" is optional, but is "count" also optional? I can't read objetive-C code (can anyone at all? I wonder...)*/
77 | /**
78 | * Set the badge count visible when the app is not running
79 | *
80 | * The count is an integer indicating what number should show up in the badge.
81 | * Passing 0 will clear the badge.
82 | * Each notification event contains a data.count value which can be used to set the badge to correct number.
83 | * @param successHandler
84 | * @param errorHandler
85 | * @param count
86 | */
87 | setApplicationIconBadgeNumber(successHandler: () => any, errorHandler: () => any, count: number): void
88 |
89 | /**
90 | * Get the current badge count visible when the app is not running
91 | * successHandler gets called with an integer which is the current badge count
92 | * @param successHandler
93 | * @param errorHandler
94 | */
95 | getApplicationIconBadgeNumber(successHandler: (count: number) => any, errorHandler: () => any): void
96 |
97 | /**
98 | * iOS only
99 | * Tells the OS that you are done processing a background push notification.
100 | * successHandler gets called when background push processing is successfully completed.
101 | * @param successHandler
102 | * @param errorHandler
103 | * @param id
104 | */
105 | finish(successHandler?: () => any, errorHandler?: () => any, id?: string): void
106 |
107 | /**
108 | * Tells the OS to clear all notifications from the Notification Center
109 | * @param successHandler Is called when the api successfully clears the notifications.
110 | * @param errorHandler Is called when the api encounters an error when attempting to clear the notifications.
111 | */
112 | clearAllNotifications(successHandler: () => any, errorHandler: () => any): void
113 |
114 | /**
115 | * Tells the OS to clear the notification that corresponds to the id argument, from the Notification Center
116 | * @param successHandler Is called when the api successfully clears the notification.
117 | * @param errorHandler Is called when the api encounters an error when attempting to clear the notification.
118 | * @param id The ID of the notification that will be cleared.
119 | */
120 | clearNotification(successHandler: () => any, errorHandler: () => any, id?: number): void
121 | }
122 |
123 | /**
124 | * platform specific initialization options.
125 | */
126 | interface InitOptions {
127 | /**
128 | * Android specific initialization options.
129 | */
130 | android?: {
131 | /**
132 | * The name of a drawable resource to use as the small-icon. The name should not include the extension.
133 | */
134 | icon?: string
135 | /**
136 | * Sets the background color of the small icon on Android 5.0 and greater.
137 | * Supported Formats - http://developer.android.com/reference/android/graphics/Color.html#parseColor(java.lang.String)
138 | */
139 | iconColor?: string
140 | /**
141 | * If true it plays the sound specified in the push data or the default system sound. Default is true.
142 | */
143 | sound?: boolean
144 | /**
145 | * If true the device vibrates on receipt of notification. Default is true.
146 | */
147 | vibrate?: boolean
148 | /**
149 | * If true the icon badge will be cleared on init and before push messages are processed. Default is false.
150 | */
151 | clearBadge?: boolean
152 | /**
153 | * If true the app clears all pending notifications when it is closed. Default is true.
154 | */
155 | clearNotifications?: boolean
156 | /**
157 | * If true will always show a notification, even when the app is on the foreground. Default is false.
158 | */
159 | forceShow?: boolean
160 | /**
161 | * If the array contains one or more strings each string will be used to subscribe to a GcmPubSub topic.
162 | */
163 | topics?: string[]
164 | /**
165 | * The key to search for text of notification. Default is 'message'.
166 | */
167 | messageKey?: string
168 | /**
169 | * The key to search for title of notification. Default is 'title'.
170 | */
171 | titleKey?: string
172 | }
173 |
174 | /**
175 | * Browser specific initialization options.
176 | */
177 | browser?: {
178 | /**
179 | * URL for the push server you want to use. Default is 'http://push.api.phonegap.com/v1/push'.
180 | */
181 | pushServiceURL?: string
182 | /**
183 | * Your GCM API key if you are using VAPID keys.
184 | */
185 | applicationServerKey?: string
186 | }
187 |
188 | /**
189 | * iOS specific initialization options.
190 | */
191 | ios?: {
192 | /**
193 | * If true|"true" the device will be set up to receive VoIP Push notifications and the other options will be ignored
194 | * since VoIP notifications are silent notifications that should be handled in the "notification" event.
195 | * Default is false|"false".
196 | */
197 | voip?: boolean | string
198 | /**
199 | * If true|"true" the device sets the badge number on receipt of notification.
200 | * Default is false|"false".
201 | * Note: the value you set this option to the first time you call the init method will be how the application always acts.
202 | * Once this is set programmatically in the init method it can only be changed manually by the user in Settings>Notifications>App Name.
203 | * This is normal iOS behaviour.
204 | */
205 | badge?: boolean | string
206 | /**
207 | * If true|"true" the device plays a sound on receipt of notification.
208 | * Default is false|"false".
209 | * Note: the value you set this option to the first time you call the init method will be how the application always acts.
210 | * Once this is set programmatically in the init method it can only be changed manually by the user in Settings>Notifications>App Name.
211 | * This is normal iOS behaviour.
212 | */
213 | sound?: boolean | string
214 | /**
215 | * If true|"true" the device shows an alert on receipt of notification.
216 | * Default is false|"false".
217 | * Note: the value you set this option to the first time you call the init method will be how the application always acts.
218 | * Once this is set programmatically in the init method it can only be changed manually by the user in Settings>Notifications>App Name.
219 | * This is normal iOS behaviour.
220 | */
221 | alert?: boolean | string
222 | /**
223 | * If true|"true" the badge will be cleared on app startup. Defaults to false|"false".
224 | */
225 | clearBadge?: boolean | string
226 | /**
227 | * The data required in order to enable Action Buttons for iOS.
228 | * Action Buttons on iOS - https://github.com/havesource/cordova-plugin-push/blob/master/docs/PAYLOAD.md#action-buttons-1
229 | */
230 | categories?: CategoryArray
231 | /**
232 | * If `true` the device can show up critical alerts. (Possible since iOS 12 with a special entitlement)
233 | * Default is false|"false".
234 | * Note: the value you set this option to the first time you call the init method will be how the application always acts.
235 | * Once this is set programmatically in the init method it can only be changed manually by the user in Settings > Notifications > `App Name`.
236 | * This is normal iOS behaviour.
237 | */
238 | critical?: boolean
239 | /**
240 | * If the array contains one or more strings each string will be used to subscribe to a FcmPubSub topic. Defaults to [].
241 | */
242 | topics?: string[],
243 | /**
244 | * If true will always show a notification, even when the app is on the foreground. Default is false.
245 | */
246 | forceShow?: boolean
247 | }
248 | }
249 |
250 | interface CategoryArray {
251 | [name: string]: CategoryAction
252 | }
253 |
254 | interface CategoryAction {
255 | yes?: CategoryActionData
256 | no?: CategoryActionData
257 | maybe?: CategoryActionData
258 | }
259 |
260 | interface CategoryActionData {
261 | /**
262 | * The javascript event you want to fire.
263 | */
264 | callback: string
265 | /**
266 | * The label for the button.
267 | */
268 | title: string
269 | /**
270 | * Whether or not to bring your app to the foreground
271 | */
272 | foreground: boolean
273 | /**
274 | * Colors the button red as a warning to the user that the action may be destructive.
275 | */
276 | destructive: boolean
277 | }
278 |
279 | interface RegistrationEventResponse {
280 | /**
281 | * The registration ID provided by the 3rd party remote push service.
282 | */
283 | registrationId: string
284 | }
285 |
286 | interface NotificationEventResponse {
287 | /**
288 | * The text of the push message sent from the 3rd party service.
289 | */
290 | message: string
291 | /**
292 | * The optional title of the push message sent from the 3rd party service.
293 | */
294 | title?: string
295 | /**
296 | * The number of messages to be displayed in the badge iOS or message count in the notification shade in Android.
297 | */
298 | count: string
299 | /**
300 | * The name of the sound file to be played upon receipt of the notification.
301 | */
302 | sound: string
303 | /**
304 | * The path of the image file to be displayed in the notification.
305 | */
306 | image: string
307 | /**
308 | * An optional collection of data sent by the 3rd party push service that does not fit in the above properties.
309 | */
310 | additionalData: NotificationEventAdditionalData
311 | }
312 |
313 | /**
314 | * TODO: document all possible properties (I only got the android ones)
315 | *
316 | * Loosened up with a dictionary notation, but all non-defined properties need to use (map['prop']) notation
317 | *
318 | * Ideally the developer would overload (merged declaration) this or create a new interface that would extend this one
319 | * so that he could specify any custom code without having to use array notation (map['prop']) for all of them.
320 | */
321 | interface NotificationEventAdditionalData {
322 | [name: string]: any
323 |
324 | /**
325 | * Whether the notification was received while the app was in the foreground.
326 | */
327 | foreground?: boolean
328 | /**
329 | * Will be true if the application is started by clicking on the push notification, false if the app is already started. (Android/iOS only)
330 | */
331 | coldstart?: boolean
332 | collapse_key?: string
333 | from?: string
334 | notId?: string
335 | }
336 |
337 | interface Channel {
338 | /**
339 | * The id of the channel. Must be unique per package. The value may be truncated if it is too long.
340 | */
341 | id: string;
342 | /**
343 | * The user visible name of the channel. The recommended maximum length is 40 characters; the value may be truncated if it is too long.
344 | */
345 | description: string;
346 | /**
347 | * The importance of the channel. This controls how interruptive notifications posted to this channel are. The importance property goes from 1 = Lowest, 2 = Low, 3 = Normal, 4 = High and 5 = Highest.
348 | */
349 | importance: number;
350 | /**
351 | * The name of the sound file to be played upon receipt of the notification in this channel. Empty string to disable sound. Cannot be changed after channel is created.
352 | */
353 | sound?: string;
354 | /**
355 | * Boolean sets whether notification posted to this channel should vibrate. Array sets custom vibration pattern. Example - vibration: [2000, 1000, 500, 500]. Cannot be changed after channel is created.
356 | */
357 | vibration?: boolean|number[];
358 | /**
359 | * Sets whether notifications posted to this channel appear on the lockscreen or not, and if so, whether they appear in a redacted form. 0 = Private, 1 = Public, -1 = Secret.
360 | */
361 | visibility?: number;
362 | }
363 |
364 | interface PushNotificationStatic {
365 | new (options: InitOptions): PushNotification
366 | /**
367 | * Initializes the plugin on the native side.
368 | * @param options An object describing relevant specific options for all target platforms.
369 | */
370 | init(options: InitOptions): PushNotification
371 | /**
372 | * Checks whether the push notification permission has been granted.
373 | * @param successCallback Is called when the api successfully retrieves the details on the permission.
374 | * @param errorCallback Is called when the api fails to retrieve the details on the permission.
375 | */
376 | hasPermission(successCallback: (data: {isEnabled: boolean}) => void, errorCallback: () => void): void;
377 | /**
378 | * Android only
379 | * Create a new notification channel for Android O and above.
380 | * @param successCallback Is called when the api successfully creates a channel.
381 | * @param errorCallback Is called when the api fails to create a channel.
382 | * @param channel The options for the channel.
383 | */
384 | createChannel(successCallback: () => void, errorCallback: () => void, channel: Channel): void;
385 | /**
386 | * Android only
387 | * Delete a notification channel for Android O and above.
388 | * @param successCallback Is called when the api successfully deletes a channel.
389 | * @param errorCallback Is called when the api fails to create a channel.
390 | * @param channelId The ID of the channel.
391 | */
392 | deleteChannel(successCallback: () => void, errorCallback: () => void, channelId: string): void;
393 | /**
394 | * Android only
395 | * Returns a list of currently configured channels.
396 | * @param successCallback Is called when the api successfully retrieves the list of channels.
397 | * @param errorCallback Is called when the api fails to retrieve the list of channels.
398 | */
399 | listChannels(successCallback: (channels: Channel[]) => void, errorCallback: () => void): void;
400 | }
401 | }
402 |
403 | interface Window {
404 | PushNotification: PhonegapPluginPush.PushNotificationStatic
405 | }
406 | declare var PushNotification: PhonegapPluginPush.PushNotificationStatic;
407 |
--------------------------------------------------------------------------------
/www/browser/push.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * Module dependencies.
3 | */
4 | cordova.require('cordova/exec');
5 |
6 | /**
7 | * PushNotification constructor.
8 | *
9 | * @param {Object} options to initiate Push Notifications.
10 | * @return {PushNotification} instance that can be monitored and cancelled.
11 | */
12 | var serviceWorker, subscription;
13 | var PushNotification = function (options) {
14 | this._handlers = {
15 | registration: [],
16 | notification: [],
17 | error: []
18 | };
19 |
20 | // require options parameter
21 | if (typeof options === 'undefined') {
22 | throw new Error('The options argument is required.');
23 | }
24 |
25 | // store the options to this object instance
26 | this.options = options;
27 |
28 | // subscription options
29 | var subOptions = { userVisibleOnly: true };
30 | if (this.options.browser && this.options.browser.applicationServerKey) {
31 | subOptions.applicationServerKey = urlBase64ToUint8Array(this.options.browser.applicationServerKey);
32 | }
33 |
34 | // triggered on registration and notification
35 | var that = this;
36 |
37 | // Add manifest.json to main HTML file
38 | var linkElement = document.createElement('link');
39 | linkElement.rel = 'manifest';
40 | linkElement.href = 'manifest.json';
41 | document.getElementsByTagName('head')[0].appendChild(linkElement);
42 |
43 | if ('serviceWorker' in navigator && 'MessageChannel' in window) {
44 | var result;
45 | var channel = new MessageChannel();
46 | channel.port1.onmessage = function (event) {
47 | that.emit('notification', event.data);
48 | };
49 |
50 | navigator.serviceWorker.register('ServiceWorker.js').then(function () {
51 | return navigator.serviceWorker.ready;
52 | })
53 | .then(function (reg) {
54 | serviceWorker = reg;
55 | reg.pushManager.subscribe(subOptions).then(function (sub) {
56 | subscription = sub;
57 | result = { registrationId: sub.endpoint.substring(sub.endpoint.lastIndexOf('/') + 1) };
58 | that.emit('registration', result);
59 |
60 | // send encryption keys to push server
61 | var xmlHttp = new XMLHttpRequest();
62 | var xmlURL = (options.browser.pushServiceURL || 'http://push.api.phonegap.com/v1/push') + '/keys';
63 | xmlHttp.open('POST', xmlURL, true);
64 |
65 | var formData = new FormData();
66 | formData.append('subscription', JSON.stringify(sub));
67 |
68 | xmlHttp.send(formData);
69 |
70 | navigator.serviceWorker.controller.postMessage(result, [channel.port2]);
71 | }).catch(function () {
72 | if (navigator.serviceWorker.controller === null) {
73 | // When you first register a SW, need a page reload to handle network operations
74 | window.location.reload();
75 | return;
76 | }
77 |
78 | throw new Error('Error subscribing for Push notifications.');
79 | });
80 | }).catch(function (error) {
81 | console.log(error);
82 | throw new Error('Error registering Service Worker');
83 | });
84 | } else {
85 | throw new Error('Service Workers are not supported on your browser.');
86 | }
87 | };
88 |
89 | /**
90 | * Unregister from push notifications
91 | */
92 |
93 | PushNotification.prototype.unregister = function (successCallback, errorCallback, options) {
94 | if (!errorCallback) { errorCallback = function () {}; }
95 |
96 | if (typeof errorCallback !== 'function') {
97 | console.log('PushNotification.unregister failure: failure parameter not a function');
98 | return;
99 | }
100 |
101 | if (typeof successCallback !== 'function') {
102 | console.log('PushNotification.unregister failure: success callback parameter must be a function');
103 | return;
104 | }
105 |
106 | var that = this;
107 | if (!options) {
108 | that._handlers = {
109 | registration: [],
110 | notification: [],
111 | error: []
112 | };
113 | }
114 |
115 | if (serviceWorker) {
116 | serviceWorker.unregister().then(function (isSuccess) {
117 | if (isSuccess) {
118 | var deviceID = subscription.endpoint.substring(subscription.endpoint.lastIndexOf('/') + 1);
119 | var xmlHttp = new XMLHttpRequest();
120 | var xmlURL = (that.options.browser.pushServiceURL || 'http://push.api.phonegap.com/v1/push') +
121 | '/keys/' + deviceID;
122 | xmlHttp.open('DELETE', xmlURL, true);
123 | xmlHttp.send();
124 |
125 | successCallback();
126 | } else {
127 | errorCallback();
128 | }
129 | });
130 | }
131 | };
132 |
133 | /**
134 | * subscribe to a topic
135 | * @param {String} topic topic to subscribe
136 | * @param {Function} successCallback success callback
137 | * @param {Function} errorCallback error callback
138 | * @return {void}
139 | */
140 | PushNotification.prototype.subscribe = function (topic, successCallback, errorCallback) {
141 | if (!errorCallback) { errorCallback = function () {}; }
142 |
143 | if (typeof errorCallback !== 'function') {
144 | console.log('PushNotification.subscribe failure: failure parameter not a function');
145 | return;
146 | }
147 |
148 | if (typeof successCallback !== 'function') {
149 | console.log('PushNotification.subscribe failure: success callback parameter must be a function');
150 | return;
151 | }
152 |
153 | successCallback();
154 | };
155 |
156 | /**
157 | * unsubscribe to a topic
158 | * @param {String} topic topic to unsubscribe
159 | * @param {Function} successCallback success callback
160 | * @param {Function} errorCallback error callback
161 | * @return {void}
162 | */
163 | PushNotification.prototype.unsubscribe = function (topic, successCallback, errorCallback) {
164 | if (!errorCallback) { errorCallback = function () {}; }
165 |
166 | if (typeof errorCallback !== 'function') {
167 | console.log('PushNotification.unsubscribe failure: failure parameter not a function');
168 | return;
169 | }
170 |
171 | if (typeof successCallback !== 'function') {
172 | console.log('PushNotification.unsubscribe failure: success callback parameter must be a function');
173 | return;
174 | }
175 |
176 | successCallback();
177 | };
178 |
179 | /**
180 | * Call this to set the application icon badge
181 | */
182 |
183 | PushNotification.prototype.setApplicationIconBadgeNumber = function (successCallback, errorCallback, badge) {
184 | if (!errorCallback) { errorCallback = function () {}; }
185 |
186 | if (typeof errorCallback !== 'function') {
187 | console.log('PushNotification.setApplicationIconBadgeNumber failure: failure parameter not a function');
188 | return;
189 | }
190 |
191 | if (typeof successCallback !== 'function') {
192 | console.log('PushNotification.setApplicationIconBadgeNumber failure: success callback parameter must be a function');
193 | return;
194 | }
195 |
196 | successCallback();
197 | };
198 |
199 | /**
200 | * Get the application icon badge
201 | */
202 |
203 | PushNotification.prototype.getApplicationIconBadgeNumber = function (successCallback, errorCallback) {
204 | if (!errorCallback) { errorCallback = function () {}; }
205 |
206 | if (typeof errorCallback !== 'function') {
207 | console.log('PushNotification.getApplicationIconBadgeNumber failure: failure parameter not a function');
208 | return;
209 | }
210 |
211 | if (typeof successCallback !== 'function') {
212 | console.log('PushNotification.getApplicationIconBadgeNumber failure: success callback parameter must be a function');
213 | return;
214 | }
215 |
216 | successCallback();
217 | };
218 |
219 | /**
220 | * Get the application icon badge
221 | */
222 |
223 | PushNotification.prototype.clearAllNotifications = function (successCallback, errorCallback) {
224 | if (!errorCallback) { errorCallback = function () {}; }
225 |
226 | if (typeof errorCallback !== 'function') {
227 | console.log('PushNotification.clearAllNotifications failure: failure parameter not a function');
228 | return;
229 | }
230 |
231 | if (typeof successCallback !== 'function') {
232 | console.log('PushNotification.clearAllNotifications failure: success callback parameter must be a function');
233 | return;
234 | }
235 |
236 | successCallback();
237 | };
238 |
239 | /**
240 | * Listen for an event.
241 | *
242 | * The following events are supported:
243 | *
244 | * - registration
245 | * - notification
246 | * - error
247 | *
248 | * @param {String} eventName to subscribe to.
249 | * @param {Function} callback triggered on the event.
250 | */
251 |
252 | PushNotification.prototype.on = function (eventName, callback) {
253 | if (Object.prototype.hasOwnProperty.call(this._handlers, eventName)) {
254 | this._handlers[eventName].push(callback);
255 | }
256 | };
257 |
258 | /**
259 | * Remove event listener.
260 | *
261 | * @param {String} eventName to match subscription.
262 | * @param {Function} handle function associated with event.
263 | */
264 |
265 | PushNotification.prototype.off = function (eventName, handle) {
266 | if (Object.prototype.hasOwnProperty.call(this._handlers, eventName)) {
267 | var handleIndex = this._handlers[eventName].indexOf(handle);
268 | if (handleIndex >= 0) {
269 | this._handlers[eventName].splice(handleIndex, 1);
270 | }
271 | }
272 | };
273 |
274 | /**
275 | * Emit an event.
276 | *
277 | * This is intended for internal use only.
278 | *
279 | * @param {String} eventName is the event to trigger.
280 | * @param {*} all arguments are passed to the event listeners.
281 | *
282 | * @return {Boolean} is true when the event is triggered otherwise false.
283 | */
284 |
285 | PushNotification.prototype.emit = function () {
286 | var args = Array.prototype.slice.call(arguments);
287 | var eventName = args.shift();
288 |
289 | if (!Object.prototype.hasOwnProperty.call(this._handlers, eventName)) {
290 | return false;
291 | }
292 |
293 | for (var i = 0, length = this._handlers[eventName].length; i < length; i++) {
294 | var callback = this._handlers[eventName][i];
295 | if (typeof callback === 'function') {
296 | callback.apply(undefined, args);
297 | } else {
298 | console.log('event handler: ' + eventName + ' must be a function');
299 | }
300 | }
301 |
302 | return true;
303 | };
304 |
305 | PushNotification.prototype.finish = function (successCallback, errorCallback, id) {
306 | if (!successCallback) { successCallback = function () {}; }
307 | if (!errorCallback) { errorCallback = function () {}; }
308 | if (!id) { id = 'handler'; }
309 |
310 | if (typeof successCallback !== 'function') {
311 | console.log('finish failure: success callback parameter must be a function');
312 | return;
313 | }
314 |
315 | if (typeof errorCallback !== 'function') {
316 | console.log('finish failure: failure parameter not a function');
317 | return;
318 | }
319 |
320 | successCallback();
321 | };
322 |
323 | /*!
324 | * Push Notification Plugin.
325 | */
326 |
327 | /**
328 | * Converts the server key to an Uint8Array
329 | *
330 | * @param base64String
331 | *
332 | * @returns {Uint8Array}
333 | */
334 | function urlBase64ToUint8Array (base64String) {
335 | const padding = '='.repeat((4 - base64String.length % 4) % 4);
336 | const base64 = (base64String + padding)
337 | .replace(/-/g, '+')
338 | .replace(/_/g, '/');
339 |
340 | const rawData = window.atob(base64);
341 | const outputArray = new Uint8Array(rawData.length);
342 |
343 | for (var i = 0; i < rawData.length; ++i) {
344 | outputArray[i] = rawData.charCodeAt(i);
345 | }
346 | return outputArray;
347 | }
348 |
349 | module.exports = {
350 | /**
351 | * Register for Push Notifications.
352 | *
353 | * This method will instantiate a new copy of the PushNotification object
354 | * and start the registration process.
355 | *
356 | * @param {Object} options
357 | * @return {PushNotification} instance
358 | */
359 |
360 | init: function (options) {
361 | return new PushNotification(options);
362 | },
363 |
364 | hasPermission: function (successCallback, errorCallback) {
365 | const granted = Notification && Notification.permission === 'granted';
366 | successCallback({
367 | isEnabled: granted
368 | });
369 | },
370 |
371 | unregister: function (successCallback, errorCallback, options) {
372 | PushNotification.unregister(successCallback, errorCallback, options);
373 | },
374 |
375 | /**
376 | * PushNotification Object.
377 | *
378 | * Expose the PushNotification object for direct use
379 | * and testing. Typically, you should use the
380 | * .init helper method.
381 | */
382 |
383 | PushNotification
384 | };
385 |
--------------------------------------------------------------------------------
/www/push.js:
--------------------------------------------------------------------------------
1 | /**
2 | * This file has been generated by Babel.
3 | *
4 | * DO NOT EDIT IT DIRECTLY
5 | *
6 | * Edit the JS source file src/js/push.js
7 | **/
8 | "use strict";
9 |
10 | function _typeof(o) { "@babel/helpers - typeof"; return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { return typeof o; } : function (o) { return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; }, _typeof(o); }
11 | function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
12 | function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor); } }
13 | function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); Object.defineProperty(Constructor, "prototype", { writable: false }); return Constructor; }
14 | function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return _typeof(key) === "symbol" ? key : String(key); }
15 | function _toPrimitive(input, hint) { if (_typeof(input) !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (_typeof(res) !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
16 | /*!
17 | * Module dependencies.
18 | */
19 |
20 | var exec = cordova.require('cordova/exec');
21 | var PushNotification = /*#__PURE__*/function () {
22 | /**
23 | * PushNotification constructor.
24 | *
25 | * @param {Object} options to initiate Push Notifications.
26 | * @return {PushNotification} instance that can be monitored and cancelled.
27 | */
28 | function PushNotification(options) {
29 | var _this = this;
30 | _classCallCheck(this, PushNotification);
31 | this.handlers = {
32 | registration: [],
33 | notification: [],
34 | error: []
35 | };
36 |
37 | // require options parameter
38 | if (typeof options === 'undefined') {
39 | throw new Error('The options argument is required.');
40 | }
41 |
42 | // store the options to this object instance
43 | this.options = options;
44 |
45 | // triggered on registration and notification
46 | var success = function success(result) {
47 | if (result && typeof result.registrationId !== 'undefined') {
48 | _this.emit('registration', result);
49 | } else if (result && result.additionalData && typeof result.additionalData.actionCallback !== 'undefined') {
50 | _this.emit(result.additionalData.actionCallback, result);
51 | } else if (result) {
52 | _this.emit('notification', result);
53 | }
54 | };
55 |
56 | // triggered on error
57 | var fail = function fail(msg) {
58 | var e = typeof msg === 'string' ? new Error(msg) : msg;
59 | _this.emit('error', e);
60 | };
61 |
62 | // wait at least one process tick to allow event subscriptions
63 | setTimeout(function () {
64 | exec(success, fail, 'PushNotification', 'init', [options]);
65 | }, 10);
66 | }
67 |
68 | /**
69 | * Unregister from push notifications
70 | */
71 | _createClass(PushNotification, [{
72 | key: "unregister",
73 | value: function unregister(successCallback) {
74 | var _this2 = this;
75 | var errorCallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
76 | var options = arguments.length > 2 ? arguments[2] : undefined;
77 | if (typeof errorCallback !== 'function') {
78 | console.log('PushNotification.unregister failure: failure parameter not a function');
79 | return;
80 | }
81 | if (typeof successCallback !== 'function') {
82 | console.log('PushNotification.unregister failure: success callback parameter must be a function');
83 | return;
84 | }
85 | var cleanHandlersAndPassThrough = function cleanHandlersAndPassThrough() {
86 | if (!options) {
87 | _this2.handlers = {
88 | registration: [],
89 | notification: [],
90 | error: []
91 | };
92 | }
93 | successCallback();
94 | };
95 | exec(cleanHandlersAndPassThrough, errorCallback, 'PushNotification', 'unregister', [options]);
96 | }
97 |
98 | /**
99 | * subscribe to a topic
100 | * @param {String} topic topic to subscribe
101 | * @param {Function} successCallback success callback
102 | * @param {Function} errorCallback error callback
103 | * @return {void}
104 | */
105 | }, {
106 | key: "subscribe",
107 | value: function subscribe(topic, successCallback) {
108 | var errorCallback = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function () {};
109 | if (typeof errorCallback !== 'function') {
110 | console.log('PushNotification.subscribe failure: failure parameter not a function');
111 | return;
112 | }
113 | if (typeof successCallback !== 'function') {
114 | console.log('PushNotification.subscribe failure: success callback parameter must be a function');
115 | return;
116 | }
117 | exec(successCallback, errorCallback, 'PushNotification', 'subscribe', [topic]);
118 | }
119 |
120 | /**
121 | * unsubscribe to a topic
122 | * @param {String} topic topic to unsubscribe
123 | * @param {Function} successCallback success callback
124 | * @param {Function} errorCallback error callback
125 | * @return {void}
126 | */
127 | }, {
128 | key: "unsubscribe",
129 | value: function unsubscribe(topic, successCallback) {
130 | var errorCallback = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : function () {};
131 | if (typeof errorCallback !== 'function') {
132 | console.log('PushNotification.unsubscribe failure: failure parameter not a function');
133 | return;
134 | }
135 | if (typeof successCallback !== 'function') {
136 | console.log('PushNotification.unsubscribe failure: success callback parameter must be a function');
137 | return;
138 | }
139 | exec(successCallback, errorCallback, 'PushNotification', 'unsubscribe', [topic]);
140 | }
141 |
142 | /**
143 | * Call this to set the application icon badge
144 | */
145 | }, {
146 | key: "setApplicationIconBadgeNumber",
147 | value: function setApplicationIconBadgeNumber(successCallback) {
148 | var errorCallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
149 | var badge = arguments.length > 2 ? arguments[2] : undefined;
150 | if (typeof errorCallback !== 'function') {
151 | console.log('PushNotification.setApplicationIconBadgeNumber failure: failure ' + 'parameter not a function');
152 | return;
153 | }
154 | if (typeof successCallback !== 'function') {
155 | console.log('PushNotification.setApplicationIconBadgeNumber failure: success ' + 'callback parameter must be a function');
156 | return;
157 | }
158 | exec(successCallback, errorCallback, 'PushNotification', 'setApplicationIconBadgeNumber', [{
159 | badge: badge
160 | }]);
161 | }
162 |
163 | /**
164 | * Get the application icon badge
165 | */
166 | }, {
167 | key: "getApplicationIconBadgeNumber",
168 | value: function getApplicationIconBadgeNumber(successCallback) {
169 | var errorCallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
170 | if (typeof errorCallback !== 'function') {
171 | console.log('PushNotification.getApplicationIconBadgeNumber failure: failure ' + 'parameter not a function');
172 | return;
173 | }
174 | if (typeof successCallback !== 'function') {
175 | console.log('PushNotification.getApplicationIconBadgeNumber failure: success ' + 'callback parameter must be a function');
176 | return;
177 | }
178 | exec(successCallback, errorCallback, 'PushNotification', 'getApplicationIconBadgeNumber', []);
179 | }
180 |
181 | /**
182 | * Clear all notifications
183 | */
184 | }, {
185 | key: "clearAllNotifications",
186 | value: function clearAllNotifications() {
187 | var successCallback = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
188 | var errorCallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
189 | if (typeof errorCallback !== 'function') {
190 | console.log('PushNotification.clearAllNotifications failure: failure parameter not a function');
191 | return;
192 | }
193 | if (typeof successCallback !== 'function') {
194 | console.log('PushNotification.clearAllNotifications failure: success callback ' + 'parameter must be a function');
195 | return;
196 | }
197 | exec(successCallback, errorCallback, 'PushNotification', 'clearAllNotifications', []);
198 | }
199 |
200 | /**
201 | * Clears notifications that have the ID specified.
202 | * @param {Function} [successCallback] Callback function to be called on success.
203 | * @param {Function} [errorCallback] Callback function to be called when an error is encountered.
204 | * @param {Number} id ID of the notification to be removed.
205 | */
206 | }, {
207 | key: "clearNotification",
208 | value: function clearNotification() {
209 | var successCallback = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
210 | var errorCallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
211 | var id = arguments.length > 2 ? arguments[2] : undefined;
212 | var idNumber = parseInt(id, 10);
213 | if (Number.isNaN(idNumber) || idNumber > Number.MAX_SAFE_INTEGER || idNumber < 0) {
214 | console.log('PushNotification.clearNotification failure: id parameter must' + 'be a valid integer.');
215 | return;
216 | }
217 | exec(successCallback, errorCallback, 'PushNotification', 'clearNotification', [idNumber]);
218 | }
219 |
220 | /**
221 | * Listen for an event.
222 | *
223 | * The following events are supported:
224 | *
225 | * - registration
226 | * - notification
227 | * - error
228 | *
229 | * @param {String} eventName to subscribe to.
230 | * @param {Function} callback triggered on the event.
231 | */
232 | }, {
233 | key: "on",
234 | value: function on(eventName, callback) {
235 | if (!Object.prototype.hasOwnProperty.call(this.handlers, eventName)) {
236 | this.handlers[eventName] = [];
237 | }
238 | this.handlers[eventName].push(callback);
239 | }
240 |
241 | /**
242 | * Remove event listener.
243 | *
244 | * @param {String} eventName to match subscription.
245 | * @param {Function} handle function associated with event.
246 | */
247 | }, {
248 | key: "off",
249 | value: function off(eventName, handle) {
250 | if (Object.prototype.hasOwnProperty.call(this.handlers, eventName)) {
251 | var handleIndex = this.handlers[eventName].indexOf(handle);
252 | if (handleIndex >= 0) {
253 | this.handlers[eventName].splice(handleIndex, 1);
254 | }
255 | }
256 | }
257 |
258 | /**
259 | * Emit an event.
260 | *
261 | * This is intended for internal use only.
262 | *
263 | * @param {String} eventName is the event to trigger.
264 | * @param {*} all arguments are passed to the event listeners.
265 | *
266 | * @return {Boolean} is true when the event is triggered otherwise false.
267 | */
268 | }, {
269 | key: "emit",
270 | value: function emit() {
271 | for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
272 | args[_key] = arguments[_key];
273 | }
274 | var eventName = args.shift();
275 | if (!Object.prototype.hasOwnProperty.call(this.handlers, eventName)) {
276 | return false;
277 | }
278 | for (var i = 0, length = this.handlers[eventName].length; i < length; i += 1) {
279 | var callback = this.handlers[eventName][i];
280 | if (typeof callback === 'function') {
281 | callback.apply(void 0, args); // eslint-disable-line n/no-callback-literal
282 | } else {
283 | console.log("event handler: ".concat(eventName, " must be a function"));
284 | }
285 | }
286 | return true;
287 | }
288 | }, {
289 | key: "finish",
290 | value: function finish() {
291 | var successCallback = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : function () {};
292 | var errorCallback = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function () {};
293 | var id = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 'handler';
294 | if (typeof successCallback !== 'function') {
295 | console.log('finish failure: success callback parameter must be a function');
296 | return;
297 | }
298 | if (typeof errorCallback !== 'function') {
299 | console.log('finish failure: failure parameter not a function');
300 | return;
301 | }
302 | exec(successCallback, errorCallback, 'PushNotification', 'finish', [id]);
303 | }
304 | }]);
305 | return PushNotification;
306 | }();
307 | /*!
308 | * Push Notification Plugin.
309 | */
310 | module.exports = {
311 | /**
312 | * Register for Push Notifications.
313 | *
314 | * This method will instantiate a new copy of the PushNotification object
315 | * and start the registration process.
316 | *
317 | * @param {Object} options
318 | * @return {PushNotification} instance
319 | */
320 |
321 | init: function init(options) {
322 | return new PushNotification(options);
323 | },
324 | hasPermission: function hasPermission(successCallback, errorCallback) {
325 | exec(successCallback, errorCallback, 'PushNotification', 'hasPermission', []);
326 | },
327 | createChannel: function createChannel(successCallback, errorCallback, channel) {
328 | exec(successCallback, errorCallback, 'PushNotification', 'createChannel', [channel]);
329 | },
330 | deleteChannel: function deleteChannel(successCallback, errorCallback, channelId) {
331 | exec(successCallback, errorCallback, 'PushNotification', 'deleteChannel', [channelId]);
332 | },
333 | listChannels: function listChannels(successCallback, errorCallback) {
334 | exec(successCallback, errorCallback, 'PushNotification', 'listChannels', []);
335 | },
336 | /**
337 | * PushNotification Object.
338 | *
339 | * Expose the PushNotification object for direct use
340 | * and testing. Typically, you should use the
341 | * .init helper method.
342 | */
343 | PushNotification: PushNotification
344 | };
--------------------------------------------------------------------------------