├── .eslintrc.js ├── .gitattributes ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── android ├── build.gradle ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── github │ └── amarcruz │ └── rnshortcutbadge │ ├── RNShortcutBadgeModule.java │ └── RNShortcutBadgePackage.java ├── badge.android.js ├── badge.ios.js ├── index.d.ts ├── index.js └── package.json /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | parserOptions: { 4 | ecmaVersion: 2016, 5 | sourceType: 'module', 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Misc 2 | # 3 | **/*.log 4 | **/*.bak 5 | 6 | # OSX & Xcode 7 | # 8 | .DS_Store 9 | build/ 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata 19 | *.xccheckout 20 | *.moved-aside 21 | DerivedData 22 | *.hmap 23 | *.ipa 24 | *.xcuserstate 25 | project.xcworkspace 26 | 27 | # Android/IntelliJ 28 | # 29 | build/ 30 | .idea 31 | .gradle 32 | local.properties 33 | *.iml 34 | 35 | # node.js 36 | # 37 | node_modules/ 38 | npm-debug.log 39 | yarn-error.log 40 | 41 | # BUCK 42 | buck-out/ 43 | \.buckd/ 44 | *.keystore 45 | 46 | # fastlane 47 | # 48 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 49 | # screenshots whenever they are needed. 50 | # For more information about the recommended setup visit: 51 | # https://docs.fastlane.tools/best-practices/source-control/ 52 | 53 | */fastlane/report.xml 54 | */fastlane/Preview.html 55 | */fastlane/screenshots 56 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ### v1.0.0-beta.2 2 | Cahnges to the API, new method `requestPermission` 3 | Support iOS 4 | 5 | ### v1.0.0-beta.1 6 | First release 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Alberto Martínez 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-native-shortcut-badge 2 | 3 | Badge for the shortcut icon in your React Native App (Android & iOS). 4 | 5 | In my country (México), software developers are poorly paid, so I have had to look for another job to earn a living and I cannot dedicate more time to maintaining this and other repositories that over the years have never generated any money for me. If anyone is interested in maintaining this repository, I'd be happy to transfer it to them, along with the associated npm package. | 6 | :---: | 7 | En mi país (México), los desarrolladores de software somos pésimamente pagados, por lo que he tenido que buscar otro trabajo para ganarme la vida y no puedo dedicar más tiempo a mantener éste y otros repositorios que a través de los años nunca me generaron dinero. Si a alguien le interesa dar mantenimiento a este repositorio, con gusto se lo transferiré, así como el paquete de npm asociado. | 8 | 9 | iOS have native support for badges and this library uses the React Native [PushNotificationIOS](https://facebook.github.io/react-native/docs/pushnotificationios) module. 10 | 11 | In Android, this is a wrapper for the amazing [ShortcutBadger](https://github.com/leolin310148/ShortcutBadger) library. 12 | 13 | ShortcutBadger is included in many notification libraries for React Native, rn-shortcut-badge offers an API and updates that do not depend on any of these libraries, although it can present the same drawbacks (before Android 8 there is no native support for badges, in Android 8 and later the badge is defined by the notifications). 14 | 15 | **NOTE**: This is Work In Progress, not fully tested. 16 | 17 | ## Setup 18 | 19 | ```bash 20 | $ yarn add react-native-shortcut-badge 21 | $ react-native link react-native-shortcut-badge 22 | ``` 23 | 24 | If you are using [Proguard](https://stuff.mit.edu/afs/sipb/project/android/sdk/android-sdk-linux/tools/proguard/docs/), add this to your android/app/proguard-rules.pro 25 | 26 | See https://github.com/leolin310148/ShortcutBadger/issues/46 27 | 28 | ``` 29 | -keep class me.leolin.shortcutbadger.impl.** { (...); } 30 | ``` 31 | 32 | ## Usage 33 | 34 | ```js 35 | import ShortcutBadge from 'react-native-shortcut-badge'; 36 | 37 | ShortcutBadge.getCount((count) => { 38 | ShortcutBadge.setCount(count + 1); 39 | }); 40 | ``` 41 | 42 | ### Properties 43 | 44 | Property | Type | Description 45 | -------- | ---- | ----------- 46 | launcher | `string` or `null` | Name of the current launcher "home", or `null` if the launcher could not be detected. 47 | supported | `boolean` | Does ShortcutBadge have support for the current launcher/device? 48 | 49 | **NOTE:** The counter remains in the storage of the device even if the launcher is not supported. 50 | 51 | ### Methods 52 | 53 | - `setCount(count: number) => Promise` 54 | 55 | Set the counter and update the badge. 56 | 57 | Returns `true` if the operation succeeds. 58 | 59 | - `getCount() => Promise` 60 | 61 | Returns the badge counter. 62 | 63 | - `requestPermission(): Promise` 64 | 65 | Request permission for Badge, mainly for iOS. 66 | 67 | Return `true` if permission is granted. 68 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | /* Top-level build file where you can add configuration options common to all sub-projects/modules. */ 2 | import groovy.json.JsonSlurper 3 | 4 | /* dynamically retrieve version from package.json */ 5 | def computeVersionName() { 6 | def slurper = new JsonSlurper() 7 | def json = slurper.parse(file('../package.json'), 'utf-8') 8 | return json.version 9 | } 10 | 11 | def DEF_BUILD_TOOLS_VERSION = '27.0.3' 12 | def DEF_MIN_SDK_VERSION = 16 13 | def DEF_COMPILE_SDK_VERSION = 27 14 | def DEF_TARGET_SDK_VERSION = 27 15 | 16 | def _buildToolsVersion = rootProject.hasProperty('buildToolsVersion') ? rootProject.buildToolsVersion : DEF_BUILD_TOOLS_VERSION 17 | def _minSdkVersion = rootProject.hasProperty('minSdkVersion') ? rootProject.minSdkVersion : DEF_MIN_SDK_VERSION 18 | def _compileSdkVersion = rootProject.hasProperty('compileSdkVersion') ? rootProject.compileSdkVersion : DEF_COMPILE_SDK_VERSION 19 | def _targetSdkVersion = rootProject.hasProperty('targetSdkVersion') ? rootProject.targetSdkVersion : DEF_TARGET_SDK_VERSION 20 | 21 | buildscript { 22 | repositories { 23 | google() 24 | jcenter() 25 | } 26 | dependencies { 27 | classpath 'com.android.tools.build:gradle:3.1.3' 28 | } 29 | } 30 | 31 | apply plugin: 'com.android.library' 32 | 33 | android { 34 | compileSdkVersion _compileSdkVersion 35 | buildToolsVersion _buildToolsVersion 36 | 37 | defaultConfig { 38 | minSdkVersion _minSdkVersion 39 | targetSdkVersion _targetSdkVersion 40 | versionCode 1 41 | versionName computeVersionName() 42 | } 43 | lintOptions { 44 | abortOnError false 45 | } 46 | } 47 | 48 | repositories { 49 | mavenCentral() 50 | google() 51 | jcenter() 52 | maven { 53 | url "$rootDir/../node_modules/react-native/android" 54 | name 'React Native (local)' 55 | } 56 | } 57 | 58 | dependencies { 59 | compileOnly 'com.facebook.react:react-native:+' 60 | implementation 'me.leolin:ShortcutBadger:1.1.+@aar' 61 | } 62 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aMarCruz/react-native-shortcut-badge/374e52a97f1490b4d0fe4176fbebdf5e92a1324f/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Feb 14 14:57:18 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 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /android/src/main/java/com/github/amarcruz/rnshortcutbadge/RNShortcutBadgeModule.java: -------------------------------------------------------------------------------- 1 | package com.github.amarcruz.rnshortcutbadge; 2 | 3 | import android.app.Notification; 4 | import android.app.NotificationManager; 5 | import android.content.Context; 6 | import android.content.Intent; 7 | import android.content.pm.PackageManager; 8 | import android.content.pm.ResolveInfo; 9 | import android.os.Build; 10 | import android.content.SharedPreferences; 11 | import android.util.Log; 12 | 13 | import com.facebook.react.bridge.Promise; 14 | import com.facebook.react.bridge.ReactApplicationContext; 15 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 16 | import com.facebook.react.bridge.ReactMethod; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | import me.leolin.shortcutbadger.ShortcutBadger; 22 | 23 | public class RNShortcutBadgeModule extends ReactContextBaseJavaModule { 24 | 25 | private static final String TAG = "RNShortcutBadge"; 26 | private static final String BADGE_FILE = "BadgeCountFile"; 27 | private static final String BADGE_KEY = "BadgeCount"; 28 | 29 | private NotificationManager mNotificationManager; 30 | private static int mNotificationId = 0; 31 | 32 | private ReactApplicationContext mReactContext; 33 | private SharedPreferences mPrefs; 34 | private boolean mSupported = false; 35 | private boolean mIsXiaomi = false; 36 | 37 | RNShortcutBadgeModule(ReactApplicationContext reactContext) { 38 | super(reactContext); 39 | 40 | mReactContext = reactContext; 41 | mPrefs = reactContext.getSharedPreferences(BADGE_FILE, Context.MODE_PRIVATE); 42 | } 43 | 44 | @Override 45 | public String getName() { 46 | return TAG; 47 | } 48 | 49 | 50 | @Override 51 | public Map getConstants() { 52 | final HashMap constants = new HashMap<>(); 53 | boolean supported = false; 54 | 55 | try { 56 | Context context = getCurrentActivity(); 57 | if (context == null) { 58 | context = mReactContext.getApplicationContext(); 59 | } 60 | int counter = mPrefs.getInt(BADGE_KEY, 0); 61 | supported = ShortcutBadger.applyCount(context, counter); 62 | 63 | if (!supported && Build.MANUFACTURER.equalsIgnoreCase("Xiaomi")) { 64 | supported = true; 65 | mIsXiaomi = true; 66 | mNotificationManager = (NotificationManager) 67 | mReactContext.getSystemService(Context.NOTIFICATION_SERVICE); 68 | } 69 | } catch (Exception e) { 70 | Log.e(TAG, "Cannot initialize ShortcutBadger", e); 71 | } 72 | 73 | mSupported = supported; 74 | 75 | constants.put("launcher", getLauncherName()); 76 | constants.put("supported", supported); 77 | 78 | return constants; 79 | } 80 | 81 | /** 82 | * Get the current position. This can return almost immediately if the location is cached or 83 | * request an update, which might take a while. 84 | */ 85 | @ReactMethod 86 | public void setCount(final int count, final Promise promise) { 87 | try { 88 | // Save the counter unconditionally 89 | mPrefs.edit().putInt(BADGE_KEY, count).apply(); 90 | 91 | if (!mSupported) { 92 | promise.resolve(false); 93 | return; 94 | } 95 | 96 | Context context = getCurrentActivity(); 97 | if (context == null) { 98 | context = mReactContext.getApplicationContext(); 99 | } 100 | boolean ok; 101 | 102 | if (mIsXiaomi) { 103 | ok = setXiaomiBadge(context, count); 104 | } else { 105 | ok = ShortcutBadger.applyCount(context, count); 106 | } 107 | 108 | if (ok) { 109 | promise.resolve(true); 110 | } else { 111 | Log.d(TAG, "Cannot set badge."); 112 | promise.resolve(false); 113 | } 114 | } catch (Exception ex) { 115 | Log.e(TAG, "Error setting the badge", ex); 116 | promise.reject(ex); 117 | } 118 | } 119 | 120 | /** 121 | * Get the badge from the storage. 122 | */ 123 | @ReactMethod 124 | public void getCount(final Promise promise) { 125 | promise.resolve(mPrefs.getInt(BADGE_KEY, 0)); 126 | } 127 | 128 | /** 129 | * Dummy method to request permissions in Android. 130 | */ 131 | @ReactMethod 132 | public void requestPermission(final Promise promise) { 133 | promise.resolve(true); 134 | } 135 | 136 | /** 137 | * Support Xiaomi devices. 138 | */ 139 | private boolean setXiaomiBadge(final Context context, final int count) { 140 | 141 | mNotificationManager.cancel(mNotificationId); 142 | mNotificationId++; 143 | 144 | Notification.Builder builder = new Notification.Builder(context) 145 | .setContentTitle("") 146 | .setContentText(""); 147 | //.setSmallIcon(R.drawable.ic_launcher); 148 | Notification notification = builder.build(); 149 | ShortcutBadger.applyNotification(context, notification, count); 150 | mNotificationManager.notify(mNotificationId, notification); 151 | 152 | return true; 153 | } 154 | 155 | /** 156 | * Find the package name of the current launcher 157 | */ 158 | private String getLauncherName() { 159 | String name = null; 160 | 161 | try { 162 | final Intent intent = new Intent(Intent.ACTION_MAIN); 163 | intent.addCategory(Intent.CATEGORY_HOME); 164 | final ResolveInfo resolveInfo = mReactContext.getPackageManager() 165 | .resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY); 166 | name = resolveInfo.activityInfo.packageName; 167 | } catch (Exception ignore) { 168 | } 169 | 170 | return name; 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /android/src/main/java/com/github/amarcruz/rnshortcutbadge/RNShortcutBadgePackage.java: -------------------------------------------------------------------------------- 1 | package com.github.amarcruz.rnshortcutbadge; 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 | public class RNShortcutBadgePackage implements ReactPackage { 14 | 15 | public RNShortcutBadgePackage() { 16 | } 17 | 18 | @Override 19 | public List createViewManagers(ReactApplicationContext reactContext) { 20 | return Collections.emptyList(); 21 | } 22 | 23 | @Override 24 | public List createNativeModules( 25 | ReactApplicationContext reactContext) { 26 | List modules = new ArrayList<>(); 27 | modules.add(new RNShortcutBadgeModule(reactContext)); 28 | return modules; 29 | } 30 | 31 | /** 32 | * Make happy Studio. 33 | */ 34 | public List> createJSModules() { 35 | return null; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /badge.android.js: -------------------------------------------------------------------------------- 1 | import { NativeModules } from 'react-native' 2 | export default NativeModules.RNShortcutBadge 3 | -------------------------------------------------------------------------------- /badge.ios.js: -------------------------------------------------------------------------------- 1 | import { PushNotificationIOS } from 'react-native' 2 | 3 | export default { 4 | launcher: null, 5 | supported: true, 6 | 7 | setCount (count) { 8 | // check permission here? 9 | PushNotificationIOS.setApplicationIconBadgeNumber(count) 10 | return Promise.resolve(true) 11 | }, 12 | 13 | getCount () { 14 | return new Promise((resolve) => { 15 | PushNotificationIOS.getApplicationIconBadgeNumber(resolve) 16 | }) 17 | }, 18 | 19 | requestPermission () { 20 | return PushNotificationIOS.requestPermissions({ badge: true }) 21 | .then((permissions) => permissions.badge) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /index.d.ts: -------------------------------------------------------------------------------- 1 | declare module "react-native-shortcut-badge" { 2 | 3 | interface ShortcutBadgeStatic { 4 | /** 5 | * Launcher home name, null if unknown 6 | */ 7 | readonly launcher: string | null; 8 | 9 | /** 10 | * If the launcher supported? 11 | */ 12 | readonly supported: boolean; 13 | 14 | /** 15 | * Set the badge count. Use 0 to remove it. 16 | */ 17 | setCount(count: number): Promise; 18 | 19 | /** 20 | * Get the badge count. 21 | */ 22 | getCount(): Promise; 23 | 24 | /** 25 | * Reads the counter from the storage 26 | */ 27 | requestPermission(): Promise; 28 | } 29 | 30 | const ShortcutBadge: ShortcutBadgeStatic; 31 | 32 | export default ShortcutBadge; 33 | } 34 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | import ShortcutBadge from './badge' 2 | export default ShortcutBadge 3 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-shortcut-badge", 3 | "description": "Badge for the shortcut icon in your React Native Android Apps", 4 | "version": "0.1.0-beta.2", 5 | "main": "index.js", 6 | "license": "MIT", 7 | "scripts": { 8 | "test": "echo No test!" 9 | }, 10 | "author": { 11 | "name": "aMarCruz", 12 | "email": "amarcruzbox-git@yahoo.com", 13 | "url": "https://github.com/aMarCruz" 14 | }, 15 | "homepage": "https://github.com/aMarCruz/react-native-shortcut-badge", 16 | "repository": { 17 | "type": "git", 18 | "url": "git+https://github.com/aMarCruz/react-native-shortcut-badge.git" 19 | }, 20 | "bugs": { 21 | "url": "https://github.com/aMarCruz/react-native-shortcut-badge/issues" 22 | }, 23 | "peerDependencies": { 24 | "react-native": ">=0.50.0" 25 | }, 26 | "dependencies": {} 27 | } 28 | --------------------------------------------------------------------------------