├── .gitignore ├── .npmignore ├── HeroUmengPush.podspec ├── README.md ├── android ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── com │ │ └── github │ │ └── reactnativehero │ │ └── umengpush │ │ ├── MeizuReceiver.kt │ │ ├── RNTUmengPushModule.kt │ │ ├── RNTUmengPushPackage.kt │ │ └── UmengPushActivity.kt │ └── res │ └── layout │ └── umeng_push_activity.xml ├── index.js ├── ios ├── RNTUmengPush.xcodeproj │ └── project.pbxproj └── RNTUmengPush │ ├── RNTUmengPush.h │ └── RNTUmengPush.m └── package.json /.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 | 40 | Podfile.lock 41 | Pods/ 42 | package-lock.json 43 | yarn.lock -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 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 | 40 | Podfile.lock 41 | Pods/ 42 | example/ 43 | package-lock.json 44 | yarn.lock -------------------------------------------------------------------------------- /HeroUmengPush.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, 'package.json'))) 4 | 5 | Pod::Spec.new do |s| 6 | s.name = "HeroUmengPush" 7 | s.version = package['version'] 8 | s.summary = package['description'] 9 | s.license = package['license'] 10 | 11 | s.authors = package['author'] 12 | s.homepage = package['homepage'] 13 | s.platform = :ios, "9.0" 14 | s.frameworks = "UserNotifications" 15 | 16 | s.source = { :git => "https://github.com/react-native-hero/umeng-push.git", :tag => "v#{s.version}" } 17 | s.source_files = "ios/**/*.{h,m}" 18 | 19 | s.dependency 'React' 20 | s.dependency 'UMCommon' 21 | s.dependency 'UMPush' 22 | s.dependency 'UMDevice' 23 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @react-native-hero/umeng-push 2 | 3 | ## Getting started 4 | 5 | Install the library using either Yarn: 6 | 7 | ``` 8 | yarn add @react-native-hero/umeng-push 9 | ``` 10 | 11 | or npm: 12 | 13 | ``` 14 | npm install --save @react-native-hero/umeng-push 15 | ``` 16 | 17 | ## Link 18 | 19 | - React Native v0.60+ 20 | 21 | For iOS, use `cocoapods` to link the package. 22 | 23 | run the following command: 24 | 25 | ``` 26 | $ cd ios && pod install 27 | ``` 28 | 29 | For android, the package will be linked automatically on build. 30 | 31 | - React Native <= 0.59 32 | 33 | run the following command to link the package: 34 | 35 | ``` 36 | $ react-native link @react-native-hero/umeng-push 37 | ``` 38 | 39 | ## Setup 40 | 41 | ![image](https://user-images.githubusercontent.com/2732303/77606227-ded8b680-6f51-11ea-9aa4-0378e79deaa7.png) 42 | 43 | 打开应用信息页面,安卓推送有 `Appkey` 和 `Umeng Message Secret` 两个字段,iOS 只有 `Appkey` 字段,后面将用这些字段初始化友盟。 44 | 45 | ### iOS 46 | 47 | 确保推送证书配置正确。举个例子,如果你的 App 有两个版本:测试版和正式版,`Bundle ID` 分别是 `com.abc.test` 和 `com.abc.prod`,那么证书必须和 `Bundle ID` 对应。 48 | 49 | 打开 Xcode,开启推送。 50 | 51 | ![image](https://user-images.githubusercontent.com/2732303/77887093-8fb9bb00-729c-11ea-8d71-8a97c1b6a3a2.png) 52 | 53 | 修改 `AppDelegate.m`,如下 54 | 55 | ```oc 56 | #import 57 | 58 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 59 | { 60 | ... 61 | // 初始化友盟基础库 62 | // channel 一般填 App Store,如果有测试环境,可按需填写 63 | // debug 表示是否打印调试信息 64 | [RNTUmengPush init:@"appKey" channel:@"App Store" debug:false launchOptions:launchOptions]; 65 | 66 | return YES; 67 | } 68 | 69 | - (void)application:(UIApplication *)application 70 | didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { 71 | [RNTUmengPush didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; 72 | } 73 | 74 | - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { 75 | [RNTUmengPush didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; 76 | } 77 | ``` 78 | 79 | ### Android 80 | 81 | 修改 `android/build.gradle`,如下: 82 | 83 | ``` 84 | allprojects { 85 | repositories { 86 | // 确保添加了华为和友盟仓库 87 | maven { url 'https://developer.huawei.com/repo/'} 88 | maven { url 'https://repo1.maven.org/maven2/' } 89 | } 90 | } 91 | ``` 92 | 93 | `android/app/build.gradle` 根据不同的包填写不同的配置,如下: 94 | 95 | ``` 96 | android { 97 | buildTypes { 98 | debug { 99 | manifestPlaceholders = [ 100 | UMENG_APP_KEY: '', 101 | UMENG_PUSH_SECRET: '', 102 | UMENG_CHANNEL: '', 103 | HUAWEI_PUSH_APP_ID: '', 104 | HONOR_PUSH_APP_ID: '', 105 | XIAOMI_PUSH_APP_ID: '', 106 | XIAOMI_PUSH_APP_KEY: '', 107 | OPPO_PUSH_APP_KEY: '', 108 | OPPO_PUSH_APP_SECRET: '', 109 | VIVO_PUSH_APP_ID: '', 110 | VIVO_PUSH_APP_KEY: '', 111 | MEIZU_PUSH_APP_ID: '', 112 | MEIZU_PUSH_APP_KEY: '', 113 | ] 114 | } 115 | release { 116 | manifestPlaceholders = [ 117 | UMENG_APP_KEY: '', 118 | UMENG_PUSH_SECRET: '', 119 | UMENG_CHANNEL: '', 120 | HUAWEI_PUSH_APP_ID: '', 121 | HONOR_PUSH_APP_ID: '', 122 | XIAOMI_PUSH_APP_ID: '', 123 | XIAOMI_PUSH_APP_KEY: '', 124 | OPPO_PUSH_APP_KEY: '', 125 | OPPO_PUSH_APP_SECRET: '', 126 | VIVO_PUSH_APP_ID: '', 127 | VIVO_PUSH_APP_KEY: '', 128 | MEIZU_PUSH_APP_ID: '', 129 | MEIZU_PUSH_APP_KEY: '', 130 | ] 131 | } 132 | } 133 | } 134 | ``` 135 | 136 | 配置厂商通道请先阅读[官方文档](https://developer.umeng.com/docs/66632/detail/98589),主要是获取各个通道的 `appId`、`appKey`、`appSecret` 等数据,并保存到友盟后台的应用信息里。 137 | 138 | 在 `MainApplication` 的 `onCreate` 方法进行初始化,如下: 139 | 140 | ```kotlin 141 | override fun onCreate() { 142 | 143 | // 配置厂商通道 Activity 展示的页面 144 | UmengPushActivity.activityView = R.layout.splash_screen_default 145 | // 配置厂商通道 Activity 跳转的 mainActivity 146 | UmengPushActivity.mainActivityClass = MainActivity::class.java 147 | 148 | val metaData = packageManager.getApplicationInfo(packageName, PackageManager.GET_META_DATA).metaData 149 | 150 | // 初始化友盟基础库 151 | // 第三个参数表示是否显示调试信息 152 | RNTUmengPushModule.init(this, metaData, false) 153 | } 154 | ``` 155 | 156 | ### 配置混淆规则 157 | 158 | 在 `android/app/proguard-rules.pro` 添加以下混淆规则,注意替换自己的包名,并且删掉 `[` 和 `]`。 159 | 160 | ``` 161 | -keep public class [您的应用包名].R$*{ 162 | public static final int *; 163 | } 164 | ``` 165 | 166 | ### 配置华为、小米、魅族厂商通道使用的 Activity 167 | 168 | `打开指定页面` 填入 `${包名}.UmengPushActivity`,比如 `com.abc.UmengPushActivity`。 169 | 170 | ![](https://user-images.githubusercontent.com/2732303/77288805-9764e700-6d13-11ea-91e1-3c2218f14bcb.png) 171 | 172 | 注意,如果在打包阶段用了别的包名,需改为对应的包名。 173 | 174 | ### 解决魅族的兼容问题 175 | 176 | 在 `drawable` 目录下添加一个图标,命名为 `stat_sys_third_app_notify.png`,建议尺寸 `64px * 64px`,图标四周留有透明。若不添加此图标,可能在部分魅族手机上无法弹出通知。 177 | 178 | ## Usage 179 | 180 | ```js 181 | import { 182 | ALIAS_TYPE, 183 | NOTIFICATION_PLAY, 184 | init, 185 | start, 186 | getTags, 187 | addTags, 188 | removeTags, 189 | setAlias, 190 | addAlias, 191 | removeAlias, 192 | setAdvanced, 193 | addListener, 194 | supportDisable, 195 | enable, 196 | disable, 197 | supportNotificationChannel, 198 | createNotificationChannel, 199 | deleteNotificationChannel, 200 | } from '@react-native-hero/push' 201 | 202 | // 注册获取 device token 203 | addListener( 204 | 'register', 205 | function (data) { 206 | data.deviceToken 207 | // 如果 app 未启动状态下,点击推送打开 app,会有两个新字段 208 | // 点击的推送 209 | data.notification 210 | // 推送的自定义参数 211 | // 字符 d、p 为友盟保留字段,不能作为自定义参数的 key,value 只能是字符串类型, 212 | // 字符总和不能超过 1000 个字符 213 | data.custom 214 | } 215 | ) 216 | 217 | // 远程推送 218 | addListener( 219 | 'remoteNotification', 220 | function (data) { 221 | // 如果点击了推送,data.clicked 是 true 222 | data.clicked 223 | // 如果推送送达并展示了,data.presented 是 true 224 | data.presented 225 | 226 | // 推送详情,如标题、内容 227 | data.notification 228 | // 推送的自定义参数 229 | // 字符 d、p 为友盟保留字段,不能作为自定义参数的 key,value 只能是字符串类型, 230 | // 字符总和不能超过 1000 个字符 231 | data.custom 232 | } 233 | ) 234 | 235 | // 透传消息 236 | // ios 通过静默推送实现,例子:payload: { aps: { 'content-available': 1 }, key1: 'value1' } 237 | // android 通过自定义消息实现,例子:payload: { display_type: 'message', body: { custom: '' }, extra: { key1: 'value1' } } 238 | // 239 | // 注意:ios aps 下面不能包含 alert、badge、sound 等字段,如果包含了其中任何一个,就会变成通知,即会显示在通知栏。 240 | // 你也可以加上 alert,比如 aps: { alert: '你有一条新消息', 'content-available': 1 },这样还是会走进 message 事件回调里,只是展现为通知形式 241 | addListener( 242 | 'message', 243 | function (data) { 244 | 245 | // ios 用于静默推送实现消息透传 246 | // 静默推送不会在通知栏展现,这一点很符合透传消息的要求 247 | // 但是苹果要求静默推送的 aps 对象下不能包含 alert、badge、sound 等字段 248 | // 如果我们加了 alert,它就会变成通知,会展现在通知栏里 249 | // 有时候,你会希望加上 alert,类似 aps: { alert: '你有一条新消息', 'content-available': 1 } 250 | // 这时依然会触发 message 事件回调,只是会带上 remoteNotification 特有的 clicked 或 presented 属性 251 | // 如果你希望和安卓表现一样,只需要过滤掉 clicked 即可,如下 252 | if (data.clicked) { 253 | return 254 | } 255 | 256 | // 这样只有当 app 在前台时,才会响应透传消息 257 | 258 | // 自定义参数 259 | // 字符 d、p 为友盟保留字段,不能作为自定义参数的 key,value 只能是字符串类型, 260 | // 字符总和不能超过 1000 个字符 261 | // 即例子中的 { key1: 'value1' } 262 | data.custom 263 | 264 | // alert 或 custom 字段中的字符串值 265 | data.message 266 | 267 | } 268 | ) 269 | 270 | // 对于安卓来说,需要等用户同意隐私政策后,再调用 init,js 的 init 才是真正的初始化 271 | // https://developer.umeng.com/docs/67966/detail/207155 272 | // 对于 ios 来说,如果无需使用『高级设置』,则可以不调用 init 方法 273 | init({ 274 | // 『安卓』app 在前台时是否显示推送 275 | notificationOnForeground: true, 276 | // 『安卓』填 AndroidManifest.xml 中 package 的值 277 | resourcePackageName: 'com.abc', 278 | // 『安卓』开启厂商推送通道,按需开启即可 279 | huaweiEnabled: true, 280 | honorEnabled: true, 281 | xiaomiEnabled: true, 282 | oppoEnabled: true, 283 | vivoEnabled: true, 284 | meizuEnabled: true, 285 | // 文档最后 『高级设置』中的所有配置项都可以适用于 init,方便在初始化时一次性做好配置 286 | }) 287 | 288 | // 注意!!! 289 | // 下面的所有方法需在 init 调用之后才可调用 290 | 291 | // 启动 292 | // 调用 start 之后才会触发 register 事件 293 | // 因此注册 register 事件应早于 start 294 | start() 295 | 296 | // 下面这些方法的具体用法和注意事项,请参考文档 297 | // https://developer.umeng.com/docs/67966/detail/98583#h1--tag-alias-4 298 | getTags().then(data => { 299 | // success 300 | data.tags 301 | }) 302 | .catch(err => { 303 | // failure 304 | }) 305 | 306 | addTags(['tag1', 'tag2']).then(data => { 307 | // success 308 | data.remain 309 | }) 310 | .catch(err => { 311 | // failure 312 | }) 313 | 314 | removeTags(['tag1', 'tag2']).then(data => { 315 | // success 316 | data.remain 317 | }) 318 | .catch(err => { 319 | // failure 320 | }) 321 | 322 | 323 | // type 如果是第三方帐号,使用导入的常量 ALIAS_TYPE.XXX 324 | // 如果是自建帐号体系,可传入自己产品的英文名或拼音 325 | setAlias('alias', 'type').then(data => { 326 | // success 327 | }) 328 | .catch(err => { 329 | // failure 330 | }) 331 | 332 | addAlias('alias', 'type').then(data => { 333 | // success 334 | }) 335 | .catch(err => { 336 | // failure 337 | }) 338 | 339 | removeAlias('alias', 'type').then(data => { 340 | // success 341 | }) 342 | .catch(err => { 343 | // failure 344 | }) 345 | 346 | // 高级设置 347 | setAdvanced({ 348 | // ios: 当应用在前台时收到推送是否弹出 Alert,默认弹出 349 | autoAlert: true, 350 | // ios: 是否允许 SDK 自动清空角标,默认自动角标清零 351 | badgeClear: true, 352 | 353 | // android: 最多显示通知的条数,当显示数目大于设置值时,若再有新通知到达,会移除一条最早的通知,只为 0-10,0 表示不限制个数 354 | displayNotificationNumber: 0, 355 | // android: 默认情况下,同一台设备在1分钟内收到同一个应用的多条通知时,不会重复提醒,同时在通知栏里新的通知会替换掉旧的通知,可以通过此配置来设置冷却时间 356 | muteDurationSeconds: 60, 357 | // android: 是否响铃,使用 NOTIFICATION_PLAY 枚举值 358 | notificationPlaySound: NOTIFICATION_PLAY.SERVER, 359 | // android: 是否点亮呼吸灯,使用 NOTIFICATION_PLAY 枚举值 360 | notificationPlayLights: NOTIFICATION_PLAY.SERVER, 361 | // android: 是否振动,使用 NOTIFICATION_PLAY 枚举值 362 | notificationPlayVibrate: NOTIFICATION_PLAY.SERVER, 363 | // android: 设置免打扰时间段,期间收到通知消息时不响铃,不闪灯,不振动 364 | noDisturbStartHour: 23, 365 | noDisturbStartMinute: 0, 366 | noDisturbEndHour: 7, 367 | noDisturbEndMinute: 0, 368 | }) 369 | 370 | 371 | // 是否支持关闭推送 372 | // 初始化 SDK 后,默认是开启推送状态,如果需要开关,可借鉴以下代码 373 | if (supportDisable) { 374 | // 关闭推送 375 | disable() 376 | // 开启推送 377 | enable() 378 | } 379 | 380 | // 是否支持推送通道(安卓 >= 8.0支持推送通道) 381 | if (supportNotificationChannel) { 382 | // 创建推送通道 383 | // 如果已创建该通道(通过 id 判断),则无视此次调用 384 | createNotificationChannel({ 385 | id: 'id', 386 | name: 'name', 387 | importance: 'max|high|low|min|default', 388 | // 下面都是可选字段 389 | // 具体参考 https://blog.csdn.net/bingeho/article/details/124454006 390 | description: 'description', 391 | lockscreenVisibility: 'secret|private|public', 392 | enableLights: true|false, 393 | enableVibration: true|false, 394 | setShowBadge: true|false, 395 | groupId: 'groupId', 396 | groupName: 'groupName', 397 | }) 398 | // 删除推送通道 399 | deleteNotificationChannel({ 400 | id: 'id' 401 | }) 402 | } 403 | 404 | ``` 405 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | // Buildscript is evaluated before everything else so we can't use safeExtGet 3 | def kotlinVersion = rootProject.ext.has('kotlinVersion') ? rootProject.ext.get('kotlinVersion') : '1.7.20' 4 | 5 | repositories { 6 | mavenCentral() 7 | google() 8 | maven { url 'https://repo1.maven.org/maven2/' } 9 | maven { url 'https://developer.huawei.com/repo/' } 10 | } 11 | 12 | dependencies { 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" 14 | classpath 'com.huawei.agconnect:agcp:1.6.0.300' 15 | } 16 | } 17 | 18 | def safeExtGet(prop, fallback) { 19 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback 20 | } 21 | 22 | apply plugin: 'com.android.library' 23 | 24 | android { 25 | compileSdkVersion safeExtGet('compileSdkVersion', 28) 26 | buildToolsVersion safeExtGet('buildToolsVersion', '28.0.3') 27 | 28 | defaultConfig { 29 | minSdkVersion safeExtGet('minSdkVersion', 16) 30 | targetSdkVersion safeExtGet('targetSdkVersion', 28) 31 | versionCode 1 32 | versionName "1.0" 33 | consumerProguardFiles 'proguard-rules.pro' 34 | } 35 | lintOptions { 36 | abortOnError false 37 | } 38 | sourceSets { 39 | main { 40 | jniLibs.srcDirs = ['libs'] 41 | } 42 | } 43 | } 44 | 45 | dependencies { 46 | implementation "com.facebook.react:react-native:${safeExtGet('reactNativeVersion', '+')}" 47 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${safeExtGet('kotlinVersion', '1.7.20')}" 48 | 49 | api 'com.umeng.umsdk:common:+' 50 | api 'com.umeng.umsdk:asms:+' 51 | api 'com.umeng.umsdk:push:+' 52 | } 53 | 54 | apply plugin: 'com.huawei.agconnect' 55 | dependencies { 56 | api 'com.umeng.umsdk:huawei-umengaccs:2.1.0' 57 | api 'com.huawei.hms:push:6.12.0.300' 58 | } 59 | 60 | dependencies { 61 | api 'com.umeng.umsdk:meizu-umengaccs:2.1.0' 62 | api 'com.umeng.umsdk:meizu-push:4.2.7' 63 | } 64 | 65 | dependencies { 66 | api 'com.umeng.umsdk:oppo-umengaccs:2.1.0' 67 | api 'com.umeng.umsdk:oppo-push:3.4.0' 68 | } 69 | 70 | dependencies { 71 | api 'com.umeng.umsdk:vivo-umengaccs:2.1.0' 72 | api 'com.umeng.umsdk:vivo-push:3.0.0.7' 73 | } 74 | 75 | dependencies { 76 | api 'com.umeng.umsdk:honor-umengaccs:2.1.0' 77 | api 'com.umeng.umsdk:honor-push:7.0.61.303' 78 | } 79 | 80 | dependencies { 81 | api 'com.umeng.umsdk:xiaomi-umengaccs:2.1.0' 82 | api 'com.umeng.umsdk:xiaomi-push:5.9.9' 83 | } 84 | 85 | apply plugin: 'kotlin-android' 86 | -------------------------------------------------------------------------------- /android/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | -dontwarn com.umeng.** 2 | -dontwarn com.taobao.** 3 | -dontwarn anet.channel.** 4 | -dontwarn anetwork.channel.** 5 | -dontwarn org.android.** 6 | -dontwarn org.apache.thrift.** 7 | -dontwarn com.xiaomi.** 8 | -dontwarn com.huawei.** 9 | -dontwarn com.meizu.** 10 | 11 | -keepattributes *Annotation* 12 | 13 | -keep class com.taobao.** {*;} 14 | -keep class org.android.** {*;} 15 | -keep class anet.channel.** {*;} 16 | -keep class com.umeng.** {*;} 17 | -keep class com.xiaomi.** {*;} 18 | -keep class com.huawei.** {*;} 19 | -keep class com.meizu.** {*;} 20 | -keep class org.apache.thrift.** {*;} 21 | 22 | -keep class com.alibaba.sdk.android.**{*;} 23 | -keep class com.ut.**{*;} 24 | -keep class com.ta.**{*;} 25 | 26 | -keep public class **.R$*{ 27 | public static final int *; 28 | } 29 | 30 | -keepclassmembers class * { 31 | public (org.json.JSONObject); 32 | } 33 | -keepclassmembers enum * { 34 | public static **[] values(); 35 | public static ** valueOf(java.lang.String); 36 | } 37 | 38 | # xiaomi 39 | -keep class org.android.agoo.xiaomi.MiPushBroadcastReceiver {*;} 40 | 41 | # huawei 42 | -ignorewarnings 43 | -keepattributes *Annotation* 44 | -keepattributes Exceptions 45 | -keepattributes InnerClasses 46 | -keepattributes Signature 47 | -keepattributes SourceFile,LineNumberTable 48 | -keep class com.huawei.hianalytics.**{*;} 49 | -keep class com.huawei.updatesdk.**{*;} 50 | -keep class com.huawei.hms.**{*;} 51 | 52 | # honor 53 | -keep class com.hihonor.android.push.** {*;} 54 | -keep class com.hihonor.push.** {*;} 55 | -keep class org.android.agoo.honor.* {*;} 56 | 57 | # meizu 58 | -keep class com.meizu.cloud.** {*;} 59 | -dontwarn com.meizu.cloud.** 60 | 61 | # oppo 62 | -keep public class * extends android.app.Service 63 | 64 | # vivo 65 | -dontwarn com.vivo.push.** 66 | -keep class com.vivo.push.** {*;} 67 | -keep class com.vivo.vms.** {*;} 68 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 11 | 15 | 19 | 20 | 24 | 28 | 29 | 33 | 37 | 38 | 42 | 46 | 47 | 51 | 55 | 59 | 63 | 64 | 69 | 70 | 71 | 72 | 73 | 77 | 78 | 79 | 80 | 85 | 86 | 87 | 88 | 89 | 93 | 94 | 95 | 96 | 97 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /android/src/main/java/com/github/reactnativehero/umengpush/MeizuReceiver.kt: -------------------------------------------------------------------------------- 1 | package com.github.reactnativehero.umengpush 2 | 3 | import org.android.agoo.mezu.MeizuPushReceiver 4 | 5 | class MeizuReceiver : MeizuPushReceiver() -------------------------------------------------------------------------------- /android/src/main/java/com/github/reactnativehero/umengpush/RNTUmengPushModule.kt: -------------------------------------------------------------------------------- 1 | package com.github.reactnativehero.umengpush 2 | 3 | import android.app.Activity 4 | import android.app.Application 5 | import android.app.Notification 6 | import android.app.NotificationChannel 7 | import android.app.NotificationChannelGroup 8 | import android.app.NotificationManager 9 | import android.content.Context 10 | import android.content.Intent 11 | import android.os.Build 12 | import android.os.Bundle 13 | import com.facebook.react.bridge.* 14 | import com.facebook.react.modules.core.DeviceEventManagerModule 15 | import com.umeng.commonsdk.UMConfigure 16 | import com.umeng.commonsdk.statistics.common.MLog 17 | import com.umeng.commonsdk.utils.UMUtils 18 | import com.umeng.message.MsgConstant 19 | import com.umeng.message.PushAgent 20 | import com.umeng.message.UmengMessageHandler 21 | import com.umeng.message.UmengNotificationClickHandler 22 | import com.umeng.message.api.UPushRegisterCallback 23 | import com.umeng.message.api.UPushSettingCallback 24 | import com.umeng.message.entity.UMessage 25 | import org.android.agoo.huawei.HuaWeiRegister 26 | import org.android.agoo.honor.HonorRegister 27 | import org.android.agoo.mezu.MeizuRegister 28 | import org.android.agoo.oppo.OppoRegister 29 | import org.android.agoo.vivo.VivoRegister 30 | import org.android.agoo.xiaomi.MiPushRegistar 31 | 32 | class RNTUmengPushModule(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { 33 | 34 | companion object { 35 | 36 | private const val ALIAS_TYPE_SINA = "sina" 37 | private const val ALIAS_TYPE_TENCENT = "tencent" 38 | private const val ALIAS_TYPE_QQ = "qq" 39 | private const val ALIAS_TYPE_WEIXIN = "weixin" 40 | private const val ALIAS_TYPE_BAIDU = "baidu" 41 | private const val ALIAS_TYPE_RENREN = "renren" 42 | private const val ALIAS_TYPE_KAIXIN = "kaixin" 43 | private const val ALIAS_TYPE_DOUBAN = "douban" 44 | private const val ALIAS_TYPE_FACEBOOK = "facebook" 45 | private const val ALIAS_TYPE_TWITTER = "twitter" 46 | 47 | // 友盟初始化参数 48 | private var appKey = "" 49 | private var pushSecret = "" 50 | private var channel = "" 51 | 52 | // 小米初始化参数 53 | private var xiaomiAppId = "" 54 | private var xiaomiAppKey = "" 55 | 56 | // oppo 初始化参数 57 | private var oppoAppKey = "" 58 | private var oppoAppSecret = "" 59 | 60 | // 魅族初始化参数 61 | private var meizuAppId = "" 62 | private var meizuAppKey = "" 63 | 64 | private var launchMessage: UMessage? = null 65 | private var pushModule: RNTUmengPushModule? = null 66 | 67 | private var isStarted = false 68 | private var isStartPending = false 69 | 70 | private var jsInitOptions: ReadableMap? = null 71 | 72 | var onNotificationPresented: ((HashMap, HashMap) -> Unit)? = null 73 | var onNotificationClicked: ((HashMap, HashMap) -> Unit)? = null 74 | 75 | // 初始化友盟基础库 76 | @JvmStatic fun init(app: Application, metaData: Bundle, debug: Boolean) { 77 | 78 | appKey = metaData.getString("UMENG_APP_KEY", "").trim() 79 | pushSecret = metaData.getString("UMENG_PUSH_SECRET", "").trim() 80 | channel = metaData.getString("UMENG_CHANNEL", "").trim() 81 | 82 | xiaomiAppId = metaData.getString("XIAOMI_PUSH_APP_ID", "").trim() 83 | xiaomiAppKey = metaData.getString("XIAOMI_PUSH_APP_KEY", "").trim() 84 | 85 | oppoAppKey = metaData.getString("OPPO_PUSH_APP_KEY", "").trim() 86 | oppoAppSecret = metaData.getString("OPPO_PUSH_APP_SECRET", "").trim() 87 | 88 | meizuAppId = metaData.getString("MEIZU_PUSH_APP_ID", "").trim() 89 | meizuAppKey = metaData.getString("MEIZU_PUSH_APP_KEY", "").trim() 90 | 91 | UMConfigure.setLogEnabled(debug) 92 | 93 | // 解决推送消息显示乱码的问题 94 | UMConfigure.preInit(app, appKey, channel) 95 | 96 | jsInitOptions?.let { 97 | pushModule?.initSDKByProcess(app, it) 98 | } 99 | 100 | } 101 | 102 | @JvmStatic fun handleMessage(currentActivity: Activity, nextActivityClass: Class<*>, msg: UMessage?) { 103 | 104 | // 离线状态收到多条推送 105 | // 点击第一条会经此 activity 启动 app 106 | // 点击其他的依然会经此 activity,这里需要做区别 107 | 108 | // 跳转到 main activity 109 | msg?.let { 110 | if (isStarted) { 111 | pushModule?.onNotificationClicked(it) 112 | } 113 | else { 114 | launchMessage = it 115 | } 116 | } 117 | 118 | val newIntent = Intent(currentActivity, nextActivityClass) 119 | newIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_TASK_ON_HOME 120 | currentActivity.startActivity(newIntent) 121 | currentActivity.overridePendingTransition(0, 0) 122 | currentActivity.finish() 123 | 124 | } 125 | 126 | } 127 | 128 | private lateinit var pushAgent: PushAgent 129 | private var deviceToken = "" 130 | 131 | override fun getName(): String { 132 | return "RNTUmengPush" 133 | } 134 | 135 | override fun getConstants(): Map { 136 | 137 | val constants: MutableMap = HashMap() 138 | 139 | constants["NOTIFICATION_PLAY_SERVER"] = MsgConstant.NOTIFICATION_PLAY_SERVER 140 | constants["NOTIFICATION_PLAY_SDK_ENABLE"] = MsgConstant.NOTIFICATION_PLAY_SDK_ENABLE 141 | constants["NOTIFICATION_PLAY_SDK_DISABLE"] = MsgConstant.NOTIFICATION_PLAY_SDK_DISABLE 142 | 143 | constants["ALIAS_TYPE_SINA"] = ALIAS_TYPE_SINA 144 | constants["ALIAS_TYPE_TENCENT"] = ALIAS_TYPE_TENCENT 145 | constants["ALIAS_TYPE_QQ"] = ALIAS_TYPE_QQ 146 | constants["ALIAS_TYPE_WEIXIN"] = ALIAS_TYPE_WEIXIN 147 | constants["ALIAS_TYPE_BAIDU"] = ALIAS_TYPE_BAIDU 148 | constants["ALIAS_TYPE_RENREN"] = ALIAS_TYPE_RENREN 149 | constants["ALIAS_TYPE_KAIXIN"] = ALIAS_TYPE_KAIXIN 150 | constants["ALIAS_TYPE_DOUBAN"] = ALIAS_TYPE_DOUBAN 151 | constants["ALIAS_TYPE_FACEBOOK"] = ALIAS_TYPE_FACEBOOK 152 | constants["ALIAS_TYPE_TWITTER"] = ALIAS_TYPE_TWITTER 153 | 154 | return constants 155 | 156 | } 157 | 158 | override fun initialize() { 159 | super.initialize() 160 | pushModule = this 161 | isStarted = false 162 | isStartPending = false 163 | jsInitOptions = null 164 | } 165 | 166 | override fun invalidate() { 167 | super.invalidate() 168 | pushModule = null 169 | isStarted = false 170 | isStartPending = false 171 | jsInitOptions = null 172 | } 173 | 174 | @ReactMethod 175 | fun init(options: ReadableMap) { 176 | val context = reactContext.applicationContext as Application 177 | initSDKByProcess(context, options) 178 | } 179 | 180 | @ReactMethod 181 | fun start() { 182 | 183 | if (deviceToken.isEmpty()) { 184 | isStartPending = true 185 | return 186 | } 187 | 188 | // 接收启动 app 的推送 189 | val map = Arguments.createMap() 190 | map.putString("deviceToken", deviceToken) 191 | 192 | // 启动参数 193 | launchMessage?.let { 194 | map.putMap("notification", toWritableMap(formatNotification(it))) 195 | map.putMap("custom", toWritableMap(formatCustom(it))) 196 | launchMessage = null 197 | } 198 | 199 | sendEvent("register", map) 200 | 201 | isStarted = true 202 | isStartPending = false 203 | 204 | } 205 | 206 | @ReactMethod 207 | fun getTags(promise: Promise) { 208 | 209 | pushAgent.tagManager.getTags { isSuccess, result -> 210 | if (isSuccess) { 211 | val map = Arguments.createMap() 212 | val list = Arguments.createArray() 213 | for (tag in result) { 214 | list.pushString(tag) 215 | } 216 | map.putArray("tags", list) 217 | promise.resolve(map) 218 | } 219 | else { 220 | promise.reject("-1", "error") 221 | } 222 | } 223 | 224 | } 225 | 226 | @ReactMethod 227 | fun addTags(tags: ReadableArray, promise: Promise) { 228 | 229 | val list = ArrayList() 230 | 231 | for (i in 0 until tags.size()) { 232 | tags.getString(i)?.let { 233 | list.add(it) 234 | } 235 | } 236 | 237 | pushAgent.tagManager.addTags( 238 | { isSuccess, result -> 239 | if (isSuccess) { 240 | val map = Arguments.createMap() 241 | result?.let { 242 | map.putInt("remain", it.remain) 243 | } 244 | promise.resolve(map) 245 | } else { 246 | promise.reject("-1", "error") 247 | } 248 | }, 249 | *list.toTypedArray() 250 | ) 251 | 252 | } 253 | 254 | @ReactMethod 255 | fun removeTags(tags: ReadableArray, promise: Promise) { 256 | 257 | val list = ArrayList() 258 | 259 | for (i in 0 until tags.size()) { 260 | tags.getString(i)?.let { 261 | list.add(it) 262 | } 263 | } 264 | 265 | pushAgent.tagManager.deleteTags( 266 | { isSuccess, result -> 267 | if (isSuccess) { 268 | val map = Arguments.createMap() 269 | result?.let { 270 | map.putInt("remain", it.remain) 271 | } 272 | promise.resolve(map) 273 | } else { 274 | promise.reject("-1", "error") 275 | } 276 | }, 277 | *list.toTypedArray() 278 | ) 279 | 280 | } 281 | 282 | @ReactMethod 283 | fun setAlias(alias: String, type: String, promise: Promise) { 284 | 285 | pushAgent.setAlias(alias, getAliasType(type)) { isSuccess, _ -> 286 | if (isSuccess) { 287 | val map = Arguments.createMap() 288 | promise.resolve(map) 289 | } 290 | else { 291 | promise.reject("-1", "error") 292 | } 293 | } 294 | 295 | } 296 | 297 | @ReactMethod 298 | fun addAlias(alias: String, type: String, promise: Promise) { 299 | 300 | pushAgent.addAlias(alias, getAliasType(type)) { isSuccess, _ -> 301 | if (isSuccess) { 302 | val map = Arguments.createMap() 303 | promise.resolve(map) 304 | } 305 | else { 306 | promise.reject("-1", "error") 307 | } 308 | } 309 | 310 | } 311 | 312 | @ReactMethod 313 | fun removeAlias(alias: String, type: String, promise: Promise) { 314 | 315 | pushAgent.deleteAlias(alias, getAliasType(type)) { isSuccess, _ -> 316 | if (isSuccess) { 317 | val map = Arguments.createMap() 318 | promise.resolve(map) 319 | } 320 | else { 321 | promise.reject("-1", "error") 322 | } 323 | } 324 | 325 | } 326 | 327 | @ReactMethod 328 | fun setAdvanced(options: ReadableMap) { 329 | setPushSetting(pushAgent, options) 330 | } 331 | 332 | @ReactMethod 333 | fun enable(promise: Promise) { 334 | pushAgent.enable(object : UPushSettingCallback { 335 | override fun onSuccess() { 336 | val map = Arguments.createMap() 337 | promise.resolve(map) 338 | } 339 | override fun onFailure(var1: String?, var2: String?) { 340 | promise.reject("-1", var1) 341 | } 342 | }) 343 | } 344 | 345 | @ReactMethod 346 | fun disable(promise: Promise) { 347 | pushAgent.disable(object : UPushSettingCallback { 348 | override fun onSuccess() { 349 | val map = Arguments.createMap() 350 | promise.resolve(map) 351 | } 352 | override fun onFailure(var1: String?, var2: String?) { 353 | promise.reject("-1", var1) 354 | } 355 | }) 356 | } 357 | 358 | @ReactMethod 359 | fun createNotificationChannel(options: ReadableMap) { 360 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 361 | 362 | val id = options.getString("id") 363 | 364 | val notificationManager = reactContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 365 | if (notificationManager.getNotificationChannel(id) != null) { 366 | return 367 | } 368 | 369 | val name = options.getString("name") 370 | val importance = when (options.getString("importance")) { 371 | "max" -> NotificationManager.IMPORTANCE_MAX 372 | "high" -> NotificationManager.IMPORTANCE_HIGH 373 | "low" -> NotificationManager.IMPORTANCE_LOW 374 | "min" -> NotificationManager.IMPORTANCE_MIN 375 | else -> NotificationManager.IMPORTANCE_DEFAULT 376 | } 377 | 378 | val mChannel = NotificationChannel(id, name, importance) 379 | if (options.hasKey("description")) { 380 | mChannel.description = options.getString("description") 381 | } 382 | // VISIBILITY_PUBLIC 显示所有通知信息 383 | // VISIBILITY_PRIVATE 只显示通知标题不显示通知内容 384 | // VISIBILITY_SECRET 不显示任何通知信息 385 | if (options.hasKey("lockscreenVisibility")) { 386 | mChannel.lockscreenVisibility = when (options.getString("lockscreenVisibility")) { 387 | "secret" -> Notification.VISIBILITY_SECRET 388 | "private" -> Notification.VISIBILITY_PRIVATE 389 | else -> Notification.VISIBILITY_PUBLIC 390 | } 391 | } 392 | if (options.hasKey("enableLights")) { 393 | mChannel.enableLights(options.getBoolean("enableLights")) 394 | } 395 | if (options.hasKey("enableVibration")) { 396 | mChannel.enableVibration(options.getBoolean("enableVibration")) 397 | } 398 | if (options.hasKey("setShowBadge")) { 399 | mChannel.setShowBadge(options.getBoolean("setShowBadge")) 400 | } 401 | 402 | // Register the channel with the system; you can't change the importance 403 | // or other notification behaviors after this 404 | if (options.hasKey("groupId") && options.hasKey("groupName")) { 405 | val groupId = options.getString("groupId") 406 | val groupName = options.getString("groupName") 407 | notificationManager.createNotificationChannelGroup(NotificationChannelGroup(groupId, groupName)) 408 | mChannel.group = groupId 409 | } 410 | 411 | notificationManager.createNotificationChannel(mChannel) 412 | 413 | } 414 | 415 | } 416 | 417 | @ReactMethod 418 | fun deleteNotificationChannel(options: ReadableMap) { 419 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { 420 | val id = options.getString("id") 421 | val notificationManager = reactContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager 422 | notificationManager.deleteNotificationChannel(id) 423 | } 424 | } 425 | 426 | private fun initSDKByProcess(context: Application, options: ReadableMap) { 427 | if (UMUtils.isMainProgress(reactContext)) { 428 | Thread { 429 | initSDK(context, options) 430 | }.start() 431 | } 432 | else { 433 | initSDK(context, options) 434 | } 435 | } 436 | 437 | private fun initSDK(context: Application, options: ReadableMap) { 438 | 439 | UMConfigure.submitPolicyGrantResult(reactContext, true) 440 | UMConfigure.init(context, appKey, channel, UMConfigure.DEVICE_TYPE_PHONE, pushSecret) 441 | 442 | pushAgent = PushAgent.getInstance(context) 443 | 444 | // UMConfigure.setLogEnabled(debug) 会设值到 MLog.DEBUG 445 | if (MLog.DEBUG) { 446 | pushAgent.isPushCheck = true 447 | } 448 | 449 | pushAgent.displayNotificationNumber = 0 450 | 451 | // 自定义资源包名 452 | if (options.hasKey("resourcePackageName")) { 453 | pushAgent.resourcePackageName = options.getString("resourcePackageName") 454 | } 455 | 456 | // app 在前台时是否显示推送 457 | // 在 pushAgent.register 方法之前调用 458 | var notificationOnForeground = true 459 | if (options.hasKey("notificationOnForeground")) { 460 | notificationOnForeground = options.getBoolean("notificationOnForeground") 461 | } 462 | pushAgent.notificationOnForeground = notificationOnForeground 463 | 464 | setPushSetting(pushAgent, options) 465 | 466 | pushAgent.messageHandler = object : UmengMessageHandler() { 467 | override fun dealWithNotificationMessage(context: Context?, msg: UMessage?) { 468 | super.dealWithNotificationMessage(context, msg) 469 | if (msg != null) { 470 | onNotificationPresented(msg) 471 | } 472 | } 473 | 474 | override fun dealWithCustomMessage(context: Context?, msg: UMessage?) { 475 | if (msg != null) { 476 | onMessage(msg) 477 | } 478 | } 479 | } 480 | 481 | // 自定义通知栏打开动作,让 js 去处理 482 | pushAgent.notificationClickHandler = object : UmengNotificationClickHandler() { 483 | override fun launchApp(context: Context?, msg: UMessage?) { 484 | super.launchApp(context, msg) 485 | if (msg != null) { 486 | if (isStarted) { 487 | onNotificationClicked(msg) 488 | } 489 | else { 490 | launchMessage = msg 491 | } 492 | } 493 | } 494 | 495 | override fun dealWithCustomAction(context: Context?, msg: UMessage?) { 496 | this.launchApp(context, msg) 497 | } 498 | } 499 | 500 | pushAgent.register(object : UPushRegisterCallback { 501 | override fun onSuccess(token: String) { 502 | deviceToken = token 503 | if (isStartPending) { 504 | start() 505 | } 506 | } 507 | 508 | override fun onFailure(errCode: String, errDesc: String) { 509 | 510 | } 511 | }) 512 | 513 | // 日活统计及多维度推送的必调用方法 514 | pushAgent.onAppStart() 515 | 516 | if (options.hasKey("huaweiEnabled") && options.getBoolean("huaweiEnabled")) { 517 | HuaWeiRegister.register(context) 518 | } 519 | if (options.hasKey("honorEnabled") && options.getBoolean("honorEnabled")) { 520 | HonorRegister.register(context) 521 | } 522 | if (options.hasKey("xiaomiEnabled") && options.getBoolean("xiaomiEnabled")) { 523 | MiPushRegistar.register(context, xiaomiAppId, xiaomiAppKey) 524 | } 525 | if (options.hasKey("oppoEnabled") && options.getBoolean("oppoEnabled")) { 526 | OppoRegister.register(context, oppoAppKey, oppoAppSecret) 527 | } 528 | if (options.hasKey("vivoEnabled") && options.getBoolean("vivoEnabled")) { 529 | VivoRegister.register(context) 530 | } 531 | if (options.hasKey("meizuEnabled") && options.getBoolean("meizuEnabled")) { 532 | MeizuRegister.register(context, meizuAppId, meizuAppKey) 533 | } 534 | 535 | jsInitOptions = options 536 | 537 | } 538 | 539 | private fun setPushSetting(pushAgent: PushAgent, options: ReadableMap) { 540 | 541 | // 设置显示通知的数量 542 | // 可以设置最多显示通知的条数,当显示数目大于设置值时,若再有新通知到达,会移除一条最早的通知 543 | // 值为 0~10,为 0 时,表示不限制显示个数 544 | if (options.hasKey("displayNotificationNumber")) { 545 | val value = options.getInt("displayNotificationNumber") 546 | if (value in 0..10) { 547 | pushAgent.displayNotificationNumber = value 548 | } 549 | } 550 | 551 | // 默认情况下,同一台设备在1分钟内收到同一个应用的多条通知时,不会重复提醒,同时在通知栏里新的通知会替换掉旧的通知 552 | // 可以通过如下方法来设置冷却时间 553 | if (options.hasKey("muteDurationSeconds")) { 554 | pushAgent.muteDurationSeconds = options.getInt("muteDurationSeconds") 555 | } 556 | 557 | // 为免过度打扰用户,SDK默认在“23:00”到“7:00”之间收到通知消息时不响铃,不振动,不闪灯 558 | // 如果需要改变默认的静音时间,可以使用以下接口: 559 | if (options.hasKey("noDisturbStartHour") 560 | && options.hasKey("noDisturbStartMinute") 561 | && options.hasKey("noDisturbEndHour") 562 | && options.hasKey("noDisturbEndMinute") 563 | ) { 564 | pushAgent.setNoDisturbMode( 565 | options.getInt("noDisturbStartHour"), 566 | options.getInt("noDisturbStartMinute"), 567 | options.getInt("noDisturbEndHour"), 568 | options.getInt("noDisturbEndMinute") 569 | ) 570 | } 571 | 572 | // 0: MsgConstant.NOTIFICATION_PLAY_SERVER 573 | // 1: MsgConstant.NOTIFICATION_PLAY_SDK_ENABLE 574 | // 2: MsgConstant.NOTIFICATION_PLAY_SDK_DISABLE 575 | 576 | // 是否响铃 577 | if (options.hasKey("notificationPlaySound")) { 578 | pushAgent.notificationPlaySound = options.getInt("notificationPlaySound") 579 | } 580 | 581 | // 是否点亮呼吸灯 582 | if (options.hasKey("notificationPlayLights")) { 583 | pushAgent.notificationPlayLights = options.getInt("notificationPlayLights") 584 | } 585 | 586 | // 是否振动 587 | if (options.hasKey("notificationPlayVibrate")) { 588 | pushAgent.notificationPlayVibrate = options.getInt("notificationPlayVibrate") 589 | } 590 | 591 | } 592 | 593 | private fun getAliasType(type: String): String { 594 | return when (type) { 595 | ALIAS_TYPE_SINA -> { 596 | "sina" 597 | } 598 | ALIAS_TYPE_TENCENT -> { 599 | "tencent" 600 | } 601 | ALIAS_TYPE_QQ -> { 602 | "qq" 603 | } 604 | ALIAS_TYPE_WEIXIN -> { 605 | "weixin" 606 | } 607 | ALIAS_TYPE_BAIDU -> { 608 | "baidu" 609 | } 610 | ALIAS_TYPE_RENREN -> { 611 | "renren" 612 | } 613 | ALIAS_TYPE_KAIXIN -> { 614 | "kaixin" 615 | } 616 | ALIAS_TYPE_DOUBAN -> { 617 | "douban" 618 | } 619 | ALIAS_TYPE_FACEBOOK -> { 620 | "facebook" 621 | } 622 | ALIAS_TYPE_TWITTER -> { 623 | "twitter" 624 | } 625 | else -> { 626 | type 627 | } 628 | } 629 | } 630 | 631 | private fun onNotificationPresented(message: UMessage) { 632 | 633 | val map = Arguments.createMap() 634 | val notification = formatNotification(message) 635 | val custom = formatCustom(message) 636 | map.putMap("notification", toWritableMap(notification)) 637 | map.putMap("custom", toWritableMap(custom)) 638 | map.putBoolean("presented", true) 639 | 640 | sendEvent("remoteNotification", map) 641 | 642 | onNotificationPresented?.invoke(notification, custom) 643 | 644 | } 645 | 646 | private fun onNotificationClicked(message: UMessage) { 647 | 648 | val map = Arguments.createMap() 649 | val notification = formatNotification(message) 650 | val custom = formatCustom(message) 651 | map.putMap("notification", toWritableMap(notification)) 652 | map.putMap("custom", toWritableMap(custom)) 653 | map.putBoolean("clicked", true) 654 | 655 | sendEvent("remoteNotification", map) 656 | 657 | onNotificationClicked?.invoke(notification, custom) 658 | 659 | } 660 | 661 | private fun onMessage(message: UMessage) { 662 | 663 | val map = Arguments.createMap() 664 | map.putString("message", message.custom) 665 | map.putMap("custom", toWritableMap(formatCustom(message))) 666 | 667 | sendEvent("message", map) 668 | 669 | } 670 | 671 | private fun sendEvent(eventName: String, params: WritableMap) { 672 | reactContext 673 | .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) 674 | .emit(eventName, params) 675 | } 676 | 677 | private fun formatNotification(msg: UMessage): HashMap { 678 | val map = HashMap() 679 | map["title"] = msg.title 680 | map["content"] = msg.text 681 | return map 682 | } 683 | 684 | private fun formatCustom(msg: UMessage): HashMap { 685 | val map = HashMap() 686 | msg.extra?.let { 687 | for ((key,value) in it) { 688 | map[key] = value 689 | } 690 | } 691 | return map 692 | } 693 | 694 | private fun toWritableMap(hashMap: HashMap): WritableMap { 695 | val result = Arguments.createMap() 696 | hashMap.forEach { 697 | result.putString(it.key, it.value) 698 | } 699 | return result 700 | } 701 | 702 | } 703 | -------------------------------------------------------------------------------- /android/src/main/java/com/github/reactnativehero/umengpush/RNTUmengPushPackage.kt: -------------------------------------------------------------------------------- 1 | package com.github.reactnativehero.umengpush 2 | 3 | import com.facebook.react.ReactPackage 4 | import com.facebook.react.bridge.NativeModule 5 | import com.facebook.react.bridge.ReactApplicationContext 6 | import com.facebook.react.uimanager.ViewManager 7 | 8 | class RNTUmengPushPackage : ReactPackage { 9 | 10 | override fun createNativeModules(reactContext: ReactApplicationContext): List { 11 | return listOf(RNTUmengPushModule(reactContext)) 12 | } 13 | 14 | override fun createViewManagers(reactContext: ReactApplicationContext): List> { 15 | return emptyList() 16 | } 17 | 18 | } -------------------------------------------------------------------------------- /android/src/main/java/com/github/reactnativehero/umengpush/UmengPushActivity.kt: -------------------------------------------------------------------------------- 1 | package com.github.reactnativehero.umengpush 2 | 3 | import android.app.Activity 4 | import android.content.Intent 5 | import android.os.Bundle 6 | import android.text.TextUtils 7 | import com.github.reactnativehero.umengpush.RNTUmengPushModule.Companion.handleMessage 8 | import com.umeng.message.UmengNotifyClick 9 | import com.umeng.message.entity.UMessage 10 | 11 | open class UmengPushActivity : Activity() { 12 | 13 | companion object { 14 | @JvmStatic var activityView: Int = 0 15 | @JvmStatic lateinit var mainActivityClass: Class<*> 16 | } 17 | 18 | private val mNotificationClick: UmengNotifyClick = object : UmengNotifyClick() { 19 | public override fun onMessage(msg: UMessage) { 20 | val body = msg.raw.toString() 21 | if (!TextUtils.isEmpty(body)) { 22 | handleMessage(this@UmengPushActivity, mainActivityClass, msg) 23 | } 24 | } 25 | } 26 | 27 | override fun onCreate(bundle: Bundle?) { 28 | super.onCreate(bundle) 29 | if (activityView != 0) { 30 | setContentView(activityView) 31 | } 32 | mNotificationClick.onCreate(this, intent) 33 | } 34 | 35 | override fun onNewIntent(intent: Intent?) { 36 | super.onNewIntent(intent) 37 | mNotificationClick.onNewIntent(intent) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /android/src/main/res/layout/umeng_push_activity.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import { NativeEventEmitter, NativeModules } from 'react-native' 2 | 3 | const { RNTUmengPush } = NativeModules 4 | 5 | const eventEmitter = new NativeEventEmitter(RNTUmengPush) 6 | 7 | export const ALIAS_TYPE = { 8 | SINA: RNTUmengPush.ALIAS_TYPE_SINA, 9 | TENCENT: RNTUmengPush.ALIAS_TYPE_TENCENT, 10 | QQ: RNTUmengPush.ALIAS_TYPE_QQ, 11 | WEIXIN: RNTUmengPush.ALIAS_TYPE_WEIXIN, 12 | BAIDU: RNTUmengPush.ALIAS_TYPE_BAIDU, 13 | RENREN: RNTUmengPush.ALIAS_TYPE_RENREN, 14 | KAIXIN: RNTUmengPush.ALIAS_TYPE_KAIXIN, 15 | DOUBAN: RNTUmengPush.ALIAS_TYPE_DOUBAN, 16 | FACEBOOK: RNTUmengPush.ALIAS_TYPE_FACEBOOK, 17 | TWITTER: RNTUmengPush.ALIAS_TYPE_TWITTER, 18 | } 19 | 20 | export const NOTIFICATION_PLAY = { 21 | SERVER: RNTUmengPush.NOTIFICATION_PLAY_SERVER, 22 | SDK_ENABLE: RNTUmengPush.NOTIFICATION_PLAY_SDK_ENABLE, 23 | SDK_DISABLE: RNTUmengPush.NOTIFICATION_PLAY_SDK_DISABLE, 24 | } 25 | 26 | export function init(options) { 27 | RNTUmengPush.init(options || {}) 28 | } 29 | 30 | export function start() { 31 | RNTUmengPush.start() 32 | } 33 | 34 | export function getTags() { 35 | return RNTUmengPush.getTags() 36 | } 37 | 38 | export function addTags(tags) { 39 | return RNTUmengPush.addTags(tags) 40 | } 41 | 42 | export function removeTags(tags) { 43 | return RNTUmengPush.removeTags(tags) 44 | } 45 | 46 | export function setAlias(alias, type) { 47 | return RNTUmengPush.setAlias(alias, type) 48 | } 49 | 50 | export function addAlias(alias, type) { 51 | return RNTUmengPush.addAlias(alias, type) 52 | } 53 | 54 | export function removeAlias(alias, type) { 55 | return RNTUmengPush.removeAlias(alias, type) 56 | } 57 | 58 | export function setAdvanced(options) { 59 | RNTUmengPush.setAdvanced(options) 60 | } 61 | 62 | export function addListener(type, listener) { 63 | return eventEmitter.addListener(type, listener) 64 | } 65 | 66 | // ios 不支持关闭推送 67 | export const supportDisable = !!RNTUmengPush.disable 68 | 69 | export function enable() { 70 | return RNTUmengPush.enable() 71 | } 72 | 73 | export function disable() { 74 | return RNTUmengPush.disable() 75 | } 76 | 77 | // ios 不支持推送通道 78 | export const supportNotificationChannel = !!RNTUmengPush.createNotificationChannel 79 | 80 | export function createNotificationChannel(options) { 81 | RNTUmengPush.createNotificationChannel(options) 82 | } 83 | 84 | export function deleteNotificationChannel(options) { 85 | RNTUmengPush.deleteNotificationChannel(options) 86 | } 87 | -------------------------------------------------------------------------------- /ios/RNTUmengPush.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1EC4B31024B8AE7700192AB7 /* RNTUmengPush.m in Sources */ = {isa = PBXBuildFile; fileRef = 1EC4B30F24B8AE7700192AB7 /* RNTUmengPush.m */; }; 11 | 1EC4B31124B8AE7700192AB7 /* RNTUmengPush.h in CopyFiles */ = {isa = PBXBuildFile; fileRef = 1EC4B30E24B8AE7700192AB7 /* RNTUmengPush.h */; }; 12 | /* End PBXBuildFile section */ 13 | 14 | /* Begin PBXCopyFilesBuildPhase section */ 15 | 1EC4B30924B8AE7700192AB7 /* CopyFiles */ = { 16 | isa = PBXCopyFilesBuildPhase; 17 | buildActionMask = 2147483647; 18 | dstPath = "include/$(PRODUCT_NAME)"; 19 | dstSubfolderSpec = 16; 20 | files = ( 21 | 1EC4B31124B8AE7700192AB7 /* RNTUmengPush.h in CopyFiles */, 22 | ); 23 | runOnlyForDeploymentPostprocessing = 0; 24 | }; 25 | /* End PBXCopyFilesBuildPhase section */ 26 | 27 | /* Begin PBXFileReference section */ 28 | 1EC4B30B24B8AE7700192AB7 /* libRNTUmengPush.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = libRNTUmengPush.a; sourceTree = BUILT_PRODUCTS_DIR; }; 29 | 1EC4B30E24B8AE7700192AB7 /* RNTUmengPush.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = RNTUmengPush.h; sourceTree = ""; }; 30 | 1EC4B30F24B8AE7700192AB7 /* RNTUmengPush.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = RNTUmengPush.m; sourceTree = ""; }; 31 | /* End PBXFileReference section */ 32 | 33 | /* Begin PBXFrameworksBuildPhase section */ 34 | 1EC4B30824B8AE7700192AB7 /* Frameworks */ = { 35 | isa = PBXFrameworksBuildPhase; 36 | buildActionMask = 2147483647; 37 | files = ( 38 | ); 39 | runOnlyForDeploymentPostprocessing = 0; 40 | }; 41 | /* End PBXFrameworksBuildPhase section */ 42 | 43 | /* Begin PBXGroup section */ 44 | 1EC4B30224B8AE7700192AB7 = { 45 | isa = PBXGroup; 46 | children = ( 47 | 1EC4B30D24B8AE7700192AB7 /* RNTUmengPush */, 48 | 1EC4B30C24B8AE7700192AB7 /* Products */, 49 | ); 50 | sourceTree = ""; 51 | }; 52 | 1EC4B30C24B8AE7700192AB7 /* Products */ = { 53 | isa = PBXGroup; 54 | children = ( 55 | 1EC4B30B24B8AE7700192AB7 /* libRNTUmengPush.a */, 56 | ); 57 | name = Products; 58 | sourceTree = ""; 59 | }; 60 | 1EC4B30D24B8AE7700192AB7 /* RNTUmengPush */ = { 61 | isa = PBXGroup; 62 | children = ( 63 | 1EC4B30E24B8AE7700192AB7 /* RNTUmengPush.h */, 64 | 1EC4B30F24B8AE7700192AB7 /* RNTUmengPush.m */, 65 | ); 66 | path = RNTUmengPush; 67 | sourceTree = ""; 68 | }; 69 | /* End PBXGroup section */ 70 | 71 | /* Begin PBXNativeTarget section */ 72 | 1EC4B30A24B8AE7700192AB7 /* RNTUmengPush */ = { 73 | isa = PBXNativeTarget; 74 | buildConfigurationList = 1EC4B31424B8AE7700192AB7 /* Build configuration list for PBXNativeTarget "RNTUmengPush" */; 75 | buildPhases = ( 76 | 1EC4B30724B8AE7700192AB7 /* Sources */, 77 | 1EC4B30824B8AE7700192AB7 /* Frameworks */, 78 | 1EC4B30924B8AE7700192AB7 /* CopyFiles */, 79 | ); 80 | buildRules = ( 81 | ); 82 | dependencies = ( 83 | ); 84 | name = RNTUmengPush; 85 | productName = RNTUmengPush; 86 | productReference = 1EC4B30B24B8AE7700192AB7 /* libRNTUmengPush.a */; 87 | productType = "com.apple.product-type.library.static"; 88 | }; 89 | /* End PBXNativeTarget section */ 90 | 91 | /* Begin PBXProject section */ 92 | 1EC4B30324B8AE7700192AB7 /* Project object */ = { 93 | isa = PBXProject; 94 | attributes = { 95 | LastUpgradeCheck = 1150; 96 | ORGANIZATIONNAME = musicode; 97 | TargetAttributes = { 98 | 1EC4B30A24B8AE7700192AB7 = { 99 | CreatedOnToolsVersion = 11.5; 100 | }; 101 | }; 102 | }; 103 | buildConfigurationList = 1EC4B30624B8AE7700192AB7 /* Build configuration list for PBXProject "RNTUmengPush" */; 104 | compatibilityVersion = "Xcode 9.3"; 105 | developmentRegion = en; 106 | hasScannedForEncodings = 0; 107 | knownRegions = ( 108 | en, 109 | Base, 110 | ); 111 | mainGroup = 1EC4B30224B8AE7700192AB7; 112 | productRefGroup = 1EC4B30C24B8AE7700192AB7 /* Products */; 113 | projectDirPath = ""; 114 | projectRoot = ""; 115 | targets = ( 116 | 1EC4B30A24B8AE7700192AB7 /* RNTUmengPush */, 117 | ); 118 | }; 119 | /* End PBXProject section */ 120 | 121 | /* Begin PBXSourcesBuildPhase section */ 122 | 1EC4B30724B8AE7700192AB7 /* Sources */ = { 123 | isa = PBXSourcesBuildPhase; 124 | buildActionMask = 2147483647; 125 | files = ( 126 | 1EC4B31024B8AE7700192AB7 /* RNTUmengPush.m in Sources */, 127 | ); 128 | runOnlyForDeploymentPostprocessing = 0; 129 | }; 130 | /* End PBXSourcesBuildPhase section */ 131 | 132 | /* Begin XCBuildConfiguration section */ 133 | 1EC4B31224B8AE7700192AB7 /* Debug */ = { 134 | isa = XCBuildConfiguration; 135 | buildSettings = { 136 | ALWAYS_SEARCH_USER_PATHS = NO; 137 | CLANG_ANALYZER_NONNULL = YES; 138 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 139 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 140 | CLANG_CXX_LIBRARY = "libc++"; 141 | CLANG_ENABLE_MODULES = YES; 142 | CLANG_ENABLE_OBJC_ARC = YES; 143 | CLANG_ENABLE_OBJC_WEAK = YES; 144 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 145 | CLANG_WARN_BOOL_CONVERSION = YES; 146 | CLANG_WARN_COMMA = YES; 147 | CLANG_WARN_CONSTANT_CONVERSION = YES; 148 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 149 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 150 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 151 | CLANG_WARN_EMPTY_BODY = YES; 152 | CLANG_WARN_ENUM_CONVERSION = YES; 153 | CLANG_WARN_INFINITE_RECURSION = YES; 154 | CLANG_WARN_INT_CONVERSION = YES; 155 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 156 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 157 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 158 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 159 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 160 | CLANG_WARN_STRICT_PROTOTYPES = YES; 161 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 162 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 163 | CLANG_WARN_UNREACHABLE_CODE = YES; 164 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 165 | COPY_PHASE_STRIP = NO; 166 | DEBUG_INFORMATION_FORMAT = dwarf; 167 | ENABLE_STRICT_OBJC_MSGSEND = YES; 168 | ENABLE_TESTABILITY = YES; 169 | GCC_C_LANGUAGE_STANDARD = gnu11; 170 | GCC_DYNAMIC_NO_PIC = NO; 171 | GCC_NO_COMMON_BLOCKS = YES; 172 | GCC_OPTIMIZATION_LEVEL = 0; 173 | GCC_PREPROCESSOR_DEFINITIONS = ( 174 | "DEBUG=1", 175 | "$(inherited)", 176 | ); 177 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 178 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 179 | GCC_WARN_UNDECLARED_SELECTOR = YES; 180 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 181 | GCC_WARN_UNUSED_FUNCTION = YES; 182 | GCC_WARN_UNUSED_VARIABLE = YES; 183 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 184 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 185 | MTL_FAST_MATH = YES; 186 | ONLY_ACTIVE_ARCH = YES; 187 | SDKROOT = iphoneos; 188 | }; 189 | name = Debug; 190 | }; 191 | 1EC4B31324B8AE7700192AB7 /* Release */ = { 192 | isa = XCBuildConfiguration; 193 | buildSettings = { 194 | ALWAYS_SEARCH_USER_PATHS = NO; 195 | CLANG_ANALYZER_NONNULL = YES; 196 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 197 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 198 | CLANG_CXX_LIBRARY = "libc++"; 199 | CLANG_ENABLE_MODULES = YES; 200 | CLANG_ENABLE_OBJC_ARC = YES; 201 | CLANG_ENABLE_OBJC_WEAK = YES; 202 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 203 | CLANG_WARN_BOOL_CONVERSION = YES; 204 | CLANG_WARN_COMMA = YES; 205 | CLANG_WARN_CONSTANT_CONVERSION = YES; 206 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 207 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 208 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 209 | CLANG_WARN_EMPTY_BODY = YES; 210 | CLANG_WARN_ENUM_CONVERSION = YES; 211 | CLANG_WARN_INFINITE_RECURSION = YES; 212 | CLANG_WARN_INT_CONVERSION = YES; 213 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 214 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 215 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 216 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 217 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 218 | CLANG_WARN_STRICT_PROTOTYPES = YES; 219 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 220 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 221 | CLANG_WARN_UNREACHABLE_CODE = YES; 222 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 223 | COPY_PHASE_STRIP = NO; 224 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 225 | ENABLE_NS_ASSERTIONS = NO; 226 | ENABLE_STRICT_OBJC_MSGSEND = YES; 227 | GCC_C_LANGUAGE_STANDARD = gnu11; 228 | GCC_NO_COMMON_BLOCKS = YES; 229 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 230 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 231 | GCC_WARN_UNDECLARED_SELECTOR = YES; 232 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 233 | GCC_WARN_UNUSED_FUNCTION = YES; 234 | GCC_WARN_UNUSED_VARIABLE = YES; 235 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 236 | MTL_ENABLE_DEBUG_INFO = NO; 237 | MTL_FAST_MATH = YES; 238 | SDKROOT = iphoneos; 239 | VALIDATE_PRODUCT = YES; 240 | }; 241 | name = Release; 242 | }; 243 | 1EC4B31524B8AE7700192AB7 /* Debug */ = { 244 | isa = XCBuildConfiguration; 245 | buildSettings = { 246 | CODE_SIGN_STYLE = Automatic; 247 | DEVELOPMENT_TEAM = N276SVAZ33; 248 | HEADER_SEARCH_PATHS = "$(SRCROOT)/../../react-native/React/**"; 249 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 250 | OTHER_LDFLAGS = "-ObjC"; 251 | PRODUCT_NAME = "$(TARGET_NAME)"; 252 | SKIP_INSTALL = YES; 253 | TARGETED_DEVICE_FAMILY = "1,2"; 254 | }; 255 | name = Debug; 256 | }; 257 | 1EC4B31624B8AE7700192AB7 /* Release */ = { 258 | isa = XCBuildConfiguration; 259 | buildSettings = { 260 | CODE_SIGN_STYLE = Automatic; 261 | DEVELOPMENT_TEAM = N276SVAZ33; 262 | HEADER_SEARCH_PATHS = "$(SRCROOT)/../../react-native/React/**"; 263 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 264 | OTHER_LDFLAGS = "-ObjC"; 265 | PRODUCT_NAME = "$(TARGET_NAME)"; 266 | SKIP_INSTALL = YES; 267 | TARGETED_DEVICE_FAMILY = "1,2"; 268 | }; 269 | name = Release; 270 | }; 271 | /* End XCBuildConfiguration section */ 272 | 273 | /* Begin XCConfigurationList section */ 274 | 1EC4B30624B8AE7700192AB7 /* Build configuration list for PBXProject "RNTUmengPush" */ = { 275 | isa = XCConfigurationList; 276 | buildConfigurations = ( 277 | 1EC4B31224B8AE7700192AB7 /* Debug */, 278 | 1EC4B31324B8AE7700192AB7 /* Release */, 279 | ); 280 | defaultConfigurationIsVisible = 0; 281 | defaultConfigurationName = Release; 282 | }; 283 | 1EC4B31424B8AE7700192AB7 /* Build configuration list for PBXNativeTarget "RNTUmengPush" */ = { 284 | isa = XCConfigurationList; 285 | buildConfigurations = ( 286 | 1EC4B31524B8AE7700192AB7 /* Debug */, 287 | 1EC4B31624B8AE7700192AB7 /* Release */, 288 | ); 289 | defaultConfigurationIsVisible = 0; 290 | defaultConfigurationName = Release; 291 | }; 292 | /* End XCConfigurationList section */ 293 | }; 294 | rootObject = 1EC4B30324B8AE7700192AB7 /* Project object */; 295 | } 296 | -------------------------------------------------------------------------------- /ios/RNTUmengPush/RNTUmengPush.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | @interface RNTUmengPush : RCTEventEmitter 6 | 7 | + (void)init:(NSString *)appKey channel:(NSString *)channel debug:(BOOL)debug launchOptions:(NSDictionary *)launchOptions; 8 | 9 | + (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; 10 | 11 | + (void)didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/RNTUmengPush/RNTUmengPush.m: -------------------------------------------------------------------------------- 1 | #import "RNTUmengPush.h" 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | @implementation RNTUmengPush 8 | 9 | static NSString *ALIAS_TYPE_SINA = @"sina"; 10 | static NSString *ALIAS_TYPE_TENCENT = @"tencent"; 11 | static NSString *ALIAS_TYPE_QQ = @"qq"; 12 | static NSString *ALIAS_TYPE_WEIXIN = @"weixin"; 13 | static NSString *ALIAS_TYPE_BAIDU = @"baidu"; 14 | static NSString *ALIAS_TYPE_RENREN = @"renren"; 15 | static NSString *ALIAS_TYPE_KAIXIN = @"kaixin"; 16 | static NSString *ALIAS_TYPE_DOUBAN = @"douban"; 17 | static NSString *ALIAS_TYPE_FACEBOOK = @"facebook"; 18 | static NSString *ALIAS_TYPE_TWITTER = @"twitter"; 19 | 20 | static RNTUmengPush *PUSH_INSTANCE = nil; 21 | static NSDictionary *LAUNCH_OPTIONS = nil; 22 | 23 | RCT_EXPORT_MODULE(RNTUmengPush); 24 | 25 | + (void)init:(NSString *)appKey channel:(NSString *)channel debug:(BOOL)debug launchOptions:(NSDictionary *)launchOptions { 26 | 27 | [UMConfigure initWithAppkey:appKey channel:channel]; 28 | [UMConfigure setLogEnabled:debug]; 29 | 30 | LAUNCH_OPTIONS = launchOptions; 31 | 32 | } 33 | 34 | + (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { 35 | 36 | if (![deviceToken isKindOfClass:[NSData class]]) { 37 | return; 38 | } 39 | 40 | const unsigned *tokenBytes = (const unsigned *)[deviceToken bytes]; 41 | NSString *hexToken = [NSString stringWithFormat:@"%08x%08x%08x%08x%08x%08x%08x%08x", 42 | ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]), 43 | ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]), 44 | ntohl(tokenBytes[6]), ntohl(tokenBytes[7])]; 45 | 46 | NSMutableDictionary *body; 47 | 48 | if ([LAUNCH_OPTIONS objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]) { 49 | NSDictionary *userInfo = [LAUNCH_OPTIONS objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey]; 50 | body = [RNTUmengPush getNotification:userInfo]; 51 | } 52 | else { 53 | body = [[NSMutableDictionary alloc] init]; 54 | } 55 | 56 | body[@"deviceToken"] = hexToken; 57 | 58 | if (PUSH_INSTANCE != nil) { 59 | [PUSH_INSTANCE sendEventWithName:@"register" body:body]; 60 | } 61 | 62 | } 63 | 64 | + (void)didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler { 65 | 66 | NSMutableDictionary *body = [RNTUmengPush getNotification:userInfo]; 67 | NSString *message = body[@"message"]; 68 | 69 | // 静默推送会走进这里,不论是什么系统版本 70 | // 普通推送,要求系统版本低于 10 才执行 71 | 72 | if ([[[UIDevice currentDevice] systemVersion]intValue] < 10) { 73 | [UMessage didReceiveRemoteNotification:userInfo]; 74 | 75 | if (PUSH_INSTANCE != nil) { 76 | if (message) { 77 | [PUSH_INSTANCE sendEventWithName:@"message" body:body]; 78 | } 79 | else { 80 | [PUSH_INSTANCE sendEventWithName:@"remoteNotification" body:body]; 81 | } 82 | } 83 | } 84 | // 系统版本 >= 10,只能是静默推送 85 | // 静默推送时,message 必然是字符串,但是必须为空,否则会变成通知 86 | else if (message != nil && message.length == 0) { 87 | if (PUSH_INSTANCE != nil) { 88 | [PUSH_INSTANCE sendEventWithName:@"message" body:body]; 89 | } 90 | } 91 | 92 | completionHandler(UIBackgroundFetchResultNewData); 93 | 94 | } 95 | 96 | // 获取自定义参数 97 | + (NSDictionary *)getCustom:(NSDictionary *)userInfo { 98 | 99 | NSMutableDictionary *custom = [[NSMutableDictionary alloc] init]; 100 | 101 | for (NSString *key in userInfo) { 102 | // d p aps 这三个是所有通知都带的字段 103 | if (![key isEqual: @"d"] && ![key isEqual: @"p"] && ![key isEqual: @"aps"]) { 104 | custom[key] = userInfo[key]; 105 | } 106 | } 107 | 108 | return custom; 109 | 110 | } 111 | 112 | // 获取推送消息 113 | + (NSMutableDictionary *)getNotification:(NSDictionary *)userInfo { 114 | 115 | NSDictionary *custom = [RNTUmengPush getCustom:userInfo]; 116 | 117 | NSMutableDictionary *resultDict = [[NSMutableDictionary alloc] init]; 118 | resultDict[@"custom"] = custom; 119 | 120 | NSDictionary *apsDict = userInfo[@"aps"]; 121 | 122 | int contentAvailable = 0; 123 | if ([apsDict objectForKey:@"content-available"]) { 124 | contentAvailable = [[NSString stringWithFormat:@"%@", apsDict[@"content-available"]] intValue]; 125 | } 126 | 127 | // 静默推送 128 | if (contentAvailable == 1) { 129 | // alert 不是个对象,而是字符串,对标安卓的 custom 字段也是个字符串 130 | resultDict[@"message"] = apsDict[@"alert"] ?: @""; 131 | } 132 | // 普通推送 133 | else { 134 | NSDictionary *alertDict = apsDict[@"alert"]; 135 | if (alertDict) { 136 | resultDict[@"notification"] = @{ 137 | @"title": alertDict[@"title"] ?: @"", 138 | @"subTitle": alertDict[@"subtitle"] ?: @"", 139 | @"content": alertDict[@"body"] ?: @"" 140 | }; 141 | } 142 | else { 143 | NSString *alertStr = userInfo[@"aps"][@"alert"]; 144 | resultDict[@"notification"] = @{ 145 | @"title": alertStr ?: @"", 146 | @"subTitle": @"", 147 | @"content": @"" 148 | }; 149 | } 150 | } 151 | 152 | return resultDict; 153 | 154 | } 155 | 156 | + (BOOL)requiresMainQueueSetup { 157 | return YES; 158 | } 159 | 160 | - (dispatch_queue_t)methodQueue { 161 | return dispatch_queue_create("com.github.reactnativehero.umengpush", DISPATCH_QUEUE_SERIAL); 162 | } 163 | 164 | - (instancetype)init { 165 | if (self = [super init]) { 166 | if (PUSH_INSTANCE) { 167 | PUSH_INSTANCE = nil; 168 | } 169 | PUSH_INSTANCE = self; 170 | } 171 | return self; 172 | } 173 | 174 | - (void)dealloc { 175 | PUSH_INSTANCE = nil; 176 | } 177 | 178 | - (NSDictionary *)constantsToExport { 179 | return @{ 180 | @"ALIAS_TYPE_SINA": ALIAS_TYPE_SINA, 181 | @"ALIAS_TYPE_TENCENT": ALIAS_TYPE_TENCENT, 182 | @"ALIAS_TYPE_QQ": ALIAS_TYPE_QQ, 183 | @"ALIAS_TYPE_WEIXIN": ALIAS_TYPE_WEIXIN, 184 | @"ALIAS_TYPE_BAIDU": ALIAS_TYPE_BAIDU, 185 | @"ALIAS_TYPE_RENREN": ALIAS_TYPE_RENREN, 186 | @"ALIAS_TYPE_KAIXIN": ALIAS_TYPE_KAIXIN, 187 | @"ALIAS_TYPE_DOUBAN": ALIAS_TYPE_DOUBAN, 188 | @"ALIAS_TYPE_FACEBOOK": ALIAS_TYPE_FACEBOOK, 189 | @"ALIAS_TYPE_TWITTER": ALIAS_TYPE_TWITTER, 190 | }; 191 | } 192 | 193 | - (NSArray *)supportedEvents { 194 | return @[ 195 | @"register", 196 | @"message", 197 | @"localNotification", 198 | @"remoteNotification", 199 | ]; 200 | } 201 | 202 | - (void)userNotificationCenter:(UNUserNotificationCenter *)center willPresentNotification:(UNNotification *)notification withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler API_AVAILABLE(ios(10.0)) { 203 | 204 | NSDictionary *userInfo = notification.request.content.userInfo; 205 | 206 | if ([notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) { 207 | // 应用处于前台时的远程推送 208 | [UMessage didReceiveRemoteNotification:userInfo]; 209 | NSMutableDictionary *body = [RNTUmengPush getNotification:userInfo]; 210 | body[@"presented"] = @YES; 211 | // 如果静默推送带了 alert 字段,则会作为通知展现 212 | [self sendNotificationToJs:body]; 213 | } 214 | else { 215 | // 应用处于前台时的本地推送接受 216 | } 217 | 218 | completionHandler(UNNotificationPresentationOptionSound|UNNotificationPresentationOptionBadge|UNNotificationPresentationOptionAlert); 219 | 220 | } 221 | 222 | - (void)userNotificationCenter:(UNUserNotificationCenter *)center didReceiveNotificationResponse:(UNNotificationResponse *)response withCompletionHandler:(void(^)(void))completionHandler API_AVAILABLE(ios(10.0)) { 223 | 224 | NSDictionary *userInfo = response.notification.request.content.userInfo; 225 | 226 | if ([response.notification.request.trigger isKindOfClass:[UNPushNotificationTrigger class]]) { 227 | // 应用处于后台时的远程推送 228 | [UMessage didReceiveRemoteNotification:userInfo]; 229 | NSMutableDictionary *body = [RNTUmengPush getNotification:userInfo]; 230 | body[@"clicked"] = @YES; 231 | // 如果静默推送带了 alert 字段,则会作为通知展现 232 | [self sendNotificationToJs:body]; 233 | } 234 | else { 235 | // 应用处于后台时的本地推送接受 236 | } 237 | 238 | } 239 | 240 | - (void)sendNotificationToJs:(NSDictionary *)body { 241 | 242 | NSString *eventName = @"remoteNotification"; 243 | 244 | if ([body objectForKey:@"message"]) { 245 | eventName = @"message"; 246 | } 247 | 248 | [self sendEventWithName:eventName body:body]; 249 | 250 | } 251 | 252 | // 初始化 253 | RCT_EXPORT_METHOD(init:(NSDictionary*)options) { 254 | [self setPushSetting:options]; 255 | } 256 | 257 | // 获取 device token 258 | RCT_EXPORT_METHOD(start) { 259 | 260 | // Push 组件基本功能配置 261 | UMessageRegisterEntity *entity = [[UMessageRegisterEntity alloc] init]; 262 | 263 | // type 是对推送的几个参数的选择,可以选择一个或者多个 264 | // 默认是三个全部打开,即:声音,弹窗,角标 265 | entity.types = UMessageAuthorizationOptionBadge|UMessageAuthorizationOptionSound|UMessageAuthorizationOptionAlert; 266 | 267 | if (@available(iOS 10.0, *)) { 268 | [UNUserNotificationCenter currentNotificationCenter].delegate = self; 269 | } 270 | 271 | dispatch_async(dispatch_get_main_queue(), ^{ 272 | 273 | [UMessage registerForRemoteNotificationsWithLaunchOptions:LAUNCH_OPTIONS Entity:entity completionHandler:^(BOOL granted, NSError * _Nullable error) { 274 | 275 | if (!granted) { 276 | [self sendEventWithName:@"register" body:@{ 277 | @"error": @"permissions is not granted." 278 | }]; 279 | } 280 | else if (error != nil) { 281 | [self sendEventWithName:@"register" body:@{ 282 | @"error": error.localizedDescription 283 | }]; 284 | } 285 | 286 | }]; 287 | 288 | }); 289 | 290 | } 291 | 292 | // 获取所有标签 293 | RCT_EXPORT_METHOD(getTags:(RCTPromiseResolveBlock)resolve 294 | reject:(RCTPromiseRejectBlock)reject) { 295 | 296 | [UMessage getTags:^(NSSet * _Nonnull responseTags, NSInteger remain, NSError * _Nonnull error) { 297 | if (error) { 298 | NSString *msg = [self getErrorMessage:error]; 299 | reject([NSString stringWithFormat:@"%ld", (long)error.code], msg, nil); 300 | } 301 | else { 302 | if ([responseTags isKindOfClass:[NSSet class]]) { 303 | NSArray *tags = responseTags.allObjects; 304 | resolve(@{ 305 | @"tags": tags, 306 | }); 307 | return; 308 | } 309 | reject(@"-1", @"error", nil); 310 | } 311 | }]; 312 | 313 | } 314 | 315 | // 添加标签 316 | RCT_EXPORT_METHOD(addTags:(NSArray *)tags 317 | resolve:(RCTPromiseResolveBlock)resolve 318 | reject:(RCTPromiseRejectBlock)reject) { 319 | 320 | [UMessage addTags:tags response:^(id _Nonnull responseObject, NSInteger remain, NSError * _Nonnull error) { 321 | 322 | if (error) { 323 | NSString *msg = [self getErrorMessage:error]; 324 | reject([NSString stringWithFormat:@"%ld", (long)error.code], msg, nil); 325 | } 326 | else { 327 | if ([responseObject isKindOfClass:[NSDictionary class]]) { 328 | NSDictionary *dict = responseObject; 329 | if ([dict[@"success"] isEqualToString:@"ok"]) { 330 | resolve(@{ 331 | @"remain": @(remain), 332 | }); 333 | return; 334 | } 335 | } 336 | reject(@"-1", @"error", nil); 337 | } 338 | 339 | }]; 340 | 341 | } 342 | 343 | // 删除标签 344 | RCT_EXPORT_METHOD(removeTags:(NSArray *)tags 345 | resolve:(RCTPromiseResolveBlock)resolve 346 | reject:(RCTPromiseRejectBlock)reject) { 347 | 348 | [UMessage deleteTags:tags response:^(id _Nonnull responseObject, NSInteger remain, NSError * _Nonnull error) { 349 | 350 | if (error) { 351 | NSString *msg = [self getErrorMessage:error]; 352 | reject([NSString stringWithFormat:@"%ld", (long)error.code], msg, nil); 353 | } 354 | else { 355 | if ([responseObject isKindOfClass:[NSDictionary class]]) { 356 | NSDictionary *dict = responseObject; 357 | if ([dict[@"success"] isEqualToString:@"ok"]) { 358 | resolve(@{ 359 | @"remain": @(remain), 360 | }); 361 | return; 362 | } 363 | } 364 | reject(@"-1", @"error", nil); 365 | } 366 | 367 | }]; 368 | 369 | } 370 | 371 | // 重置别名 372 | RCT_EXPORT_METHOD(setAlias:(NSString *)alias 373 | type:(NSString *)type 374 | resolve:(RCTPromiseResolveBlock)resolve 375 | reject:(RCTPromiseRejectBlock)reject) { 376 | 377 | NSString *innerType = [self getAliasType:type]; 378 | 379 | [UMessage setAlias:alias type:innerType response:^(id _Nonnull responseObject, NSError * _Nonnull error) { 380 | 381 | if (error) { 382 | NSString *msg = [self getErrorMessage:error]; 383 | reject([NSString stringWithFormat:@"%ld", (long)error.code], msg, nil); 384 | } 385 | else { 386 | if ([responseObject isKindOfClass:[NSDictionary class]]) { 387 | NSDictionary *dict = responseObject; 388 | if ([dict[@"success"] isEqualToString:@"ok"]) { 389 | resolve(@{}); 390 | return; 391 | } 392 | } 393 | reject(@"-1", @"error", nil); 394 | } 395 | 396 | }]; 397 | 398 | } 399 | 400 | // 绑定别名 401 | RCT_EXPORT_METHOD(addAlias:(NSString *)alias 402 | type:(NSString *)type 403 | resolve:(RCTPromiseResolveBlock)resolve 404 | reject:(RCTPromiseRejectBlock)reject) { 405 | 406 | NSString *innerType = [self getAliasType:type]; 407 | 408 | [UMessage addAlias:alias type:innerType response:^(id _Nonnull responseObject, NSError * _Nonnull error) { 409 | 410 | if (error) { 411 | NSString *msg = [self getErrorMessage:error]; 412 | reject([NSString stringWithFormat:@"%ld", (long)error.code], msg, nil); 413 | } 414 | else { 415 | if ([responseObject isKindOfClass:[NSDictionary class]]) { 416 | NSDictionary *dict = responseObject; 417 | if ([dict[@"success"] isEqualToString:@"ok"]) { 418 | resolve(@{}); 419 | return; 420 | } 421 | } 422 | reject(@"-1", @"error", nil); 423 | } 424 | 425 | }]; 426 | 427 | } 428 | 429 | // 移除别名 430 | RCT_EXPORT_METHOD(removeAlias:(NSString *)alias 431 | type:(NSString *)type 432 | resolve:(RCTPromiseResolveBlock)resolve 433 | reject:(RCTPromiseRejectBlock)reject) { 434 | 435 | NSString *innerType = [self getAliasType:type]; 436 | 437 | [UMessage removeAlias:alias type:innerType response:^(id _Nonnull responseObject, NSError * _Nonnull error) { 438 | 439 | if (error) { 440 | NSString *msg = [self getErrorMessage:error]; 441 | reject([NSString stringWithFormat:@"%ld", (long)error.code], msg, nil); 442 | } 443 | else { 444 | if ([responseObject isKindOfClass:[NSDictionary class]]) { 445 | NSDictionary *dict = responseObject; 446 | if ([dict[@"success"] isEqualToString:@"ok"]) { 447 | resolve(@{}); 448 | return; 449 | } 450 | } 451 | reject(@"-1", @"error", nil); 452 | } 453 | 454 | }]; 455 | 456 | } 457 | 458 | // 高级设置 459 | RCT_EXPORT_METHOD(setAdvanced:(NSDictionary*)options) { 460 | 461 | [self setPushSetting:options]; 462 | 463 | } 464 | 465 | - (void)setPushSetting:(NSDictionary *)options { 466 | 467 | // 当应用在前台运行收到 Push 时是否弹出 Alert 框 468 | if ([options objectForKey:@"autoAlert"]) { 469 | [UMessage setAutoAlert:[RCTConvert BOOL:options[@"autoAlert"]]]; 470 | } 471 | 472 | // 设置是否允许 SDK 自动清空角标,默认自动角标清零 473 | if ([options objectForKey:@"badgeClear"]) { 474 | [UMessage setBadgeClear:[RCTConvert BOOL:options[@"badgeClear"]]]; 475 | } 476 | 477 | } 478 | 479 | - (NSString *)getAliasType:(NSString *)type { 480 | 481 | // 默认用外面传入的 482 | NSString *aliasType = type; 483 | 484 | // 新浪微博 485 | if ([type isEqualToString:ALIAS_TYPE_SINA]) { 486 | aliasType = kUMessageAliasTypeSina; 487 | } 488 | // 腾讯微博 489 | else if ([type isEqualToString:ALIAS_TYPE_TENCENT]) { 490 | aliasType = kUMessageAliasTypeTencent; 491 | } 492 | // QQ 493 | else if ([type isEqualToString:ALIAS_TYPE_QQ]) { 494 | aliasType = kUMessageAliasTypeQQ; 495 | } 496 | // 微信 497 | else if ([type isEqualToString:ALIAS_TYPE_WEIXIN]) { 498 | aliasType = kUMessageAliasTypeWeiXin; 499 | } 500 | // 百度 501 | else if ([type isEqualToString:ALIAS_TYPE_BAIDU]) { 502 | aliasType = kUMessageAliasTypeBaidu; 503 | } 504 | // 人人网 505 | else if ([type isEqualToString:ALIAS_TYPE_RENREN]) { 506 | aliasType = kUMessageAliasTypeRenRen; 507 | } 508 | // 开心网 509 | else if ([type isEqualToString:ALIAS_TYPE_KAIXIN]) { 510 | aliasType = kUMessageAliasTypeKaixin; 511 | } 512 | // 豆瓣 513 | else if ([type isEqualToString:ALIAS_TYPE_DOUBAN]) { 514 | aliasType = kUMessageAliasTypeDouban; 515 | } 516 | // facebook 517 | else if ([type isEqualToString:ALIAS_TYPE_FACEBOOK]) { 518 | aliasType = kUMessageAliasTypeFacebook; 519 | } 520 | // twitter 521 | else if ([type isEqualToString:ALIAS_TYPE_TWITTER]) { 522 | aliasType = kUMessageAliasTypeTwitter; 523 | } 524 | 525 | return aliasType; 526 | 527 | } 528 | 529 | - (NSString *)getErrorMessage:(NSError *)error { 530 | switch (error.code) { 531 | case kUMessageErrorUnknown: 532 | return @"未知错误"; 533 | break; 534 | case kUMessageErrorResponseErr: 535 | return @"响应出错"; 536 | break; 537 | case kUMessageErrorOperateErr: 538 | return @"操作失败"; 539 | break; 540 | case kUMessageErrorParamErr: 541 | return @"参数非法"; 542 | break; 543 | case kUMessageErrorDependsErr: 544 | return @"条件不足(如:还未获取device_token,添加tag是不成功的)"; 545 | break; 546 | case kUMessageErrorServerSetErr: 547 | return @"服务器限定操作"; 548 | break; 549 | default: 550 | break; 551 | } 552 | return error.localizedDescription; 553 | } 554 | 555 | @end 556 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@react-native-hero/umeng-push", 3 | "version": "0.4.1", 4 | "description": "react native umeng push", 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/react-native-hero/umeng-push.git" 12 | }, 13 | "keywords": [ 14 | "react", 15 | "native", 16 | "umeng push" 17 | ], 18 | "author": "musicode", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/react-native-hero/umeng-push/issues" 22 | }, 23 | "homepage": "https://github.com/react-native-hero/umeng-push#readme" 24 | } --------------------------------------------------------------------------------