├── .github └── ISSUE_TEMPLATE │ └── bug-report.md ├── .gitignore ├── .npmignore ├── LICENSE ├── README.md ├── android ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── jeepeng │ │ └── react │ │ └── xgpush │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── com │ │ │ └── jeepeng │ │ │ └── react │ │ │ └── xgpush │ │ │ ├── Constants.java │ │ │ ├── PushMessage.java │ │ │ ├── PushModule.java │ │ │ ├── PushPackage.java │ │ │ └── receiver │ │ │ ├── HWReceiver.java │ │ │ └── MessageReceiver.java │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── com │ └── jeepeng │ └── react │ └── xgpush │ └── ExampleUnitTest.java ├── example ├── .babelrc ├── .buckconfig ├── .flowconfig ├── .gitattributes ├── .gitignore ├── .watchmanconfig ├── __tests__ │ └── index.js ├── android │ ├── app │ │ ├── BUCK │ │ ├── build.gradle │ │ ├── proguard-rules.pro │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── assets │ │ │ ├── index.android.bundle │ │ │ └── index.android.bundle.meta │ │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── keystores │ │ ├── BUCK │ │ └── debug.keystore.properties │ └── settings.gradle ├── app.json ├── app │ ├── Touchable.js │ └── index.js ├── index.js ├── ios │ ├── example-tvOS │ │ └── Info.plist │ ├── example-tvOSTests │ │ └── Info.plist │ ├── example.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── example-tvOS.xcscheme │ │ │ └── example.xcscheme │ ├── example │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Base.lproj │ │ │ └── LaunchScreen.xib │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── example.entitlements │ │ └── main.m │ └── exampleTests │ │ ├── Info.plist │ │ └── exampleTests.m ├── package-lock.json └── package.json ├── index.js ├── ios ├── SDK │ └── XGPush.h ├── XGPush.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcuserdata │ │ │ └── Jeepeng.xcuserdatad │ │ │ └── UserInterfaceState.xcuserstate │ └── xcuserdata │ │ └── Jeepeng.xcuserdatad │ │ └── xcschemes │ │ ├── XGPush.xcscheme │ │ └── xcschememanagement.plist ├── XGPush │ ├── XGPushManager.h │ └── XGPushManager.m └── libXG-SDK.a ├── package.json └── react-native-xinge-push.podspec /.github/ISSUE_TEMPLATE/bug-report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | 5 | --- 6 | 7 | **描述** 8 | 请清晰简洁地描述你遇到的问题 9 | 10 | **复现步骤** 11 | 重现bug的步骤: 12 | 1. Go to '...' 13 | 2. Click on '....' 14 | 3. Scroll down to '....' 15 | 4. See error 16 | 17 | **预期的行为** 18 | 请清晰简洁地描述你预期会发生什么 19 | 20 | **截图** 21 | 如果需要,请添加屏幕截图以帮助解释您的问题 22 | 23 | **手机 (请填写下面信息):** 24 | - 手机型号: [e.g. 华为 Mate 9] 25 | - 系统版本: [e.g. Android 8.0] 26 | - 版本 [e.g. 0.6] 27 | 28 | **附加信息** 29 | 在这里添加关于问题的其他信息 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 50 | 51 | fastlane/report.xml 52 | fastlane/Preview.html 53 | fastlane/screenshots 54 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | example/ 2 | 3 | .idea 4 | android/build/ 5 | android/*.iml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## 信鸽推送服务应该会在2020年10月31号之前停止免费服务,故 react-native-xinge-push 应该也不会再有维护。谢谢大家的支持~ 2 | 3 | 4 | ## 版本对照表 5 | react-native-xinge-push | 信鸽SDK(Android) | 信鸽SDK(iOS) 6 | ---|---|--- 7 | 1.1 | 4.3.5 | v3.3.7 8 | 1.0 | 4.3.2 | v3.3.5 9 | 0.6 | 3.2.2 | 3.1.1 10 | 0.4~0.5 | 3.2.2 | 2.5.0 11 | 0.3 | 3.1.0 | 2.5.0 12 | 13 | ## 简介 - 20191122 更新 Android SDK 4.3.5 14 | Android SDK 更新之后发现会有些异常无法启动service 15 | 需要配置:https://xg.qq.com/docs/android_access/android_p_compatibility.html 16 | 17 | 18 | ## 简介 - 20190809 正式更新 19 | 20 | 经过一段时间的项目测试和完善,虽然中间遇到很多推送的坑。但是还是一直坚持使用信鸽推送,第一免费,第二支持内地厂商通道以及海外FCM同时推送。 21 | 22 | ## TODO 23 | - [ ] 如何在React Native 中优雅的使用这个推送 (Redux+Saga实战) 24 | 25 | ## New Features 26 | - [x] Android 适配FCM,小米,华为(魅族没申请到无法测试)文档 27 | - [x] 支持厂商通道的intent来点击回调获取参数或者跳转自定义页面 28 | - [x] 修改注册机制,信鸽推送在一开启App时就开始注册而非在RN触发注册 29 | - [x] Android 配置无需在RN配置,直接在 app.gradle 文件配置即可 30 | - [x] Andrroid getInititalNotficaiton 功能完成并在FCM,小米,华为机均测试通过 31 | - [x] iOS 在getInititalNotficaiton 之后清除通知 32 | 33 | ## Done 34 | - [x] 支持Fcm 集成 35 | - [x] 计划更新 iOS SDK 到 v3.3.5 36 | - [x] 计划更新 Android SDK v4.3.2 厂商通道 37 | - [x] 升级信鸽Android SDK 到 v3.2.2 38 | - [x] 适配华为官方推送通道 39 | - [x] 适配小米官方推送通道 40 | - [x] 适配魅族官方推送通道 41 | - [x] 升级信鸽iOS SDK 到 v3.1.0 42 | 43 | ## 简介 - 20190227 更新 44 | 因为项目开发决定采用信鸽推送,鉴于信鸽官方并不支持 React Native,所幸有Jeepeng 推出了 https://github.com/Jeepeng/react-native-xinge-push, 是已经好久没有更新了。同时这个库遇到 https://github.com/Jeepeng/react-native-xinge-push/issues/22 这个问题,所幸https://github.com/wanxsb/react-native-xinge-push 在这个版本已经解决。接下去因为项目会涉及xinge推送的开发以及维护,所以会有些时间帮忙维护下一个库。本人比较熟悉iOS&Android原生开发,同时负责的团队也正在往React Native过渡,希望可以给社区贡献一点力量。 45 | 46 | 47 | 48 | 49 | ## install 50 | ``` 51 | npm install --save https://github.com/PandaQQ/react-native-xinge-push.git 52 | ``` 53 | 54 | ## link 55 | 56 | ``` 57 | react-native link react-native-xinge-push 58 | ``` 59 | 60 | ## usage 61 | ### Android 62 | 63 | Android 的推送简直就是个无敌大坑啊!!!!!但是终于我还是搞定啦哈哈哈哈~~ 64 | 65 | 66 | #### Android 集成攻略 67 | 68 | 众所周知,当Application启动完成之后才开始启动React Native。所以在React Native中初始化并且注册信鸽推送服务其实并不是特别理想。因为当App 已经被杀死的时候,就无法获取推送的消息内容并作下一步的业务。所以在Android平台下,我们必须要注册信鸽服务于React Native之前。 69 | 70 | 71 | - 1. 在 build.gradle (Module:app) 下配置 manifestPlaceholders, (注意 my_xm_xg_id和my_xm_xg_key需要在XM-后添加id和key, 因为我发现Android读取全是数字的id和key会自动转成Float - *神一样的操作,写这个的人出来看我不打死他!*) 72 | 73 | ```javascript 74 | manifestPlaceholders = [ 75 | XG_ACCESS_ID : "", 76 | XG_ACCESS_KEY: "", 77 | HW_APPID : "", 78 | PACKAGE_NAME : "", 79 | my_xm_xg_id : "XM-", 80 | my_xm_xg_key: "XM-", 81 | ] 82 | ``` 83 | 84 | - 2. 在项目的AndroidManifest.xml 85 | 86 | ```xml 87 | 90 | 93 | ``` 94 | 95 | - 3. 在项目的AndroidManifest.xml 中的 MainActivity下添加 intent-filter 96 | 97 | ```xml 98 | 105 | 106 | 107 | 108 | 109 | 112 | 113 | 114 | ``` 115 | 116 | - 4. 在MainActivity 中添加 intent 接受方法 117 | 118 | ```java 119 | @Override 120 | public void onCreate(Bundle instance) { 121 | 122 | super.onCreate(instance); 123 | //// 集成厂商通道之后,点击推送通知使用 intent 推送内容 124 | Uri uri = getIntent().getData(); 125 | PushMessage.getInstance().setAllValueIntentUrl(uri); 126 | } 127 | ``` 128 | 129 | #### Android intent 测试 130 | 131 | https://xg.qq.com/docs/android_access/android_faq.html#消息点击事件以及跳转页面方法 132 | 133 | - 测试过上述连接给出的方法再做总结,下面这段代码不行,获取不到参数,但是和使用无关: 134 | 135 | ```java 136 | 137 | Uri uri = getIntent().getData(); 138 | if (uri != null) { 139 | String url = uri.toString(); 140 | UrlQuerySanitizer sanitizer = new UrlQuerySanitizer(); 141 | sanitizer.setUnregisteredParameterValueSanitizer(UrlQuerySanitizer.getAllButNulLegal()); 142 | sanitizer.parseUrl(url); 143 | String value1 = sanitizer.getValue("key1"); 144 | String value2 = sanitizer.getValue("key2"); 145 | Log.i("XG" , "value1 = " + value1 + " value2 = " + value2); 146 | } 147 | 148 | ``` 149 | 150 | - intent 调用sample,注意key要符合定义,否则库收不到参数。(title, content和custom_content) 151 | 152 | ``` 153 | xgscheme://com.xg.push/notify_detail?title=aa&content=bb&custom_content= 154 | ``` 155 | 156 | #### FCM通道集成指南 157 | https://xg.qq.com/docs/android_access/FCM.html 158 | 159 | #### 小米通道集成指南 160 | https://xg.qq.com/docs/android_access/mi_push.html 161 | 162 | #### 华为推送通道集成指南 163 | http://xg.qq.com/docs/android_access/huawei_push.html 164 | 165 | 1. 确认已在信鸽管理台中「应用配置-厂商&海外通道」中填写相关的应用信息。通常,相关配置将在1个小时后生效,请您耐心等待,在生效后再进行下一个步骤 166 | 2. 将集成好的App(测试版本)安装在测试机上,并且运行App 167 | 3. 保持App在前台运行,尝试对设备进行单推/全推 168 | 4. 如果应用收到消息,将App退到后台,并且杀掉所有App进程 169 | 5. 再次进行单推/全推,如果能够收到推送,则表明厂商通道集成成功 170 | 171 | ###### 注意事项 172 | 消息目前将其理解为两类:静默消息和普通通知 173 | 静默消息不会弹窗,通知会弹窗 174 | 175 | 如果在EMUI 8.0(Android 8)上,出现发通知成功但通知栏不显示的情况,并在Logcat看到以下错误: 176 | ``` 177 | E/NotificationService: No Channel found for pkg=com.jeepeng.push, channelId=null, id=995033369, tag=null, opPkg=com.huawei.android.pushagent, callingUid=10060, userId=0, incomingUserId=0, notificationUid=10261, notification=Notification(channel=null pri=0 contentView=null vibrate=null sound=default tick defaults=0x1 flags=0x10 color=0x00000000 vis=PRIVATE) 178 | ``` 179 | 180 | 需要将`targetSdkVersion`[降到25](https://stackoverflow.com/questions/45668079/notificationchannel-issue-in-android-o) 181 | 182 | #### 183 | 184 | 185 | 186 | ### iOS 187 | AppDelegate.m: 188 | 189 | ```oc 190 | /** 191 | * Copyright (c) 2015-present, Facebook, Inc. 192 | * All rights reserved. 193 | * 194 | * This source code is licensed under the BSD-style license found in the 195 | * LICENSE file in the root directory of this source tree. An additional grant 196 | * of patent rights can be found in the PATENTS file in the same directory. 197 | */ 198 | 199 | #import "AppDelegate.h" 200 | 201 | #import 202 | #import 203 | #import 204 | #import 205 | 206 | @implementation AppDelegate 207 | 208 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 209 | { 210 | NSURL *jsCodeLocation; 211 | 212 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 213 | 214 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 215 | moduleName:@"example" 216 | initialProperties:nil 217 | launchOptions:launchOptions]; 218 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 219 | 220 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 221 | UIViewController *rootViewController = [UIViewController new]; 222 | rootViewController.view = rootView; 223 | self.window.rootViewController = rootViewController; 224 | [self.window makeKeyAndVisible]; 225 | // 统计消息推送的抵达情况 226 | [[XGPush defaultManager] reportXGNotificationInfo:launchOptions]; 227 | return YES; 228 | } 229 | 230 | // Required to register for notifications 231 | - (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings 232 | { 233 | [XGPushManager didRegisterUserNotificationSettings:notificationSettings]; 234 | } 235 | 236 | - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { 237 | [XGPushManager didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; 238 | } 239 | 240 | // Required for the registrationError event. 241 | - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { 242 | NSLog(@"[XGPush] register APNS fail.\n[XGPush] reason : %@", error); 243 | [XGPushManager didFailToRegisterForRemoteNotificationsWithError:error]; 244 | } 245 | 246 | // Required for the localNotification event. 247 | - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification 248 | { 249 | [XGPushManager didReceiveLocalNotification:notification]; 250 | } 251 | 252 | /** 253 | 收到通知消息的回调,通常此消息意味着有新数据可以读取(iOS 7.0+) 254 | 255 | @param application UIApplication 实例 256 | @param userInfo 推送时指定的参数 257 | @param completionHandler 完成回调 258 | */ 259 | - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { 260 | NSLog(@"[XGPush] receive slient Notification"); 261 | NSLog(@"[XGPush] userinfo %@", userInfo); 262 | UIApplicationState state = [application applicationState]; 263 | BOOL isClicked = (state != UIApplicationStateActive); 264 | NSMutableDictionary *remoteNotification = [NSMutableDictionary dictionaryWithDictionary:userInfo]; 265 | if(isClicked) { 266 | remoteNotification[@"clicked"] = @YES; 267 | remoteNotification[@"background"] = @YES; 268 | } 269 | [[XGPush defaultManager] reportXGNotificationInfo:remoteNotification]; 270 | [XGPushManager didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; 271 | } 272 | 273 | @end 274 | 275 | ``` 276 | 277 | ## Example 278 | 279 | see `example` folder for more details 280 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 26 5 | buildToolsVersion '27.0.3' 6 | 7 | defaultConfig { 8 | minSdkVersion 16 9 | targetSdkVersion 26 10 | versionCode 1 11 | versionName "1.0" 12 | } 13 | defaultConfig { 14 | consumerProguardFiles 'proguard-rules.pro' 15 | manifestPlaceholders = [ 16 | XG_ACCESS_ID: "", 17 | XG_ACCESS_KEY: "", 18 | HW_APPID: "" 19 | ] 20 | } 21 | 22 | sourceSets { 23 | main { 24 | jniLibs.srcDirs = ['libs'] 25 | } 26 | } 27 | } 28 | 29 | dependencies { 30 | compile fileTree(dir: 'libs', include: ['*.jar']) 31 | testCompile 'junit:junit:4.12' 32 | compile "com.facebook.react:react-native:+" 33 | compile "me.leolin:ShortcutBadger:1.1.21@aar" 34 | // compile 'com.tencent.xinge:xinge:3.2.4-beta' 35 | // compile 'com.tencent.wup:wup:1.0.0.E-release' 36 | // compile 'com.tencent.mid:mid:4.0.6-release' 37 | // compile 'com.tencent.xinge:xghw:3.2.4-beta' 38 | // compile 'com.tencent.xinge:mipush:3.2.4-beta' 39 | // compile 'com.tencent.xinge:xgmz:3.2.4-beta' 40 | //增加以下依赖 41 | 42 | implementation 'com.tencent.xinge:xinge:4.3.7-release' 43 | implementation 'com.tencent.wup:wup:1.0.0.E-Release' 44 | implementation 'com.tencent.jg:jg:1.1' 45 | implementation 'com.tencent.mid:mid:4.0.7-Release' 46 | implementation 'com.tencent.xinge:mipush:4.3.2-xiaomi-release' 47 | implementation 'com.tencent.xinge:xgmz:4.3.2-meizu-release' 48 | implementation 'com.tencent.xinge:xghw:4.3.2-huawei-release' 49 | //fcm通道需要增加以下依赖并将google-services.json放进你应用model的根路径下。 50 | implementation 'com.tencent.xinge:fcm:4.3.2-release' 51 | implementation 'com.google.firebase:firebase-messaging:17.3.1' 52 | 53 | } 54 | -------------------------------------------------------------------------------- /android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Library/Android/SDK/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | ## 信鸽 20 | -keep public class * extends android.app.Service 21 | -keep public class * extends android.content.BroadcastReceiver 22 | -keep class com.tencent.android.tpush.** {*;} 23 | -keep class com.tencent.mid.** {*;} 24 | -keep class com.qq.taf.jce.** {*;} 25 | -keep class com.tencent.bigdata.** {*;} 26 | 27 | ## 华为通道 28 | -ignorewarning 29 | -keepattributes *Annotation* 30 | -keepattributes Exceptions 31 | -keepattributes InnerClasses 32 | -keepattributes Signature 33 | -keepattributes SourceFile,LineNumberTable 34 | -keep class com.hianalytics.android.**{*;} 35 | -keep class com.huawei.updatesdk.**{*;} 36 | -keep class com.huawei.hms.**{*;} 37 | -keep class com.huawei.android.hms.agent.**{*;} 38 | 39 | ## 小米通道 40 | -keep class com.xiaomi.**{*;} 41 | -keep public class * extends com.xiaomi.mipush.sdk.PushMessageReceiver 42 | 43 | ## 魅族通道 44 | -dontwarn com.meizu.cloud.pushsdk.** 45 | -keep class com.meizu.cloud.pushsdk.**{*;} 46 | -------------------------------------------------------------------------------- /android/src/androidTest/java/com/jeepeng/react/xgpush/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.jeepeng.react.xgpush; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 8 | 9 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /android/src/main/java/com/jeepeng/react/xgpush/Constants.java: -------------------------------------------------------------------------------- 1 | package com.jeepeng.react.xgpush; 2 | 3 | /** 4 | * 常量 5 | * Created by Jeepeng on 16/8/15. 6 | */ 7 | public class Constants { 8 | public static final String ACTION_ON_REGISTERED = "XGPushNotificationOnRegisterResult"; 9 | public static final String ACTION_ON_TEXT_MESSAGE = "XGPushNotificationOnTextMessage"; 10 | public static final String ACTION_ON_NOTIFICATION_SHOWED = "XGPushNotificationOnShowed"; 11 | public static final String ACTION_ON_NOTIFICATION_CLICKED = "XGPushNotificationOnClicked"; 12 | 13 | public static final String EVENT_REGISTERED = "remoteNotificationsRegistered"; 14 | public static final String EVENT_REMOTE_NOTIFICATION_RECEIVED = "remoteNotificationReceived"; 15 | public static final String EVENT_LOCAL_NOTIFICATION_RECEIVED = "localNotificationReceived"; 16 | public static final String EVENT_MESSAGE_RECEIVED = "messageReceived"; 17 | } 18 | -------------------------------------------------------------------------------- /android/src/main/java/com/jeepeng/react/xgpush/PushMessage.java: -------------------------------------------------------------------------------- 1 | package com.jeepeng.react.xgpush; 2 | 3 | import android.net.Uri; 4 | import android.net.UrlQuerySanitizer; 5 | import android.util.Log; 6 | 7 | public class PushMessage { 8 | private static final PushMessage ourInstance = new PushMessage(); 9 | 10 | public static PushMessage getInstance() { 11 | return ourInstance; 12 | } 13 | 14 | private String Title = null; 15 | private String Content = null; 16 | private String CustomContent = null; 17 | public boolean hasValue = false; 18 | 19 | public String getTitle() { 20 | return Title; 21 | } 22 | 23 | public void setTitle(String title) { 24 | Title = title; 25 | } 26 | 27 | public String getContent() { 28 | return Content; 29 | } 30 | 31 | public void setContent(String content) { 32 | Content = content; 33 | } 34 | 35 | public String getCustomContent() { 36 | return CustomContent; 37 | } 38 | 39 | public void setCustomContent(String customContent) { 40 | CustomContent = customContent; 41 | } 42 | 43 | public void clearAll() { 44 | CustomContent = null; 45 | Content = null; 46 | Title = null; 47 | hasValue = false; 48 | } 49 | 50 | public void setAllValue (String title, String content, String customContent) { 51 | this.Title = title; 52 | this.Content = content; 53 | this.CustomContent = customContent; 54 | this.hasValue = true; 55 | } 56 | 57 | 58 | 59 | private PushMessage() { 60 | } 61 | 62 | public void setAllValueIntentUrl (Uri pushUri) { 63 | 64 | if(pushUri != null) { 65 | 66 | String title= pushUri.getQueryParameter("title"); 67 | String content= pushUri.getQueryParameter("content"); 68 | String customContent = pushUri.getQueryParameter("custom_content"); 69 | 70 | Log.d("XGQQ_PushMessage", "1. " + title); 71 | Log.d("XGQQ_PushMessage", "2. " + content); 72 | Log.d("XGQQ_PushMessage", "3. " + customContent); 73 | 74 | setAllValue(title, content, customContent); 75 | } 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /android/src/main/java/com/jeepeng/react/xgpush/PushModule.java: -------------------------------------------------------------------------------- 1 | package com.jeepeng.react.xgpush; 2 | 3 | import android.app.Activity; 4 | import android.content.BroadcastReceiver; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.IntentFilter; 8 | import android.support.annotation.Nullable; 9 | import android.util.Log; 10 | 11 | import android.content.pm.ApplicationInfo; 12 | import android.content.pm.PackageManager; 13 | 14 | import com.facebook.react.bridge.ActivityEventListener; 15 | import com.facebook.react.bridge.Arguments; 16 | import com.facebook.react.bridge.Callback; 17 | import com.facebook.react.bridge.LifecycleEventListener; 18 | import com.facebook.react.bridge.Promise; 19 | import com.facebook.react.bridge.ReactApplicationContext; 20 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 21 | import com.facebook.react.bridge.ReactMethod; 22 | import com.facebook.react.bridge.WritableMap; 23 | import com.facebook.react.modules.core.DeviceEventManagerModule; 24 | import com.tencent.android.tpush.XGIOperateCallback; 25 | import com.tencent.android.tpush.XGLocalMessage; 26 | import com.tencent.android.tpush.XGPushClickedResult; 27 | import com.tencent.android.tpush.XGPushConfig; 28 | import com.tencent.android.tpush.XGPushManager; 29 | import com.tencent.android.tpush.encrypt.Rijndael; 30 | 31 | import me.leolin.shortcutbadger.ShortcutBadger; 32 | 33 | /** 34 | * 信鸽推送 35 | * Created by Jeepeng on 16/8/3. 36 | */ 37 | public class PushModule extends ReactContextBaseJavaModule implements ActivityEventListener, LifecycleEventListener { 38 | 39 | public static final String MODULE_NAME = "XGPushManager"; 40 | 41 | private Context reactContext; 42 | private int badge = 0; 43 | private String XM_ACCESS_ID = null; 44 | private String XM_ACCESS_KEY = null; 45 | 46 | public PushModule(ReactApplicationContext reactContext) { 47 | super(reactContext); 48 | reactContext.addActivityEventListener(this); 49 | reactContext.addLifecycleEventListener(this); 50 | this.reactContext = reactContext; 51 | registerReceivers(); 52 | 53 | XGPushConfig.enableFcmPush(this.reactContext, true); 54 | XGPushConfig.setHuaweiDebug(true); 55 | XGPushConfig.enableOtherPush(this.reactContext, true); 56 | 57 | // Handle MI PUSH CONFIG 58 | //////////////////////////////////////////////////// 59 | //////////////////////////////////////////////////// 60 | try { 61 | ApplicationInfo appInfo = reactContext.getPackageManager().getApplicationInfo(reactContext.getPackageName(), 62 | PackageManager.GET_META_DATA); 63 | 64 | XM_ACCESS_ID = appInfo.metaData.getString("MY_XM_XG_ID"); 65 | XM_ACCESS_KEY = appInfo.metaData.getString("MY_XM_XG_KEY"); 66 | 67 | } catch (PackageManager.NameNotFoundException e) { 68 | e.printStackTrace(); 69 | } 70 | 71 | if((XM_ACCESS_ID != null && XM_ACCESS_ID.length() != 0) || (XM_ACCESS_KEY != null && XM_ACCESS_KEY.length() != 0)) { 72 | 73 | Log.d("XGQQ", "1. " + XM_ACCESS_ID.replace("XM-", "")); 74 | Log.d("XGQQ", "2. " + XM_ACCESS_KEY.replace("XM-","")); 75 | XGPushConfig.setMiPushAppId(this.reactContext, XM_ACCESS_ID.replace("XM-", "")); 76 | XGPushConfig.setMiPushAppKey(this.reactContext, XM_ACCESS_KEY.replace("XM-","")); 77 | } 78 | //////////////////////////////////////////////////// 79 | //////////////////////////////////////////////////// 80 | 81 | XGPushManager.registerPush(this.reactContext); 82 | 83 | } 84 | 85 | @Override 86 | public String getName() { 87 | return MODULE_NAME; 88 | } 89 | 90 | private void sendEvent(String eventName, @Nullable WritableMap params) { 91 | if(getReactApplicationContext().hasActiveCatalystInstance()){ 92 | getReactApplicationContext() 93 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class) 94 | .emit(eventName, params); 95 | } 96 | } 97 | 98 | private void registerReceivers() { 99 | IntentFilter intentFilter = new IntentFilter(); 100 | intentFilter.addAction(Constants.ACTION_ON_REGISTERED); 101 | intentFilter.addAction(Constants.ACTION_ON_TEXT_MESSAGE); 102 | intentFilter.addAction(Constants.ACTION_ON_NOTIFICATION_CLICKED); 103 | intentFilter.addAction(Constants.ACTION_ON_NOTIFICATION_SHOWED); 104 | 105 | reactContext.registerReceiver(new BroadcastReceiver() { 106 | @Override 107 | public void onReceive(Context context, Intent intent) { 108 | WritableMap params = Arguments.createMap(); 109 | switch (intent.getAction()){ 110 | case Constants.ACTION_ON_REGISTERED: 111 | intent.getBundleExtra("notification"); 112 | String token = intent.getStringExtra("token"); 113 | params.putString("deviceToken", token); 114 | 115 | sendEvent(Constants.EVENT_REGISTERED, params); 116 | break; 117 | case Constants.ACTION_ON_TEXT_MESSAGE: 118 | String title = intent.getStringExtra("title"); 119 | String content = intent.getStringExtra("content"); 120 | String customContent = intent.getStringExtra("custom_content"); 121 | params.putString("title", title); 122 | params.putString("content", content); 123 | params.putString("custom_content", customContent); 124 | 125 | sendEvent(Constants.EVENT_MESSAGE_RECEIVED, params); 126 | break; 127 | case Constants.ACTION_ON_NOTIFICATION_SHOWED: 128 | params.putString("title", intent.getStringExtra("title")); 129 | params.putString("content", intent.getStringExtra("content")); 130 | params.putString("custom_content", intent.getStringExtra("custom_content")); 131 | 132 | sendEvent(Constants.EVENT_REMOTE_NOTIFICATION_RECEIVED, params); 133 | break; 134 | case Constants.ACTION_ON_NOTIFICATION_CLICKED: 135 | params.putString("title", intent.getStringExtra("title")); 136 | params.putString("content", intent.getStringExtra("content")); 137 | params.putString("custom_content", intent.getStringExtra("custom_content")); 138 | params.putBoolean("clicked", true); 139 | 140 | sendEvent(Constants.EVENT_REMOTE_NOTIFICATION_RECEIVED, params); 141 | break; 142 | default: 143 | break; 144 | } 145 | 146 | 147 | } 148 | }, intentFilter); 149 | } 150 | 151 | /***************************************************************** 152 | * XGPushManager功能类 153 | * (对于本类提供的set和enable方法,要在XGPushManager接口前调用才能及时生效) 154 | *****************************************************************/ 155 | 156 | /** 157 | * 启动并注册APP 158 | */ 159 | @ReactMethod 160 | public void registerPush(final Promise promise) { 161 | XGPushManager.registerPush(this.reactContext, new XGIOperateCallback() { 162 | @Override 163 | public void onSuccess(Object date, int flag) { 164 | promise.resolve(date); 165 | } 166 | 167 | @Override 168 | public void onFail(Object data, int errCode, String msg) { 169 | promise.resolve(data); 170 | } 171 | }); 172 | } 173 | 174 | /** 175 | * 启动并注册APP,同时绑定账号,推荐有帐号体系的APP使用 176 | * (此接口会覆盖设备之前绑定过的账号,仅当前注册的账号生效) 177 | * @param account 178 | * @param promise 179 | */ 180 | @ReactMethod 181 | public void bindAccount(String account, final Promise promise) { 182 | XGPushManager.bindAccount(this.reactContext, account, new XGIOperateCallback() { 183 | @Override 184 | public void onSuccess(Object date, int flag) { 185 | promise.resolve(date); 186 | } 187 | 188 | @Override 189 | public void onFail(Object data, int errCode, String msg) { 190 | promise.reject(String.valueOf(errCode), msg); 191 | } 192 | }); 193 | } 194 | 195 | /** 196 | * 启动并注册APP,同时绑定账号,推荐有帐号体系的APP使用 197 | * (此接口保留之前的账号,只做增加操作,一个token下最多只能有3个账号超过限制会自动顶掉之前绑定的账号) 198 | * @param account 199 | * @param promise 200 | */ 201 | @ReactMethod 202 | public void appendAccount(String account, final Promise promise) { 203 | XGPushManager.appendAccount(this.reactContext, account, new XGIOperateCallback() { 204 | @Override 205 | public void onSuccess(Object date, int flag) { 206 | promise.resolve(date); 207 | } 208 | 209 | @Override 210 | public void onFail(Object data, int errCode, String msg) { 211 | promise.reject(String.valueOf(errCode), msg); 212 | } 213 | }); 214 | } 215 | 216 | /** 217 | * 解绑指定账号 218 | * @param account 219 | * @param promise 220 | */ 221 | @ReactMethod 222 | public void delAccount(String account, final Promise promise) { 223 | XGPushManager.delAccount(this.reactContext, account, new XGIOperateCallback() { 224 | @Override 225 | public void onSuccess(Object date, int flag) { 226 | promise.resolve(date); 227 | } 228 | 229 | @Override 230 | public void onFail(Object data, int errCode, String msg) { 231 | promise.reject(String.valueOf(errCode), msg); 232 | } 233 | }); 234 | } 235 | 236 | /** 237 | * 反注册 238 | * @param promise 239 | */ 240 | @ReactMethod 241 | public void unregisterPush(final Promise promise) { 242 | XGPushManager.unregisterPush(this.reactContext, new XGIOperateCallback() { 243 | @Override 244 | public void onSuccess(Object data, int flag) { 245 | WritableMap map = Arguments.createMap(); 246 | map.putString("data", (String) data); 247 | map.putInt("flag", flag); 248 | promise.resolve(map); 249 | } 250 | 251 | @Override 252 | public void onFail(Object data, int errCode, String msg) { 253 | promise.reject(String.valueOf(errCode), msg); 254 | } 255 | }); 256 | } 257 | 258 | /** 259 | * 设置tag 260 | * @param tagName 261 | */ 262 | @ReactMethod 263 | public void setTag(String tagName) { 264 | XGPushManager.setTag(this.reactContext, tagName); 265 | } 266 | 267 | /** 268 | * 删除tag 269 | * @param tagName 270 | */ 271 | @ReactMethod 272 | public void deleteTag(String tagName) { 273 | XGPushManager.deleteTag(this.reactContext, tagName); 274 | } 275 | 276 | @ReactMethod 277 | public void addLocalNotification(String title, String content) { 278 | XGLocalMessage message = new XGLocalMessage(); 279 | message.setTitle(title); 280 | message.setContent(content); 281 | Log.i(MODULE_NAME, title); 282 | Log.i(MODULE_NAME, content); 283 | XGPushManager.addLocalNotification(this.reactContext, message); 284 | } 285 | 286 | /** 287 | * 检测通知栏是否关闭 288 | * @param promise 289 | */ 290 | @ReactMethod 291 | public void isNotificationOpened(Promise promise) { 292 | promise.resolve(XGPushManager.isNotificationOpened(this.reactContext)); 293 | } 294 | 295 | /***************************************************************** 296 | * XGPushConfig配置类 297 | * (对于本类提供的set和enable方法,要在XGPushManager接口前调用才能及时生效) 298 | *****************************************************************/ 299 | 300 | /** 301 | * 初始化 302 | * @param accessId 303 | * @param accessKey 304 | */ 305 | @ReactMethod 306 | public void init(int accessId, String accessKey) { 307 | XGPushConfig.setAccessId(this.reactContext, accessId); 308 | XGPushConfig.setAccessKey(this.reactContext, accessKey); 309 | } 310 | 311 | /** 312 | * 是否开启debug模式,即输出logcat日志重要:为保证数据的安全性,发布前必须设置为false) 313 | */ 314 | @ReactMethod 315 | public void enableDebug(boolean isDebug) { 316 | XGPushConfig.enableDebug(this.reactContext, isDebug); 317 | } 318 | 319 | /** 320 | * 开启logcat输出,方便debug,发布时请关闭 321 | */ 322 | @ReactMethod 323 | public void isEnableDebug(Promise promise) { 324 | promise.resolve(XGPushConfig.isEnableDebug(this.reactContext)); 325 | } 326 | 327 | /** 328 | * 获取设备的token,只有注册成功才能获取到正常的结果 329 | * @param promise 330 | */ 331 | @ReactMethod 332 | public void getToken(Promise promise) { 333 | promise.resolve(XGPushConfig.getToken(this.reactContext)); 334 | } 335 | 336 | /** 337 | * 设置上报通知栏是否关闭 默认打开 338 | * @param debugMode 339 | */ 340 | @ReactMethod 341 | public void setReportNotificationStatusEnable(boolean debugMode) { 342 | XGPushConfig.setReportNotificationStatusEnable(this.reactContext, debugMode); 343 | } 344 | 345 | /** 346 | * 设置上报APP 列表,用于智能推送 默认打开 347 | * @param debugMode 348 | */ 349 | @ReactMethod 350 | public void setReportApplistEnable(boolean debugMode) { 351 | XGPushConfig.setReportApplistEnable(this.reactContext, debugMode); 352 | } 353 | 354 | @ReactMethod 355 | public void setAccessId(String accessId) { 356 | try{ 357 | XGPushConfig.setAccessId(this.reactContext, Long.parseLong(accessId)); 358 | } catch (NumberFormatException exception) { 359 | exception.printStackTrace(); 360 | } 361 | } 362 | 363 | @ReactMethod 364 | public long getAccessId() { 365 | return XGPushConfig.getAccessId(this.reactContext); 366 | } 367 | 368 | @ReactMethod 369 | public void setAccessKey(String accessKey) { 370 | XGPushConfig.setAccessKey(this.reactContext, accessKey); 371 | } 372 | 373 | /** 374 | * 获取accessKey 375 | * @return accessKey 376 | */ 377 | @ReactMethod 378 | public String getAccessKey() { 379 | return XGPushConfig.getAccessKey(this.reactContext); 380 | } 381 | 382 | /** 383 | * 第三方推送开关 384 | * 需要在 registerPush 之前调用 385 | */ 386 | @ReactMethod 387 | public void enableOtherPush(boolean isEnable) { 388 | XGPushConfig.enableOtherPush(this.reactContext, isEnable); 389 | } 390 | 391 | @ReactMethod 392 | public void setHuaweiDebug(boolean isDebug) { 393 | XGPushConfig.setHuaweiDebug(isDebug); 394 | } 395 | 396 | @ReactMethod 397 | public void initXiaomi(String appId, String appKey) { 398 | XGPushConfig.setMiPushAppId(this.reactContext, appId); 399 | XGPushConfig.setMiPushAppKey(this.reactContext, appKey); 400 | } 401 | 402 | @ReactMethod 403 | public void initMeizu(String appId, String appKey) { 404 | //设置魅族APPID和APPKEY 405 | XGPushConfig.setMzPushAppId(this.reactContext, appId); 406 | XGPushConfig.setMzPushAppKey(this.reactContext, appKey); 407 | } 408 | 409 | 410 | @ReactMethod 411 | public void initFcm(boolean isEnable){ 412 | XGPushConfig.enableFcmPush(this.reactContext, isEnable); 413 | } 414 | 415 | @ReactMethod 416 | public void getInitialNotification(Promise promise) { 417 | 418 | WritableMap params = Arguments.createMap(); 419 | Log.d("getInitialNotification", ">>>>>>>>>>>>>>>>>"); 420 | try { 421 | 422 | PushMessage mymessage = PushMessage.getInstance(); 423 | if(mymessage.hasValue) { 424 | params.putString("title", mymessage.getTitle()); 425 | params.putString("content", mymessage.getContent()); 426 | params.putString("custom_content", mymessage.getCustomContent()); 427 | Log.d("getInitialNotification", "title: "+ mymessage.getTitle()+ "content: " + mymessage.getContent() + "custom_content: " + mymessage.getCustomContent()); 428 | mymessage.clearAll(); 429 | promise.resolve(params); 430 | } 431 | else { 432 | promise.resolve(null); 433 | } 434 | 435 | } catch (Exception e) { 436 | Log.d("getInitialNotification", "Have Exception"); 437 | e.printStackTrace(); 438 | promise.resolve(null); 439 | } 440 | 441 | } 442 | 443 | @ReactMethod 444 | public void getApplicationIconBadgeNumber(Callback callback) { 445 | callback.invoke(this.badge); 446 | } 447 | 448 | @ReactMethod 449 | public void setApplicationIconBadgeNumber(int number) { 450 | this.badge = number; 451 | ShortcutBadger.applyCount(this.reactContext, number); 452 | } 453 | 454 | @Override 455 | public void onHostResume() { 456 | XGPushManager.onActivityStarted(getCurrentActivity()); 457 | } 458 | 459 | @Override 460 | public void onHostPause() { 461 | XGPushManager.onActivityStoped(getCurrentActivity()); 462 | } 463 | 464 | @Override 465 | public void onHostDestroy() { 466 | 467 | } 468 | 469 | @Override 470 | public void onActivityResult(Activity activity, int requestCode, int resultCode, Intent data) { 471 | 472 | } 473 | 474 | @Override 475 | public void onNewIntent(Intent intent) { 476 | Log.d("onNewIntent", ">>>>>>>>>>>>>>>>>"); 477 | Activity activity = getCurrentActivity(); 478 | if (activity != null) { 479 | activity.setIntent(intent); // 后台运行时点击通知会调用 480 | } 481 | } 482 | } 483 | -------------------------------------------------------------------------------- /android/src/main/java/com/jeepeng/react/xgpush/PushPackage.java: -------------------------------------------------------------------------------- 1 | package com.jeepeng.react.xgpush; 2 | 3 | import com.facebook.react.ReactPackage; 4 | import com.facebook.react.bridge.JavaScriptModule; 5 | import com.facebook.react.bridge.NativeModule; 6 | import com.facebook.react.bridge.ReactApplicationContext; 7 | import com.facebook.react.uimanager.ViewManager; 8 | 9 | import java.util.ArrayList; 10 | import java.util.Collections; 11 | import java.util.List; 12 | 13 | /** 14 | * 信鸽推送 15 | * Created by Jeepeng on 16/8/3. 16 | */ 17 | public class PushPackage implements ReactPackage { 18 | @Override 19 | public List createNativeModules(ReactApplicationContext reactContext) { 20 | List modules = new ArrayList<>(); 21 | modules.add(new PushModule(reactContext)); 22 | return modules; 23 | } 24 | 25 | @Override 26 | public List createViewManagers(ReactApplicationContext reactContext) { 27 | return Collections.emptyList(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /android/src/main/java/com/jeepeng/react/xgpush/receiver/HWReceiver.java: -------------------------------------------------------------------------------- 1 | package com.jeepeng.react.xgpush.receiver; 2 | 3 | import android.content.Context; 4 | import android.os.Bundle; 5 | import android.os.Environment; 6 | import android.os.Looper; 7 | import android.widget.Toast; 8 | 9 | import com.huawei.hms.support.api.push.PushReceiver; 10 | 11 | import java.io.FileWriter; 12 | import java.io.IOException; 13 | 14 | public class HWReceiver extends PushReceiver { 15 | 16 | @Override 17 | public void onEvent(Context context, Event arg1, Bundle arg2) { 18 | super.onEvent(context, arg1, arg2); 19 | 20 | showToast("onEvent" + arg1 + " Bundle " + arg2 , context); 21 | } 22 | 23 | @Override 24 | public boolean onPushMsg(Context context, byte[] arg1, Bundle arg2) { 25 | 26 | showToast("onPushMsg" + new String(arg1) + " Bundle " + arg2 , context); 27 | return super.onPushMsg(context, arg1, arg2); 28 | } 29 | 30 | @Override 31 | public void onPushMsg(Context context, byte[] arg1, String arg2) { 32 | 33 | showToast("onPushMsg" + new String(arg1) + " arg2 " + arg2 , context); 34 | super.onPushMsg(context, arg1, arg2); 35 | } 36 | 37 | @Override 38 | public void onPushState(Context context, boolean arg1) { 39 | 40 | showToast("onPushState" + arg1, context); 41 | super.onPushState(context, arg1); 42 | } 43 | 44 | @Override 45 | public void onToken(Context context, String arg1, Bundle arg2) { 46 | super.onToken(context, arg1, arg2); 47 | 48 | showToast(" onToken" + arg1 + "bundke " + arg2, context); 49 | } 50 | 51 | @Override 52 | public void onToken(Context context, String arg1) { 53 | super.onToken(context, arg1); 54 | showToast(" onToken" + arg1 , context); 55 | } 56 | 57 | public void showToast(final String toast, final Context context) 58 | { 59 | 60 | new Thread(new Runnable() { 61 | 62 | @Override 63 | public void run() { 64 | Looper.prepare(); 65 | Toast.makeText(context, toast, Toast.LENGTH_SHORT).show(); 66 | Looper.loop(); 67 | } 68 | }).start(); 69 | } 70 | 71 | private void writeToFile(String conrent) { 72 | String SDPATH = Environment.getExternalStorageDirectory() + "/huawei.txt"; 73 | try { 74 | FileWriter fileWriter = new FileWriter(SDPATH, true); 75 | 76 | fileWriter.write(conrent+"\r\n"); 77 | fileWriter.flush(); 78 | fileWriter.close(); 79 | } catch (IOException e) { 80 | // TODO Auto-generated catch block 81 | e.printStackTrace(); 82 | } 83 | } 84 | 85 | 86 | } 87 | -------------------------------------------------------------------------------- /android/src/main/java/com/jeepeng/react/xgpush/receiver/MessageReceiver.java: -------------------------------------------------------------------------------- 1 | package com.jeepeng.react.xgpush.receiver; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | 7 | import com.jeepeng.react.xgpush.PushMessage; 8 | import com.jeepeng.react.xgpush.Constants; 9 | import com.tencent.android.tpush.XGPushBaseReceiver; 10 | import com.tencent.android.tpush.XGPushClickedResult; 11 | import com.tencent.android.tpush.XGPushRegisterResult; 12 | import com.tencent.android.tpush.XGPushShowedResult; 13 | import com.tencent.android.tpush.XGPushTextMessage; 14 | 15 | import org.json.JSONException; 16 | import org.json.JSONObject; 17 | 18 | import me.leolin.shortcutbadger.ShortcutBadger; 19 | 20 | /** 21 | * 消息接收器 22 | * Created by Jeepeng on 16/8/4. 23 | */ 24 | public class MessageReceiver extends XGPushBaseReceiver { 25 | 26 | /** 27 | * 注册结果 28 | * @param context 29 | * @param errorCode 30 | * @param xgPushRegisterResult 31 | */ 32 | @Override 33 | public void onRegisterResult(Context context, int errorCode, XGPushRegisterResult xgPushRegisterResult) { 34 | if(errorCode == 0) { 35 | Intent intent = new Intent(Constants.ACTION_ON_REGISTERED); 36 | intent.putExtra("token", xgPushRegisterResult.getToken()); 37 | context.sendBroadcast(intent); 38 | } 39 | } 40 | 41 | /** 42 | * 反注册结果 43 | * @param context 44 | * @param errorCode 45 | */ 46 | @Override 47 | public void onUnregisterResult(Context context, int errorCode) { 48 | 49 | } 50 | 51 | 52 | /** 53 | * 设置标签结果 54 | * @param context 55 | * @param errorCode 56 | * @param tagName 57 | */ 58 | @Override 59 | public void onSetTagResult(Context context, int errorCode, String tagName) { 60 | 61 | } 62 | 63 | /** 64 | * 删除标签结果 65 | * @param context 66 | * @param errorCode 67 | * @param tagName 68 | */ 69 | @Override 70 | public void onDeleteTagResult(Context context, int errorCode, String tagName) { 71 | 72 | } 73 | 74 | /** 75 | * 收到消息 76 | * @param context 77 | * @param xgPushTextMessage 78 | */ 79 | @Override 80 | public void onTextMessage(Context context, XGPushTextMessage xgPushTextMessage) { 81 | Intent intent = new Intent(Constants.ACTION_ON_TEXT_MESSAGE); 82 | intent.putExtra("title", xgPushTextMessage.getTitle()); 83 | intent.putExtra("content", xgPushTextMessage.getContent()); 84 | intent.putExtra("custom_content", xgPushTextMessage.getCustomContent()); 85 | context.sendBroadcast(intent); 86 | } 87 | 88 | /** 89 | * 通知被打开触发的结果 90 | * @param context 91 | * @param notification 92 | */ 93 | @Override 94 | public void onNotifactionClickedResult(Context context, XGPushClickedResult notification) { 95 | if (context == null || notification == null) { 96 | return; 97 | } 98 | if (notification.getActionType() == XGPushClickedResult.NOTIFACTION_CLICKED_TYPE) { 99 | // 通知在通知栏被点击啦。。。。。 100 | 101 | PushMessage message = PushMessage.getInstance(); 102 | message.setAllValue(notification.getTitle(), notification.getContent(), notification.getCustomContent()); 103 | 104 | 105 | Intent intent = new Intent(Constants.ACTION_ON_NOTIFICATION_CLICKED); 106 | Bundle bundle = new Bundle(); 107 | bundle.putString("content", notification.getContent()); 108 | bundle.putString("title", notification.getTitle()); 109 | bundle.putString("custom_content", notification.getCustomContent()); 110 | intent.putExtra("notification", bundle); 111 | 112 | intent.putExtra("title", notification.getTitle()); 113 | intent.putExtra("content", notification.getContent()); 114 | intent.putExtra("custom_content", notification.getCustomContent()); 115 | intent.putExtra("activity", notification.getActivityName()); 116 | intent.putExtra("msgId", notification.getMsgId()); 117 | intent.putExtra("notificationActionType", notification.getNotificationActionType()); 118 | context.sendBroadcast(intent); 119 | } else if (notification.getActionType() == XGPushClickedResult.NOTIFACTION_DELETED_TYPE) { 120 | // 通知被清除啦。。。。 121 | // APP自己处理通知被清除后的相关动作 122 | } 123 | } 124 | 125 | /** 126 | * 通知被展示触发的结果,可以在此保存APP收到的通知 127 | * @param context 128 | * @param xgPushShowedResult 129 | */ 130 | @Override 131 | public void onNotifactionShowedResult(Context context, XGPushShowedResult xgPushShowedResult) { 132 | // set app icon badge 133 | try { 134 | JSONObject obj = new JSONObject(xgPushShowedResult.getCustomContent()); 135 | int badge = obj.optInt("badge", -1); 136 | if (badge >= 0) { 137 | ShortcutBadger.applyCount(context, badge); 138 | } 139 | } catch (JSONException e) { 140 | e.printStackTrace(); 141 | } 142 | Intent intent = new Intent(Constants.ACTION_ON_NOTIFICATION_SHOWED); 143 | Bundle bundle = new Bundle(); 144 | bundle.putString("content", xgPushShowedResult.getContent()); 145 | bundle.putString("title", xgPushShowedResult.getTitle()); 146 | bundle.putString("custom_content", xgPushShowedResult.getCustomContent()); 147 | intent.putExtra("notification", bundle); 148 | 149 | intent.putExtra("title", xgPushShowedResult.getTitle()); 150 | intent.putExtra("content", xgPushShowedResult.getContent()); 151 | intent.putExtra("custom_content", xgPushShowedResult.getCustomContent()); 152 | intent.putExtra("activity", xgPushShowedResult.getActivity()); 153 | intent.putExtra("msgId", xgPushShowedResult.getMsgId()); 154 | intent.putExtra("notificationId", xgPushShowedResult.getNotifactionId()); 155 | intent.putExtra("notificationActionType", xgPushShowedResult.getNotificationActionType()); 156 | context.sendBroadcast(intent); 157 | } 158 | } 159 | -------------------------------------------------------------------------------- /android/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | react-native-xinge-push 3 | 4 | -------------------------------------------------------------------------------- /android/src/test/java/com/jeepeng/react/xgpush/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.jeepeng.react.xgpush; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test 12 | public void addition_isCorrect() throws Exception { 13 | assertEquals(4, 2 + 2); 14 | } 15 | } -------------------------------------------------------------------------------- /example/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": ["react-native"] 3 | } 4 | -------------------------------------------------------------------------------- /example/.buckconfig: -------------------------------------------------------------------------------- 1 | 2 | [android] 3 | target = Google Inc.:Google APIs:23 4 | 5 | [maven_repositories] 6 | central = https://repo1.maven.org/maven2 7 | -------------------------------------------------------------------------------- /example/.flowconfig: -------------------------------------------------------------------------------- 1 | [ignore] 2 | ; We fork some components by platform 3 | .*/*[.]android.js 4 | 5 | ; Ignore "BUCK" generated dirs 6 | /\.buckd/ 7 | 8 | ; Ignore unexpected extra "@providesModule" 9 | .*/node_modules/.*/node_modules/fbjs/.* 10 | 11 | ; Ignore duplicate module providers 12 | ; For RN Apps installed via npm, "Libraries" folder is inside 13 | ; "node_modules/react-native" but in the source repo it is in the root 14 | .*/Libraries/react-native/React.js 15 | 16 | ; Ignore polyfills 17 | .*/Libraries/polyfills/.* 18 | 19 | ; Ignore metro 20 | .*/node_modules/metro/.* 21 | 22 | [include] 23 | 24 | [libs] 25 | node_modules/react-native/Libraries/react-native/react-native-interface.js 26 | node_modules/react-native/flow/ 27 | node_modules/react-native/flow-github/ 28 | 29 | [options] 30 | emoji=true 31 | 32 | module.system=haste 33 | 34 | munge_underscores=true 35 | 36 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub' 37 | 38 | module.file_ext=.js 39 | module.file_ext=.jsx 40 | module.file_ext=.json 41 | module.file_ext=.native.js 42 | 43 | suppress_type=$FlowIssue 44 | suppress_type=$FlowFixMe 45 | suppress_type=$FlowFixMeProps 46 | suppress_type=$FlowFixMeState 47 | 48 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\) 49 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(\\)? *\\(site=[a-z,_]*react_native[a-z,_]*\\)?)\\)?:? #[0-9]+ 50 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy 51 | suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError 52 | 53 | unsafe.enable_getters_and_setters=true 54 | 55 | [version] 56 | ^0.61.0 57 | -------------------------------------------------------------------------------- /example/.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | 25 | # Android/IntelliJ 26 | # 27 | build/ 28 | .idea 29 | .gradle 30 | local.properties 31 | *.iml 32 | 33 | # node.js 34 | # 35 | node_modules/ 36 | npm-debug.log 37 | yarn-error.log 38 | 39 | # BUCK 40 | buck-out/ 41 | \.buckd/ 42 | *.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | /android/app/release/ 56 | -------------------------------------------------------------------------------- /example/.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /example/__tests__/index.js: -------------------------------------------------------------------------------- 1 | import 'react-native'; 2 | import React from 'react'; 3 | import Index from '../index.js'; 4 | 5 | // Note: test renderer must be required after react-native. 6 | import renderer from 'react-test-renderer'; 7 | 8 | it('renders correctly', () => { 9 | const tree = renderer.create( 10 | 11 | ); 12 | }); 13 | -------------------------------------------------------------------------------- /example/android/app/BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | lib_deps = [] 12 | 13 | for jarfile in glob(['libs/*.jar']): 14 | name = 'jars__' + jarfile[jarfile.rindex('/') + 1: jarfile.rindex('.jar')] 15 | lib_deps.append(':' + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | 21 | for aarfile in glob(['libs/*.aar']): 22 | name = 'aars__' + aarfile[aarfile.rindex('/') + 1: aarfile.rindex('.aar')] 23 | lib_deps.append(':' + name) 24 | android_prebuilt_aar( 25 | name = name, 26 | aar = aarfile, 27 | ) 28 | 29 | android_library( 30 | name = "all-libs", 31 | exported_deps = lib_deps, 32 | ) 33 | 34 | android_library( 35 | name = "app-code", 36 | srcs = glob([ 37 | "src/main/java/**/*.java", 38 | ]), 39 | deps = [ 40 | ":all-libs", 41 | ":build_config", 42 | ":res", 43 | ], 44 | ) 45 | 46 | android_build_config( 47 | name = "build_config", 48 | package = "com.example", 49 | ) 50 | 51 | android_resource( 52 | name = "res", 53 | package = "com.example", 54 | res = "src/main/res", 55 | ) 56 | 57 | android_binary( 58 | name = "app", 59 | keystore = "//android/keystores:debug", 60 | manifest = "src/main/AndroidManifest.xml", 61 | package_type = "debug", 62 | deps = [ 63 | ":app-code", 64 | ], 65 | ) 66 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation 19 | * entryFile: "index.android.js", 20 | * 21 | * // whether to bundle JS and assets in debug mode 22 | * bundleInDebug: false, 23 | * 24 | * // whether to bundle JS and assets in release mode 25 | * bundleInRelease: true, 26 | * 27 | * // whether to bundle JS and assets in another build variant (if configured). 28 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 29 | * // The configuration property can be in the following formats 30 | * // 'bundleIn${productFlavor}${buildType}' 31 | * // 'bundleIn${buildType}' 32 | * // bundleInFreeDebug: true, 33 | * // bundleInPaidRelease: true, 34 | * // bundleInBeta: true, 35 | * 36 | * // whether to disable dev mode in custom build variants (by default only disabled in release) 37 | * // for example: to disable dev mode in the staging build type (if configured) 38 | * devDisabledInStaging: true, 39 | * // The configuration property can be in the following formats 40 | * // 'devDisabledIn${productFlavor}${buildType}' 41 | * // 'devDisabledIn${buildType}' 42 | * 43 | * // the root of your project, i.e. where "package.json" lives 44 | * root: "../../", 45 | * 46 | * // where to put the JS bundle asset in debug mode 47 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 48 | * 49 | * // where to put the JS bundle asset in release mode 50 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 51 | * 52 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 53 | * // require('./image.png')), in debug mode 54 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 55 | * 56 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 57 | * // require('./image.png')), in release mode 58 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 59 | * 60 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 61 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 62 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 63 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 64 | * // for example, you might want to remove it from here. 65 | * inputExcludes: ["android/**", "ios/**"], 66 | * 67 | * // override which node gets called and with what additional arguments 68 | * nodeExecutableAndArgs: ["node"], 69 | * 70 | * // supply additional arguments to the packager 71 | * extraPackagerArgs: [] 72 | * ] 73 | */ 74 | 75 | project.ext.react = [ 76 | entryFile: "index.js" 77 | ] 78 | 79 | apply from: "../../node_modules/react-native/react.gradle" 80 | 81 | /** 82 | * Set this to true to create two separate APKs instead of one: 83 | * - An APK that only works on ARM devices 84 | * - An APK that only works on x86 devices 85 | * The advantage is the size of the APK is reduced by about 4MB. 86 | * Upload all the APKs to the Play Store and people will download 87 | * the correct one based on the CPU architecture of their device. 88 | */ 89 | def enableSeparateBuildPerCPUArchitecture = false 90 | 91 | /** 92 | * Run Proguard to shrink the Java bytecode in release builds. 93 | */ 94 | def enableProguardInReleaseBuilds = false 95 | 96 | android { 97 | compileSdkVersion 26 98 | buildToolsVersion '27.0.3' 99 | 100 | defaultConfig { 101 | applicationId "com.jeepeng.push" 102 | minSdkVersion 16 103 | targetSdkVersion 26 104 | versionCode 3 105 | versionName "1.0.1" 106 | ndk { 107 | abiFilters "armeabi-v7a", "x86" 108 | } 109 | manifestPlaceholders = [ 110 | XG_ACCESS_ID: "2100209996", 111 | XG_ACCESS_KEY: "AHW931HVZ42A", 112 | HW_APPID: "100225811", 113 | PACKAGE_NAME: "com.jeepeng.push" 114 | ] 115 | } 116 | splits { 117 | abi { 118 | reset() 119 | enable enableSeparateBuildPerCPUArchitecture 120 | universalApk false // If true, also generate a universal APK 121 | include "armeabi-v7a", "x86" 122 | } 123 | } 124 | buildTypes { 125 | release { 126 | minifyEnabled enableProguardInReleaseBuilds 127 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 128 | } 129 | } 130 | // applicationVariants are e.g. debug, release 131 | applicationVariants.all { variant -> 132 | variant.outputs.each { output -> 133 | // For each separate APK per architecture, set a unique version code as described here: 134 | // http://tools.android.com/tech-docs/new-build-system/user-guide/apk-splits 135 | def versionCodes = ["armeabi-v7a":1, "x86":2] 136 | def abi = output.getFilter(OutputFile.ABI) 137 | if (abi != null) { // null for the universal-debug, universal-release variants 138 | output.versionCodeOverride = 139 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 140 | } 141 | } 142 | } 143 | } 144 | 145 | dependencies { 146 | compile project(':react-native-xinge-push') 147 | compile fileTree(dir: "libs", include: ["*.jar"]) 148 | compile "com.android.support:appcompat-v7:26.1.0" 149 | compile "com.facebook.react:react-native:+" // From node_modules 150 | } 151 | 152 | // Run this once to be able to run the application with BUCK 153 | // puts all compile dependencies into folder libs for BUCK to use 154 | task copyDownloadableDepsToLibs(type: Copy) { 155 | from configurations.compile 156 | into 'libs' 157 | } 158 | -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Disabling obfuscation is useful if you collect stack traces from production crashes 20 | # (unless you are using a system that supports de-obfuscate the stack traces). 21 | -dontobfuscate 22 | 23 | # React Native 24 | 25 | # Keep our interfaces so they can be used by other ProGuard rules. 26 | # See http://sourceforge.net/p/proguard/bugs/466/ 27 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.DoNotStrip 28 | -keep,allowobfuscation @interface com.facebook.proguard.annotations.KeepGettersAndSetters 29 | -keep,allowobfuscation @interface com.facebook.common.internal.DoNotStrip 30 | 31 | # Do not strip any method/class that is annotated with @DoNotStrip 32 | -keep @com.facebook.proguard.annotations.DoNotStrip class * 33 | -keep @com.facebook.common.internal.DoNotStrip class * 34 | -keepclassmembers class * { 35 | @com.facebook.proguard.annotations.DoNotStrip *; 36 | @com.facebook.common.internal.DoNotStrip *; 37 | } 38 | 39 | -keepclassmembers @com.facebook.proguard.annotations.KeepGettersAndSetters class * { 40 | void set*(***); 41 | *** get*(); 42 | } 43 | 44 | -keep class * extends com.facebook.react.bridge.JavaScriptModule { *; } 45 | -keep class * extends com.facebook.react.bridge.NativeModule { *; } 46 | -keepclassmembers,includedescriptorclasses class * { native ; } 47 | -keepclassmembers class * { @com.facebook.react.uimanager.UIProp ; } 48 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactProp ; } 49 | -keepclassmembers class * { @com.facebook.react.uimanager.annotations.ReactPropGroup ; } 50 | 51 | -dontwarn com.facebook.react.** 52 | 53 | # TextLayoutBuilder uses a non-public Android constructor within StaticLayout. 54 | # See libs/proxy/src/main/java/com/facebook/fbui/textlayoutbuilder/proxy for details. 55 | -dontwarn android.text.StaticLayout 56 | 57 | # okhttp 58 | 59 | -keepattributes Signature 60 | -keepattributes *Annotation* 61 | -keep class okhttp3.** { *; } 62 | -keep interface okhttp3.** { *; } 63 | -dontwarn okhttp3.** 64 | 65 | # okio 66 | 67 | -keep class sun.misc.Unsafe { *; } 68 | -dontwarn java.nio.file.* 69 | -dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement 70 | -dontwarn okio.** 71 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 15 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /example/android/app/src/main/assets/index.android.bundle.meta: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PandaQQ/react-native-xinge-push/3b77b89fa834236fde2220f600d388a4f2d35ec8/example/android/app/src/main/assets/index.android.bundle.meta -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. 9 | * This is used to schedule rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "example"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import android.app.Application; 4 | 5 | import com.facebook.react.ReactApplication; 6 | import com.jeepeng.react.xgpush.PushPackage; 7 | import com.facebook.react.ReactNativeHost; 8 | import com.facebook.react.ReactPackage; 9 | import com.facebook.react.shell.MainReactPackage; 10 | import com.facebook.soloader.SoLoader; 11 | 12 | import java.util.Arrays; 13 | import java.util.List; 14 | 15 | public class MainApplication extends Application implements ReactApplication { 16 | 17 | private final ReactNativeHost mReactNativeHost = new ReactNativeHost(this) { 18 | @Override 19 | public boolean getUseDeveloperSupport() { 20 | return BuildConfig.DEBUG; 21 | } 22 | 23 | @Override 24 | protected List getPackages() { 25 | return Arrays.asList( 26 | new MainReactPackage(), 27 | new PushPackage() 28 | ); 29 | } 30 | 31 | @Override 32 | protected String getJSMainModuleName() { 33 | return "index"; 34 | } 35 | }; 36 | 37 | @Override 38 | public ReactNativeHost getReactNativeHost() { 39 | return mReactNativeHost; 40 | } 41 | 42 | @Override 43 | public void onCreate() { 44 | super.onCreate(); 45 | SoLoader.init(this, /* native exopackage */ false); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PandaQQ/react-native-xinge-push/3b77b89fa834236fde2220f600d388a4f2d35ec8/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PandaQQ/react-native-xinge-push/3b77b89fa834236fde2220f600d388a4f2d35ec8/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PandaQQ/react-native-xinge-push/3b77b89fa834236fde2220f600d388a4f2d35ec8/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PandaQQ/react-native-xinge-push/3b77b89fa834236fde2220f600d388a4f2d35ec8/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | RN-XGPush 3 | 4 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | google() 7 | } 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.1.0' 10 | 11 | // NOTE: Do not place your application dependencies here; they belong 12 | // in the individual module build.gradle files 13 | } 14 | } 15 | 16 | allprojects { 17 | repositories { 18 | mavenLocal() 19 | jcenter() 20 | maven { 21 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 22 | url "$rootDir/../node_modules/react-native/android" 23 | } 24 | google() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | android.useDeprecatedNdk=true 21 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PandaQQ/react-native-xinge-push/3b77b89fa834236fde2220f600d388a4f2d35ec8/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Mar 27 17:51:16 CST 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /example/android/keystores/BUCK: -------------------------------------------------------------------------------- 1 | keystore( 2 | name = "debug", 3 | properties = "debug.keystore.properties", 4 | store = "debug.keystore", 5 | visibility = [ 6 | "PUBLIC", 7 | ], 8 | ) 9 | -------------------------------------------------------------------------------- /example/android/keystores/debug.keystore.properties: -------------------------------------------------------------------------------- 1 | key.store=debug.keystore 2 | key.alias=androiddebugkey 3 | key.store.password=android 4 | key.alias.password=android 5 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'example' 2 | include ':react-native-xinge-push' 3 | project(':react-native-xinge-push').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-xinge-push/android') 4 | 5 | include ':app' 6 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "displayName": "example" 4 | } -------------------------------------------------------------------------------- /example/app/Touchable.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by Jeepeng on 2017/3/3. 3 | */ 4 | 5 | import React from 'react'; 6 | import { 7 | Platform, 8 | TouchableNativeFeedback, 9 | TouchableHighlight, 10 | } from 'react-native'; 11 | 12 | const Touchable = ({ onPress, children, style }) => { 13 | const child = React.Children.only(children); 14 | if (Platform.OS === 'android') { 15 | return ( 16 | 17 | {child} 18 | 19 | ); 20 | } 21 | return ( 22 | 23 | {child} 24 | 25 | ); 26 | }; 27 | 28 | export default Touchable; 29 | -------------------------------------------------------------------------------- /example/app/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React, { Component } from 'react'; 8 | import { 9 | Platform, 10 | StyleSheet, 11 | Text, 12 | View, 13 | TouchableNativeFeedback, 14 | TouchableHighlight 15 | } from 'react-native'; 16 | import XGPush from 'react-native-xinge-push'; 17 | 18 | const Touchable = Platform.OS === 'android' ? TouchableNativeFeedback : TouchableHighlight; 19 | 20 | class Example extends Component { 21 | 22 | constructor() { 23 | super(); 24 | this.state = { 25 | isDebug: false 26 | }; 27 | this._enableDebug = this._enableDebug.bind(this); 28 | this._isEnableDebug = this._isEnableDebug.bind(this); 29 | 30 | // 初始化推送 31 | this.initPush(); 32 | } 33 | 34 | initPush() { 35 | XGPush.enableDebug(true) 36 | // 初始化 37 | if(Platform.OS === 'android') { 38 | // 请将1111111111修改为APP的AccessId,10位数字 39 | // 请将YOUR_ACCESS_KEY修改为APP的AccessKey 40 | XGPush.init(2100209996, 'AHW931HVZ42A'); 41 | } else { 42 | // 请将1111111111修改为APP的AccessId,10位数字 43 | // 请将YOUR_ACCESS_KEY修改为APP的AccessKey 44 | XGPush.init(2200209997, 'ITD46N87JA4K'); 45 | } 46 | 47 | XGPush.setHuaweiDebug(true); 48 | 49 | // 小米 50 | XGPush.initXiaomi('appId', 'appKey'); 51 | 52 | // 魅族 53 | XGPush.initMeizu('appId', 'appKey'); 54 | 55 | // 华为请到 build.gradle manifestPlaceholders 配置 56 | 57 | // 第三方推送开关(华为、小米、魅族) 58 | XGPush.enableOtherPush(true); 59 | 60 | // 注册 61 | XGPush.register('jeepeng') 62 | .then(result => { 63 | // do something 64 | }) 65 | .catch(err => { 66 | console.log(err); 67 | }); 68 | } 69 | 70 | componentDidMount() { 71 | XGPush.addEventListener('register', this._onRegister); 72 | XGPush.addEventListener('message', this._onMessage); 73 | XGPush.addEventListener('notification', this._onNotification); 74 | } 75 | 76 | componentWillUnmount() { 77 | XGPush.removeEventListener('register', this._onRegister); 78 | XGPush.removeEventListener('message', this._onMessage); 79 | XGPush.removeEventListener('notification', this._onNotification); 80 | } 81 | 82 | /** 83 | * 注册成功 84 | * @param deviceToken 85 | * @private 86 | */ 87 | _onRegister(deviceToken) { 88 | alert('onRegister: ' + deviceToken); 89 | } 90 | 91 | /** 92 | * 透传消息到达 93 | * @param message 94 | * @private 95 | */ 96 | _onMessage(message) { 97 | alert('收到透传消息: ' + JSON.stringify(message.content)); 98 | } 99 | 100 | /** 101 | * 通知到达 102 | * @param notification 103 | * @private 104 | */ 105 | _onNotification(notification) { 106 | console.log(notification) 107 | if(notification.clicked === true) { 108 | alert('app处于后台时收到通知' + JSON.stringify(notification)); 109 | } else { 110 | alert('app处于前台时收到通知' + JSON.stringify(notification)); 111 | } 112 | } 113 | 114 | /** 115 | * 获取初始通知(点击通知后) 116 | * @private 117 | */ 118 | _getInitialNotification() { 119 | XGPush.getInitialNotification().then((result) => { 120 | alert(JSON.stringify(result)); 121 | }); 122 | } 123 | 124 | _enableDebug() { 125 | XGPush.enableDebug(!this.state.isDebug); 126 | } 127 | 128 | _isEnableDebug() { 129 | XGPush.isEnableDebug().then(result => { 130 | this.setState({ 131 | isDebug: result 132 | }); 133 | alert(result); 134 | }); 135 | } 136 | 137 | _setApplicationIconBadgeNumber(number = 0) { 138 | XGPush.setApplicationIconBadgeNumber(number); 139 | } 140 | 141 | _getApplicationIconBadgeNumber() { 142 | XGPush.getApplicationIconBadgeNumber((number) => alert(number)); 143 | } 144 | 145 | render() { 146 | return ( 147 | 148 | 149 | 150 | getInitialNotification 151 | 152 | 153 | { this._enableDebug() }} underlayColor="#ddd"> 154 | 155 | enableDebug 156 | 157 | 158 | { this._isEnableDebug() }} underlayColor="#ddd"> 159 | 160 | isEnableDebug 161 | 162 | 163 | { this._setApplicationIconBadgeNumber(99) }} underlayColor="#ddd"> 164 | 165 | setApplicationIconBadgeNumber: 99 166 | 167 | 168 | { this._getApplicationIconBadgeNumber() }} underlayColor="#ddd"> 169 | 170 | getApplicationIconBadgeNumber 171 | 172 | 173 | 174 | ); 175 | } 176 | } 177 | 178 | const styles = StyleSheet.create({ 179 | container: { 180 | flex: 1, 181 | backgroundColor: '#fff', 182 | paddingTop: 20, 183 | }, 184 | list: { 185 | marginTop: 15, 186 | backgroundColor: '#fff', 187 | }, 188 | item: { 189 | height: 45, 190 | alignItems: 'center', 191 | justifyContent: 'center', 192 | borderBottomWidth: StyleSheet.hairlineWidth, 193 | borderBottomColor: '#efefef' 194 | }, 195 | }); 196 | 197 | export default Example; 198 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Sample React Native App 3 | * https://github.com/facebook/react-native 4 | * @flow 5 | */ 6 | 7 | import React from 'react'; 8 | import { AppRegistry } from 'react-native'; 9 | 10 | import App from './app/index.js'; 11 | 12 | AppRegistry.registerComponent('example', () => App); 13 | -------------------------------------------------------------------------------- /example/ios/example-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UIViewControllerBasedStatusBarAppearance 38 | 39 | NSLocationWhenInUseUsageDescription 40 | 41 | NSAppTransportSecurity 42 | 43 | 44 | NSExceptionDomains 45 | 46 | localhost 47 | 48 | NSExceptionAllowsInsecureHTTPLoads 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /example/ios/example-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | org.reactjs.native.example.$(PRODUCT_NAME:rfc1034identifier) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/ios/example.xcodeproj/xcshareddata/xcschemes/example-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 70 | 71 | 77 | 78 | 79 | 80 | 81 | 82 | 92 | 94 | 100 | 101 | 102 | 103 | 104 | 105 | 111 | 113 | 119 | 120 | 121 | 122 | 124 | 125 | 128 | 129 | 130 | -------------------------------------------------------------------------------- /example/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 62 | 68 | 69 | 70 | 71 | 72 | 78 | 79 | 80 | 81 | 82 | 83 | 94 | 96 | 102 | 103 | 104 | 105 | 106 | 107 | 113 | 115 | 121 | 122 | 123 | 124 | 126 | 127 | 130 | 131 | 132 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | @interface AppDelegate : UIResponder 13 | 14 | @property (nonatomic, strong) UIWindow *window; 15 | 16 | @end 17 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import "AppDelegate.h" 11 | 12 | #import 13 | #import 14 | #import 15 | #import 16 | 17 | @implementation AppDelegate 18 | 19 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 20 | { 21 | NSURL *jsCodeLocation; 22 | #ifdef DEBUG 23 | [[RCTBundleURLProvider sharedSettings] setJsLocation:@"192.168.0.154"]; 24 | #endif 25 | jsCodeLocation = [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 26 | 27 | RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:jsCodeLocation 28 | moduleName:@"example" 29 | initialProperties:nil 30 | launchOptions:launchOptions]; 31 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 32 | 33 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 34 | UIViewController *rootViewController = [UIViewController new]; 35 | rootViewController.view = rootView; 36 | self.window.rootViewController = rootViewController; 37 | [self.window makeKeyAndVisible]; 38 | // 统计消息推送的抵达情况 39 | [[XGPush defaultManager] reportXGNotificationInfo:launchOptions]; 40 | return YES; 41 | } 42 | 43 | // Required to register for notifications 44 | - (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings 45 | { 46 | [XGPushManager didRegisterUserNotificationSettings:notificationSettings]; 47 | } 48 | 49 | - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { 50 | [XGPushManager didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; 51 | } 52 | 53 | // Required for the registrationError event. 54 | - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { 55 | NSLog(@"[XGPush] register APNS fail.\n[XGPush] reason : %@", error); 56 | [XGPushManager didFailToRegisterForRemoteNotificationsWithError:error]; 57 | } 58 | 59 | // Required for the localNotification event. 60 | - (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification 61 | { 62 | [XGPushManager didReceiveLocalNotification:notification]; 63 | } 64 | 65 | /** 66 | 收到静默消息的回调,通常此消息意味着有新数据可以读取(iOS 7.0+) 67 | 68 | @param application UIApplication 实例 69 | @param userInfo 推送时指定的参数 70 | @param completionHandler 完成回调 71 | */ 72 | - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { 73 | NSLog(@"[XGPush] receive slient Message"); 74 | NSLog(@"[XGPush] userinfo %@", userInfo); 75 | 76 | [[XGPush defaultManager] reportXGNotificationInfo:userInfo]; 77 | [XGPushManager didReceiveRemoteMessage:userInfo fetchCompletionHandler:completionHandler]; 78 | } 79 | 80 | 81 | 82 | 83 | @end 84 | -------------------------------------------------------------------------------- /example/ios/example/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 21 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/ios/example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSExceptionDomains 30 | 31 | localhost 32 | 33 | NSExceptionAllowsInsecureHTTPLoads 34 | 35 | 36 | 37 | 38 | NSLocationWhenInUseUsageDescription 39 | 40 | UIBackgroundModes 41 | 42 | remote-notification 43 | 44 | UILaunchStoryboardName 45 | LaunchScreen 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UIViewControllerBasedStatusBarAppearance 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /example/ios/example/example.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | aps-environment 6 | development 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/example/main.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | 12 | #import "AppDelegate.h" 13 | 14 | int main(int argc, char * argv[]) { 15 | @autoreleasepool { 16 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/ios/exampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/ios/exampleTests/exampleTests.m: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2015-present, Facebook, Inc. 3 | * All rights reserved. 4 | * 5 | * This source code is licensed under the BSD-style license found in the 6 | * LICENSE file in the root directory of this source tree. An additional grant 7 | * of patent rights can be found in the PATENTS file in the same directory. 8 | */ 9 | 10 | #import 11 | #import 12 | 13 | #import 14 | #import 15 | 16 | #define TIMEOUT_SECONDS 600 17 | #define TEXT_TO_LOOK_FOR @"Welcome to React Native!" 18 | 19 | @interface exampleTests : XCTestCase 20 | 21 | @end 22 | 23 | @implementation exampleTests 24 | 25 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 26 | { 27 | if (test(view)) { 28 | return YES; 29 | } 30 | for (UIView *subview in [view subviews]) { 31 | if ([self findSubviewInView:subview matching:test]) { 32 | return YES; 33 | } 34 | } 35 | return NO; 36 | } 37 | 38 | - (void)testRendersWelcomeScreen 39 | { 40 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 41 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 42 | BOOL foundElement = NO; 43 | 44 | __block NSString *redboxError = nil; 45 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 46 | if (level >= RCTLogLevelError) { 47 | redboxError = message; 48 | } 49 | }); 50 | 51 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 52 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 53 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 54 | 55 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 56 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 57 | return YES; 58 | } 59 | return NO; 60 | }]; 61 | } 62 | 63 | RCTSetLogFunction(RCTDefaultLogFunction); 64 | 65 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 66 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 67 | } 68 | 69 | 70 | @end 71 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "start": "node node_modules/react-native/local-cli/cli.js start", 7 | "test": "jest", 8 | "android": "node node_modules/react-native/local-cli/cli.js run-android", 9 | "ios": "node node_modules/react-native/local-cli/cli.js run-ios --simulator 'iPhone 8'", 10 | "bundle-android": "react-native bundle --entry-file index.js --bundle-output ./android/app/src/main/assets/index.android.bundle --assets-dest ./android/app/src/main/res --platform android --dev false", 11 | "bundle-ios": "react-native bundle --entry-file index.js --bundle-output ./ios/main.jsbundle --assets-dest ./ios --platform ios --dev false", 12 | "log-android": "react-native log-android" 13 | }, 14 | "dependencies": { 15 | "react": "^16.3.1", 16 | "react-native": "^0.52.3", 17 | "react-native-xinge-push": "file:.." 18 | }, 19 | "devDependencies": { 20 | "babel-jest": "^21.2.0", 21 | "babel-preset-react-native": "^4.0.0", 22 | "jest": "^21.2.1", 23 | "react-test-renderer": "^16.3.1" 24 | }, 25 | "jest": { 26 | "preset": "react-native" 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 信鸽推送 3 | * Created by Jeepeng on 16/8/3. 4 | */ 5 | 6 | import { 7 | Platform, 8 | NativeModules, 9 | NativeEventEmitter, 10 | } from 'react-native'; 11 | 12 | let { XGPushManager } = NativeModules; 13 | let XGNativeEventEmitter = new NativeEventEmitter(XGPushManager); 14 | 15 | let _handlers = new Map(); 16 | 17 | const EventMapping = Platform.select({ 18 | android: { 19 | register: 'remoteNotificationsRegistered', 20 | notification: 'remoteNotificationReceived', 21 | localNotification: 'localNotificationReceived', 22 | message: 'messageReceived' 23 | }, 24 | ios: { 25 | register: 'remoteNotificationsRegistered', 26 | notification: 'remoteNotificationReceived', 27 | message: 'remoteMessageReceived', 28 | localNotification: 'localNotificationReceived', 29 | }, 30 | }); 31 | 32 | class XGPush { 33 | 34 | static init(accessId, accessKey) { 35 | let accessIdNum = Number(accessId); 36 | if (isNaN(accessIdNum)) { 37 | console.error(`[XGPush init] accessId is not a number!`); 38 | } else { 39 | if (Platform.OS === 'ios') { 40 | XGPushManager.startXGWithAppID(accessIdNum, accessKey); 41 | } else { 42 | XGPushManager.init(accessIdNum, accessKey); 43 | } 44 | } 45 | } 46 | 47 | static register(account) { 48 | if (Platform.OS === 'ios') { 49 | return XGPushManager.bindWithAccount(account); 50 | } else { 51 | return XGPushManager.registerPush(); 52 | } 53 | } 54 | 55 | static setTag(tagName) { 56 | if (Platform.OS === 'ios') { 57 | return XGPushManager.bindWithTag(tagName); 58 | } else { 59 | return XGPushManager.setTag(tagName); 60 | } 61 | } 62 | 63 | static deleteTag(tagName) { 64 | if (Platform.OS === 'ios') { 65 | return XGPushManager.unbindWithTag(tagName); 66 | } else { 67 | return XGPushManager.deleteTag(tagName); 68 | } 69 | } 70 | 71 | static unRegister() { 72 | if (Platform.OS === 'ios') { 73 | return XGPushManager.stopXGNotification(); 74 | } else { 75 | return XGPushManager.unregisterPush(); 76 | } 77 | } 78 | 79 | static setApplicationIconBadgeNumber(number) { 80 | XGPushManager.setApplicationIconBadgeNumber(number); 81 | } 82 | 83 | static getApplicationIconBadgeNumber(callback) { 84 | XGPushManager.getApplicationIconBadgeNumber(callback); 85 | } 86 | 87 | static checkPermissions(callback) { 88 | if (Platform.OS === 'ios') { 89 | return XGPushManager.checkPermissions(callback); 90 | } 91 | } 92 | 93 | static getInitialNotification() { 94 | return XGPushManager.getInitialNotification(); 95 | } 96 | 97 | static onLocalNotification(callback) { 98 | this.addEventListener('localNotification', callback) 99 | } 100 | 101 | /** 102 | * 透传消息 Android only 103 | */ 104 | static onMessage(callback) { 105 | if (Platform.OS === 'android') { 106 | this.addEventListener('message', callback) 107 | } 108 | } 109 | 110 | static addEventListener(eventType, callback) { 111 | let event = EventMapping[eventType]; 112 | if (!event) { 113 | console.warn('XGPush only supports `notification`, `register`, `message` ,and `localNotification` events'); 114 | return; 115 | } 116 | let listener = XGNativeEventEmitter.addListener(event, (data) => { 117 | let result = data; 118 | if (eventType === 'register') { 119 | result = data['deviceToken']; 120 | } 121 | callback(result); 122 | }); 123 | _handlers.set(callback, listener); 124 | } 125 | 126 | static removeEventListener(eventType, callback) { 127 | if (!EventMapping[eventType]) { 128 | console.warn('XGPush only supports `notification`, `register`, `message` and `localNotification` events'); 129 | return; 130 | } 131 | let listener = _handlers.get(callback); 132 | if (listener) { 133 | listener.remove(); 134 | _handlers.delete(callback); 135 | } 136 | } 137 | 138 | static enableDebug(isDebug = true) { 139 | if (Platform.OS === 'ios') { 140 | XGPushManager.setEnableDebug(isDebug); 141 | } else { 142 | XGPushManager.enableDebug(isDebug); 143 | } 144 | } 145 | 146 | static isEnableDebug() { 147 | return XGPushManager.isEnableDebug(); 148 | } 149 | 150 | /**************************** android only ************************/ 151 | 152 | /** 153 | * 获取设备的token,只有注册成功才能获取到正常的结果 154 | */ 155 | static getToken() { 156 | if (Platform.OS === 'android') { 157 | return XGPushManager.getToken(); 158 | } else { 159 | return Promise.resolve(); 160 | } 161 | } 162 | 163 | /** 164 | * 设置上报通知栏是否关闭 默认打开 165 | */ 166 | static setReportNotificationStatusEnable() { 167 | if (Platform.OS === 'android') { 168 | XGPushManager.setReportNotificationStatusEnable(); 169 | } 170 | } 171 | 172 | /** 173 | * 设置上报APP 列表,用于智能推送 默认打开 174 | */ 175 | static setReportApplistEnable() { 176 | if (Platform.OS === 'android') { 177 | XGPushManager.setReportApplistEnable(); 178 | } 179 | } 180 | 181 | /** 182 | * 打开第三方推送(在 registerPush 之前调用) 183 | * @param isEnable 184 | */ 185 | static enableOtherPush(isEnable = true) { 186 | if (Platform.OS === 'android') { 187 | XGPushManager.enableOtherPush(isEnable); 188 | } 189 | } 190 | 191 | /** 192 | * 打开华为通道debug模式 193 | * @param isDebug 194 | */ 195 | static setHuaweiDebug(isDebug = true) { 196 | if (Platform.OS === 'android') { 197 | XGPushManager.setHuaweiDebug(isDebug); 198 | } 199 | } 200 | 201 | static initXiaomi(appId, appKey) { 202 | if (Platform.OS === 'android') { 203 | XGPushManager.initXiaomi(appId, appKey); 204 | } 205 | } 206 | 207 | static initMeizu(appId, appKey) { 208 | if (Platform.OS === 'android') { 209 | XGPushManager.initMeizu(appId, appKey); 210 | } 211 | } 212 | 213 | static initFcm(isEnable = true){ 214 | if(Platform.OS === 'android'){ 215 | XGPushManager.initFcm(isEnable); 216 | } 217 | } 218 | 219 | 220 | /**************************** ios only ************************/ 221 | 222 | 223 | } 224 | 225 | export default XGPush; 226 | -------------------------------------------------------------------------------- /ios/SDK/XGPush.h: -------------------------------------------------------------------------------- 1 | // 2 | // 信鸽核心接口 3 | // XG-SDK 4 | // 5 | // Created by xiangchen on 13-10-18. 6 | // Update by uweiyuan on 4/08/17. 7 | // Copyright (c) 2013年 XG. All rights reserved. 8 | // 9 | 10 | #import 11 | #import 12 | 13 | @class CLLocation; 14 | 15 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 16 | #import 17 | #endif 18 | 19 | /** 20 | @brief 点击行为对象的属性配置 21 | 22 | - XGNotificationActionOptionNone: 无 23 | - XGNotificationActionOptionAuthenticationRequired: 需要认证的选项 24 | - XGNotificationActionOptionDestructive: 具有破坏意义的选项 25 | - XGNotificationActionOptionForeground: 打开应用的选项 26 | */ 27 | typedef NS_ENUM(NSUInteger, XGNotificationActionOptions) { 28 | XGNotificationActionOptionNone = (0), 29 | XGNotificationActionOptionAuthenticationRequired = (1 << 0), 30 | XGNotificationActionOptionDestructive = (1 << 1), 31 | XGNotificationActionOptionForeground = (1 << 2) 32 | }; 33 | 34 | /** 35 | * @brief 定义了一个可以在通知栏中点击的事件对象 36 | */ 37 | @interface XGNotificationAction : NSObject 38 | 39 | /** 40 | @brief 在通知消息中创建一个可以点击的事件行为 41 | 42 | @param identifier 行为唯一标识 43 | @param title 行为名称 44 | @param options 行为支持的选项 45 | @return 行为对象 46 | @note 通知栏带有点击事件的特性,只有在iOS8+以上支持,iOS 8 or earlier的版本,此方法返回空 47 | */ 48 | + (nullable id)actionWithIdentifier:(nonnull NSString *)identifier title:(nonnull NSString *)title options:(XGNotificationActionOptions)options; 49 | 50 | /** 51 | @brief 点击行为的标识 52 | */ 53 | @property (nullable, nonatomic, copy, readonly) NSString *identifier; 54 | 55 | /** 56 | @brief 点击行为的标题 57 | */ 58 | @property (nullable, nonatomic, copy, readonly) NSString *title; 59 | 60 | /** 61 | @brief 点击行为的特性 62 | */ 63 | @property (readonly, nonatomic) XGNotificationActionOptions options; 64 | 65 | @end 66 | 67 | 68 | /** 69 | @brief 分类对象的属性配置 70 | 71 | - XGNotificationCategoryOptionNone: 无 72 | - XGNotificationCategoryOptionCustomDismissAction: 发送消失事件给UNUserNotificationCenter(iOS 10 or later)对象 73 | - XGNotificationCategoryOptionAllowInCarPlay: 允许CarPlay展示此类型的消息 74 | */ 75 | typedef NS_OPTIONS(NSUInteger, XGNotificationCategoryOptions) { 76 | XGNotificationCategoryOptionNone = (0), 77 | XGNotificationCategoryOptionCustomDismissAction = (1 << 0), 78 | XGNotificationCategoryOptionAllowInCarPlay = (1 << 1) 79 | }; 80 | 81 | 82 | /** 83 | * 通知栏中消息指定的分类,分类主要用来管理一组关联的Action,以实现不同分类对应不同的Actions 84 | */ 85 | @interface XGNotificationCategory : NSObject 86 | 87 | 88 | /** 89 | @brief 创建分类对象,用以管理通知栏的Action对象 90 | 91 | @param identifier 分类对象的标识 92 | @param actions 当前分类拥有的行为对象组 93 | @param intentIdentifiers 用以表明可以通过Siri识别的标识 94 | @param options 分类的特性 95 | @return 管理点击行为的分类对象 96 | @note 通知栏带有点击事件的特性,只有在iOS8+以上支持,iOS 8 or earlier的版本,此方法返回空 97 | */ 98 | + (nullable id)categoryWithIdentifier:(nonnull NSString *)identifier actions:(nullable NSArray *)actions intentIdentifiers:(nullable NSArray *)intentIdentifiers options:(XGNotificationCategoryOptions)options; 99 | 100 | /** 101 | @brief 分类对象的标识 102 | */ 103 | @property (nonnull, readonly, copy, nonatomic) NSString *identifier; 104 | 105 | /** 106 | @brief 分类对象拥有的点击行为组 107 | */ 108 | @property (nonnull, readonly, copy, nonatomic) NSArray *actions; 109 | 110 | /** 111 | @brief 可用以Siri意图的标识组 112 | */ 113 | @property (nullable, readonly, copy, nonatomic) NSArray *intentIdentifiers; 114 | 115 | /** 116 | @brief 分类的特性 117 | */ 118 | @property (readonly, nonatomic) XGNotificationCategoryOptions options; 119 | 120 | @end 121 | 122 | /** 123 | @brief 注册通知支持的类型 124 | 125 | - XGUserNotificationTypeNone: 无 126 | - XGUserNotificationTypeBadge: 支持应用角标 127 | - XGUserNotificationTypeSound: 支持铃声 128 | - XGUserNotificationTypeAlert: 支持弹框 129 | - XGUserNotificationTypeCarPlay: 支持CarPlay,iOS 10.0+ 130 | - XGUserNotificationTypeCriticalAlert: 支持紧急提醒播放声音, iOS 12.0+ 131 | - XGUserNotificationTypeProvidesAppNotificationSettings: 让系统在应用内通知设置中显示按钮, iOS 12.0+ 132 | - XGUserNotificationTypeProvisional: 能够将非中断通知临时发布到 Notification Center, iOS 12.0+ 133 | - XGUserNotificationTypeNewsstandContentAvailability: 支持 Newsstand, iOS 3.0–8.0 134 | */ 135 | typedef NS_OPTIONS(NSUInteger, XGUserNotificationTypes) { 136 | XGUserNotificationTypeNone = (0), 137 | XGUserNotificationTypeBadge = (1 << 0), 138 | XGUserNotificationTypeSound = (1 << 1), 139 | XGUserNotificationTypeAlert = (1 << 2), 140 | XGUserNotificationTypeCarPlay = (1 << 3), 141 | XGUserNotificationTypeCriticalAlert = (1 << 4), 142 | XGUserNotificationTypeProvidesAppNotificationSettings = (1 << 5), 143 | XGUserNotificationTypeProvisional = (1 << 6), 144 | XGUserNotificationTypeNewsstandContentAvailability = (1 << 3) 145 | }; 146 | 147 | /** 148 | @brief 管理推送消息通知栏的样式和特性 149 | */ 150 | @interface XGNotificationConfigure : NSObject 151 | 152 | /** 153 | @brief 配置通知栏对象,主要是为了配置消息通知的样式和行为特性 154 | 155 | @param categories 通知栏中支持的分类集合 156 | @param types 注册通知的样式 157 | @return 配置对象 158 | */ 159 | + (nullable instancetype)configureNotificationWithCategories:(nullable NSSet *)categories types:(XGUserNotificationTypes)types; 160 | 161 | - (nonnull instancetype)init NS_UNAVAILABLE; 162 | /** 163 | @brief 返回消息通知栏配置对象 164 | */ 165 | @property (readonly, nullable, strong, nonatomic) NSSet *categories; 166 | 167 | 168 | /** 169 | @brief 返回注册推送的样式类型 170 | */ 171 | @property (readonly, nonatomic) XGUserNotificationTypes types; 172 | 173 | /** 174 | @brief 默认的注册推送的样式类型 175 | */ 176 | @property (readonly, nonatomic) XGUserNotificationTypes defaultTypes; 177 | 178 | @end 179 | 180 | 181 | /** 182 | @brief 设备token绑定的类型,绑定指定类型之后,就可以在信鸽前端按照指定的类型进行指定范围的推送 183 | 184 | - XGPushTokenBindTypeNone: 当前设备token不绑定任何类型,可以使用token单推,或者是全量推送(3.2.0+ 不推荐使用 ) 185 | - XGPushTokenBindTypeAccount: 当前设备token与账号绑定之后,可以使用账号推送 186 | - XGPushTokenBindTypeTag: 当前设备token与指定标签绑定之后,可以使用标签推送 187 | */ 188 | typedef NS_ENUM(NSUInteger, XGPushTokenBindType) { 189 | XGPushTokenBindTypeNone = (0), 190 | XGPushTokenBindTypeAccount = (1 << 0), 191 | XGPushTokenBindTypeTag = (1 << 1) 192 | }; 193 | 194 | /** 195 | @brief 定义了一组关于设备token绑定,解绑账号和标签的回调方法,用以监控绑定和解绑的情况 196 | */ 197 | @protocol XGPushTokenManagerDelegate 198 | 199 | @optional 200 | 201 | /** 202 | @brief 监控token对象绑定的情况 203 | 204 | @param identifier token对象绑定的标识 205 | @param type token对象绑定的类型 206 | @param error token对象绑定的结果信息 207 | */ 208 | - (void)xgPushDidBindWithIdentifier:(nonnull NSString *)identifier type:(XGPushTokenBindType)type error:(nullable NSError *)error; 209 | 210 | /** 211 | @brief 监控token对象解绑的情况 212 | 213 | @param identifier token对象解绑的标识 214 | @param type token对象解绑的类型 215 | @param error token对象解绑的结果信息 216 | */ 217 | - (void)xgPushDidUnbindWithIdentifier:(nonnull NSString *)identifier type:(XGPushTokenBindType)type error:(nullable NSError *)error; 218 | 219 | /** 220 | @brief 监控token对象identifiers绑定的情况 221 | 222 | @param identifiers token对象绑定的标识 223 | @param type token对象绑定的类型 224 | @param error token对象绑定的结果信息 225 | */ 226 | - (void)xgPushDidBindWithIdentifiers:(nonnull NSArray *)identifiers type:(XGPushTokenBindType)type error:(nullable NSError *)error; 227 | 228 | /** 229 | @brief 监控token对象identifiers解绑的情况 230 | 231 | @param identifiers token对象解绑的标识 232 | @param type token对象解绑的类型 233 | @param error token对象解绑的结果信息 234 | */ 235 | - (void)xgPushDidUnbindWithIdentifiers:(nonnull NSArray *)identifiers type:(XGPushTokenBindType)type error:(nullable NSError *)error; 236 | 237 | /** 238 | @brief 监控token对象更新已绑定标识的情况 239 | 240 | @param identifiers token对象更新后的标识 241 | @param type token对象更新类型 242 | @param error token对象更新标识的结果信息 243 | */ 244 | - (void)xgPushDidUpdatedBindedIdentifiers:(nonnull NSArray *)identifiers bindType:(XGPushTokenBindType)type error:(nullable NSError *)error; 245 | 246 | 247 | /** 248 | @brief 监控清除token对象绑定标识的情况 249 | 250 | @param type token对象清除的类型 251 | @param error token对象清除标识的结果信息 252 | */ 253 | - (void)xgPushDidClearAllIdentifiers:(XGPushTokenBindType)type error:(nullable NSError *)error; 254 | 255 | 256 | @end 257 | 258 | @interface XGPushTokenManager : NSObject 259 | 260 | /** 261 | @brief 创建设备token的管理对象,用来管理token的绑定与解绑操作 262 | 263 | @return 设备token管理对象 264 | @note 此类的 APIs 调用都是以 Token 在信鸽服务上完成注册为前提 265 | */ 266 | 267 | + (nonnull instancetype)defaultTokenManager; 268 | 269 | - (nonnull instancetype)init NS_UNAVAILABLE; 270 | /** 271 | @brief 设备token管理操作的代理对象 272 | */ 273 | @property (weak, nonatomic, nullable) id delegate; 274 | 275 | /** 276 | @brief 返回当前设备token的字符串 277 | */ 278 | @property (copy, nonatomic, nullable, readonly) NSString *deviceTokenString; 279 | 280 | /** 281 | @brief 为token对象设置绑定类型和标识 282 | 283 | @param identifier 指定绑定标识 284 | @param type 指定绑定类型 285 | */ 286 | - (void)bindWithIdentifier:(nonnull NSString *)identifier type:(XGPushTokenBindType)type; 287 | 288 | /** 289 | @brief 根据类型和标识为token对象解绑 290 | 291 | @param identifier 指定解绑标识 292 | @param type 指定解绑类型 293 | @note 若需要解绑全部标签,建议使用 removeAllTags: 接口 294 | */ 295 | - (void)unbindWithIdentifer:(nonnull NSString *)identifier type:(XGPushTokenBindType)type; 296 | 297 | /** 298 | @brief 根据指定类型查询当前token对象绑定的标识 299 | 300 | @param type 指定绑定类型 301 | @return 当前token对象绑定的标识 302 | */ 303 | - (nullable NSArray *)identifiersWithType:(XGPushTokenBindType)type; 304 | 305 | /** 306 | @brief 为token对象设置绑定类型和标识 307 | 308 | @param identifiers 指定绑定标识,标签字符串不允许有空格或者是tab字符 309 | @param type 指定绑定类型 310 | @note 对于账号操作,需要使用json数组,例如: 311 | [ 312 | {"account" : "account1", "account_type" : 1}, 313 | {"account" : "account2","account_type" : 0} 314 | ] 315 | 账号类型,请参照: http://xg.qq.com/docs/server_api/v3/push_api_v3.html#账号类型 316 | */ 317 | - (void)bindWithIdentifiers:(nonnull NSArray *)identifiers type:(XGPushTokenBindType)type; 318 | 319 | /** 320 | @brief 根据类型和标识为token对象解绑 321 | 322 | @param identifiers 指定解绑标识,标签字符串不允许有空格或者是tab字符 323 | @param type 指定解绑类型 324 | @note 标签字符串不允许有空格或者是tab字符;对于账号操作,需要使用json数组,例如: 325 | [ 326 | {"account" : "account1", "account_type" : 1}, 327 | {"account" : "account2","account_type" : 0} 328 | ] 329 | 账号类型,请参照: http://xg.qq.com/docs/server_api/v3/push_api_v3.html#账号类型 330 | */ 331 | - (void)unbindWithIdentifers:(nonnull NSArray *)identifiers type:(XGPushTokenBindType)type; 332 | 333 | /** 334 | @brief 根据类型,覆盖原有的标识;若之前没有绑定标识,则会执行新增标识 335 | 336 | @param identifiers 标签标识字符串数组,标签字符串不允许有空格或者是tab字符 337 | @param type 标识类型 338 | @note 若指定为标签类型,此接口会将当前 Token 对应的旧有的标签全部替换为当前的标签;若指定账号类型,对于账号操作,则需要使用json数组,例如: 339 | [ 340 | {"account" : "account1", "account_type" : 1}, 341 | {"account" : "account2","account_type" : 0} 342 | ] 343 | 账号类型,请参照: http://xg.qq.com/docs/server_api/v3/push_api_v3.html#账号类型 344 | */ 345 | - (void)updateBindedIdentifiers:(nonnull NSArray *)identifiers bindType:(XGPushTokenBindType)type; 346 | 347 | /** 348 | @brief 根据标识类型,清除所有标识 349 | 350 | @param type 标识类型 351 | */ 352 | - (void)clearAllIdentifiers:(XGPushTokenBindType)type; 353 | 354 | 355 | @end 356 | 357 | 358 | /** 359 | @brief 监控信鸽服务启动和设备token注册的一组方法 360 | */ 361 | @protocol XGPushDelegate 362 | 363 | @optional 364 | 365 | - (void)xgPushDidReceiveRemoteNotification:(nonnull id)notification withCompletionHandler:(nullable void (^)(NSUInteger))completionHandler; 366 | 367 | 368 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 369 | /** 370 | @brief 处理iOS 10 UNUserNotification.framework的对应的方法 371 | 372 | @param center [UNUserNotificationCenter currentNotificationCenter] 373 | @param notification 通知对象 374 | @param completionHandler 回调对象,必须调用 375 | */ 376 | - (void)xgPushUserNotificationCenter:(nonnull UNUserNotificationCenter *)center willPresentNotification:(nullable UNNotification *)notification withCompletionHandler:(nonnull void (^)(UNNotificationPresentationOptions options))completionHandler __IOS_AVAILABLE(10.0); 377 | 378 | /** 379 | @brief 处理iOS 10 UNUserNotification.framework的对应的方法 380 | 381 | @param center [UNUserNotificationCenter currentNotificationCenter] 382 | @param response 用户对通知消息的响应对象 383 | @param completionHandler 回调对象,必须调用 384 | */ 385 | - (void)xgPushUserNotificationCenter:(nonnull UNUserNotificationCenter *)center didReceiveNotificationResponse:(nullable UNNotificationResponse *)response withCompletionHandler:(nonnull void (^)(void))completionHandler __IOS_AVAILABLE(10.0); 386 | 387 | #endif 388 | 389 | /** 390 | @brief 监控信鸽推送服务地启动情况 391 | 392 | @param isSuccess 信鸽推送是否启动成功 393 | @param error 信鸽推送启动错误的信息 394 | */ 395 | - (void)xgPushDidFinishStart:(BOOL)isSuccess error:(nullable NSError *)error; 396 | 397 | /** 398 | @brief 监控信鸽服务的终止情况 399 | 400 | @param isSuccess 信鸽推送是否终止 401 | @param error 信鸽推动终止错误的信息 402 | */ 403 | - (void)xgPushDidFinishStop:(BOOL)isSuccess error:(nullable NSError *)error; 404 | 405 | 406 | /** 407 | @brief 监控信鸽服务上报推送消息的情况 408 | 409 | @param isSuccess 上报是否成功 410 | @param error 上报失败的信息 411 | */ 412 | - (void)xgPushDidReportNotification:(BOOL)isSuccess error:(nullable NSError *)error; 413 | 414 | 415 | /** 416 | @brief 监控设置信鸽服务器下发角标的情况 417 | 418 | @param isSuccess isSuccess 上报是否成功 419 | @param error 设置失败的信息 420 | */ 421 | - (void)xgPushDidSetBadge:(BOOL)isSuccess error:(nullable NSError *)error; 422 | 423 | /** 424 | @brief 设备token注册信鸽服务的回调 425 | 426 | @param deviceToken 当前设备的token 427 | @param error 错误信息 428 | */ 429 | - (void)xgPushDidRegisteredDeviceToken:(nullable NSString *)deviceToken error:(nullable NSError *)error; 430 | 431 | @end 432 | 433 | /** 434 | @brief 管理信鸽推送服务的对象,负责注册推送权限、消息的管理、调试模式的开关设置等 435 | */ 436 | @interface XGPush : NSObject 437 | 438 | #pragma mark - 初始化相关 439 | 440 | /** 441 | @brief 获取信鸽推送管理的单例对象 442 | 443 | @return 信鸽推送对象 444 | */ 445 | + (nonnull instancetype)defaultManager; 446 | 447 | /** 448 | @brief 关于信鸽推送SDK接口协议的对象 449 | */ 450 | @property (weak, nonatomic, nullable, readonly) id delegate; 451 | 452 | 453 | /** 454 | @brief 信鸽推送管理对象,管理推送的配置选项,例如,注册推送的样式 455 | */ 456 | @property (nullable, strong, nonatomic) XGNotificationConfigure *notificationConfigure; 457 | 458 | 459 | /** 460 | @brief 这个开关表明是否打印信鸽SDK的日志信息 461 | */ 462 | @property (assign, getter=isEnableDebug) BOOL enableDebug; 463 | 464 | /** 465 | @brief 返回信鸽推送服务的状态 466 | */ 467 | @property (assign, readonly) BOOL xgNotificationStatus __deprecated_msg("XG SDK 3.3+, instead, you can use deviceTokenDidRegisteredXGService property"); 468 | 469 | 470 | /** 471 | @brief 设备在信鸽服务中的是否处于注册状态 472 | */ 473 | @property (assign, readonly) BOOL deviceDidRegisteredXG; 474 | 475 | /** 476 | @brief 管理应用角标 477 | */ 478 | @property (nonatomic) NSInteger xgApplicationBadgeNumber; 479 | 480 | /** 481 | @brief 通过使用在信鸽官网注册的应用的信息,启动信鸽推送服务 482 | 483 | @param appID 通过前台申请的应用ID 484 | @param appKey 通过前台申请的appKey 485 | @param delegate 回调对象 486 | @note 接口所需参数必须要正确填写,反之信鸽服务将不能正确为应用推送消息 487 | */ 488 | - (void)startXGWithAppID:(uint32_t)appID appKey:(nonnull NSString *)appKey delegate:(nullable id)delegate; 489 | 490 | /** 491 | @brief 停止信鸽推送服务 492 | @note 调用此方法将导致当前设备不再接受信鸽服务推送的消息.如果再次需要接收信鸽服务的消息推送,则必须需要再次调用startXG:withAppKey:delegate:方法重启信鸽推送服务 493 | */ 494 | - (void)stopXGNotification; 495 | 496 | /** 497 | @brief 上报应用收到的推送信息,以便信鸽服务能够统计相关数据,包括但不限于:1.推送消息被点击的次数,2.消息曝光的次数 498 | 499 | @param info 应用接收到的推送消息对象的内容 500 | @note 请在实现application delegate 的 application:didFinishLaunchingWithOptions:或者application:didReceiveRemoteNotification:的方法中调用此接口,参数就使用这两个方法中的NSDictionaryl类型的参数即可,从而完成推送消息的数据统计 501 | */ 502 | - (void)reportXGNotificationInfo:(nonnull NSDictionary *)info; 503 | 504 | /** 505 | @brief 上报地理位置信息 506 | 507 | @param latitude 纬度 508 | @param longitude 经度 509 | */ 510 | - (void)reportLocationWithLatitude:(double)latitude longitude:(double)longitude; 511 | 512 | /** 513 | @brief 上报当前App角标数到信鸽服务器 514 | 515 | @param badgeNumber 应用的角标数 516 | @note 此接口是为了实现角标+1的功能,服务器会在这个数值基础上进行角标数新增的操作,调用成功之后,会覆盖之前值 517 | */ 518 | - (void)setBadge:(NSInteger)badgeNumber; 519 | 520 | /** 521 | @brief 上报推送消息的用户行为 522 | 523 | @param identifier 用户行为标识 524 | @note 此接口即统计推送消息中开发者预先设置或者是系统预置的行为标识,可以了解到用户是如何处理推送消息的,又统计消息的点击次数 525 | */ 526 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 527 | - (void)reportXGNotificationResponse:(nullable UNNotificationResponse *)response __IOS_AVAILABLE(10.0); 528 | #endif 529 | 530 | /** 531 | @brief 查询设备通知权限是否被用户允许 532 | 533 | @param handler 查询结果的返回方法 534 | @note iOS 10 or later 回调是异步地执行 535 | */ 536 | - (void)deviceNotificationIsAllowed:(nonnull void (^)(BOOL isAllowed))handler; 537 | 538 | /** 539 | @brief 查看SDK的版本 540 | 541 | @return sdk版本号 542 | */ 543 | - (nonnull NSString *)sdkVersion; 544 | 545 | @end 546 | 547 | -------------------------------------------------------------------------------- /ios/XGPush.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2D9EBB202345D36200E0F598 /* libXG-SDK.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 2D9EBB1F2345D36200E0F598 /* libXG-SDK.a */; }; 11 | C50FF02A1D54262A00CEF3D0 /* XGPushManager.m in Sources */ = {isa = PBXBuildFile; fileRef = C50FF0291D54262A00CEF3D0 /* XGPushManager.m */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | C50FF0221D54262A00CEF3D0 /* Copy Files */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = "include/$(PRODUCT_NAME)"; 19 | dstSubfolderSpec = 16; 20 | files = ( 21 | ); 22 | name = "Copy Files"; 23 | runOnlyForDeploymentPostprocessing = 0; 24 | }; 25 | /* End PBXCopyFilesBuildPhase section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 2D9EBB1F2345D36200E0F598 /* libXG-SDK.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libXG-SDK.a"; sourceTree = ""; }; 29 | C50FF0241D54262A00CEF3D0 /* libXGPush.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libXGPush.a; sourceTree = BUILT_PRODUCTS_DIR; }; 30 | C50FF0271D54262A00CEF3D0 /* XGPushManager.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = XGPushManager.h; sourceTree = ""; }; 31 | C50FF0291D54262A00CEF3D0 /* XGPushManager.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = XGPushManager.m; sourceTree = ""; }; 32 | C5D48E311D5428D2004BD09A /* XGPush.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = XGPush.h; path = SDK/XGPush.h; sourceTree = ""; }; 33 | C5D48E761D54731F004BD09A /* libReact.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libReact.a; path = "../../react-native/React/build/Debug-iphoneos/libReact.a"; sourceTree = ""; }; 34 | /* End PBXFileReference section */ 35 | 36 | /* Begin PBXFrameworksBuildPhase section */ 37 | C50FF0211D54262A00CEF3D0 /* Frameworks */ = { 38 | isa = PBXFrameworksBuildPhase; 39 | buildActionMask = 2147483647; 40 | files = ( 41 | 2D9EBB202345D36200E0F598 /* libXG-SDK.a in Frameworks */, 42 | ); 43 | runOnlyForDeploymentPostprocessing = 0; 44 | }; 45 | /* End PBXFrameworksBuildPhase section */ 46 | 47 | /* Begin PBXGroup section */ 48 | C50FF01B1D54262A00CEF3D0 = { 49 | isa = PBXGroup; 50 | children = ( 51 | C5D48E321D5428DE004BD09A /* Frameworks */, 52 | C5D48E2F1D5428C1004BD09A /* SDK */, 53 | C50FF0261D54262A00CEF3D0 /* XGPush */, 54 | C50FF0251D54262A00CEF3D0 /* Products */, 55 | ); 56 | sourceTree = ""; 57 | }; 58 | C50FF0251D54262A00CEF3D0 /* Products */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | C50FF0241D54262A00CEF3D0 /* libXGPush.a */, 62 | ); 63 | name = Products; 64 | sourceTree = ""; 65 | }; 66 | C50FF0261D54262A00CEF3D0 /* XGPush */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | C50FF0271D54262A00CEF3D0 /* XGPushManager.h */, 70 | C50FF0291D54262A00CEF3D0 /* XGPushManager.m */, 71 | ); 72 | path = XGPush; 73 | sourceTree = ""; 74 | }; 75 | C5D48E2F1D5428C1004BD09A /* SDK */ = { 76 | isa = PBXGroup; 77 | children = ( 78 | C5D48E311D5428D2004BD09A /* XGPush.h */, 79 | ); 80 | name = SDK; 81 | sourceTree = ""; 82 | }; 83 | C5D48E321D5428DE004BD09A /* Frameworks */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 2D9EBB1F2345D36200E0F598 /* libXG-SDK.a */, 87 | C5D48E761D54731F004BD09A /* libReact.a */, 88 | ); 89 | name = Frameworks; 90 | sourceTree = ""; 91 | }; 92 | /* End PBXGroup section */ 93 | 94 | /* Begin PBXNativeTarget section */ 95 | C50FF0231D54262A00CEF3D0 /* XGPush */ = { 96 | isa = PBXNativeTarget; 97 | buildConfigurationList = C50FF02D1D54262A00CEF3D0 /* Build configuration list for PBXNativeTarget "XGPush" */; 98 | buildPhases = ( 99 | C50FF0201D54262A00CEF3D0 /* Sources */, 100 | C50FF0211D54262A00CEF3D0 /* Frameworks */, 101 | C50FF0221D54262A00CEF3D0 /* Copy Files */, 102 | ); 103 | buildRules = ( 104 | ); 105 | dependencies = ( 106 | ); 107 | name = XGPush; 108 | productName = RCTXGPush; 109 | productReference = C50FF0241D54262A00CEF3D0 /* libXGPush.a */; 110 | productType = "com.apple.product-type.library.static"; 111 | }; 112 | /* End PBXNativeTarget section */ 113 | 114 | /* Begin PBXProject section */ 115 | C50FF01C1D54262A00CEF3D0 /* Project object */ = { 116 | isa = PBXProject; 117 | attributes = { 118 | LastUpgradeCheck = 0730; 119 | ORGANIZATIONNAME = Jeepeng; 120 | TargetAttributes = { 121 | C50FF0231D54262A00CEF3D0 = { 122 | CreatedOnToolsVersion = 7.3.1; 123 | }; 124 | }; 125 | }; 126 | buildConfigurationList = C50FF01F1D54262A00CEF3D0 /* Build configuration list for PBXProject "XGPush" */; 127 | compatibilityVersion = "Xcode 3.2"; 128 | developmentRegion = English; 129 | hasScannedForEncodings = 0; 130 | knownRegions = ( 131 | English, 132 | en, 133 | ); 134 | mainGroup = C50FF01B1D54262A00CEF3D0; 135 | productRefGroup = C50FF0251D54262A00CEF3D0 /* Products */; 136 | projectDirPath = ""; 137 | projectRoot = ""; 138 | targets = ( 139 | C50FF0231D54262A00CEF3D0 /* XGPush */, 140 | ); 141 | }; 142 | /* End PBXProject section */ 143 | 144 | /* Begin PBXSourcesBuildPhase section */ 145 | C50FF0201D54262A00CEF3D0 /* Sources */ = { 146 | isa = PBXSourcesBuildPhase; 147 | buildActionMask = 2147483647; 148 | files = ( 149 | C50FF02A1D54262A00CEF3D0 /* XGPushManager.m in Sources */, 150 | ); 151 | runOnlyForDeploymentPostprocessing = 0; 152 | }; 153 | /* End PBXSourcesBuildPhase section */ 154 | 155 | /* Begin XCBuildConfiguration section */ 156 | C50FF02B1D54262A00CEF3D0 /* Debug */ = { 157 | isa = XCBuildConfiguration; 158 | buildSettings = { 159 | ALWAYS_SEARCH_USER_PATHS = NO; 160 | CLANG_ANALYZER_NONNULL = YES; 161 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 162 | CLANG_CXX_LIBRARY = "libc++"; 163 | CLANG_ENABLE_MODULES = YES; 164 | CLANG_ENABLE_OBJC_ARC = YES; 165 | CLANG_WARN_BOOL_CONVERSION = YES; 166 | CLANG_WARN_CONSTANT_CONVERSION = YES; 167 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 168 | CLANG_WARN_EMPTY_BODY = YES; 169 | CLANG_WARN_ENUM_CONVERSION = YES; 170 | CLANG_WARN_INT_CONVERSION = YES; 171 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 172 | CLANG_WARN_UNREACHABLE_CODE = YES; 173 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 174 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 175 | COPY_PHASE_STRIP = NO; 176 | DEBUG_INFORMATION_FORMAT = dwarf; 177 | ENABLE_STRICT_OBJC_MSGSEND = YES; 178 | ENABLE_TESTABILITY = YES; 179 | GCC_C_LANGUAGE_STANDARD = gnu99; 180 | GCC_DYNAMIC_NO_PIC = NO; 181 | GCC_NO_COMMON_BLOCKS = YES; 182 | GCC_OPTIMIZATION_LEVEL = 0; 183 | GCC_PREPROCESSOR_DEFINITIONS = ( 184 | "DEBUG=1", 185 | "$(inherited)", 186 | ); 187 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 188 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 189 | GCC_WARN_UNDECLARED_SELECTOR = YES; 190 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 191 | GCC_WARN_UNUSED_FUNCTION = YES; 192 | GCC_WARN_UNUSED_VARIABLE = YES; 193 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 194 | MTL_ENABLE_DEBUG_INFO = YES; 195 | ONLY_ACTIVE_ARCH = YES; 196 | SDKROOT = iphoneos; 197 | }; 198 | name = Debug; 199 | }; 200 | C50FF02C1D54262A00CEF3D0 /* Release */ = { 201 | isa = XCBuildConfiguration; 202 | buildSettings = { 203 | ALWAYS_SEARCH_USER_PATHS = NO; 204 | CLANG_ANALYZER_NONNULL = YES; 205 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 206 | CLANG_CXX_LIBRARY = "libc++"; 207 | CLANG_ENABLE_MODULES = YES; 208 | CLANG_ENABLE_OBJC_ARC = YES; 209 | CLANG_WARN_BOOL_CONVERSION = YES; 210 | CLANG_WARN_CONSTANT_CONVERSION = YES; 211 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 212 | CLANG_WARN_EMPTY_BODY = YES; 213 | CLANG_WARN_ENUM_CONVERSION = YES; 214 | CLANG_WARN_INT_CONVERSION = YES; 215 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 216 | CLANG_WARN_UNREACHABLE_CODE = YES; 217 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 218 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 219 | COPY_PHASE_STRIP = NO; 220 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 221 | ENABLE_NS_ASSERTIONS = NO; 222 | ENABLE_STRICT_OBJC_MSGSEND = YES; 223 | GCC_C_LANGUAGE_STANDARD = gnu99; 224 | GCC_NO_COMMON_BLOCKS = YES; 225 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 226 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 227 | GCC_WARN_UNDECLARED_SELECTOR = YES; 228 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 229 | GCC_WARN_UNUSED_FUNCTION = YES; 230 | GCC_WARN_UNUSED_VARIABLE = YES; 231 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 232 | MTL_ENABLE_DEBUG_INFO = NO; 233 | SDKROOT = iphoneos; 234 | VALIDATE_PRODUCT = YES; 235 | }; 236 | name = Release; 237 | }; 238 | C50FF02E1D54262A00CEF3D0 /* Debug */ = { 239 | isa = XCBuildConfiguration; 240 | buildSettings = { 241 | HEADER_SEARCH_PATHS = ( 242 | "$(inherited)", 243 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 244 | "$(SRCROOT)/../../react-native/React/**", 245 | ); 246 | LIBRARY_SEARCH_PATHS = ( 247 | "$(inherited)", 248 | "$(PROJECT_DIR)/SDK/**", 249 | "$(PROJECT_DIR)", 250 | ); 251 | OTHER_LDFLAGS = "-ObjC"; 252 | PRODUCT_NAME = XGPush; 253 | SKIP_INSTALL = YES; 254 | }; 255 | name = Debug; 256 | }; 257 | C50FF02F1D54262A00CEF3D0 /* Release */ = { 258 | isa = XCBuildConfiguration; 259 | buildSettings = { 260 | HEADER_SEARCH_PATHS = ( 261 | "$(inherited)", 262 | /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include, 263 | "$(SRCROOT)/../../react-native/React/**", 264 | ); 265 | LIBRARY_SEARCH_PATHS = ( 266 | "$(inherited)", 267 | "$(PROJECT_DIR)/SDK/**", 268 | "$(PROJECT_DIR)", 269 | ); 270 | OTHER_LDFLAGS = "-ObjC"; 271 | PRODUCT_NAME = XGPush; 272 | SKIP_INSTALL = YES; 273 | }; 274 | name = Release; 275 | }; 276 | /* End XCBuildConfiguration section */ 277 | 278 | /* Begin XCConfigurationList section */ 279 | C50FF01F1D54262A00CEF3D0 /* Build configuration list for PBXProject "XGPush" */ = { 280 | isa = XCConfigurationList; 281 | buildConfigurations = ( 282 | C50FF02B1D54262A00CEF3D0 /* Debug */, 283 | C50FF02C1D54262A00CEF3D0 /* Release */, 284 | ); 285 | defaultConfigurationIsVisible = 0; 286 | defaultConfigurationName = Release; 287 | }; 288 | C50FF02D1D54262A00CEF3D0 /* Build configuration list for PBXNativeTarget "XGPush" */ = { 289 | isa = XCConfigurationList; 290 | buildConfigurations = ( 291 | C50FF02E1D54262A00CEF3D0 /* Debug */, 292 | C50FF02F1D54262A00CEF3D0 /* Release */, 293 | ); 294 | defaultConfigurationIsVisible = 0; 295 | defaultConfigurationName = Release; 296 | }; 297 | /* End XCConfigurationList section */ 298 | }; 299 | rootObject = C50FF01C1D54262A00CEF3D0 /* Project object */; 300 | } 301 | -------------------------------------------------------------------------------- /ios/XGPush.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/XGPush.xcodeproj/project.xcworkspace/xcuserdata/Jeepeng.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PandaQQ/react-native-xinge-push/3b77b89fa834236fde2220f600d388a4f2d35ec8/ios/XGPush.xcodeproj/project.xcworkspace/xcuserdata/Jeepeng.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /ios/XGPush.xcodeproj/xcuserdata/Jeepeng.xcuserdatad/xcschemes/XGPush.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 55 | 56 | 57 | 63 | 64 | 66 | 67 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /ios/XGPush.xcodeproj/xcuserdata/Jeepeng.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | XGPush.xcscheme 8 | 9 | orderHint 10 | 2 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | C50FF0231D54262A00CEF3D0 16 | 17 | primary 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /ios/XGPush/XGPushManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // XGPushManager.h 3 | // XGPushManager 4 | // 5 | // Created by Jeepeng on 16/8/5. 6 | // Copyright © 2016年 Jeepeng. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | extern NSString *const RCTRemoteNotificationReceived; 12 | 13 | @interface XGPushManager : RCTEventEmitter 14 | 15 | typedef void (^RCTRemoteNotificationCallback)(UIBackgroundFetchResult result); 16 | 17 | #if !TARGET_OS_TV 18 | + (void)didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings; 19 | + (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; 20 | + (void)didReceiveRemoteNotification:(NSDictionary *)notification; 21 | + (void)didReceiveRemoteNotification:(NSDictionary *)notification fetchCompletionHandler:(RCTRemoteNotificationCallback)completionHandler; 22 | + (void)didReceiveRemoteMessage:(NSDictionary *)notification; 23 | + (void)didReceiveRemoteMessage:(NSDictionary *)notification fetchCompletionHandler:(RCTRemoteNotificationCallback)completionHandler; 24 | + (void)didReceiveLocalNotification:(UILocalNotification *)notification; 25 | + (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error; 26 | #endif 27 | 28 | @end 29 | -------------------------------------------------------------------------------- /ios/XGPush/XGPushManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // XGPushManager.m 3 | // 参考了 react-native 官方 PushNotificationIOS 的代码 4 | // Created by Jeepeng on 16/8/5. 5 | // Copyright © 2016年 Jeepeng. All rights reserved. 6 | // 7 | 8 | #import "XGPushManager.h" 9 | #import "XGPush.h" 10 | 11 | #import 12 | 13 | #import 14 | #import 15 | #import 16 | #import 17 | 18 | NSString *const MYRCTRemoteNotificationReceived = @"RemoteNotificationReceived"; 19 | NSString *const RCTRemoteMessageReceived = @"RemoteMessageReceived"; 20 | 21 | static NSString *const kLocalNotificationReceived = @"LocalNotificationReceived"; 22 | static NSString *const kRemoteNotificationsRegistered = @"RemoteNotificationsRegistered"; 23 | static NSString *const kRegisterUserNotificationSettings = @"RegisterUserNotificationSettings"; 24 | static NSString *const kRemoteNotificationRegistrationFailed = @"RemoteNotificationRegistrationFailed"; 25 | 26 | static NSString *const kErrorUnableToRequestPermissions = @"E_UNABLE_TO_REQUEST_PERMISSIONS"; 27 | 28 | #if !TARGET_OS_TV 29 | @implementation RCTConvert (NSCalendarUnit) 30 | 31 | RCT_ENUM_CONVERTER(NSCalendarUnit, 32 | (@{ 33 | @"year": @(NSCalendarUnitYear), 34 | @"month": @(NSCalendarUnitMonth), 35 | @"week": @(NSCalendarUnitWeekOfYear), 36 | @"day": @(NSCalendarUnitDay), 37 | @"hour": @(NSCalendarUnitHour), 38 | @"minute": @(NSCalendarUnitMinute) 39 | }), 40 | 0, 41 | integerValue) 42 | 43 | @end 44 | 45 | @interface XGPushManager () 46 | @property (nonatomic, strong) NSMutableDictionary *remoteNotificationCallbacks; 47 | @end 48 | 49 | @implementation RCTConvert (UILocalNotification) 50 | 51 | + (UILocalNotification *)UILocalNotification:(id)json 52 | { 53 | NSDictionary *details = [self NSDictionary:json]; 54 | BOOL isSilent = [RCTConvert BOOL:details[@"isSilent"]]; 55 | UILocalNotification *notification = [UILocalNotification new]; 56 | notification.alertTitle = [RCTConvert NSString:details[@"alertTitle"]]; 57 | notification.fireDate = [RCTConvert NSDate:details[@"fireDate"]] ?: [NSDate date]; 58 | notification.alertBody = [RCTConvert NSString:details[@"alertBody"]]; 59 | notification.alertAction = [RCTConvert NSString:details[@"alertAction"]]; 60 | notification.userInfo = [RCTConvert NSDictionary:details[@"userInfo"]]; 61 | notification.category = [RCTConvert NSString:details[@"category"]]; 62 | notification.repeatInterval = [RCTConvert NSCalendarUnit:details[@"repeatInterval"]]; 63 | if (details[@"applicationIconBadgeNumber"]) { 64 | notification.applicationIconBadgeNumber = [RCTConvert NSInteger:details[@"applicationIconBadgeNumber"]]; 65 | } 66 | if (!isSilent) { 67 | notification.soundName = [RCTConvert NSString:details[@"soundName"]] ?: UILocalNotificationDefaultSoundName; 68 | } 69 | return notification; 70 | } 71 | 72 | RCT_ENUM_CONVERTER(UIBackgroundFetchResult, (@{ 73 | @"UIBackgroundFetchResultNewData": @(UIBackgroundFetchResultNewData), 74 | @"UIBackgroundFetchResultNoData": @(UIBackgroundFetchResultNoData), 75 | @"UIBackgroundFetchResultFailed": @(UIBackgroundFetchResultFailed), 76 | }), UIBackgroundFetchResultNoData, integerValue) 77 | 78 | @end 79 | #endif //TARGET_OS_TV 80 | 81 | @implementation XGPushManager 82 | { 83 | RCTPromiseResolveBlock _requestPermissionsResolveBlock; 84 | } 85 | 86 | #if !TARGET_OS_TV 87 | 88 | static NSDictionary *RCTFormatLocalNotification(UILocalNotification *notification) 89 | { 90 | NSMutableDictionary *formattedLocalNotification = [NSMutableDictionary dictionary]; 91 | if (notification.fireDate) { 92 | NSDateFormatter *formatter = [NSDateFormatter new]; 93 | [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"]; 94 | NSString *fireDateString = [formatter stringFromDate:notification.fireDate]; 95 | formattedLocalNotification[@"fireDate"] = fireDateString; 96 | } 97 | formattedLocalNotification[@"alertAction"] = RCTNullIfNil(notification.alertAction); 98 | formattedLocalNotification[@"alertBody"] = RCTNullIfNil(notification.alertBody); 99 | formattedLocalNotification[@"applicationIconBadgeNumber"] = @(notification.applicationIconBadgeNumber); 100 | formattedLocalNotification[@"category"] = RCTNullIfNil(notification.category); 101 | formattedLocalNotification[@"soundName"] = RCTNullIfNil(notification.soundName); 102 | formattedLocalNotification[@"userInfo"] = RCTNullIfNil(RCTJSONClean(notification.userInfo)); 103 | formattedLocalNotification[@"remote"] = @NO; 104 | return formattedLocalNotification; 105 | } 106 | 107 | static NSDictionary *RCTFormatUNNotification(UNNotification *notification) 108 | { 109 | NSMutableDictionary *formattedNotification = [NSMutableDictionary dictionary]; 110 | UNNotificationContent *content = notification.request.content; 111 | 112 | formattedNotification[@"identifier"] = notification.request.identifier; 113 | 114 | if (notification.date) { 115 | NSDateFormatter *formatter = [NSDateFormatter new]; 116 | [formatter setDateFormat:@"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ"]; 117 | NSString *dateString = [formatter stringFromDate:notification.date]; 118 | formattedNotification[@"date"] = dateString; 119 | } 120 | 121 | formattedNotification[@"title"] = RCTNullIfNil(content.title); 122 | formattedNotification[@"body"] = RCTNullIfNil(content.body); 123 | formattedNotification[@"category"] = RCTNullIfNil(content.categoryIdentifier); 124 | formattedNotification[@"thread-id"] = RCTNullIfNil(content.threadIdentifier); 125 | formattedNotification[@"userInfo"] = RCTNullIfNil(RCTJSONClean(content.userInfo)); 126 | 127 | return formattedNotification; 128 | } 129 | 130 | #endif //TARGET_OS_TV 131 | 132 | RCT_EXPORT_MODULE() 133 | 134 | - (dispatch_queue_t)methodQueue 135 | { 136 | return dispatch_get_main_queue(); 137 | } 138 | 139 | #if !TARGET_OS_TV 140 | - (void)startObserving 141 | { 142 | [[NSNotificationCenter defaultCenter] addObserver:self 143 | selector:@selector(handleLocalNotificationReceived:) 144 | name:kLocalNotificationReceived 145 | object:nil]; 146 | 147 | [[NSNotificationCenter defaultCenter] addObserver:self 148 | selector:@selector(handleRemoteMessageReceived:) 149 | name:RCTRemoteMessageReceived 150 | object:nil]; 151 | [[NSNotificationCenter defaultCenter] addObserver:self 152 | selector:@selector(handleRemoteNotificationReceived:) 153 | name:MYRCTRemoteNotificationReceived 154 | object:nil]; 155 | [[NSNotificationCenter defaultCenter] addObserver:self 156 | selector:@selector(handleRegisterUserNotificationSettings:) 157 | name:kRegisterUserNotificationSettings 158 | object:nil]; 159 | [[NSNotificationCenter defaultCenter] addObserver:self 160 | selector:@selector(handleRemoteNotificationsRegistered:) 161 | name:kRemoteNotificationsRegistered 162 | object:nil]; 163 | [[NSNotificationCenter defaultCenter] addObserver:self 164 | selector:@selector(handleRemoteNotificationRegistrationError:) 165 | name:kRemoteNotificationRegistrationFailed 166 | object:nil]; 167 | } 168 | 169 | - (void)stopObserving 170 | { 171 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 172 | } 173 | 174 | - (NSArray *)supportedEvents 175 | { 176 | return @[@"localNotificationReceived", 177 | @"remoteNotificationReceived", 178 | @"remoteMessageReceived", 179 | @"remoteNotificationsRegistered", 180 | @"remoteNotificationRegistrationError"]; 181 | } 182 | 183 | + (void)didRegisterUserNotificationSettings:(__unused UIUserNotificationSettings *)notificationSettings 184 | { 185 | if ([UIApplication instancesRespondToSelector:@selector(registerForRemoteNotifications)]) { 186 | [RCTSharedApplication() registerForRemoteNotifications]; 187 | [[NSNotificationCenter defaultCenter] postNotificationName:kRegisterUserNotificationSettings 188 | object:self 189 | userInfo:@{@"notificationSettings": notificationSettings}]; 190 | } 191 | } 192 | 193 | + (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken 194 | { 195 | NSMutableString *hexString = [NSMutableString string]; 196 | NSUInteger deviceTokenLength = deviceToken.length; 197 | const unsigned char *bytes = deviceToken.bytes; 198 | for (NSUInteger i = 0; i < deviceTokenLength; i++) { 199 | [hexString appendFormat:@"%02x", bytes[i]]; 200 | } 201 | [[NSNotificationCenter defaultCenter] postNotificationName:kRemoteNotificationsRegistered 202 | object:self 203 | userInfo:@{@"deviceToken" : [hexString copy]}]; 204 | } 205 | 206 | + (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error 207 | { 208 | [[NSNotificationCenter defaultCenter] postNotificationName:kRemoteNotificationRegistrationFailed 209 | object:self 210 | userInfo:@{@"error": error}]; 211 | } 212 | 213 | // iOS 10 新增 API 214 | // iOS 10 会走新 API, iOS 10 以前会走到老 API 215 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= __IPHONE_10_0 216 | // App 用户点击通知 217 | // App 用户选择通知中的行为 218 | // App 用户在通知中心清除消息 219 | // 无论本地推送还是远程推送都会走这个回调 220 | - (void)xgPushUserNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void (^)(void))completionHandler { 221 | UNNotification * notification = response.notification; 222 | 223 | if ([response.actionIdentifier isEqualToString:@"xgaction001"]) { 224 | NSLog(@"click from Action1"); 225 | } else if ([response.actionIdentifier isEqualToString:@"xgaction002"]) { 226 | NSLog(@"click from Action2"); 227 | } 228 | UIApplicationState state = [RCTSharedApplication() applicationState]; 229 | NSLog(@"state = %ld, UIApplicationStateActive = %ld", (long)state, (long)UIApplicationStateActive); 230 | 231 | BOOL isBackground = (state != UIApplicationStateActive); 232 | NSMutableDictionary *remoteNotification = [NSMutableDictionary dictionaryWithDictionary:notification.request.content.userInfo]; 233 | remoteNotification[@"clicked"] = @YES; 234 | if(isBackground) { 235 | remoteNotification[@"background"] = @YES; 236 | } 237 | NSLog(@"[XGPush] click notification %@", remoteNotification); 238 | NSDictionary * userInfo = @{@"notification": remoteNotification}; 239 | [[NSNotificationCenter defaultCenter] postNotificationName:MYRCTRemoteNotificationReceived 240 | object:self 241 | userInfo:userInfo]; 242 | [[XGPush defaultManager] reportXGNotificationResponse:response]; 243 | 244 | completionHandler(); 245 | } 246 | 247 | // App 在前台弹通知需要调用这个接口 248 | - (void)xgPushUserNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions))completionHandler { 249 | NSDictionary * userInfo = notification.request.content.userInfo; 250 | NSLog(@"[XGPush] willPresentNotification notification %@", userInfo); 251 | [[XGPush defaultManager] reportXGNotificationInfo:userInfo]; 252 | completionHandler(UNNotificationPresentationOptionBadge | UNNotificationPresentationOptionSound | UNNotificationPresentationOptionAlert); 253 | } 254 | #endif 255 | 256 | + (void)didReceiveRemoteNotification:(NSDictionary *)notification 257 | { 258 | NSDictionary *userInfo = @{@"notification": notification}; 259 | [[NSNotificationCenter defaultCenter] postNotificationName:MYRCTRemoteNotificationReceived 260 | object:self 261 | userInfo:userInfo]; 262 | } 263 | 264 | + (void)didReceiveRemoteNotification:(NSDictionary *)notification 265 | fetchCompletionHandler:(RCTRemoteNotificationCallback)completionHandler 266 | { 267 | NSDictionary *userInfo = @{@"notification": notification, @"completionHandler": completionHandler}; 268 | NSLog(@"%s, notification is %@", __FUNCTION__, notification); 269 | [[NSNotificationCenter defaultCenter] postNotificationName:MYRCTRemoteNotificationReceived 270 | object:self 271 | userInfo:userInfo]; 272 | } 273 | 274 | + (void)didReceiveRemoteMessage:(NSDictionary *)notification 275 | { 276 | NSDictionary *userInfo = @{@"notification": notification}; 277 | [[NSNotificationCenter defaultCenter] postNotificationName:RCTRemoteMessageReceived 278 | object:self 279 | userInfo:userInfo]; 280 | } 281 | 282 | + (void)didReceiveRemoteMessage:(NSDictionary *)notification 283 | fetchCompletionHandler:(RCTRemoteNotificationCallback)completionHandler 284 | { 285 | NSDictionary *userInfo = @{@"notification": notification, @"completionHandler": completionHandler}; 286 | NSLog(@"%s, notification is %@", __FUNCTION__, notification); 287 | [[NSNotificationCenter defaultCenter] postNotificationName:RCTRemoteMessageReceived 288 | object:self 289 | userInfo:userInfo]; 290 | } 291 | 292 | + (void)didReceiveLocalNotification:(UILocalNotification *)notification 293 | { 294 | NSLog(@"%s, notification is %@", __FUNCTION__, notification); 295 | [[NSNotificationCenter defaultCenter] postNotificationName:kLocalNotificationReceived 296 | object:self 297 | userInfo:RCTFormatLocalNotification(notification)]; 298 | } 299 | 300 | - (void)handleLocalNotificationReceived:(NSNotification *)notification 301 | { 302 | [self sendEventWithName:@"localNotificationReceived" body:notification.userInfo]; 303 | } 304 | 305 | - (void)handleRemoteMessageReceived:(NSNotification *)notification 306 | { 307 | NSMutableDictionary *remoteNotification = [NSMutableDictionary dictionary]; 308 | remoteNotification[@"content"] = notification.userInfo[@"notification"]; 309 | RCTRemoteNotificationCallback completionHandler = notification.userInfo[@"completionHandler"]; 310 | NSString *notificationId = [[NSUUID UUID] UUIDString]; 311 | remoteNotification[@"notificationId"] = notificationId; 312 | remoteNotification[@"remote"] = @YES; 313 | if (completionHandler) { 314 | if (!self.remoteNotificationCallbacks) { 315 | // Lazy initialization 316 | self.remoteNotificationCallbacks = [NSMutableDictionary dictionary]; 317 | } 318 | self.remoteNotificationCallbacks[notificationId] = completionHandler; 319 | } 320 | 321 | [self sendEventWithName:@"remoteMessageReceived" body:remoteNotification]; 322 | } 323 | 324 | - (void)handleRemoteNotificationReceived:(NSNotification *)notification 325 | { 326 | NSMutableDictionary *remoteNotification = [NSMutableDictionary dictionaryWithDictionary:notification.userInfo[@"notification"]]; 327 | RCTRemoteNotificationCallback completionHandler = notification.userInfo[@"completionHandler"]; 328 | NSString *notificationId = [[NSUUID UUID] UUIDString]; 329 | remoteNotification[@"notificationId"] = notificationId; 330 | remoteNotification[@"remote"] = @YES; 331 | if (completionHandler) { 332 | if (!self.remoteNotificationCallbacks) { 333 | // Lazy initialization 334 | self.remoteNotificationCallbacks = [NSMutableDictionary dictionary]; 335 | } 336 | self.remoteNotificationCallbacks[notificationId] = completionHandler; 337 | } 338 | 339 | [self sendEventWithName:@"remoteNotificationReceived" body:remoteNotification]; 340 | } 341 | 342 | - (void)handleRemoteNotificationsRegistered:(NSNotification *)notification 343 | { 344 | [self sendEventWithName:@"remoteNotificationsRegistered" body:notification.userInfo]; 345 | } 346 | 347 | - (void)handleRemoteNotificationRegistrationError:(NSNotification *)notification 348 | { 349 | NSError *error = notification.userInfo[@"error"]; 350 | NSDictionary *errorDetails = @{ 351 | @"message": error.localizedDescription, 352 | @"code": @(error.code), 353 | @"details": error.userInfo, 354 | }; 355 | NSLog(@"%s, notification is %@", __FUNCTION__, notification); 356 | [self sendEventWithName:@"remoteNotificationRegistrationError" body:errorDetails]; 357 | } 358 | 359 | - (void)handleRegisterUserNotificationSettings:(NSNotification *)notification 360 | { 361 | if (_requestPermissionsResolveBlock == nil) { 362 | return; 363 | } 364 | 365 | UIUserNotificationSettings *notificationSettings = notification.userInfo[@"notificationSettings"]; 366 | NSDictionary *notificationTypes = @{ 367 | @"alert": @((notificationSettings.types & UIUserNotificationTypeAlert) > 0), 368 | @"sound": @((notificationSettings.types & UIUserNotificationTypeSound) > 0), 369 | @"badge": @((notificationSettings.types & UIUserNotificationTypeBadge) > 0), 370 | }; 371 | 372 | _requestPermissionsResolveBlock(notificationTypes); 373 | // Clean up listener added in requestPermissions 374 | [self removeListeners:1]; 375 | _requestPermissionsResolveBlock = nil; 376 | } 377 | 378 | RCT_EXPORT_METHOD(onFinishRemoteNotification:(NSString *)notificationId fetchResult:(UIBackgroundFetchResult)result) { 379 | RCTRemoteNotificationCallback completionHandler = self.remoteNotificationCallbacks[notificationId]; 380 | if (!completionHandler) { 381 | RCTLogError(@"There is no completion handler with notification id: %@", notificationId); 382 | return; 383 | } 384 | completionHandler(result); 385 | [self.remoteNotificationCallbacks removeObjectForKey:notificationId]; 386 | } 387 | 388 | /** 389 | * Update the application icon badge number on the home screen 390 | */ 391 | RCT_EXPORT_METHOD(setApplicationIconBadgeNumber:(NSInteger)number) 392 | { 393 | RCTSharedApplication().applicationIconBadgeNumber = number; 394 | } 395 | 396 | /** 397 | * Get the current application icon badge number on the home screen 398 | */ 399 | RCT_EXPORT_METHOD(getApplicationIconBadgeNumber:(RCTResponseSenderBlock)callback) 400 | { 401 | callback(@[@(RCTSharedApplication().applicationIconBadgeNumber)]); 402 | } 403 | 404 | RCT_EXPORT_METHOD(requestPermissions:(NSDictionary *)permissions 405 | resolver:(RCTPromiseResolveBlock)resolve 406 | rejecter:(RCTPromiseRejectBlock)reject) 407 | { 408 | if (RCTRunningInAppExtension()) { 409 | reject(kErrorUnableToRequestPermissions, nil, RCTErrorWithMessage(@"Requesting push notifications is currently unavailable in an app extension")); 410 | return; 411 | } 412 | 413 | if (_requestPermissionsResolveBlock != nil) { 414 | RCTLogError(@"Cannot call requestPermissions twice before the first has returned."); 415 | return; 416 | } 417 | 418 | // Add a listener to make sure that startObserving has been called 419 | [self addListener:@"remoteNotificationsRegistered"]; 420 | _requestPermissionsResolveBlock = resolve; 421 | 422 | UIUserNotificationType types = UIUserNotificationTypeNone; 423 | if (permissions) { 424 | if ([RCTConvert BOOL:permissions[@"alert"]]) { 425 | types |= UIUserNotificationTypeAlert; 426 | } 427 | if ([RCTConvert BOOL:permissions[@"badge"]]) { 428 | types |= UIUserNotificationTypeBadge; 429 | } 430 | if ([RCTConvert BOOL:permissions[@"sound"]]) { 431 | types |= UIUserNotificationTypeSound; 432 | } 433 | } else { 434 | types = UIUserNotificationTypeAlert | UIUserNotificationTypeBadge | UIUserNotificationTypeSound; 435 | } 436 | 437 | UIUserNotificationSettings *notificationSettings = 438 | [UIUserNotificationSettings settingsForTypes:types categories:nil]; 439 | [RCTSharedApplication() registerUserNotificationSettings:notificationSettings]; 440 | } 441 | 442 | RCT_EXPORT_METHOD(abandonPermissions) 443 | { 444 | [RCTSharedApplication() unregisterForRemoteNotifications]; 445 | } 446 | 447 | RCT_EXPORT_METHOD(checkPermissions:(RCTResponseSenderBlock)callback) 448 | { 449 | if (RCTRunningInAppExtension()) { 450 | callback(@[@{@"alert": @NO, @"badge": @NO, @"sound": @NO}]); 451 | return; 452 | } 453 | 454 | NSUInteger types = [RCTSharedApplication() currentUserNotificationSettings].types; 455 | callback(@[@{ 456 | @"alert": @((types & UIUserNotificationTypeAlert) > 0), 457 | @"badge": @((types & UIUserNotificationTypeBadge) > 0), 458 | @"sound": @((types & UIUserNotificationTypeSound) > 0), 459 | }]); 460 | } 461 | 462 | RCT_EXPORT_METHOD(presentLocalNotification:(UILocalNotification *)notification) 463 | { 464 | [RCTSharedApplication() presentLocalNotificationNow:notification]; 465 | } 466 | 467 | RCT_EXPORT_METHOD(scheduleLocalNotification:(UILocalNotification *)notification) 468 | { 469 | [RCTSharedApplication() scheduleLocalNotification:notification]; 470 | } 471 | 472 | RCT_EXPORT_METHOD(cancelAllLocalNotifications) 473 | { 474 | [RCTSharedApplication() cancelAllLocalNotifications]; 475 | } 476 | 477 | RCT_EXPORT_METHOD(cancelLocalNotifications:(NSDictionary *)userInfo) 478 | { 479 | for (UILocalNotification *notification in RCTSharedApplication().scheduledLocalNotifications) { 480 | __block BOOL matchesAll = YES; 481 | NSDictionary *notificationInfo = notification.userInfo; 482 | // Note: we do this with a loop instead of just `isEqualToDictionary:` 483 | // because we only require that all specified userInfo values match the 484 | // notificationInfo values - notificationInfo may contain additional values 485 | // which we don't care about. 486 | [userInfo enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) { 487 | if (![notificationInfo[key] isEqual:obj]) { 488 | matchesAll = NO; 489 | *stop = YES; 490 | } 491 | }]; 492 | if (matchesAll) { 493 | [RCTSharedApplication() cancelLocalNotification:notification]; 494 | } 495 | } 496 | } 497 | 498 | RCT_EXPORT_METHOD(getInitialNotification:(RCTPromiseResolveBlock)resolve 499 | reject:(__unused RCTPromiseRejectBlock)reject) 500 | { 501 | NSMutableDictionary *initialNotification = 502 | [self.bridge.launchOptions[UIApplicationLaunchOptionsRemoteNotificationKey] mutableCopy]; 503 | 504 | UILocalNotification *initialLocalNotification = 505 | self.bridge.launchOptions[UIApplicationLaunchOptionsLocalNotificationKey]; 506 | 507 | // Remove all delivered notifications after get initial notifications 508 | if ([UNUserNotificationCenter class]) { 509 | UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; 510 | [center removeAllDeliveredNotifications]; 511 | } 512 | 513 | if (initialNotification) { 514 | initialNotification[@"remote"] = @YES; 515 | resolve(initialNotification); 516 | } else if (initialLocalNotification) { 517 | resolve(RCTFormatLocalNotification(initialLocalNotification)); 518 | } else { 519 | resolve((id)kCFNull); 520 | } 521 | } 522 | 523 | RCT_EXPORT_METHOD(getScheduledLocalNotifications:(RCTResponseSenderBlock)callback) 524 | { 525 | NSArray *scheduledLocalNotifications = RCTSharedApplication().scheduledLocalNotifications; 526 | NSMutableArray *formattedScheduledLocalNotifications = [NSMutableArray new]; 527 | for (UILocalNotification *notification in scheduledLocalNotifications) { 528 | [formattedScheduledLocalNotifications addObject:RCTFormatLocalNotification(notification)]; 529 | } 530 | callback(@[formattedScheduledLocalNotifications]); 531 | } 532 | 533 | RCT_EXPORT_METHOD(removeAllDeliveredNotifications) 534 | { 535 | if ([UNUserNotificationCenter class]) { 536 | UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; 537 | [center removeAllDeliveredNotifications]; 538 | } 539 | } 540 | 541 | RCT_EXPORT_METHOD(removeDeliveredNotifications:(NSArray *)identifiers) 542 | { 543 | if ([UNUserNotificationCenter class]) { 544 | UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; 545 | [center removeDeliveredNotificationsWithIdentifiers:identifiers]; 546 | } 547 | } 548 | 549 | RCT_EXPORT_METHOD(getDeliveredNotifications:(RCTResponseSenderBlock)callback) 550 | { 551 | if ([UNUserNotificationCenter class]) { 552 | UNUserNotificationCenter *center = [UNUserNotificationCenter currentNotificationCenter]; 553 | [center getDeliveredNotificationsWithCompletionHandler:^(NSArray *_Nonnull notifications) { 554 | NSMutableArray *formattedNotifications = [NSMutableArray new]; 555 | 556 | for (UNNotification *notification in notifications) { 557 | [formattedNotifications addObject:RCTFormatUNNotification(notification)]; 558 | } 559 | callback(@[formattedNotifications]); 560 | }]; 561 | } 562 | } 563 | 564 | /********************* 信鸽 *********************/ 565 | 566 | #pragma mark - XGPushDelegate 567 | - (void)xgPushDidFinishStart:(BOOL)isSuccess error:(NSError *)error { 568 | NSLog(@"%s, 启动信鸽服务 %@, error %@", __FUNCTION__, isSuccess?@"成功":@"失败", error); 569 | } 570 | 571 | - (void)xgPushDidFinishStop:(BOOL)isSuccess error:(NSError *)error { 572 | NSLog(@"%s, 注销信鸽服务 %@, error %@", __FUNCTION__, isSuccess?@"成功":@"失败", error); 573 | } 574 | 575 | #pragma mark - XGPushTokenManagerDelegate 576 | - (void)xgPushDidBindWithIdentifier:(NSString *)identifier type:(XGPushTokenBindType)type error:(NSError *)error { 577 | NSLog(@"%s, id is %@, error %@", __FUNCTION__, identifier, error); 578 | } 579 | 580 | - (void)xgPushDidUnbindWithIdentifier:(NSString *)identifier type:(XGPushTokenBindType)type error:(NSError *)error { 581 | NSLog(@"%s, id is %@, error %@", __FUNCTION__, identifier, error); 582 | } 583 | 584 | /** 585 | * 打开 Debug 模式以后可以在终端看到详细的信鸽 Debug 信息.方便定位问题 586 | */ 587 | RCT_EXPORT_METHOD(setEnableDebug:(BOOL)enableDebug) 588 | { 589 | [[XGPush defaultManager] setEnableDebug:enableDebug]; 590 | } 591 | 592 | /** 593 | * 查看debug开关是否打开 594 | */ 595 | RCT_EXPORT_METHOD(isEnableDebug:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) 596 | { 597 | BOOL debugEnabled = [[XGPush defaultManager] isEnableDebug]; 598 | resolve(@(debugEnabled)); 599 | } 600 | 601 | /** 602 | @brief 通过使用在信鸽官网注册的应用的信息,启动信鸽推送服务 603 | 604 | @param appID 通过前台申请的应用ID 605 | @param appKey 通过前台申请的appKey 606 | @param delegate 回调对象 607 | @note 接口所需参数必须要正确填写,反之信鸽服务将不能正确为应用推送消息 608 | */ 609 | RCT_EXPORT_METHOD(startXGWithAppID:(uint64_t)appID appKey:(nonnull NSString *)appKey) 610 | { 611 | [[XGPush defaultManager] startXGWithAppID:appID appKey:appKey delegate:self]; 612 | } 613 | 614 | /** 615 | @brief 停止信鸽推送服务 616 | @note 调用此方法将导致当前设备不再接受信鸽服务推送的消息.如果再次需要接收信鸽服务的消息推送,则必须需要再次调用startXG:withAppKey:delegate:方法重启信鸽推送服务 617 | */ 618 | RCT_EXPORT_METHOD(stopXGNotification) 619 | { 620 | [[XGPush defaultManager] stopXGNotification]; 621 | } 622 | 623 | /** 624 | @brief 上报地理位置信息 625 | 626 | @param latitude 纬度 627 | @param longitude 经度 628 | */ 629 | RCT_EXPORT_METHOD(reportLocationWithLatitude:(double)latitude longitude:(double)longitude) { 630 | [[XGPush defaultManager] reportLocationWithLatitude: latitude longitude: longitude]; 631 | } 632 | 633 | /** 634 | @brief 上报当前App角标数到信鸽服务器 635 | 636 | @param badgeNumber 应用的角标数 637 | @note (后台维护中)此接口是为了实现角标+1的功能,服务器会在这个数值基础上进行角标数新增的操作,调用成功之后,会覆盖之前值。 638 | */ 639 | RCT_EXPORT_METHOD(setBadge:(NSInteger)badgeNumber) { 640 | [[XGPush defaultManager] setBadge: badgeNumber]; 641 | } 642 | 643 | /** 644 | @brief 查询设备通知权限是否被用户允许 645 | 646 | @param handler 查询结果的返回方法 647 | @note iOS 10 or later 回调是异步地执行 648 | */ 649 | RCT_EXPORT_METHOD(deviceNotificationIsAllowed:(nonnull void (^)(BOOL isAllowed))handler) { 650 | 651 | } 652 | 653 | /** 654 | * 绑定标签,一个设备可以绑定多个标签(多次调用) 655 | */ 656 | RCT_EXPORT_METHOD(bindWithTag:tag) 657 | { 658 | [[XGPushTokenManager defaultTokenManager] bindWithIdentifier:tag type:XGPushTokenBindTypeTag]; 659 | } 660 | 661 | /** 662 | 绑定账号 663 | */ 664 | RCT_EXPORT_METHOD(bindWithAccount:account 665 | resolver:(RCTPromiseResolveBlock)resolve 666 | rejecter:(RCTPromiseRejectBlock)reject) 667 | { 668 | [[XGPushTokenManager defaultTokenManager] bindWithIdentifier:account type:XGPushTokenBindTypeAccount]; 669 | resolve([[XGPushTokenManager defaultTokenManager] deviceTokenString]); 670 | } 671 | 672 | 673 | /** 674 | 解绑标签 675 | */ 676 | RCT_EXPORT_METHOD(unbindWithTag:tag) 677 | { 678 | [[XGPushTokenManager defaultTokenManager] unbindWithIdentifer:tag type:XGPushTokenBindTypeTag]; 679 | } 680 | 681 | /** 682 | 解绑账号 683 | */ 684 | RCT_EXPORT_METHOD(unbindWithAccount:account) 685 | { 686 | [[XGPushTokenManager defaultTokenManager] unbindWithIdentifer:account type:XGPushTokenBindTypeAccount]; 687 | } 688 | 689 | /** 690 | 获取版本号 691 | */ 692 | RCT_EXPORT_METHOD(sdkVersion:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) 693 | { 694 | NSString* version = [[XGPush defaultManager] sdkVersion]; 695 | resolve(version); 696 | } 697 | 698 | #else //TARGET_OS_TV 699 | 700 | - (NSArray *)supportedEvents 701 | { 702 | return @[]; 703 | } 704 | 705 | #endif //TARGET_OS_TV 706 | 707 | @end 708 | -------------------------------------------------------------------------------- /ios/libXG-SDK.a: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PandaQQ/react-native-xinge-push/3b77b89fa834236fde2220f600d388a4f2d35ec8/ios/libXG-SDK.a -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-xinge-push", 3 | "version": "0.8.0", 4 | "description": "Xinge push for react-native", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/wanxsb/react-native-xinge-push.git" 12 | }, 13 | "keywords": [ 14 | "xinge", 15 | "xinge-push", 16 | "xgpush", 17 | "react-native" 18 | ], 19 | "author": "Jeepeng", 20 | "license": "Apache-2.0", 21 | "bugs": { 22 | "url": "https://github.com/wanxsb/react-native-xinge-push/issues" 23 | }, 24 | "homepage": "https://github.com/wanxsb/react-native-xinge-push#readme" 25 | } 26 | -------------------------------------------------------------------------------- /react-native-xinge-push.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod spec lint react-native-xinge-push.podspec' to ensure this is a 3 | # valid spec and to remove all comments including this before submitting the spec. 4 | # 5 | # To learn more about Podspec attributes see https://guides.cocoapods.org/syntax/podspec.html 6 | # To see working Podspecs in the CocoaPods repo see https://github.com/CocoaPods/Specs/ 7 | # 8 | 9 | require 'json' 10 | 11 | package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) 12 | 13 | Pod::Spec.new do |s| 14 | 15 | s.name = 'react-native-xinge-push' 16 | s.version = package['version'] 17 | s.summary = package['description'] 18 | s.license = package['license'] 19 | 20 | s.authors = package['author'] 21 | s.homepage = package['homepage'] 22 | s.platform = :ios, "9.0" 23 | 24 | s.source = { :git => "https://github.com/PandaQQ/react-native-xinge-push.git", :tag => "v#{s.version}" } 25 | s.source_files = "ios/**/*.{h,m}" 26 | 27 | s.dependency 'React' 28 | s.vendored_libraries = "ios/libXG-SDK.a" 29 | s.requires_arc = true 30 | s.frameworks = 'SystemConfiguration','CoreTelephony', 'UserNotifications' 31 | s.library = 'sqlite3','c++','z' 32 | # spec.public_header_files = "Classes/**/*.h" 33 | 34 | 35 | # ――― Resources ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 36 | # 37 | # A list of resources included with the Pod. These are copied into the 38 | # target bundle with a build phase script. Anything else will be cleaned. 39 | # You can preserve files from being cleaned, please don't preserve 40 | # non-essential files like tests, examples and documentation. 41 | # 42 | 43 | # spec.resource = "icon.png" 44 | # spec.resources = "Resources/*.png" 45 | 46 | # spec.preserve_paths = "FilesToSave", "MoreFilesToSave" 47 | 48 | 49 | # ――― Project Linking ―――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 50 | # 51 | # Link your library with frameworks, or libraries. Libraries do not include 52 | # the lib prefix of their name. 53 | # 54 | 55 | # spec.framework = "SomeFramework" 56 | # spec.frameworks = "SomeFramework", "AnotherFramework" 57 | 58 | # spec.library = "iconv" 59 | # spec.libraries = "iconv", "xml2" 60 | 61 | 62 | # ――― Project Settings ――――――――――――――――――――――――――――――――――――――――――――――――――――――――― # 63 | # 64 | # If your library depends on compiler flags you can set them in the xcconfig hash 65 | # where they will only apply to your library. If you depend on other Podspecs 66 | # you can include multiple dependencies to ensure it works. 67 | 68 | # spec.requires_arc = true 69 | 70 | # spec.xcconfig = { "HEADER_SEARCH_PATHS" => "$(SDKROOT)/usr/include/libxml2" } 71 | # spec.dependency "JSONKit", "~> 1.4" 72 | 73 | end 74 | --------------------------------------------------------------------------------