args.object;
19 |
20 | /*
21 | A page’s bindingContext is an object that should be used to perform
22 | data binding between XML markup and TypeScript code. Properties
23 | on the bindingContext can be accessed using the {{ }} syntax in XML.
24 | In this example, the {{ message }} and {{ onTap }} bindings are resolved
25 | against the object returned by createViewModel().
26 |
27 | You can learn more about data binding in NativeScript at
28 | https://docs.nativescript.org/core-concepts/data-binding.
29 | */
30 | page.bindingContext = new PushTestModel();
31 | }
--------------------------------------------------------------------------------
/demo/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "demo",
3 | "version": "1.1.0",
4 | "nativescript": {
5 | "id": "org.nativescript.ppTest",
6 | "tns-ios": {
7 | "version": "4.2.0"
8 | },
9 | "tns-android": {
10 | "version": "4.2.0"
11 | }
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "https://github.com/NativeScript/push-plugin.git"
16 | },
17 | "license": "Apache-2.0",
18 | "dependencies": {
19 | "nativescript-push-notifications": "../src",
20 | "nativescript-theme-core": "^1.0.4",
21 | "tns-core-modules": "~4.2.0"
22 | },
23 | "devDependencies": {
24 | "babel-traverse": "6.26.0",
25 | "babel-types": "6.26.0",
26 | "babylon": "6.18.0",
27 | "lazy": "1.0.11",
28 | "nativescript-css-loader": "~0.26.0",
29 | "nativescript-dev-typescript": "~0.7.0",
30 | "nativescript-dev-webpack": "~0.15.0",
31 | "tslint": "^5.8.0",
32 | "typescript": "~2.8.1"
33 | },
34 | "scripts": {
35 | "build.plugin": "cd ../src && npm run build",
36 | "ci.tslint": "npm run build.plugin && npm i && tslint --config ../tslint.json 'app/**/*.ts' --exclude '**/node_modules/**'",
37 | "ci.android.build": "npm run build.plugin && tns build android",
38 | "ci.ios.build": "npm run build.plugin && tns build ios",
39 | "generate-android-snapshot": "generate-android-snapshot --targetArchs arm,arm64,ia32 --install"
40 | }
41 | }
--------------------------------------------------------------------------------
/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "class-name": true,
4 | "comment-format": [
5 | true,
6 | "check-space"
7 | ],
8 | "indent": [
9 | true,
10 | "spaces"
11 | ],
12 | "no-duplicate-variable": true,
13 | "no-eval": true,
14 | "no-internal-module": true,
15 | "no-trailing-whitespace": true,
16 | "no-var-keyword": true,
17 | "one-line": [
18 | true,
19 | "check-open-brace",
20 | "check-whitespace"
21 | ],
22 | "quotemark": [
23 | false,
24 | "double"
25 | ],
26 | "semicolon": [
27 | true,
28 | "always"
29 | ],
30 | "triple-equals": [
31 | true,
32 | "allow-null-check"
33 | ],
34 | "typedef-whitespace": [
35 | true,
36 | {
37 | "call-signature": "nospace",
38 | "index-signature": "nospace",
39 | "parameter": "nospace",
40 | "property-declaration": "nospace",
41 | "variable-declaration": "nospace"
42 | }
43 | ],
44 | "variable-name": [
45 | true,
46 | "ban-keywords"
47 | ],
48 | "whitespace": [
49 | true,
50 | "check-branch",
51 | "check-decl",
52 | "check-operator",
53 | "check-separator",
54 | "check-type"
55 | ]
56 | }
57 | }
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
27 |
28 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/demo/app/app.ts:
--------------------------------------------------------------------------------
1 | /*
2 | In NativeScript, the app.ts file is the entry point to your application.
3 | You can use this file to perform app-level initialization, but the primary
4 | purpose of the file is to pass control to the app’s first module.
5 | */
6 |
7 | import * as app from 'tns-core-modules/application';
8 |
9 | // ANDROID ONLY!
10 | // this event is used to handle notifications that have been received while the app is not in the foreground
11 | // in iOS the system invokes the notificationCallbackIOS method automatically when a notification is tapped
12 | app.on(app.resumeEvent, function(args) {
13 | if (args.android) {
14 | const act = args.android;
15 | const intent = act.getIntent();
16 | const extras = intent.getExtras();
17 | console.log("Resuming activity");
18 | if (extras) {
19 | console.log("If your notification has data (key: value) pairs, they will be listed here:");
20 | const keys = extras.keySet();
21 | const iterator = keys.iterator();
22 | while (iterator.hasNext()) {
23 | const key = iterator.next();
24 | console.log(key + ": " + extras.get(key).toString());
25 | // clear the used keys in order to avoid getting them back
26 | // when manually switching the application between background and foreground
27 | intent.removeExtra(key);
28 | }
29 | }
30 | }
31 | });
32 |
33 | app.start({ moduleName: 'main-page' });
34 | /*
35 | Do not place any code after the application has been started as it will not
36 | be executed on iOS.
37 | */
38 |
--------------------------------------------------------------------------------
/src/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "nativescript-push-notifications",
3 | "version": "1.1.6",
4 | "description": "Receive push notifications in your NativeScript app on Android and iOS.",
5 | "main": "push-plugin",
6 | "typings": "index.d.ts",
7 | "author": "NativeScript Team",
8 | "bugs": {
9 | "url": "https://github.com/NativeScript/push-plugin/issues"
10 | },
11 | "repository": {
12 | "type": "git",
13 | "url": "https://github.com/NativeScript/push-plugin.git"
14 | },
15 | "license": "Apache-2.0",
16 | "homepage": "https://github.com/NativeScript/push-plugin",
17 | "readmeFilename": "README.md",
18 | "nativescript": {
19 | "platforms": {
20 | "ios": "3.0.0",
21 | "android": "3.0.0"
22 | },
23 | "hooks": [
24 | {
25 | "type": "before-prepare",
26 | "script": "hooks/before-prepare.js",
27 | "inject": true
28 | }
29 | ]
30 | },
31 | "scripts": {
32 | "build": "npm i && tsc",
33 | "tslint": "cd .. && tslint \"**/*.ts\" --config tslint.json --exclude \"**/node_modules/**\"",
34 | "clean": "cd ../demo && rimraf hooks node_modules platforms && cd ../src && rimraf node_modules",
35 | "ci.tslint": "npm i && tslint '**/*.ts' --config ../tslint.json --exclude '**/node_modules/**'",
36 | "postinstall": "node scripts/postinstall.js",
37 | "preuninstall": "node scripts/preuninstall.js"
38 | },
39 | "dependencies": {
40 | "nativescript-hook": "~0.2.4"
41 | },
42 | "devDependencies": {
43 | "rimraf": "^2.6.2",
44 | "tns-core-modules": "^3.3.0",
45 | "tns-platform-declarations": "^3.3.0",
46 | "tslint": "^5.8.0",
47 | "typescript": "^2.7.2"
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleDisplayName
8 | ${PRODUCT_NAME}
9 | CFBundleExecutable
10 | ${EXECUTABLE_NAME}
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | ${PRODUCT_NAME}
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1.0
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIRequiresFullScreen
28 |
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UISupportedInterfaceOrientations
34 |
35 | UIInterfaceOrientationPortrait
36 | UIInterfaceOrientationLandscapeLeft
37 | UIInterfaceOrientationLandscapeRight
38 |
39 | UISupportedInterfaceOrientations~ipad
40 |
41 | UIInterfaceOrientationPortrait
42 | UIInterfaceOrientationPortraitUpsideDown
43 | UIInterfaceOrientationLandscapeLeft
44 | UIInterfaceOrientationLandscapeRight
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/native-src/android/app/src/main/java/com/telerik/pushplugin/UnregisterTokenThread.java:
--------------------------------------------------------------------------------
1 | package com.telerik.pushplugin;
2 |
3 | import android.util.Log;
4 | import com.google.firebase.iid.FirebaseInstanceId;
5 |
6 | import java.io.IOException;
7 |
8 | /**
9 | * Responsible for unregister device from GCM service functionality.
10 | * By design, this must happen in async way in a Thread.
11 | */
12 | public class UnregisterTokenThread extends Thread {
13 | private static final String TAG = "UnregisterTokenThread";
14 |
15 | private final String projectId;
16 | private final PushPluginListener callbacks;
17 |
18 | public UnregisterTokenThread(String projectId, PushPluginListener callbacks) {
19 | this.projectId = projectId;
20 | this.callbacks = callbacks;
21 | }
22 |
23 | @Override
24 | public void run() {
25 | try {
26 | deleteTokenFromGCM();
27 | } catch (IOException e) {
28 | e.printStackTrace();
29 | }
30 | }
31 |
32 | private void deleteTokenFromGCM() throws IOException {
33 | try {
34 | FirebaseInstanceId.getInstance().deleteToken(this.projectId, "FCM");
35 | } catch (IOException e) {
36 | if (callbacks != null) {
37 | callbacks.error("Invalid project ID.");
38 | }
39 | return;
40 | }
41 |
42 | Log.d(TAG, "Token deleted!");
43 |
44 | if(callbacks != null) {
45 | callbacks.success("Device unregistered!");
46 | }
47 |
48 | // TODO: Wrap the whole callback.
49 | PushPlugin.isActive = false;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/native-src/android/app/src/main/java/com/telerik/pushplugin/ObtainTokenThread.java:
--------------------------------------------------------------------------------
1 | package com.telerik.pushplugin;
2 |
3 | import android.util.Log;
4 | import com.google.firebase.iid.FirebaseInstanceId;
5 |
6 | import java.io.IOException;
7 |
8 | /**
9 | * Responsible for obtaining a Token from the GCM service.
10 | * By design, this must happen in async way in a Thread.
11 | */
12 | public class ObtainTokenThread extends Thread {
13 | private static final String TAG = "ObtainTokenThread";
14 | private final PushPluginListener callbacks;
15 |
16 | private String token;
17 | private final String projectId;
18 |
19 | public ObtainTokenThread(String projectId, PushPluginListener callbacks) {
20 | this.projectId = projectId;
21 | this.callbacks = callbacks;
22 | }
23 |
24 | @Override
25 | public void run() {
26 | try {
27 | this.token = getTokenFromGCM();
28 | } catch (IOException e) {
29 | this.callbacks.error("Error while retrieving a token: " + e.getMessage());
30 | e.printStackTrace();
31 | }
32 | }
33 |
34 | private String getTokenFromGCM() throws IOException {
35 | this.token = FirebaseInstanceId.getInstance().getToken(this.projectId, "FCM");
36 |
37 | if(this.callbacks != null) {
38 | Log.d(TAG, "Calling listener callback with token: " + this.token);
39 | this.callbacks.success(this.token);
40 | } else {
41 | Log.d(TAG, "Token call returned, but no callback provided.");
42 | }
43 |
44 | // TODO: Wrap the whole callback.
45 | PushPlugin.isActive = true;
46 | return this.token;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/native-src/ios/PushPlugin.xcodeproj/project.xcworkspace/xcshareddata/PushPlugin.xccheckout:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDESourceControlProjectFavoriteDictionaryKey
6 |
7 | IDESourceControlProjectIdentifier
8 | BC8B754B-9C8D-47D0-ABA9-1598CAAC06C3
9 | IDESourceControlProjectName
10 | PushPlugin
11 | IDESourceControlProjectOriginsDictionary
12 |
13 | E8C553F8FBDE6C62B10352AA36517C14BC12A66F
14 | https://github.com/telerik/push-plugin-ios.git
15 |
16 | IDESourceControlProjectPath
17 | PushPlugin.xcodeproj
18 | IDESourceControlProjectRelativeInstallPathDictionary
19 |
20 | E8C553F8FBDE6C62B10352AA36517C14BC12A66F
21 | ../..
22 |
23 | IDESourceControlProjectURL
24 | https://github.com/telerik/push-plugin-ios.git
25 | IDESourceControlProjectVersion
26 | 111
27 | IDESourceControlProjectWCCIdentifier
28 | E8C553F8FBDE6C62B10352AA36517C14BC12A66F
29 | IDESourceControlProjectWCConfigurations
30 |
31 |
32 | IDESourceControlRepositoryExtensionIdentifierKey
33 | public.vcs.git
34 | IDESourceControlWCCIdentifierKey
35 | E8C553F8FBDE6C62B10352AA36517C14BC12A66F
36 | IDESourceControlWCCName
37 | push-plugin-ios
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/Android/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
18 |
19 |
21 |
22 |
23 |
31 |
32 |
34 |
35 |
36 |
42 |
43 |
45 |
46 |
--------------------------------------------------------------------------------
/demo/app/main-page.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
12 |
13 |
14 |
15 |
16 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/publish/pack.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -e
3 | set -o pipefail
4 |
5 | CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
6 | SOURCE_DIR="$CURRENT_DIR/../src"
7 | TO_SOURCE_DIR="$CURRENT_DIR/src"
8 | PACK_DIR="$CURRENT_DIR/package"
9 | ROOT_DIR="$CURRENT_DIR/.."
10 | PUBLISH=--publish
11 |
12 | # pass native android or ios as arguments to skip native building
13 | ARGS={$1""}
14 |
15 | install(){
16 | cd $CURRENT_DIR
17 | npm i
18 | }
19 |
20 | pack() {
21 | echo 'Clearing /src and /package...'
22 | node_modules/.bin/rimraf "$TO_SOURCE_DIR"
23 | node_modules/.bin/rimraf "$PACK_DIR"
24 |
25 | cd $SOURCE_DIR
26 | npm i
27 | cd $CURRENT_DIR
28 |
29 | if [[ $ARGS != *"native"* ]]; then
30 |
31 | if [ $ARGS != *"android"* ]; then
32 | # compile native android
33 | echo 'Building native android...'
34 | ./build-android.sh
35 | else
36 | echo 'Building native android was skipped...'
37 | fi
38 |
39 | if [ $ARGS != *"ios"* ]; then
40 | # compile native ios
41 | echo 'Building native ios...'
42 | ./build-ios.sh
43 | else
44 | echo 'Building native ios was skipped...'
45 | fi
46 | else
47 | echo "Native build was skipped, using existing native binaries..."
48 | fi
49 |
50 | # copy src
51 | echo 'Copying src...'
52 | node_modules/.bin/ncp "$SOURCE_DIR" "$TO_SOURCE_DIR"
53 |
54 | # copy LICENSE to src
55 | echo 'Copying README & LICENSE to /src...'
56 | node_modules/.bin/ncp "$ROOT_DIR"/LICENSE "$TO_SOURCE_DIR"/LICENSE
57 | node_modules/.bin/ncp "$ROOT_DIR"/README.md "$TO_SOURCE_DIR"/README.md
58 |
59 | # compile package and copy files required by npm
60 | echo 'Building /src...'
61 | cd "$TO_SOURCE_DIR"
62 | node_modules/.bin/tsc
63 | cd ..
64 |
65 | echo 'Creating package...'
66 | # create package dir
67 | mkdir "$PACK_DIR"
68 |
69 | # create the package
70 | cd "$PACK_DIR"
71 | npm pack "$TO_SOURCE_DIR"
72 | echo "Package created in $PACK_DIR"
73 |
74 | # delete source directory used to create the package
75 | cd ..
76 | node_modules/.bin/rimraf "$TO_SOURCE_DIR"
77 | }
78 |
79 | install && pack
80 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "size" : "29x29",
5 | "idiom" : "iphone",
6 | "filename" : "icon-29.png",
7 | "scale" : "1x"
8 | },
9 | {
10 | "size" : "29x29",
11 | "idiom" : "iphone",
12 | "filename" : "icon-29@2x.png",
13 | "scale" : "2x"
14 | },
15 | {
16 | "size" : "29x29",
17 | "idiom" : "iphone",
18 | "filename" : "icon-29@3x.png",
19 | "scale" : "3x"
20 | },
21 | {
22 | "size" : "40x40",
23 | "idiom" : "iphone",
24 | "filename" : "icon-40@2x.png",
25 | "scale" : "2x"
26 | },
27 | {
28 | "size" : "40x40",
29 | "idiom" : "iphone",
30 | "filename" : "icon-40@3x.png",
31 | "scale" : "3x"
32 | },
33 | {
34 | "size" : "60x60",
35 | "idiom" : "iphone",
36 | "filename" : "icon-60@2x.png",
37 | "scale" : "2x"
38 | },
39 | {
40 | "size" : "60x60",
41 | "idiom" : "iphone",
42 | "filename" : "icon-60@3x.png",
43 | "scale" : "3x"
44 | },
45 | {
46 | "size" : "29x29",
47 | "idiom" : "ipad",
48 | "filename" : "icon-29.png",
49 | "scale" : "1x"
50 | },
51 | {
52 | "size" : "29x29",
53 | "idiom" : "ipad",
54 | "filename" : "icon-29@2x.png",
55 | "scale" : "2x"
56 | },
57 | {
58 | "size" : "40x40",
59 | "idiom" : "ipad",
60 | "filename" : "icon-40.png",
61 | "scale" : "1x"
62 | },
63 | {
64 | "size" : "40x40",
65 | "idiom" : "ipad",
66 | "filename" : "icon-40@2x.png",
67 | "scale" : "2x"
68 | },
69 | {
70 | "size" : "76x76",
71 | "idiom" : "ipad",
72 | "filename" : "icon-76.png",
73 | "scale" : "1x"
74 | },
75 | {
76 | "size" : "76x76",
77 | "idiom" : "ipad",
78 | "filename" : "icon-76@2x.png",
79 | "scale" : "2x"
80 | },
81 | {
82 | "size" : "83.5x83.5",
83 | "idiom" : "ipad",
84 | "filename" : "icon-83.5@2x.png",
85 | "scale" : "2x"
86 | },
87 | {
88 | "size" : "1024x1024",
89 | "idiom" : "ios-marketing",
90 | "filename" : "icon-1024.png",
91 | "scale" : "1x"
92 | }
93 | ],
94 | "info" : {
95 | "version" : 1,
96 | "author" : "xcode"
97 | }
98 | }
--------------------------------------------------------------------------------
/native-src/ios/README.md:
--------------------------------------------------------------------------------
1 | # Push Plugin for iOS
2 |
3 | This is the native Objective-C code for push notifications for the iOS platform, which is designed to be easily used in NativeScript.
4 |
5 | # Build
6 |
7 | The project has a manual build step that is building both for simulator and real device and then produces a universal .framework from the two versions.
8 |
9 | The output folder is the same as the default build folder for the project (right click the .framework file -> Show in finder) and is called iphoneos-universal.
10 |
11 | # API
12 |
13 | ## PushManager.m
14 |
15 | The PushManager class is used to swizzle the default event callbacks, that are needed to handle push notifications functionality. If you already have a callback attached for those events, they will be called before the actual implementation in the plugin.
16 |
17 | ## Push.m
18 |
19 | - Register - use to subscribe the device for Push Notifications. The options object is a dictionary that is specified in the docs and contains the registration settings.
20 |
21 | > -(void)register:(NSMutableDictionary *)options
22 |
23 | - Unregister - use to unsubscribe from Push Notifications
24 |
25 | > - (void)unregister
26 |
27 | - areNotificationsEnabled - returns true/false if the notifications are enabled/disabled for the current device
28 |
29 | > - (void)areNotificationsEnabled
30 |
31 | - didRegisterForRemoteNotificationsWithDeviceToken - called if the registration for push notifications was successfull. It will return the device token as a result
32 |
33 | > - (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
34 |
35 | - didFailToRegisterForRemoteNotificationsWithError - called when an error occurs during registration for push notifications.
36 |
37 | > - (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
38 |
39 | - notificationReceived - called when a new push notifications is received
40 |
41 | > - (void)notificationReceived
42 |
43 | - setApplicationIconBadgeNumber - used to set a specific number for the application badge. The options object must contain a key "@badge" with an int value
44 |
45 | > - (void)setApplicationIconBadgeNumber:(NSMutableDictionary *)options
46 |
47 | - registerUserNotificationSettings - used to register the device for interactive push notifications
48 |
49 | > - (void)registerUserNotificationSettings:(NSDictionary*)options
50 |
--------------------------------------------------------------------------------
/publish/build-ios.sh:
--------------------------------------------------------------------------------
1 | set -e
2 | set -o pipefail
3 |
4 | CURRENT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
5 |
6 | SOURCE_NAME="PushPlugin"
7 | IOS_SOURCE_DIR="$CURRENT_DIR/../native-src/ios"
8 |
9 | PROJECT_NAME="$SOURCE_NAME.xcodeproj"
10 | TARGET_NAME="$SOURCE_NAME"
11 | FRAMEWORK_NAME="$SOURCE_NAME"
12 |
13 | BUILD_DIR="$IOS_SOURCE_DIR/build/intermediates/${FRAMEWORK_NAME}"
14 | BUILD_FOR_DEVICE_DIR="$BUILD_DIR/Release-iphoneos"
15 | BUILD_FOR_SIMULATOR_DIR="$BUILD_DIR/Release-iphonesimulator"
16 | BUILD_OUTPUT_DIR="$IOS_SOURCE_DIR/build/outputs"
17 |
18 | PLUGIN_TARGET_DIR="$CURRENT_DIR/../src/platforms"
19 | PLUGIN_TARGET_SUBDIR="$PLUGIN_TARGET_DIR/ios"
20 |
21 | cd $IOS_SOURCE_DIR
22 |
23 | if [ -d "$BUILD_DIR" ]; then
24 | rm -rf "$BUILD_DIR"
25 | fi
26 |
27 | echo "Build for iphonesimulator"
28 | xcodebuild -project $PROJECT_NAME -scheme $TARGET_NAME \
29 | -configuration Release \
30 | -sdk iphonesimulator \
31 | GCC_PREPROCESSOR_DEFINITIONS='$GCC_PREPROCESSOR_DEFINITIONS ' \
32 | CONFIGURATION_BUILD_DIR=$BUILD_FOR_SIMULATOR_DIR \
33 | clean build -quiet
34 |
35 | echo "Build for iphoneos"
36 | xcodebuild -project $PROJECT_NAME -scheme $TARGET_NAME \
37 | -configuration Release \
38 | -sdk iphoneos \
39 | GCC_PREPROCESSOR_DEFINITIONS='$GCC_PREPROCESSOR_DEFINITIONS ' \
40 | CONFIGURATION_BUILD_DIR=$BUILD_FOR_DEVICE_DIR \
41 | clean build archive -quiet
42 |
43 | if [ -d "$BUILD_OUTPUT_DIR/$FRAMEWORK_NAME.framework" ]; then
44 | rm -rf "$BUILD_OUTPUT_DIR/$FRAMEWORK_NAME.framework"
45 | fi
46 |
47 | mkdir -p "$BUILD_OUTPUT_DIR/$FRAMEWORK_NAME.framework"
48 |
49 | cp -fr "$BUILD_FOR_DEVICE_DIR/$FRAMEWORK_NAME.framework/$FRAMEWORK_NAME.framework" "$BUILD_OUTPUT_DIR"
50 |
51 | echo "Build fat framework"
52 | xcrun -sdk iphoneos lipo -create \
53 | $BUILD_FOR_SIMULATOR_DIR/$FRAMEWORK_NAME.framework/$FRAMEWORK_NAME \
54 | $BUILD_FOR_DEVICE_DIR/$FRAMEWORK_NAME.framework/$FRAMEWORK_NAME.framework/$FRAMEWORK_NAME \
55 | -o "$BUILD_OUTPUT_DIR/$FRAMEWORK_NAME.framework/$FRAMEWORK_NAME"
56 |
57 | rm -rf $BUILD_DIR
58 |
59 | echo "$FRAMEWORK_NAME.framework was built in $BUILD_OUTPUT_DIR"
60 |
61 | if [ ! -d $PLUGIN_TARGET_DIR ]; then
62 | mkdir $PLUGIN_TARGET_DIR
63 | fi
64 |
65 | if [ ! -d $PLUGIN_TARGET_SUBDIR ]; then
66 | mkdir $PLUGIN_TARGET_SUBDIR
67 | fi
68 |
69 | cp -R "$BUILD_OUTPUT_DIR/$FRAMEWORK_NAME.framework" $PLUGIN_TARGET_SUBDIR
70 |
71 | echo "iOS Framework was copied to $PLUGIN_TARGET_SUBDIR"
72 |
73 | cd $CURRENT_DIR
74 |
--------------------------------------------------------------------------------
/src/index.d.ts:
--------------------------------------------------------------------------------
1 | // Android only
2 | export declare interface FcmNotificaion {
3 | getBody(): string;
4 | getBodyLocalizationArgs(): string[];
5 | getBodyLocalizationKey(): string;
6 | getClickAction(): string;
7 | getColor(): string;
8 | getIcon(): string;
9 | getSound(): string;
10 | getTag(): string;
11 | getTitle(): string;
12 | getTitleLocalizationArgs(): string[];
13 | getTitleLocalizationKey(): string;
14 | }
15 | export declare function register(options: {
16 | senderID: string;
17 | notificationCallbackAndroid?: (stringifiedData: String, fcmNotification: any) => void;
18 | }, successCallback: (fcmRegistrationToken: string) => void, errorCallback: (errorMessage: string) => void): void;
19 | export declare function unregister(onSuccessCallback: (successMessage: string) => void, onErrorCallback: (errorMessage: string) => void, options: {
20 | senderID: string;
21 | }): void;
22 | export declare function onMessageReceived(onSuccessCallback: (message: string, stringifiedData: string, fcmNotification: FcmNotificaion) => void): void;
23 | export declare function onTokenRefresh(onSuccessCallback: () => void): void;
24 |
25 | // iOS only
26 | export declare interface IosInteractiveNotificationAction {
27 | identifier: string;
28 | title: string;
29 | activationMode?: string;
30 | destructive?: boolean;
31 | authenticationRequired?: boolean;
32 | behavior?: string;
33 | }
34 | export declare interface IosInteractiveNotificationCategory {
35 | identifier: string;
36 | actionsForDefaultContext: string[];
37 | actionsForMinimalContext: string[];
38 | }
39 | export declare interface IosRegistrationOptions {
40 | badge: boolean;
41 | sound: boolean;
42 | alert: boolean;
43 | clearBadge: boolean;
44 | interactiveSettings: {
45 | actions: IosInteractiveNotificationAction[];
46 | categories: IosInteractiveNotificationCategory[];
47 | };
48 | notificationCallbackIOS: (message: any) => void;
49 | }
50 | export declare interface NSError {
51 | code: number;
52 | domain: string;
53 | userInfo: any;
54 | }
55 | export declare function register(options: IosRegistrationOptions, successCallback: (token: string) => void, errorCallback: (error: NSError) => void): void;
56 | export declare function registerUserNotificationSettings(success: () => void, error: (error: NSError) => void): void;
57 | export declare function unregister(done: (context: any) => void): void;
58 |
59 | // Common
60 | export declare function areNotificationsEnabled(done: (areEnabled: Boolean) => void): void;
61 |
--------------------------------------------------------------------------------
/native-src/android/app/src/main/java/com/telerik/pushplugin/PushLifecycleCallbacks.java:
--------------------------------------------------------------------------------
1 | package com.telerik.pushplugin;
2 |
3 |
4 | import android.app.Activity;
5 | import android.app.Application;
6 | import android.os.Bundle;
7 | import android.util.Log;
8 |
9 | /**
10 | * Subscribe to the Pause and Resume activity events in order to toggle the PushPlugin's status.
11 | * When the PushPlugin is not in active state - i.e. at foreground, notifications should be created
12 | * and published in the Notification Center, otherwise they're passed directly to the application
13 | * by invoking the onMessageReceived callback.
14 | */
15 | public class PushLifecycleCallbacks implements Application.ActivityLifecycleCallbacks {
16 |
17 | private static PushLifecycleCallbacks callbacks = new PushLifecycleCallbacks();
18 |
19 | /**
20 | * Register for the application's events
21 | * @param app
22 | */
23 | public static void registerCallbacks(Application app) {
24 | if (app == null) {
25 | Log.d("PushLifecycleCallbacks", "The application is null, it's not passed correctly!");
26 | throw new RuntimeException("The application is null, it's not passed correctly!");
27 | }
28 |
29 | // clean up, not to leak and register it N times...
30 | Log.d("PushLifecycleCallbacks", "Unregistering the activity lifecycle callbacks...");
31 | app.unregisterActivityLifecycleCallbacks(callbacks);
32 |
33 | Log.d("PushLifecycleCallbacks", "Registering the activity lifecycle callbacks...");
34 | app.registerActivityLifecycleCallbacks(callbacks);
35 | }
36 |
37 | public void onActivityPaused(Activity activity) {
38 | Log.d(PushPlugin.TAG, "onActivityPaused: Application has been stopped.");
39 |
40 | // the application is being stopped -> the push plugin is not in active/foreground state anymore
41 | PushPlugin.isActive = false;
42 | }
43 |
44 | public void onActivityResumed(Activity activity) {
45 | Log.d(PushPlugin.TAG, "onActivityResumed: Application has been started");
46 |
47 | // the application has been resumed-> the push plugin is now in active/foreground state
48 | PushPlugin.isActive = true;
49 | }
50 |
51 | public void onActivityCreated(Activity activity, Bundle bundle) {
52 | }
53 |
54 | public void onActivityDestroyed(Activity activity) {
55 | }
56 |
57 | public void onActivitySaveInstanceState(Activity activity,
58 | Bundle outState) {
59 | }
60 |
61 | public void onActivityStarted(Activity activity) {
62 | }
63 |
64 | public void onActivityStopped(Activity activity) {
65 | }
66 | }
--------------------------------------------------------------------------------
/native-src/android/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/native-src/android/app/src/main/java/com/telerik/pushplugin/JsonObjectExtended.java:
--------------------------------------------------------------------------------
1 | package com.telerik.pushplugin;
2 |
3 |
4 | import android.util.Log;
5 |
6 | import org.json.JSONArray;
7 | import org.json.JSONObject;
8 | import java.util.Collection;
9 | import java.util.Map;
10 |
11 |
12 | public class JsonObjectExtended extends JSONObject{
13 | //Overwrite for the method wrap as it is only available in Api Level 19+ and this ensures
14 | //it will work on lower level deployments
15 |
16 | /**
17 | * Wraps the given object if necessary.
18 | *
19 | * If the object is null or , returns {@link #NULL}.
20 | * If the object is a {@code JSONArray} or {@code JSONObject}, no wrapping is necessary.
21 | * If the object is {@code NULL}, no wrapping is necessary.
22 | * If the object is an array or {@code Collection}, returns an equivalent {@code JSONArray}.
23 | * If the object is a {@code Map}, returns an equivalent {@code JSONObject}.
24 | * If the object is a primitive wrapper type or {@code String}, returns the object.
25 | * Otherwise if the object is from a {@code java} package, returns the result of {@code toString}.
26 | * If wrapping fails, returns null.
27 | */
28 | public static Object wrap(Object o) {
29 | if (o == null) {
30 | return NULL;
31 | }
32 | if (o instanceof JSONArray || o instanceof JSONObject) {
33 | return o;
34 | }
35 | if (o.equals(NULL)) {
36 | return o;
37 | }
38 | try {
39 | if (o instanceof Collection) {
40 | return new JSONArray((Collection) o);
41 | } else if (o instanceof Object[]) {
42 | Log.d("jsonconverter", "is converting array");
43 | Object[] arr = (Object[]) o;
44 | JSONArray jsonArr = new JSONArray();
45 | for (Object elem: arr) {
46 | jsonArr.put(elem);
47 | }
48 | Log.d("jsonconverter", jsonArr.toString());
49 | return jsonArr;
50 | }
51 | if (o instanceof Map) {
52 | return new JSONObject((Map) o);
53 | }
54 | if (o instanceof Boolean ||
55 | o instanceof Byte ||
56 | o instanceof Character ||
57 | o instanceof Double ||
58 | o instanceof Float ||
59 | o instanceof Integer ||
60 | o instanceof Long ||
61 | o instanceof Short ||
62 | o instanceof String) {
63 | return o;
64 | }
65 | if (o.getClass().getPackage().getName().startsWith("java.")) {
66 | return o.toString();
67 | }
68 | } catch (Exception ignored) {
69 | }
70 | return null;
71 | }
72 |
73 | }
74 |
--------------------------------------------------------------------------------
/src/push-plugin.android.ts:
--------------------------------------------------------------------------------
1 | import * as app from 'tns-core-modules/application';
2 | declare var com: any;
3 |
4 | export declare interface FcmNotificaion {
5 | getBody(): string;
6 | getBodyLocalizationArgs(): string[];
7 | getBodyLocalizationKey(): string;
8 | getClickAction(): string;
9 | getColor(): string;
10 | getIcon(): string;
11 | getSound(): string;
12 | getTag(): string;
13 | getTitle(): string;
14 | getTitleLocalizationArgs(): string[];
15 | getTitleLocalizationKey(): string;
16 | }
17 |
18 | (() => {
19 | let registerLifecycleEvents = () => {
20 | com.telerik.pushplugin.PushLifecycleCallbacks.registerCallbacks(app.android.nativeApp);
21 | };
22 |
23 | // Hook on the application events
24 | if (app.android.nativeApp) {
25 | registerLifecycleEvents();
26 | } else {
27 | app.on(app.launchEvent, registerLifecycleEvents);
28 | }
29 | })();
30 |
31 | export function register(options: { senderID: string, notificationCallbackAndroid?: (stringifiedData: String, fcmNotification: any) => void }, successCallback: (fcmRegistrationToken: string) => void, errorCallback: (errorMessage: string) => void) {
32 | com.telerik.pushplugin.PushPlugin.register(app.android.context, options.senderID,
33 | new com.telerik.pushplugin.PushPluginListener(
34 | {
35 | success: (fcmRegistrationToken: string) => {
36 | if (options && typeof options.notificationCallbackAndroid === 'function') {
37 | onMessageReceived(options.notificationCallbackAndroid);
38 | }
39 |
40 | successCallback(fcmRegistrationToken);
41 | },
42 | error: errorCallback
43 | })
44 | );
45 | }
46 |
47 | export function unregister(onSuccessCallback: (successMessage: string) => void, onErrorCallback: (errorMessage: string) => void, options: { senderID: string }) {
48 | com.telerik.pushplugin.PushPlugin.unregister(app.android.context, options.senderID, new com.telerik.pushplugin.PushPluginListener(
49 | {
50 | success: onSuccessCallback,
51 | error: onErrorCallback
52 | }
53 | ));
54 | }
55 |
56 | export function onMessageReceived(onSuccessCallback: (message: string, stringifiedData: string, fcmNotification: FcmNotificaion) => void) {
57 | com.telerik.pushplugin.PushPlugin.setOnMessageReceivedCallback(
58 | new com.telerik.pushplugin.PushPluginListener(
59 | {
60 | success: onSuccessCallback
61 | })
62 | );
63 | }
64 |
65 | export function onTokenRefresh(onSuccessCallback: () => void) {
66 | com.telerik.pushplugin.PushPlugin.setOnTokenRefreshCallback(
67 | new com.telerik.pushplugin.PushPluginListener(
68 | {
69 | success: onSuccessCallback
70 | })
71 | );
72 | }
73 |
74 | export function areNotificationsEnabled(onSuccessCallback: (areEnabled: boolean) => void) {
75 | const bool = com.telerik.pushplugin.PushPlugin.areNotificationsEnabled();
76 | onSuccessCallback(bool);
77 | }
78 |
--------------------------------------------------------------------------------
/native-src/ios/PushPlugin.xcodeproj/xcuserdata/backendservtestuser.xcuserdatad/xcschemes/PushPluginLibrary.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
32 |
33 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
60 |
61 |
67 |
68 |
69 |
70 |
72 |
73 |
76 |
77 |
78 |
--------------------------------------------------------------------------------
/demo/app/main-view-model.ts:
--------------------------------------------------------------------------------
1 | import { Observable } from "tns-core-modules/data/observable";
2 | import * as pushPlugin from "nativescript-push-notifications";
3 |
4 | export class PushTestModel extends Observable {
5 |
6 | private pushSettings = {
7 | // Android settings
8 | senderID: "", // Android: Required setting with the sender/project number
9 | notificationCallbackAndroid: (stringifiedData: String, fcmNotification: any) => {
10 | const notificationBody = fcmNotification && fcmNotification.getBody();
11 | this.updateMessage("Message received!\n" + notificationBody + "\n" + stringifiedData);
12 | },
13 |
14 | // iOS settings
15 | badge: true, // Enable setting badge through Push Notification
16 | sound: true, // Enable playing a sound
17 | alert: true, // Enable creating a alert
18 | notificationCallbackIOS: (message: any) => {
19 | this.updateMessage("Message received!\n" + JSON.stringify(message));
20 | }
21 | };
22 |
23 | private _counter: number;
24 | private _message: string;
25 |
26 | constructor() {
27 | super();
28 | this.message = "";
29 | this.updateMessage("App started.");
30 |
31 | let self = this;
32 | this.onRegisterButtonTap();
33 | }
34 |
35 | get message(): string {
36 | return this._message;
37 | }
38 |
39 | set message(value: string) {
40 | if (this._message !== value) {
41 | this._message = value;
42 | this.notifyPropertyChange("message", value);
43 | }
44 | }
45 |
46 | onCheckButtonTap() {
47 | let self = this;
48 | pushPlugin.areNotificationsEnabled((areEnabled: Boolean) => {
49 | self.updateMessage("Are Notifications enabled: " + !!areEnabled);
50 | });
51 | }
52 |
53 | onRegisterButtonTap() {
54 | let self = this;
55 | pushPlugin.register(this.pushSettings, (token: String) => {
56 | self.updateMessage("Device registered. Access token: " + token);
57 | // token displayed in console for easier copying and debugging durng development
58 | console.log("Device registered. Access token: " + token);
59 |
60 | if (pushPlugin.registerUserNotificationSettings) {
61 | pushPlugin.registerUserNotificationSettings(() => {
62 | self.updateMessage("Successfully registered for interactive push.");
63 | }, (err) => {
64 | self.updateMessage("Error registering for interactive push: " + JSON.stringify(err));
65 | });
66 | }
67 | }, (errorMessage: String) => {
68 | self.updateMessage(JSON.stringify(errorMessage));
69 | });
70 | }
71 |
72 | onUnregisterButtonTap() {
73 | let self = this;
74 | pushPlugin.unregister(
75 | (successMessage: String) => {
76 | self.updateMessage(successMessage);
77 | },
78 | (errorMessage: String) => {
79 | self.updateMessage(JSON.stringify(errorMessage));
80 | },
81 | this.pushSettings
82 | );
83 | }
84 |
85 | private updateMessage(text: String) {
86 | this.message += text + "\n";
87 | }
88 |
89 | }
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | branches:
2 | only:
3 | - master
4 | matrix:
5 | include:
6 | - stage: "Lint"
7 | language: node_js
8 | os: linux
9 | node_js: "8"
10 | script: cd src && npm run ci.tslint && cd ../demo && npm run ci.tslint
11 | - stage: "WebPack and Build"
12 | os: osx
13 | env:
14 | - Platform="iOS"
15 | - Xcode="9.2"
16 | - Webpack="true"
17 | osx_image: xcode9.2
18 | language: node_js
19 | node_js: "8"
20 | jdk: oraclejdk8
21 | script: cd demo && npm run build.plugin && npm i && tns build ios --bundle --env.uglify
22 | - os: linux
23 | language: android
24 | env:
25 | - Platform="Android"
26 | - AndroidSDK="26"
27 | - Webpack="true"
28 | jdk: oraclejdk8
29 | before_install: nvm install 8.11.4
30 | script: >
31 | echo { \"project_info\": { \"project_number\": \"424050927453\", \"firebase_url\": \"https://pptest-53916.firebaseio.com\", \"project_id\": \"pptest-53916\", \"storage_bucket\": \"pptest-53916.appspot.com\" }, \"client\": [ { \"client_info\": { \"mobilesdk_app_id\": \"1:424050927453:android:21d2dd8987e1e3d1\", \"android_client_info\": { \"package_name\": \"org.nativescript.ppTest\" } }, \"oauth_client\": [ { \"client_id\": \"424050927453-o7gv6bcmknujjoaaoa5r4d452cutb9jf.apps.googleusercontent.com\", \"client_type\": 3 } ], \"api_key\": [ { \"current_key\": \"AIzaSyDr3fqH7Z8-vhBNpAges_KFTA7oOoylGvU\" } ], \"services\": { \"analytics_service\": { \"status\": 1 }, \"appinvite_service\": { \"status\": 1, \"other_platform_oauth_client\": [] }, \"ads_service\": { \"status\": 2 } } } ], \"configuration_version\": \"1\"} > demo/app/App_Resources/Android/google-services.json &&
32 | cd demo && npm run build.plugin && npm i && tns build android --bundle --env.uglify
33 | - language: android
34 | env:
35 | - Platform="Android"
36 | - AndroidSDK="26"
37 | os: linux
38 | jdk: oraclejdk8
39 | before_install: nvm install 8.11.4
40 | script: >
41 | echo { \"project_info\": { \"project_number\": \"424050927453\", \"firebase_url\": \"https://pptest-53916.firebaseio.com\", \"project_id\": \"pptest-53916\", \"storage_bucket\": \"pptest-53916.appspot.com\" }, \"client\": [ { \"client_info\": { \"mobilesdk_app_id\": \"1:424050927453:android:21d2dd8987e1e3d1\", \"android_client_info\": { \"package_name\": \"org.nativescript.ppTest\" } }, \"oauth_client\": [ { \"client_id\": \"424050927453-o7gv6bcmknujjoaaoa5r4d452cutb9jf.apps.googleusercontent.com\", \"client_type\": 3 } ], \"api_key\": [ { \"current_key\": \"AIzaSyDr3fqH7Z8-vhBNpAges_KFTA7oOoylGvU\" } ], \"services\": { \"analytics_service\": { \"status\": 1 }, \"appinvite_service\": { \"status\": 1, \"other_platform_oauth_client\": [] }, \"ads_service\": { \"status\": 2 } } } ], \"configuration_version\": \"1\"} > demo/app/App_Resources/Android/google-services.json &&
42 | cd demo && npm run ci.android.build
43 | - os: osx
44 | env:
45 | - Platform="iOS"
46 | - Xcode="9.2"
47 | osx_image: xcode9.2
48 | language: node_js
49 | node_js: "8"
50 | jdk: oraclejdk8
51 | script: cd demo && npm run ci.ios.build
52 |
53 | android:
54 | components:
55 | - tools
56 | - platform-tools
57 | - build-tools-27.0.3
58 | - android-23
59 | - android-26
60 | - extra-android-m2repository
61 |
62 | install:
63 | - echo no | npm install -g nativescript
64 | - tns usage-reporting disable
65 | - tns error-reporting disable
66 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
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 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to NativeScript Push Notifications
2 |
3 | :+1: First of all, thank you for taking the time to contribute! :+1:
4 |
5 | Here are some guides on how to do that:
6 |
7 |
8 |
9 | - [Code of Conduct](#code-of-conduct)
10 | - [Reporting Bugs](#reporting-bugs)
11 | - [Requesting Features](#requesting-features)
12 | - [Submitting a PR](#submitting-a-pr)
13 | - [Where to Start](#where-to-start)
14 |
15 |
16 |
17 | ## Code of Conduct
18 | Help us keep a healthy and open community. We expect all participants in this project to adhere to the [NativeScript Code Of Conduct](https://github.com/NativeScript/codeofconduct).
19 |
20 |
21 | ## Reporting Bugs
22 |
23 | 1. Always update to the most recent master release; the bug may already be resolved.
24 | 2. Search for similar issues in the issues list for this repo; it may already be an identified problem.
25 | 3. If this is a bug or problem that is clear, simple, and is unlikely to require any discussion -- it is OK to open an issue on GitHub with a reproduction of the bug including workflows and screenshots. If possible, submit a Pull Request with a failing test, entire application or module. If you'd rather take matters into your own hands, fix the bug yourself (jump down to the [Submitting a PR](#submitting-a-pr) section).
26 |
27 | ## Requesting Features
28 |
29 | 1. Use Github Issues to submit feature requests.
30 | 2. First, search for a similar request and extend it if applicable. This way it would be easier for the community to track the features.
31 | 3. When requesting a new feature, please provide as much detail as possible about why you need the feature in your apps. We prefer that you explain a need rather than explain a technical solution for it. That might trigger a nice conversation on finding the best and broadest technical solution to a specific need.
32 |
33 | ## Submitting a PR
34 |
35 | Before you begin make sure there is an issue for the bug or feature you will be working on.
36 |
37 | Following these steps is the best way to get your code included in the project:
38 |
39 | 1. Fork and clone the push-plugin repo:
40 | ```bash
41 | git clone https://github.com//push-plugin.git
42 | # Navigate to the newly cloned directory
43 | cd push-plugin
44 | # Add an "upstream" remote pointing to the original repo.
45 | git remote add upstream https://github.com/NativeScript/push-plugin.git
46 | ```
47 |
48 | 2. Read our [development workflow guide](DevelopmentWorkflow.md) for local setup
49 |
50 | 3. Create a branch for your PR
51 | ```bash
52 | git checkout -b master
53 | ```
54 |
55 | 4. The fun part! Make your code changes. Make sure you:
56 | - Follow the [code conventions guide](https://github.com/NativeScript/NativeScript/blob/master/CodingConvention.md).
57 | - Follow the [commit message guidelines](https://github.com/NativeScript/NativeScript/blob/master/CONTRIBUTING.md#-commit-message-guidelines)
58 | - Update the README if you make changes to the plugin API
59 |
60 | 5. Before you submit your PR:
61 | - Rebase your changes to the latest master: `git pull --rebase upstream master`.
62 | - Ensure your changes pass tslint validation. (run `npm run tslint` in the `src` folder).
63 |
64 | 6. Push your fork. If you have rebased you might have to use force-push your branch:
65 | ```
66 | git push origin --force
67 | ```
68 |
69 | 7. [Submit your pull request](https://github.com/NativeScript/push-plugin/compare) and compare to `NativeScript/push-plugin`. Please, fill in the Pull Request template - it will help us better understand the PR and increase the chances of it getting merged quickly.
70 |
71 | It's our turn from there on! We will review the PR and discuss changes you might have to make before merging it! Thanks!
72 |
73 | ## Where to Start
74 |
75 | If you want to contribute, but you are not sure where to start - look for issues labeled [`help wanted`](https://github.com/NativeScript/push-plugin/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22).
76 |
--------------------------------------------------------------------------------
/DevelopmentWorkflow.md:
--------------------------------------------------------------------------------
1 | # Development Workflow
2 |
3 |
4 |
5 | - [Prerequisites](#prerequisites)
6 | - [Develop locally](#develop-locally)
7 | - [Build the plugin](#build-the-plugin)
8 | - [Testing the plugin](#testing-the-plugin)
9 |
10 |
11 |
12 | ## Prerequisites
13 |
14 | - Install your native toolchain and NativeScript as [described in the docs](https://docs.nativescript.org/start/quick-setup)
15 |
16 | - Review [NativeScript plugins documentation](https://docs.nativescript.org/plugins/plugins) for more details on plugins development
17 |
18 | ## Develop locally
19 |
20 | For local development we recommend using the npm commands provided in the plugin's package.json
21 |
22 | To run and develop using TypeScript demo:
23 |
24 | ```bash
25 | # Go to demo directory
26 | cd push-plugin/demo
27 | # Build the plugin. This compiles all TypeScript files in the plugin directory, which is referenced in the demo's package.json
28 | npm run build.plugin
29 | # Install demo dependencies
30 | npm i
31 | # Run the demo for iOS or Android.
32 | tns run ios
33 | tns run android
34 | ```
35 |
36 | After all the changes are done make sure to test them in all the demo app.
37 |
38 | Available npm commands from the plugin source directory:
39 |
40 | - `build` - builds the plugin so it can be used in the demo app.
41 | - `tslint` - check plugin's TypeScript sources for linting errors.
42 | - `clean` - clean the demo and plugin directories.
43 |
44 | ## Build the plugin
45 |
46 | In order to build the native projects in the `native-src` directory, you need to have Xcode for the ios native project and Java or Android Studio for the Gradle build of the Android native project. Note that this is not required if you don't plan to update the native code - prebuilt binaries for iOS and Android are included in the `src/platforms` directory.
47 |
48 | ## Testing the plugin
49 |
50 | Before building and running the demo for the first time, you should change the application ID (set by default to `org.nativescript.ppTest`) in the `demo/package.json` file to match your application in Firebase and/or provision in iOS. For Android, you should also update the `demo/app/App_Resources/Android/app.gradle` file with the new application ID.
51 |
52 | - Android - make sure you have added your `google-services.json` file to the `demo/app/App_Resources/Android` directory and that you have updated the `demo/app/main-view-model.ts` file with the sender ID of from your Firebase configuration. After the demo app is started the console will show the registration token of the device. For example:
53 |
54 | w7ycrQS0sU:APA91bHCAxiFqonJb77cc785txYZ_0nrWe_sLRZm_nG32h4lhaJhZw-mquBh0rlmaoRVQBhnRsWiMTOWOcbCzuvGCOVKo7UAxog8JEufQO-nOJo3C2cMpPsT9RfiZVgaDc2tK9ezRUf9
55 |
56 | To test push notifications for the device, you can use the following web request:
57 |
58 | curl -X POST --header "Authorization: key=" --Header "Content-Type: application/json" https://fcm.googleapis.com/fcm/send -d "{\"notification\":{\"title\": \"My title\", \"text\": \"My text\", \"badge\": \"1\", \"sound\": \"default\"}, \"data\":{\"foo\":\"bar\"}, \"priority\": \"High\", \"to\": \"\"}"
59 |
60 | where is the Server key from the Firebase Cloud Messaging Settings page and is the token you copied from the console.
61 |
62 | - iOS - make sure that you are using the correct provisioning profile and app ID with enabled APNs. After the application is started the console will show the registration token for the device. For example:
63 |
64 | 7058206f33224ff472976d5a80a1c913b4133c5815cca829d2e4d92a82e1c3b6
65 |
66 | To test push notifications for the device, you can use a third party app like [Pusher](https://github.com/noodlewerk/NWPusher). Open the app, load the correct push certificate and use the device ID from the console to send a message.
67 |
68 | For details on plugins development workflow, read [NativeScript plugins documentation](https://docs.nativescript.org/plugins/building-plugins#step-2-set-up-a-development-workflow) covering that topic.
69 |
--------------------------------------------------------------------------------
/src/platforms/ios/PushPlugin.framework/_CodeSignature/CodeResources:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | files
6 |
7 | Headers/Push.h
8 |
9 | jvS3k3DIaqEsomyLBVgeFR07gYc=
10 |
11 | Headers/PushManager.h
12 |
13 | d95Fh/vTDm2MkfSPt5nD0zMZWn0=
14 |
15 | Headers/PushPlugin.h
16 |
17 | LySoYEsAzpK2asnf2zLlenP8k0U=
18 |
19 | Info.plist
20 |
21 | KUYYX6c0fxbWE2o9MWh9QaorTa8=
22 |
23 | Modules/module.modulemap
24 |
25 | q4trkXWUceyVi1oTw69Ea7/hOw8=
26 |
27 |
28 | files2
29 |
30 | Headers/Push.h
31 |
32 | hash
33 |
34 | jvS3k3DIaqEsomyLBVgeFR07gYc=
35 |
36 | hash2
37 |
38 | zNYU2D/BKumbue7VnqkzQmfuNfi59ZI1cG3oFj3MJas=
39 |
40 |
41 | Headers/PushManager.h
42 |
43 | hash
44 |
45 | d95Fh/vTDm2MkfSPt5nD0zMZWn0=
46 |
47 | hash2
48 |
49 | Ni5kEdaJCdgDagOzL/jRkKsRPxIBgCuPQyRTGdDXJiI=
50 |
51 |
52 | Headers/PushPlugin.h
53 |
54 | hash
55 |
56 | LySoYEsAzpK2asnf2zLlenP8k0U=
57 |
58 | hash2
59 |
60 | HLtxzGVrefqS3qbia+iJFO6fH7VF6WJ3Ll5lkVdX/vg=
61 |
62 |
63 | Modules/module.modulemap
64 |
65 | hash
66 |
67 | q4trkXWUceyVi1oTw69Ea7/hOw8=
68 |
69 | hash2
70 |
71 | /3XrGSQGbFipA1nShtA52gQCgaffGNP1egLU620nmIs=
72 |
73 |
74 |
75 | rules
76 |
77 | ^
78 |
79 | ^.*\.lproj/
80 |
81 | optional
82 |
83 | weight
84 | 1000
85 |
86 | ^.*\.lproj/locversion.plist$
87 |
88 | omit
89 |
90 | weight
91 | 1100
92 |
93 | ^version.plist$
94 |
95 |
96 | rules2
97 |
98 | .*\.dSYM($|/)
99 |
100 | weight
101 | 11
102 |
103 | ^
104 |
105 | weight
106 | 20
107 |
108 | ^(.*/)?\.DS_Store$
109 |
110 | omit
111 |
112 | weight
113 | 2000
114 |
115 | ^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/
116 |
117 | nested
118 |
119 | weight
120 | 10
121 |
122 | ^.*
123 |
124 | ^.*\.lproj/
125 |
126 | optional
127 |
128 | weight
129 | 1000
130 |
131 | ^.*\.lproj/locversion.plist$
132 |
133 | omit
134 |
135 | weight
136 | 1100
137 |
138 | ^Info\.plist$
139 |
140 | omit
141 |
142 | weight
143 | 20
144 |
145 | ^PkgInfo$
146 |
147 | omit
148 |
149 | weight
150 | 20
151 |
152 | ^[^/]+$
153 |
154 | nested
155 |
156 | weight
157 | 10
158 |
159 | ^embedded\.provisionprofile$
160 |
161 | weight
162 | 20
163 |
164 | ^version\.plist$
165 |
166 | weight
167 | 20
168 |
169 |
170 |
171 |
172 |
--------------------------------------------------------------------------------
/demo/app/App_Resources/iOS/Assets.xcassets/LaunchImage.launchimage/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "extent" : "full-screen",
5 | "idiom" : "iphone",
6 | "subtype" : "736h",
7 | "filename" : "Default-736h@3x.png",
8 | "minimum-system-version" : "8.0",
9 | "orientation" : "portrait",
10 | "scale" : "3x"
11 | },
12 | {
13 | "extent" : "full-screen",
14 | "idiom" : "iphone",
15 | "subtype" : "736h",
16 | "filename" : "Default-Landscape@3x.png",
17 | "minimum-system-version" : "8.0",
18 | "orientation" : "landscape",
19 | "scale" : "3x"
20 | },
21 | {
22 | "extent" : "full-screen",
23 | "idiom" : "iphone",
24 | "subtype" : "667h",
25 | "filename" : "Default-667h@2x.png",
26 | "minimum-system-version" : "8.0",
27 | "orientation" : "portrait",
28 | "scale" : "2x"
29 | },
30 | {
31 | "orientation" : "portrait",
32 | "idiom" : "iphone",
33 | "filename" : "Default@2x.png",
34 | "extent" : "full-screen",
35 | "minimum-system-version" : "7.0",
36 | "scale" : "2x"
37 | },
38 | {
39 | "extent" : "full-screen",
40 | "idiom" : "iphone",
41 | "subtype" : "retina4",
42 | "filename" : "Default-568h@2x.png",
43 | "minimum-system-version" : "7.0",
44 | "orientation" : "portrait",
45 | "scale" : "2x"
46 | },
47 | {
48 | "orientation" : "portrait",
49 | "idiom" : "ipad",
50 | "filename" : "Default-Portrait.png",
51 | "extent" : "full-screen",
52 | "minimum-system-version" : "7.0",
53 | "scale" : "1x"
54 | },
55 | {
56 | "orientation" : "landscape",
57 | "idiom" : "ipad",
58 | "filename" : "Default-Landscape.png",
59 | "extent" : "full-screen",
60 | "minimum-system-version" : "7.0",
61 | "scale" : "1x"
62 | },
63 | {
64 | "orientation" : "portrait",
65 | "idiom" : "ipad",
66 | "filename" : "Default-Portrait@2x.png",
67 | "extent" : "full-screen",
68 | "minimum-system-version" : "7.0",
69 | "scale" : "2x"
70 | },
71 | {
72 | "orientation" : "landscape",
73 | "idiom" : "ipad",
74 | "filename" : "Default-Landscape@2x.png",
75 | "extent" : "full-screen",
76 | "minimum-system-version" : "7.0",
77 | "scale" : "2x"
78 | },
79 | {
80 | "orientation" : "portrait",
81 | "idiom" : "iphone",
82 | "filename" : "Default.png",
83 | "extent" : "full-screen",
84 | "scale" : "1x"
85 | },
86 | {
87 | "orientation" : "portrait",
88 | "idiom" : "iphone",
89 | "filename" : "Default@2x.png",
90 | "extent" : "full-screen",
91 | "scale" : "2x"
92 | },
93 | {
94 | "orientation" : "portrait",
95 | "idiom" : "iphone",
96 | "filename" : "Default-568h@2x.png",
97 | "extent" : "full-screen",
98 | "subtype" : "retina4",
99 | "scale" : "2x"
100 | },
101 | {
102 | "orientation" : "portrait",
103 | "idiom" : "ipad",
104 | "extent" : "to-status-bar",
105 | "scale" : "1x"
106 | },
107 | {
108 | "orientation" : "portrait",
109 | "idiom" : "ipad",
110 | "filename" : "Default-Portrait.png",
111 | "extent" : "full-screen",
112 | "scale" : "1x"
113 | },
114 | {
115 | "orientation" : "landscape",
116 | "idiom" : "ipad",
117 | "extent" : "to-status-bar",
118 | "scale" : "1x"
119 | },
120 | {
121 | "orientation" : "landscape",
122 | "idiom" : "ipad",
123 | "filename" : "Default-Landscape.png",
124 | "extent" : "full-screen",
125 | "scale" : "1x"
126 | },
127 | {
128 | "orientation" : "portrait",
129 | "idiom" : "ipad",
130 | "extent" : "to-status-bar",
131 | "scale" : "2x"
132 | },
133 | {
134 | "orientation" : "portrait",
135 | "idiom" : "ipad",
136 | "filename" : "Default-Portrait@2x.png",
137 | "extent" : "full-screen",
138 | "scale" : "2x"
139 | },
140 | {
141 | "orientation" : "landscape",
142 | "idiom" : "ipad",
143 | "extent" : "to-status-bar",
144 | "scale" : "2x"
145 | },
146 | {
147 | "orientation" : "landscape",
148 | "idiom" : "ipad",
149 | "filename" : "Default-Landscape@2x.png",
150 | "extent" : "full-screen",
151 | "scale" : "2x"
152 | }
153 | ],
154 | "info" : {
155 | "version" : 1,
156 | "author" : "xcode"
157 | }
158 | }
--------------------------------------------------------------------------------
/native-src/ios/PushPlugin.xcodeproj/xcuserdata/backendservtestuser.xcuserdatad/xcschemes/PushPlugin.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
29 |
35 |
36 |
37 |
38 |
39 |
44 |
45 |
47 |
53 |
54 |
55 |
56 |
57 |
63 |
64 |
65 |
66 |
75 |
76 |
82 |
83 |
84 |
85 |
86 |
87 |
93 |
94 |
100 |
101 |
102 |
103 |
105 |
106 |
109 |
110 |
111 |
--------------------------------------------------------------------------------
/native-src/android/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/MIGRATE-TO-FIREBASE.md:
--------------------------------------------------------------------------------
1 | # Migration guide push-plugin -> Firebase plugin
2 | If you have an app that uses push-plugin for push notifications and need to switch to nativescript-plugin-firebase, this guide can help you. If you are just starting with push notifications, however, it would be best to use [Firebase plugin](https://github.com/EddyVerbruggen/nativescript-plugin-firebase) and refer [its documentation on messaging](https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/master/docs/NON_FIREBASE_MESSAGING.md).
3 |
4 | > In case you're stuck, check out [the 'push' demo app](https://github.com/EddyVerbruggen/nativescript-plugin-firebase/tree/master/demo-push) in the Firebase plugin repo.
5 |
6 | #### 1. Add the plugin to your app
7 | Go to your app's root folder in terminal app and execute
8 | ```bash
9 | tns plugin add nativescript-plugin-firebase
10 | ```
11 | > Upon plugin installation, you'll be prompted to choose which features to use. Choose "yes" for "Are you using this plugin ONLY as a Push Notification client for an external (non-Firebase) Push service?". By default, the plugin saves the configuration as a file (firebase.nativescript.json) to use it when reinstalling the plugin.
12 |
13 | #### 2. Setup
14 | Add `GoogleService-Info.plist` (for iOS) or `google-services.json` (for Android) in App_Resources/iOS (and App_Resources/Android, respectively). These are the configuration files that come from your Firebase apps. If you don't have such yet, go to https://console.firebase.google.com and create one. See [Firebase plugin's docs](https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/master/docs/MESSAGING.md) for more info on initial setup.
15 |
16 | #### 3. Initialization prerequisite
17 | Make sure you [`require` the plugin in `app.ts` / `main.ts` / `main.aot.ts`](https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/55cfb4f69cf8939f9101712fed22383196b08d36/demo/app/app.ts#L5)
18 | *before* `application.start()`, and do `init()` *after* the app has started (not in `app.ts` - not even in a timeout; move it out of `app.ts` entirely!). This ensures the notifications will be receivable in the background.
19 |
20 | EXAMPLE:
21 | ```js
22 | // in app.ts
23 | // ...
24 | require("nativescript-plugin-firebase");
25 | // ...
26 | app.start({ moduleName: 'main-page' });
27 | ```
28 |
29 | #### 4. Add some code [to register for push notifications](https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/master/docs/NON_FIREBASE_MESSAGING.md#registerforpushnotifications)
30 |
31 | EXAMPLE: The following code using push-plugin:
32 |
33 | ```typescript
34 | private pushSettings = {
35 | notificationCallbackIOS: (message: any) => {
36 | this.updateMessage("Message received!\n" + JSON.stringify(message));
37 | }
38 | };
39 |
40 | pushPlugin.register(this.pushSettings, (token: String) => {
41 | console.log("Device registered. Access token: " + token);
42 |
43 | pushPlugin.registerUserNotificationSettings(() => {
44 | console.log("Successfully registered for push.");
45 | }, (err) => {
46 | console.log(("Error registering for interactive push: " + JSON.stringify(err));
47 | });
48 | }, (errorMessage: String) => {
49 | console.log((JSON.stringify(errorMessage));
50 | });
51 | ```
52 |
53 | ... could be rewriten using the Firebase plugin like:
54 |
55 | ```typescript
56 | import { messaging } from "nativescript-plugin-firebase/messaging";
57 |
58 | messaging.registerForPushNotifications({
59 | onPushTokenReceivedCallback: (token: string): void => {
60 | console.log("Firebase plugin received a push token: " + token);
61 | },
62 |
63 | onMessageReceivedCallback: (message: Message) => {
64 | console.log("Push message received: " + message.title);
65 | }
66 | }).then(() => console.log("Registered for push"));
67 | ```
68 |
69 | #### 5. Testing push notifications
70 | To test with real messages, you can use the UI in Firebase Console, use the `https://fcm.googleapis.com/fcm/send` API, or a third-party app like Pusher. See the [testing docs section in the Firebase plugin](https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/master/docs/NON_FIREBASE_MESSAGING.md#testing-push-notifications).
71 |
72 | #### 6. Interactive Push Notifications - in push plugin they are set in the options argument passed to pushPlugin.register(options, callback) method. In the Firebase plugin, it is done in a very similar way:
73 |
74 | ```typescript
75 | import { messaging } from "nativescript-plugin-firebase/messaging";
76 | ...
77 | const model = new messaging.PushNotificationModel();
78 | model.iosSettings = new messaging.IosPushSettings();
79 | model.iosSettings.interactiveSettings = new messaging.IosInteractivePushSettings();
80 | model.iosSettings.interactiveSettings.actions = [<>]
81 |
82 | model.iosSettings.interactiveSettings.categories = [{ identifier: "SOME CATEGORY" }];
83 |
84 | model.onNotificationActionTakenCallback = () => {
85 | // callback to hook to if you want to handle what action have been taken by the user
86 | };
87 |
88 | messaging.registerForInteractivePush(model: messaging.PushNotificationModel);
89 | ```
90 |
91 | Some lines in the above example have been omitted. See [Firebase plugin's interactive notifications docs](https://github.com/EddyVerbruggen/nativescript-plugin-firebase/blob/958b4639c557b3aee80c044fe917acffdbea27a7/docs/NON_FIREBASE_MESSAGING.md#interactive-notifications-ios-only-for-now) for more details.
92 |
93 | #### 7. areNotificationsEnabled() API
94 | In the Firebase plugin it is pretty similar to the one in push-plugin, and even simpler to use:
95 |
96 | ```typescript
97 | import { messaging } from "nativescript-plugin-firebase/messaging";
98 |
99 | const areTheyEnabled = messaging.areNotificationsEnabled(); // synchronous, retruns boolean;
100 | ```
101 |
102 | This API is also supported in Android, SDK version 24 and above
103 |
--------------------------------------------------------------------------------
/native-src/android/app/src/main/java/com/telerik/pushplugin/PushPlugin.java:
--------------------------------------------------------------------------------
1 | package com.telerik.pushplugin;
2 |
3 | import android.content.Context;
4 | import android.util.Log;
5 | import com.google.firebase.messaging.FirebaseMessagingService;
6 | import com.google.firebase.messaging.RemoteMessage;
7 |
8 | import org.json.JSONException;
9 |
10 | import java.util.Map;
11 |
12 | /**
13 | * Push plugin extends the GCM Listener Service and has to be registered in the AndroidManifest
14 | * in order to receive Notification Messages.
15 | */
16 | public class PushPlugin extends FirebaseMessagingService {
17 | static final String TAG = "PushPlugin";
18 |
19 | static boolean isActive = false;
20 | private static JsonObjectExtended cachedData;
21 | private static PushPluginListener onMessageReceivedCallback;
22 | private static PushPluginListener onTokenRefreshCallback;
23 |
24 | /**
25 | * Register the application in GCM
26 | *
27 | * @param appContext
28 | * @param projectId
29 | * @param callbacks
30 | */
31 | public static void register(Context appContext, String projectId, PushPluginListener callbacks) {
32 | if (callbacks == null) {
33 | Log.d(TAG, "Registering without providing a callback!");
34 | }
35 |
36 | PushPlugin.getRegisterTokenInThread(projectId, callbacks);
37 | }
38 |
39 | /**
40 | * Unregister the application from GCM
41 | *
42 | * @param appContext
43 | * @param projectId
44 | * @param callbacks
45 | */
46 | public static void unregister(Context appContext, String projectId, PushPluginListener callbacks) {
47 | if (callbacks == null) {
48 | Log.d(TAG, "Unregister without providing a callback!");
49 | }
50 | try {
51 | UnregisterTokenThread t = new UnregisterTokenThread(projectId, callbacks);
52 | t.start();
53 | } catch (Exception ex) {
54 | callbacks.error("Thread failed to start: " + ex.getMessage());
55 | }
56 | }
57 |
58 | /**
59 | * Set the on message received callback
60 | *
61 | * @param callbacks
62 | */
63 | public static void setOnMessageReceivedCallback(PushPluginListener callbacks) {
64 | onMessageReceivedCallback = callbacks;
65 | RemoteMessage.Notification whatever = null;
66 |
67 | if (cachedData != null) {
68 | Log.d(TAG, "Cached data is not empty!");
69 | executeOnMessageReceivedCallback(cachedData, whatever);
70 | cachedData = null;
71 | }
72 | }
73 |
74 | /**
75 | * Execute the onMessageReceivedCallback with the data passed.
76 | * In case the callback is not present, cache the data;
77 | *
78 | * @param data
79 | * @param notif
80 | */
81 | public static void executeOnMessageReceivedCallback(Map data, RemoteMessage.Notification notif) {
82 | JsonObjectExtended jsonData = convertMapToJson(data);
83 | executeOnMessageReceivedCallback(jsonData, notif);
84 | }
85 |
86 | private static JsonObjectExtended convertMapToJson(Map data) {
87 | JsonObjectExtended json = new JsonObjectExtended();
88 |
89 | if (data != null) {
90 | try {
91 | for (String key: data.keySet()) {
92 | json.put(key, JsonObjectExtended.wrap(data.get(key)));
93 | }
94 | json.put("foreground", PushPlugin.isActive);
95 | } catch (JSONException ex) {
96 | Log.d(TAG, "Error thrown while parsing push notification data bundle to json: " + ex.getMessage());
97 | }
98 | }
99 |
100 | return json;
101 | }
102 |
103 | private static void executeOnMessageReceivedCallback(JsonObjectExtended dataAsJson, RemoteMessage.Notification notif) {
104 | if (onMessageReceivedCallback != null) {
105 | Log.d(TAG, "Passing data and notification to callback...");
106 | onMessageReceivedCallback.success(dataAsJson.toString(), notif);
107 | } else {
108 | Log.d(TAG, "No callback function - caching the data for later retrieval.");
109 | cachedData = dataAsJson;
110 | }
111 | }
112 |
113 | /**
114 | * Handles the push messages receive event.
115 | */
116 | @Override
117 | public void onMessageReceived(RemoteMessage message) {
118 | Map data = message.getData();
119 | RemoteMessage.Notification notif = message.getNotification();
120 | Log.d(TAG, "New Push Message: " + data);
121 | Log.d(TAG, "Msg notification: " + notif);
122 |
123 | if (notif != null) {
124 | Log.d(TAG, "Notification body: " + notif.getBody());
125 | }
126 |
127 | executeOnMessageReceivedCallback(data, notif);
128 | }
129 |
130 | /**
131 | * Set the on token refresh callback
132 | *
133 | * @param callbacks
134 | */
135 | public static void setOnTokenRefreshCallback(PushPluginListener callbacks) {
136 | onTokenRefreshCallback = callbacks;
137 | }
138 |
139 |
140 | /**
141 | * Execute the onTokeRefreshCallback.
142 | */
143 | public static void executeOnTokenRefreshCallback() {
144 | if (onTokenRefreshCallback != null) {
145 | Log.d(TAG, "Executing token refresh callback.");
146 | onTokenRefreshCallback.success(null);
147 | } else {
148 | Log.d(TAG, "No token refresh callback");
149 | }
150 | }
151 |
152 | /**
153 | * This method always returns true. It is here only for legacy purposes.
154 | */
155 | public static boolean areNotificationsEnabled() {
156 | return true;
157 | }
158 |
159 | private static void getRegisterTokenInThread(String projectId, PushPluginListener callbacks) {
160 | try {
161 | ObtainTokenThread t = new ObtainTokenThread(projectId, callbacks);
162 | t.start();
163 | } catch (Exception ex) {
164 | callbacks.error("Thread failed to start: " + ex.getMessage());
165 | }
166 | }
167 | }
168 |
--------------------------------------------------------------------------------
/src/hooks/utils.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var fs = require('fs');
3 | var os = require('os');
4 |
5 | var PLUGIN_VERSION = '3.1.1';
6 | var _log = console.log.bind(console);
7 |
8 | function targetsAndroid(projectDir) {
9 | var pkg = require(path.join(projectDir, 'package.json'));
10 | if (!pkg.nativescript) {
11 | throw new Error('Not a NativeScript project');
12 | }
13 |
14 | return ('tns-android' in pkg.nativescript);
15 | }
16 |
17 | function buildGradleExists(platformsDir) {
18 | return fs.existsSync(_getProjectBuildGradlePath(platformsDir));
19 | }
20 |
21 | function checkForGoogleServicesJson(projectDir, resourcesDir) {
22 | var androidIsTargeted = targetsAndroid(projectDir);
23 | var resourcesPath = path.join(resourcesDir, 'Android', 'google-services.json');
24 |
25 | if (androidIsTargeted && !fs.existsSync(resourcesPath)) {
26 | _log('|!| google-services.json appears to be missing. Please make sure it is present in the "app/App_Resources/Android" folder, in order to use FCM push notifications |!|');
27 | }
28 | }
29 |
30 | function addOnPluginInstall(platformsDir, resourcesDir) {
31 | if (buildGradleExists(platformsDir)) {
32 | addIfNecessary(platformsDir, resourcesDir);
33 | }
34 | }
35 |
36 | function addIfNecessary(platformsDir, resourcesDir) {
37 | _amendBuildGradle(platformsDir, function(pluginImported, pluginApplied, fileContents) {
38 | if (!pluginImported) {
39 | fileContents.projectFileContents = _addPluginImport(fileContents.projectFileContents);
40 | }
41 |
42 | if (!pluginApplied) {
43 | if (fileContents.appFileContents) {
44 | fileContents.appFileContents = _addPluginApplication(fileContents.appFileContents);
45 | } else {
46 | fileContents.projectFileContents = _addPluginApplication(fileContents.projectFileContents);
47 | }
48 | }
49 | return fileContents;
50 | });
51 |
52 | _copyGoogleServices(resourcesDir, platformsDir);
53 | }
54 |
55 | function removeIfPresent(platformsDir) {
56 | _amendBuildGradle(platformsDir, function(pluginImported, pluginApplied, fileContents) {
57 | if (pluginImported) {
58 | fileContents.projectFileContents = _removePluginImport(fileContents.projectFileContents);
59 | }
60 |
61 | if (pluginApplied) {
62 | if (fileContents.appFileContents) {
63 | fileContents.appFileContents = _removePluginApplication(fileContents.appFileContents);
64 | } else {
65 | fileContents.projectFileContents = _removePluginApplication(fileContents.projectFileContents);
66 | }
67 | }
68 | return fileContents;
69 | });
70 | }
71 |
72 | function setLogger(logFunc) {
73 | _log = logFunc;
74 | }
75 |
76 | // ============= private
77 |
78 | var _quotesRegExp = '["\']';
79 | var _versionRegExp = '[^\'"]+';
80 | var _pluginImportName = 'com.google.gms:google-services';
81 | var _pluginApplicationName = 'com.google.gms.google-services';
82 |
83 | function _copyGoogleServices(resourcesDir, platformsDir) {
84 | var srcServicesFile = path.join(resourcesDir, 'Android', 'google-services.json');
85 | var dstServicesFile = path.join(platformsDir, 'android', 'app', 'google-services.json');
86 | if (fs.existsSync(srcServicesFile) && !fs.existsSync(dstServicesFile) && fs.existsSync(path.join(platformsDir, 'android', 'app'))) {
87 | // try to copy google-services config file to platform app directory
88 | fs.writeFileSync(dstServicesFile, fs.readFileSync(srcServicesFile, 'utf-8'));
89 | }
90 | }
91 |
92 | function _amendBuildGradle(platformsDir, applyAmendment) {
93 | if (!buildGradleExists(platformsDir)) {
94 | return _log('build.gradle file not found');
95 | }
96 |
97 | var projectPath = _getProjectBuildGradlePath(platformsDir);
98 | var appPath = _getAppBuildGradlePath(platformsDir);
99 |
100 | if (!fs.existsSync(appPath)) {
101 | // NativeScript <= 3.3.1
102 | appPath = null;
103 | }
104 | var fileContents = {
105 | projectFileContents: fs.readFileSync(projectPath, 'utf8'),
106 | appFileContents: appPath ? fs.readFileSync(appPath, 'utf8') : null
107 | };
108 | var pluginImported = _checkForImport(fileContents.projectFileContents);
109 | var pluginApplied = _checkForApplication(fileContents.appFileContents ? fileContents.appFileContents : fileContents.projectFileContents);
110 | var newContents = applyAmendment(pluginImported, pluginApplied, fileContents);
111 |
112 | fs.writeFileSync(projectPath, newContents.projectFileContents, 'utf8');
113 | if (appPath) {
114 | fs.writeFileSync(appPath, newContents.appFileContents, 'utf8');
115 | }
116 | }
117 |
118 | function _removePluginImport(buildGradleContents) {
119 | var regExpStr = '\\s*classpath +' + _formNamePartOfRegExp(true) + '{0,0}:' + _versionRegExp + _quotesRegExp;
120 | var regExp = new RegExp(regExpStr, 'i');
121 | return buildGradleContents.replace(regExp, '');
122 | }
123 |
124 | function _removePluginApplication(buildGradleContents) {
125 | var regExpStr = '\\s*apply plugin: +' + _formNamePartOfRegExp(false);
126 | var regExp = new RegExp(regExpStr, 'i');
127 | return buildGradleContents.replace(regExp, '');
128 | }
129 |
130 | function _addPluginImport(buildGradleContents) {
131 | var androidGradle = 'com.android.tools.build:gradle';
132 | var insertBeforeDoubleQuotes = 'classpath "' + androidGradle;
133 | var insertBeforeSingleQuotes = 'classpath \'' + androidGradle;
134 | var quoteToInsert = '"'
135 | var matchedString = insertBeforeDoubleQuotes;
136 | var ind = buildGradleContents.indexOf(insertBeforeDoubleQuotes);
137 |
138 | if (ind === -1) {
139 | ind = buildGradleContents.indexOf(insertBeforeSingleQuotes);
140 | quoteToInsert = '\'';
141 | matchedString = insertBeforeSingleQuotes;
142 | }
143 |
144 | if (ind === -1) {
145 | _log('build.gradle has unexpected contents -- please check it manually');
146 | return buildGradleContents;
147 | }
148 |
149 | var result = buildGradleContents.substring(0, ind);
150 | result += 'classpath ' + quoteToInsert + _pluginImportName + ':' + PLUGIN_VERSION + quoteToInsert + os.EOL;
151 | result += '\t\t' + matchedString;
152 | result += buildGradleContents.substring(ind + matchedString.length);
153 | return result;
154 | }
155 |
156 | function _addPluginApplication(buildGradleContents) {
157 | buildGradleContents += os.EOL + 'apply plugin: "' + _pluginApplicationName + '"' + os.EOL;
158 | return buildGradleContents;
159 | }
160 |
161 | function _formNamePartOfRegExp(useImportName) {
162 | var name = useImportName ? _pluginImportName : _pluginApplicationName;
163 | return _quotesRegExp + name + _quotesRegExp;
164 | }
165 |
166 | function _checkForImport(buildGradleContents) {
167 | var re = new RegExp('classpath +' + _formNamePartOfRegExp(true) + '{0,0}', 'i');
168 | return re.test(buildGradleContents);
169 | }
170 |
171 | function _checkForApplication(buildGradleContents) {
172 | var re = new RegExp('apply plugin: +' + _formNamePartOfRegExp(false), 'i');
173 | return re.test(buildGradleContents);
174 | }
175 |
176 | function _getProjectBuildGradlePath(platformsDir) {
177 | return path.join(platformsDir, 'android', 'build.gradle');
178 | }
179 |
180 | function _getAppBuildGradlePath(platformsDir) {
181 | return path.join(platformsDir, 'android', 'app', 'build.gradle');
182 | }
183 | // ============= end private
184 |
185 | module.exports = {
186 | removeIfPresent: removeIfPresent,
187 | setLogger: setLogger,
188 | addIfNecessary: addIfNecessary,
189 | targetsAndroid: targetsAndroid,
190 | buildGradleExists: buildGradleExists,
191 | addOnPluginInstall: addOnPluginInstall,
192 | checkForGoogleServicesJson: checkForGoogleServicesJson
193 | };
--------------------------------------------------------------------------------
/src/push-plugin.ios.ts:
--------------------------------------------------------------------------------
1 | import * as app from 'tns-core-modules/application';
2 | const iosApp = app.ios;
3 |
4 | declare var Push: any;
5 | declare var PushManager: any;
6 |
7 | export declare interface IosInteractiveNotificationAction {
8 | identifier: string;
9 | title: string;
10 | activationMode?: string;
11 | destructive?: boolean;
12 | authenticationRequired?: boolean;
13 | behavior?: string;
14 | }
15 |
16 | export declare interface IosInteractiveNotificationCategory {
17 | identifier: string;
18 | actionsForDefaultContext: string[];
19 | actionsForMinimalContext: string[];
20 | }
21 |
22 | export declare interface IosRegistrationOptions {
23 | badge: boolean;
24 | sound: boolean;
25 | alert: boolean;
26 | clearBadge: boolean;
27 | interactiveSettings: {
28 | actions: IosInteractiveNotificationAction[],
29 | categories: IosInteractiveNotificationCategory[]
30 | };
31 | notificationCallbackIOS: (message: any) => void;
32 | }
33 |
34 | export declare interface NSError {
35 | code: number;
36 | domain: string;
37 | userInfo: any;
38 | }
39 |
40 | let pushHandler;
41 | let pushManager;
42 | let pushSettings;
43 | (() => {
44 | if (!pushSettings) {
45 | pushSettings = {};
46 | }
47 |
48 | if (!pushHandler) {
49 | pushHandler = Push.alloc().init();
50 | }
51 |
52 | if (!pushManager) {
53 | pushManager = PushManager.alloc().init();
54 | }
55 | })();
56 |
57 |
58 | const _init = (settings: IosRegistrationOptions) => {
59 | if (!!pushSettings.isInitialized) return;
60 |
61 | // initialize the native push plugin
62 | pushSettings.settings = settings;
63 | pushSettings.notificationCallbackIOS = settings.notificationCallbackIOS;
64 |
65 | // subscribe to the notification received event.
66 | _addObserver("notificationReceived", (context: any) => {
67 | const userInfo = JSON.parse(context.userInfo.objectForKey('message'));
68 | pushSettings.notificationCallbackIOS(userInfo);
69 | });
70 |
71 | pushSettings.isInitialized = true;
72 | };
73 |
74 | const _mapCategories = (interactiveSettings: any) => {
75 | let categories = [];
76 |
77 | for (let i = 0; i < interactiveSettings.categories.length; i++) {
78 | const currentCategory = interactiveSettings.categories[i];
79 | let mappedCategory = {
80 | identifier: currentCategory.identifier,
81 | actionsForDefaultContext: [],
82 | actionsForMinimalContext: []
83 | };
84 |
85 | for (let j = 0; j < interactiveSettings.actions.length; j++) {
86 | const currentAction = interactiveSettings.actions[j];
87 |
88 | if (currentCategory.actionsForMinimalContext.indexOf(currentAction.identifier) > -1) {
89 | mappedCategory.actionsForMinimalContext.push(currentAction);
90 | }
91 |
92 | if (currentCategory.actionsForDefaultContext.indexOf(currentAction.identifier) > -1) {
93 | mappedCategory.actionsForDefaultContext.push(currentAction);
94 | }
95 | }
96 | categories.push(mappedCategory);
97 |
98 | }
99 | return categories;
100 | };
101 |
102 | let _addObserver = (eventName: string, callback: (context: any) => void) => {
103 | return iosApp.addNotificationObserver(eventName, callback);
104 | };
105 |
106 | let _removeObserver = function (observer: () => void, eventName: string) {
107 | iosApp.removeNotificationObserver(observer, eventName);
108 | };
109 |
110 | export function register(settings: IosRegistrationOptions, success: (token: String) => void, error: (error: NSError) => void) {
111 | _init(settings);
112 | if (!pushSettings.didRegisterObserver) { // make sure that the events are not attached more than once
113 | pushSettings.didRegisterObserver = _addObserver("didRegisterForRemoteNotificationsWithDeviceToken", (result: any) => {
114 | _removeObserver(pushSettings.didRegisterObserver, "didRegisterForRemoteNotificationsWithDeviceToken");
115 | pushSettings.didRegisterObserver = undefined;
116 | const token = result.userInfo.objectForKey('message');
117 | success(token);
118 | });
119 | }
120 |
121 | if (!pushSettings.didFailToRegisterObserver) {
122 | pushSettings.didFailToRegisterObserver = _addObserver("didFailToRegisterForRemoteNotificationsWithError", (e: NSError) => {
123 | _removeObserver(pushSettings.didFailToRegisterObserver, "didFailToRegisterForRemoteNotificationsWithError");
124 | pushSettings.didFailToRegisterObserver = undefined;
125 | error(e);
126 | });
127 | }
128 |
129 | pushHandler.register(pushSettings.settings);
130 | }
131 |
132 | export function registerUserNotificationSettings(success: () => void, error: (error: NSError) => void) {
133 | if (pushSettings.settings && pushSettings.settings.interactiveSettings) {
134 | const interactiveSettings = pushSettings.settings.interactiveSettings;
135 | let notificationTypes = [];
136 | if (pushSettings.settings.alert) {
137 | notificationTypes.push("alert");
138 | }
139 | if (pushSettings.settings.badge) {
140 | notificationTypes.push("badge");
141 | }
142 | if (pushSettings.settings.sound) {
143 | notificationTypes.push("sound");
144 | }
145 |
146 | if (!pushSettings.registerUserSettingsObserver) {
147 | pushSettings.registerUserSettingsObserver = _addObserver("didRegisterUserNotificationSettings", () => {
148 | _removeObserver(pushSettings.registerUserSettingsObserver, "didRegisterUserNotificationSettings");
149 |
150 | pushSettings.registerUserSettingsObserver = undefined;
151 | success();
152 | });
153 | }
154 |
155 | if (!pushSettings.failToRegisterUserSettingsObserver) {
156 | pushSettings.failToRegisterUserSettingsObserver = _addObserver("failToRegisterUserNotificationSettings", (e: NSError) => {
157 | _removeObserver(pushSettings.didFailToRegisterObserver, "failToRegisterUserNotificationSettings");
158 |
159 | pushSettings.failToRegisterUserSettingsObserver = undefined;
160 | error(e);
161 | });
162 | }
163 |
164 | pushHandler.registerUserNotificationSettings({
165 | types: notificationTypes,
166 | categories: _mapCategories(interactiveSettings)
167 | });
168 | } else {
169 | success();
170 | }
171 | }
172 |
173 | export function unregister(done: (context: any) => void) {
174 | if (!pushSettings.didUnregisterObserver) {
175 | pushSettings.didUnregisterObserver = _addObserver("didUnregister", (context: any) => {
176 | _removeObserver(pushSettings.didUnregisterObserver, "didUnregister");
177 |
178 | pushSettings.didUnregisterObserver = undefined;
179 | done(context);
180 | });
181 | }
182 |
183 | pushHandler.unregister();
184 | }
185 |
186 | export function areNotificationsEnabled(done: (areEnabled: Boolean) => void) {
187 | if (!pushSettings.areNotificationsEnabledObserver) {
188 | pushSettings.areNotificationsEnabledObserver = _addObserver("areNotificationsEnabled", function (result) {
189 | const areEnabledStr = result.userInfo.objectForKey('message');
190 | const areEnabled = areEnabledStr === "true";
191 |
192 | _removeObserver(pushSettings.areNotificationsEnabledObserver, "areNotificationsEnabled");
193 | pushSettings.areNotificationsEnabledObserver = undefined;
194 | done(areEnabled);
195 | });
196 | }
197 |
198 | pushHandler.areNotificationsEnabled();
199 | }
200 |
--------------------------------------------------------------------------------
/native-src/ios/PushPlugin/PushManager.m:
--------------------------------------------------------------------------------
1 | #import "PushManager.h"
2 | #import
3 | #import
4 | #import "Push.h"
5 |
6 | @implementation PushManager
7 | /*__attribute__((constructor))
8 | void myFunction() {
9 | @autoreleasepool {
10 | NSLog(@"stuff happened early");
11 | }
12 | }*/
13 |
14 | static IMP didRegisterOriginalMethod = NULL;
15 | static IMP didFailOriginalMethod = NULL;
16 | static IMP didReceiveOriginalMethod = NULL;
17 | static IMP didBecomeActiveOriginalMethod = NULL;
18 | static IMP handleActionWithIdentifierOriginalMethod = NULL;
19 |
20 | + (void)load {
21 | [[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidFinishLaunchingNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
22 |
23 | UIApplication *app = [UIApplication sharedApplication];
24 | id appDelegate = app.delegate;
25 |
26 | // didBecomeActive swizzle
27 | /*
28 | Method didBecomeActiveMethod = class_getInstanceMethod([PushManager class], @selector(my_applicationDidBecomeActive:));
29 | IMP didBecomeActiveImp = method_getImplementation(didBecomeActiveMethod);
30 | const char* didBecomeActiveTypes = method_getTypeEncoding(didBecomeActiveMethod);
31 |
32 | Method didBecomeActiveOriginal = class_getInstanceMethod(appDelegate.class, @selector(applicationDidBecomeActive:));
33 | if (didBecomeActiveOriginal) {
34 | didBecomeActiveOriginalMethod = method_getImplementation(didBecomeActiveOriginal);
35 | method_exchangeImplementations(didBecomeActiveOriginal, didBecomeActiveMethod);
36 | } else {
37 | class_addMethod(appDelegate.class, @selector(applicationDidBecomeActive:), didBecomeActiveImp, didBecomeActiveTypes);
38 | }
39 | */
40 |
41 | // didRegisterForRemoteNotificationsWithDeviceToken swizzle
42 | Method didRegisterMethod = class_getInstanceMethod([PushManager class], @selector(my_application:didRegisterForRemoteNotificationsWithDeviceToken:));
43 | IMP didRegisterMethodImp = method_getImplementation(didRegisterMethod);
44 | const char* didRegisterTypes = method_getTypeEncoding(didRegisterMethod);
45 |
46 | Method didRegisterOriginal = class_getInstanceMethod(appDelegate.class, @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:));
47 | if (didRegisterOriginal) {
48 | didRegisterOriginalMethod = method_getImplementation(didRegisterOriginal);
49 | method_exchangeImplementations(didRegisterOriginal, didRegisterMethod);
50 | } else {
51 | class_addMethod(appDelegate.class, @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:), didRegisterMethodImp, didRegisterTypes);
52 | }
53 |
54 | // didFailToRegisterForRemoteNotificationsWithError swizzle
55 | Method didFailMethod = class_getInstanceMethod([PushManager class], @selector(my_application:didFailToRegisterForRemoteNotificationsWithError:));
56 | IMP didFailMethodImp = method_getImplementation(didFailMethod);
57 | const char* didFailTypes = method_getTypeEncoding(didFailMethod);
58 |
59 | Method didFailOriginal = class_getInstanceMethod(appDelegate.class, @selector(application:didFailToRegisterForRemoteNotificationsWithError:));
60 | if (didFailOriginal) {
61 | didFailOriginalMethod = method_getImplementation(didFailOriginal);
62 | method_exchangeImplementations(didFailOriginal, didFailMethod);
63 | } else {
64 | class_addMethod(appDelegate.class, @selector(application:didFailToRegisterForRemoteNotificationsWithError:), didFailMethodImp, didFailTypes);
65 | }
66 |
67 | // didReceiveRemoteNotification swizzle
68 | Method didReceiveMethod = class_getInstanceMethod([PushManager class], @selector(my_application:didReceiveRemoteNotification:));
69 | IMP didReceiveMethodImp = method_getImplementation(didReceiveMethod);
70 | const char* didReceiveTypes = method_getTypeEncoding(didReceiveMethod);
71 |
72 | Method didReceiveOriginal = class_getInstanceMethod(appDelegate.class, @selector(application:didReceiveRemoteNotification:));
73 | if (didReceiveOriginal) {
74 | didReceiveOriginalMethod = method_getImplementation(didReceiveOriginal);
75 | method_exchangeImplementations(didReceiveOriginal, didReceiveMethod);
76 | } else {
77 | class_addMethod(appDelegate.class, @selector(application:didReceiveRemoteNotification:), didReceiveMethodImp, didReceiveTypes);
78 | }
79 |
80 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
81 | // handleActionWithIdentifier swizzle
82 | Method handleActionWithIdentifierMethod = class_getInstanceMethod([PushManager class], @selector(my_application:handleActionWithIdentifier:forRemoteNotification:completionHandler:));
83 | IMP handleActionWithIdentifierMethodImp = method_getImplementation(handleActionWithIdentifierMethod);
84 | const char* handleActionWithIdentifierTypes = method_getTypeEncoding(handleActionWithIdentifierMethod);
85 |
86 | Method handleActionWithIdentifierOriginal = class_getInstanceMethod(appDelegate.class, @selector(application:handleActionWithIdentifier:forRemoteNotification:completionHandler:));
87 | if (handleActionWithIdentifierOriginal) {
88 | handleActionWithIdentifierOriginalMethod = method_getImplementation(handleActionWithIdentifierOriginal);
89 | method_exchangeImplementations(handleActionWithIdentifierOriginal, handleActionWithIdentifierMethod);
90 | } else {
91 | class_addMethod(appDelegate.class, @selector(application:handleActionWithIdentifier:forRemoteNotification:completionHandler:), handleActionWithIdentifierMethodImp, handleActionWithIdentifierTypes);
92 | }
93 | #endif
94 | }];
95 |
96 | }
97 |
98 | -(id)init
99 | {
100 | [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(createNotificationChecker:)
101 | name:@"UIApplicationDidFinishLaunchingNotification" object:nil];
102 |
103 | [[NSNotificationCenter defaultCenter]addObserver:self
104 | selector:@selector(my_applicationDidBecomeActive:)
105 | name:@"UIApplicationDidBecomeActiveNotification" object:nil];
106 |
107 | return self;
108 | }
109 |
110 | - (void)createNotificationChecker:(NSNotification *)notification
111 | {
112 | if (notification)
113 | {
114 | NSDictionary *launchOptions = [notification userInfo];
115 | if (launchOptions)
116 | [Push sharedInstance].launchNotification = [launchOptions objectForKey: @"UIApplicationLaunchOptionsRemoteNotificationKey"];
117 | }
118 | }
119 |
120 | - (void)my_applicationDidBecomeActive:(UIApplication *)application
121 | {
122 | [UIApplication sharedApplication].applicationIconBadgeNumber = 0;
123 | if ([Push sharedInstance].launchNotification) {
124 | [Push sharedInstance].notificationMessage = [Push sharedInstance].launchNotification;
125 | [Push sharedInstance].launchNotification = nil;
126 | [[Push sharedInstance] performSelectorOnMainThread:@selector(notificationReceived) withObject:[Push sharedInstance] waitUntilDone:NO];
127 | }
128 | }
129 |
130 | - (void)my_application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
131 | if (didRegisterOriginalMethod) {
132 | void (*originalImp)(id, SEL, UIApplication *, NSData *) = didRegisterOriginalMethod;
133 | originalImp(self, @selector(application:didRegisterForRemoteNotificationsWithDeviceToken:), application, deviceToken);
134 | }
135 | NSLog(@"%@", deviceToken);
136 | [[Push sharedInstance] didRegisterForRemoteNotificationsWithDeviceToken:deviceToken];
137 | }
138 |
139 | - (void)my_application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
140 | if (didReceiveOriginalMethod) {
141 | void (*originalImp)(id, SEL, UIApplication *, NSDictionary *) = didReceiveOriginalMethod;
142 | originalImp(self, @selector(application:didReceiveRemoteNotification:), application, userInfo);
143 | }
144 | NSLog(@"didReceiveNotification");
145 |
146 | UIApplicationState appState = UIApplicationStateActive;
147 | if ([application respondsToSelector:@selector(applicationState)]) {
148 | appState = application.applicationState;
149 | }
150 |
151 | if (appState == UIApplicationStateActive || appState == UIApplicationStateBackground) {
152 | [Push sharedInstance].notificationMessage = userInfo;
153 | [Push sharedInstance].isInline = appState == UIApplicationStateActive ;
154 | [[Push sharedInstance] notificationReceived];
155 | } else {
156 | [Push sharedInstance].launchNotification = userInfo;
157 | }
158 | }
159 |
160 | - (void)my_application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
161 | if (didFailOriginalMethod) {
162 | void (*originalImp)(id, SEL, UIApplication *, NSError *) = didFailOriginalMethod;
163 | originalImp(self, @selector(application:didFailToRegisterForRemoteNotificationsWithError:), application, error);
164 | }
165 | NSLog(@"Error registering...");
166 | [[Push sharedInstance] didFailToRegisterForRemoteNotificationsWithError:error];
167 | }
168 |
169 | - (void)my_application:(UIApplication *) application handleActionWithIdentifier: (NSString *) identifier forRemoteNotification: (NSDictionary *) notification completionHandler: (void (^)()) completionHandler {
170 |
171 | NSLog(@"handle action with identifier");
172 |
173 | NSMutableDictionary *mutableNotification = [notification mutableCopy];
174 |
175 | [mutableNotification setObject:identifier forKey:@"identifier"];
176 | if (application.applicationState == UIApplicationStateActive) {
177 | [Push sharedInstance].notificationMessage = mutableNotification;
178 | [Push sharedInstance].isInline = YES;
179 | [[Push sharedInstance] notificationReceived];
180 | } else {
181 | [Push sharedInstance].notificationMessage = mutableNotification;
182 | [[Push sharedInstance] performSelectorOnMainThread:@selector(notificationReceived) withObject:[Push sharedInstance] waitUntilDone:NO];
183 | }
184 |
185 | if (handleActionWithIdentifierOriginalMethod) {
186 | void (*originalImp)(id, SEL, UIApplication *, NSString *, NSDictionary *, void(^)()) = handleActionWithIdentifierOriginalMethod;
187 | originalImp(self, @selector(application:handleActionWithIdentifier:forRemoteNotification:completionHandler:), application, identifier, notification, completionHandler);
188 | } else {
189 | completionHandler();
190 | }
191 | }
192 |
193 |
194 | @end
195 |
--------------------------------------------------------------------------------
/native-src/android/app/app.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | generateDebugSources
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright (c) 2015-2019 Progress Software Corporation
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
--------------------------------------------------------------------------------
/native-src/ios/PushPlugin/Push.m:
--------------------------------------------------------------------------------
1 | #import "Push.h"
2 | #import
3 | #import
4 | #import
5 |
6 | const NSString * badgeKey = @"badge";
7 | const NSString * soundKey = @"sound";
8 | const NSString * alertKey = @"alert";
9 | const NSString * areNotificationsEnabledEventName = @"areNotificationsEnabled";
10 | const NSString * didUnregisterEventName = @"didUnregister";
11 | const NSString * didRegisterEventName = @"didRegisterForRemoteNotificationsWithDeviceToken";
12 | const NSString * didFailToRegisterEventName = @"didFailToRegisterForRemoteNotificationsWithError";
13 | const NSString * notificationReceivedEventName = @"notificationReceived";
14 | const NSString * setBadgeNumberEventName = @"setApplicationIconBadgeNumber";
15 | const NSString * didRegisterUserNotificationSettingsEventName = @"didRegisterUserNotificationSettings";
16 | const NSString * failToRegisterUserNotificationSettingsEventName = @"failToRegisterUserNotificationSettings";
17 |
18 | static char launchNotificationKey;
19 |
20 | @implementation Push
21 |
22 | @synthesize notificationMessage;
23 | @synthesize isInline;
24 |
25 | + (instancetype)sharedInstance
26 | {
27 | static Push *sharedInstance = nil;
28 | static dispatch_once_t onceToken;
29 | dispatch_once(&onceToken, ^{
30 | sharedInstance = [[Push alloc] init];
31 | });
32 | return sharedInstance;
33 | }
34 |
35 | - (void)areNotificationsEnabled
36 | {
37 | BOOL registered;
38 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
39 | if ([[UIApplication sharedApplication] respondsToSelector:@selector(isRegisteredForRemoteNotifications)]) {
40 | registered = [[UIApplication sharedApplication] isRegisteredForRemoteNotifications];
41 | } else {
42 | UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
43 | registered = types != UIRemoteNotificationTypeNone;
44 | }
45 | #else
46 | UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
47 | registered = types != UIRemoteNotificationTypeNone;
48 | #endif
49 | NSString * booleanString = (registered) ? @"true" : @"false";
50 | [self success:areNotificationsEnabledEventName WithMessage:booleanString];
51 | }
52 |
53 | - (void)unregister
54 | {
55 | [[UIApplication sharedApplication] unregisterForRemoteNotifications];
56 | [self success:didUnregisterEventName WithMessage:@"Success"];
57 | }
58 |
59 | -(void)register:(NSMutableDictionary *)options
60 | {
61 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
62 | UIUserNotificationType UserNotificationTypes = UIUserNotificationTypeNone;
63 | if([self isTrue: badgeKey fromOptions: options]) UserNotificationTypes |= UIUserNotificationTypeBadge;
64 | if([self isTrue: soundKey fromOptions: options]) UserNotificationTypes |= UIUserNotificationTypeSound;
65 | if([self isTrue: alertKey fromOptions: options]) UserNotificationTypes |= UIUserNotificationTypeAlert;
66 | #endif
67 | UIRemoteNotificationType notificationTypes = UIRemoteNotificationTypeNone;
68 | notificationTypes |= UIRemoteNotificationTypeNewsstandContentAvailability;
69 |
70 | if([self isTrue: badgeKey fromOptions: options]) notificationTypes |= UIRemoteNotificationTypeBadge;
71 | if([self isTrue: soundKey fromOptions: options]) notificationTypes |= UIRemoteNotificationTypeSound;
72 | if([self isTrue: alertKey fromOptions: options]) notificationTypes |= UIRemoteNotificationTypeAlert;
73 |
74 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
75 | UserNotificationTypes |= UIUserNotificationActivationModeBackground;
76 | #endif
77 |
78 | if (notificationTypes == UIRemoteNotificationTypeNone)
79 | NSLog(@"PushPlugin.register: Push notification type is set to none");
80 |
81 | isInline = NO;
82 |
83 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
84 | if ([[UIApplication sharedApplication]respondsToSelector:@selector(registerUserNotificationSettings:)]) {
85 | UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UserNotificationTypes categories:nil];
86 | [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
87 | [[UIApplication sharedApplication] registerForRemoteNotifications];
88 | } else {
89 | [[UIApplication sharedApplication] registerForRemoteNotificationTypes:notificationTypes];
90 | }
91 | #else
92 | [[UIApplication sharedApplication] registerForRemoteNotificationTypes:notificationTypes];
93 | #endif
94 |
95 | if (notificationMessage)
96 | [self notificationReceived];
97 | }
98 |
99 | - (BOOL)isTrue:(NSString *)key fromOptions:(NSMutableDictionary *)options
100 | {
101 | id arg = [options objectForKey:key];
102 |
103 | if([arg isKindOfClass:[NSString class]]) return [arg isEqualToString:@"true"];
104 |
105 | if([arg boolValue]) return true;
106 |
107 | return false;
108 | }
109 |
110 |
111 | - (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
112 | {
113 | NSMutableDictionary *results = [NSMutableDictionary dictionary];
114 | NSString *token = [[[[deviceToken description] stringByReplacingOccurrencesOfString:@"<"withString:@""]
115 | stringByReplacingOccurrencesOfString:@">" withString:@""]
116 | stringByReplacingOccurrencesOfString: @" " withString: @""];
117 | [results setValue:token forKey:@"deviceToken"];
118 |
119 | #if !TARGET_IPHONE_SIMULATOR
120 | // Get Bundle Info for Remote Registration (handy if you have more than one app)
121 | [results setValue:[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"] forKey:@"appName"];
122 | [results setValue:[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] forKey:@"appVersion"];
123 |
124 | // Check what Notifications the user has turned on. We registered for all three, but they may have manually disabled some or all of them.
125 | NSUInteger rntypes;
126 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
127 | if([UIUserNotificationSettings class]){
128 | rntypes = [[[UIApplication sharedApplication] currentUserNotificationSettings] types];
129 | } else {
130 | rntypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
131 | }
132 | #else
133 | rntypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
134 | #endif
135 |
136 | // Set the defaults to disabled unless we find otherwise...
137 | NSString *pushBadge = @"disabled";
138 | NSString *pushAlert = @"disabled";
139 | NSString *pushSound = @"disabled";
140 |
141 | // Check what Registered Types are turned on. This is a bit tricky since if two are enabled, and one is off, it will return a number 2... not telling you which
142 | // one is actually disabled. So we are literally checking to see if rnTypes matches what is turned on, instead of by number. The "tricky" part is that the
143 | // single notification types will only match if they are the ONLY one enabled. Likewise, when we are checking for a pair of notifications, it will only be
144 | // true if those two notifications are on. This is why the code is written this way
145 | if(rntypes & UIRemoteNotificationTypeBadge){
146 | pushBadge = @"enabled";
147 | }
148 | if(rntypes & UIRemoteNotificationTypeAlert) {
149 | pushAlert = @"enabled";
150 | }
151 | if(rntypes & UIRemoteNotificationTypeSound) {
152 | pushSound = @"enabled";
153 | }
154 |
155 | [results setValue:pushBadge forKey:@"pushBadge"];
156 | [results setValue:pushAlert forKey:@"pushAlert"];
157 | [results setValue:pushSound forKey:@"pushSound"];
158 |
159 | // Get the users Device Model, Display Name, Token & Version Number
160 | UIDevice *dev = [UIDevice currentDevice];
161 | [results setValue:dev.name forKey:@"deviceName"];
162 | [results setValue:dev.model forKey:@"deviceModel"];
163 | [results setValue:dev.systemVersion forKey:@"deviceSystemVersion"];
164 |
165 | [self success:didRegisterEventName WithMessage:[NSString stringWithFormat:@"%@", token]];
166 | #endif
167 | }
168 |
169 | - (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
170 | {
171 | [self fail:didFailToRegisterEventName WithMessage:@"" withError:error];
172 | }
173 |
174 | - (void)notificationReceived
175 | {
176 | if (self.notificationMessage)
177 | {
178 |
179 | NSMutableString *jsonStr = [NSMutableString stringWithString:@"{"];
180 |
181 | [self parseDictionary:self.notificationMessage intoJSON:jsonStr];
182 |
183 | if (isInline)
184 | {
185 | [jsonStr appendFormat:@"\"foreground\":\"%d\"", 1];
186 | isInline = NO;
187 | }
188 | else
189 | [jsonStr appendFormat:@"\"foreground\":\"%d\"", 0];
190 |
191 | [jsonStr appendString:@"}"];
192 |
193 | NSLog(@"Msg: %@", jsonStr);
194 |
195 | [self success:notificationReceivedEventName WithMessage:jsonStr];
196 | self.notificationMessage = nil;
197 | }
198 | }
199 |
200 | -(void)parseDictionary:(NSDictionary *)inDictionary intoJSON:(NSMutableString *)jsonString
201 | {
202 | NSArray *keys = [inDictionary allKeys];
203 | NSString *key;
204 |
205 | for (key in keys)
206 | {
207 | id thisObject = [inDictionary objectForKey:key];
208 |
209 | if ([thisObject isKindOfClass:[NSDictionary class]])
210 | [self parseDictionary:thisObject intoJSON:jsonString];
211 | else if ([thisObject isKindOfClass:[NSString class]])
212 | [jsonString appendFormat:@"\"%@\":\"%@\",",
213 | key,
214 | [[[[inDictionary objectForKey:key]
215 | stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"]
216 | stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""]
217 | stringByReplacingOccurrencesOfString:@"\n" withString:@"\\n"]];
218 | else {
219 | [jsonString appendFormat:@"\"%@\":\"%@\",", key, [inDictionary objectForKey:key]];
220 | }
221 | }
222 | }
223 |
224 | - (void)setApplicationIconBadgeNumber:(NSMutableDictionary *)options
225 | {
226 | int badge = [[options objectForKey:badgeKey] intValue] ?: 0;
227 |
228 | [[UIApplication sharedApplication] setApplicationIconBadgeNumber:badge];
229 |
230 | [self success:setBadgeNumberEventName WithMessage:[NSString stringWithFormat:@"app badge count set to %d", badge]];
231 | }
232 |
233 | - (void)registerUserNotificationSettings:(NSDictionary*)options
234 | {
235 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
236 | if (![[UIApplication sharedApplication]respondsToSelector:@selector(registerUserNotificationSettings:)]) {
237 | [self success:didRegisterUserNotificationSettingsEventName WithMessage:[NSString stringWithFormat:@"%@", @"user notifications not supported for this ios version."]];
238 | return;
239 | }
240 |
241 | NSArray *categories = [options objectForKey:@"categories"];
242 | if (categories == nil) {
243 | [self fail:failToRegisterUserNotificationSettingsEventName WithMessage:@"No categories specified" withError:nil];
244 | return;
245 | }
246 | NSMutableArray *nsCategories = [[NSMutableArray alloc] initWithCapacity:[categories count]];
247 |
248 | for (NSDictionary *category in categories) {
249 | // ** 1. create the actions for this category
250 | NSMutableArray *nsActionsForDefaultContext = [[NSMutableArray alloc] initWithCapacity:4];
251 | NSArray *actionsForDefaultContext = [category objectForKey:@"actionsForDefaultContext"];
252 | if (actionsForDefaultContext == nil) {
253 | [self fail:failToRegisterUserNotificationSettingsEventName WithMessage:@"Category doesn't contain actionsForDefaultContext" withError:nil];
254 | return;
255 | }
256 | if (![self createNotificationAction:category actions:actionsForDefaultContext nsActions:nsActionsForDefaultContext]) {
257 | return;
258 | }
259 |
260 | NSMutableArray *nsActionsForMinimalContext = [[NSMutableArray alloc] initWithCapacity:2];
261 | NSArray *actionsForMinimalContext = [category objectForKey:@"actionsForMinimalContext"];
262 | if (actionsForMinimalContext == nil) {
263 | [self fail:failToRegisterUserNotificationSettingsEventName WithMessage:@"Category doesn't contain actionsForMinimalContext" withError:nil];
264 | return;
265 | }
266 | if (![self createNotificationAction:category actions:actionsForMinimalContext nsActions:nsActionsForMinimalContext]) {
267 | return;
268 | }
269 |
270 | // ** 2. create the category
271 | UIMutableUserNotificationCategory *nsCategory = [[UIMutableUserNotificationCategory alloc] init];
272 | // Identifier to include in your push payload and local notification
273 | NSString *identifier = [category objectForKey:@"identifier"];
274 | if (identifier == nil) {
275 | [self fail:failToRegisterUserNotificationSettingsEventName WithMessage:@"Category doesn't contain identifier" withError:nil];
276 | return;
277 | }
278 | nsCategory.identifier = identifier;
279 | // Add the actions to the category and set the action context
280 | [nsCategory setActions:nsActionsForDefaultContext forContext:UIUserNotificationActionContextDefault];
281 | // Set the actions to present in a minimal context
282 | [nsCategory setActions:nsActionsForMinimalContext forContext:UIUserNotificationActionContextMinimal];
283 | [nsCategories addObject:nsCategory];
284 | }
285 |
286 | // ** 3. Determine the notification types
287 | NSArray *types = [options objectForKey:@"types"];
288 | if (types == nil) {
289 | [self fail:failToRegisterUserNotificationSettingsEventName WithMessage:@"No types specified" withError:nil];
290 | return;
291 | }
292 | UIUserNotificationType nsTypes = UIUserNotificationTypeNone;
293 | for (NSString *type in types) {
294 | if ([type isEqualToString:badgeKey]) {
295 | nsTypes |= UIUserNotificationTypeBadge;
296 | } else if ([type isEqualToString:alertKey]) {
297 | nsTypes |= UIUserNotificationTypeAlert;
298 | } else if ([type isEqualToString:soundKey]) {
299 | nsTypes |= UIUserNotificationTypeSound;
300 | } else {
301 | [self fail:failToRegisterUserNotificationSettingsEventName WithMessage:[NSString stringWithFormat:@"Unsupported type: %@, use one of badge, alert, sound", type] withError:nil];
302 | }
303 | }
304 |
305 | // ** 4. Register the notification categories
306 | NSSet *nsCategorySet = [NSSet setWithArray:nsCategories];
307 |
308 |
309 | UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:nsTypes categories:nsCategorySet];
310 | [[UIApplication sharedApplication] registerUserNotificationSettings:settings];
311 | #endif
312 | [self success:didRegisterUserNotificationSettingsEventName WithMessage:[NSString stringWithFormat:@"%@", @"user notifications registered"]];
313 | }
314 |
315 | #if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000
316 | - (BOOL)createNotificationAction:(NSDictionary *)category
317 | actions:(NSArray *) actions
318 | nsActions:(NSMutableArray *)nsActions
319 | {
320 | for (NSDictionary *action in actions) {
321 | UIMutableUserNotificationAction *nsAction = [[UIMutableUserNotificationAction alloc] init];
322 | // Define an ID string to be passed back to your app when you handle the action
323 | NSString *identifier = [action objectForKey:@"identifier"];
324 | if (identifier == nil) {
325 | [self fail:failToRegisterUserNotificationSettingsEventName WithMessage:@"Action doesn't contain identifier" withError:nil];
326 | return NO;
327 | }
328 | nsAction.identifier = identifier;
329 | // Localized text displayed in the action button
330 | NSString *title = [action objectForKey:@"title"];
331 | if (title == nil) {
332 | [self fail:failToRegisterUserNotificationSettingsEventName WithMessage:@"Action doesn't contain title" withError:nil];
333 | return NO;
334 | }
335 | nsAction.title = title;
336 | // If you need to show UI, choose foreground (background gives your app a few seconds to run)
337 | BOOL isForeground = [@"foreground" isEqualToString:[action objectForKey:@"activationMode"]];
338 | nsAction.activationMode = isForeground ? UIUserNotificationActivationModeForeground : UIUserNotificationActivationModeBackground;
339 | // Destructive actions display in red
340 | BOOL isDestructive = [[action objectForKey:@"destructive"] isEqual:[NSNumber numberWithBool:YES]];
341 | nsAction.destructive = isDestructive;
342 | // Set whether the action requires the user to authenticate
343 | BOOL isAuthRequired = [[action objectForKey:@"authenticationRequired"] isEqual:[NSNumber numberWithBool:YES]];
344 | nsAction.authenticationRequired = isAuthRequired;
345 | [nsActions addObject:nsAction];
346 | }
347 | return YES;
348 | }
349 | #endif
350 |
351 | -(void)success:(NSString *)eventName WithDictionary:(NSMutableDictionary *)userInfo
352 | {
353 | [[NSNotificationCenter defaultCenter]
354 | postNotificationName:eventName
355 | object:self userInfo:userInfo];
356 | }
357 |
358 | -(void)success:(NSString *)eventName WithMessage:(NSString *)message
359 | {
360 | NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
361 | [userInfo setValue:message forKey:@"message"];
362 | [[NSNotificationCenter defaultCenter]
363 | postNotificationName:eventName
364 | object:self userInfo:userInfo];
365 | }
366 |
367 | -(void)fail:(NSString *)eventName WithMessage:(NSString *)message withError:(NSError *)error
368 | {
369 | NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
370 | NSString *errorMessage = (error) ? [NSString stringWithFormat:@"%@ - %@", message, [error localizedDescription]] : message;
371 | [userInfo setValue:errorMessage forKey:@"message"];
372 |
373 | [[NSNotificationCenter defaultCenter]
374 | postNotificationName:eventName
375 | object:self userInfo:userInfo];
376 | }
377 |
378 | - (NSMutableArray *)launchNotification
379 | {
380 | return objc_getAssociatedObject(self, &launchNotificationKey);
381 | }
382 |
383 | - (void)setLaunchNotification:(NSDictionary *)aDictionary
384 | {
385 | objc_setAssociatedObject(self, &launchNotificationKey, aDictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
386 | }
387 |
388 | - (void)dealloc
389 | {
390 | self.launchNotification = nil;
391 | }
392 |
393 | @end
394 |
--------------------------------------------------------------------------------