diff --git a/definitions.d.ts b/definitions.d.ts new file mode 100644 index 0000000..367bf4c --- /dev/null +++ b/definitions.d.ts @@ -0,0 +1,60 @@ +export declare interface IosInteractiveNotificationAction { + identifier: string; + title: string; + activationMode?: string; + destructive?: boolean; + authenticationRequired?: boolean; + behavior?: string; +} + +export declare interface IosInteractiveNotificationCategory { + identifier: string; + actionsForDefaultContext: string[]; + actionsForMinimalContext: string[]; +} + +export declare interface IosRegistrationOptions { + badge: boolean; + sound: boolean; + alert: boolean; + clearBadge: boolean; + interactiveSettings: { + actions: IosInteractiveNotificationAction[], + categories: IosInteractiveNotificationCategory[] + }, + notificationCallbackIOS: (message: string) => void; +} + +export declare interface NSError { + code: number; + domain: string; + userInfo: any +} + +export declare interface FcmNotificaion { + getBody(): string; + getBodyLocalizationArgs(): string[]; + getBodyLocalizationKey(): string; + getClickAction(): string; + getColor(): string; + getIcon(): string; + getSound(): string; + getTag(): string; + getTitle(): string; + getTitleLocalizationArgs(): string[]; + getTitleLocalizationKey(): string; +} + +// Common +export declare function register(options: IosRegistrationOptions, successCallback: (token: string) => void, errorCallback: (error: NSError) => void): void; +export declare function register(options: { senderID: string }, successCallback: (fcmRegistrationToken: string) => void, errorCallback: (errorMessage: string) => void): void; +export declare function unregister(successCallback: (successMessage: string) => void): void; // iOS +export declare function unregister(successCallback: (successMessage: string) => void, errorCallback: (errorMessage: string) => void, options: { senderID: string }): void; +export declare function areNotificationsEnabled(callback: (boolean) => void): void; + +// Android only +export declare function onMessageReceived(callback: (message: string, stringifiedData: string, fcmNotification: FcmNotificaion) => void): void; +export declare function onTokenRefresh(callback: () => void): void; + +// iOS only +export declare function registerUserNotificationSettings(successCallback: () => void, errorCallback: (error: NSError) => void): void; diff --git a/hooks/before-prepare.js b/hooks/before-prepare.js new file mode 100644 index 0000000..f40057b --- /dev/null +++ b/hooks/before-prepare.js @@ -0,0 +1,18 @@ +var utils = require('./utils'); + +module.exports = function ($logger, $projectData) { + + // No need to check if file exists cause it's a before-prepare hook + // and if project targets Android, platforms dir is already created. + // If Android is not set up correctly, platform is not added and we log an error + + utils.setLogger(_log); + + if (utils.targetsAndroid($projectData.projectDir)) { + utils.addIfNecessary($projectData.platformsDir); + } + + function _log (str) { + $logger.info('nativescript-push-notifications -- ' + str); + } +}; diff --git a/hooks/utils.js b/hooks/utils.js new file mode 100644 index 0000000..1910a84 --- /dev/null +++ b/hooks/utils.js @@ -0,0 +1,159 @@ +var path = require('path'); +var fs = require('fs'); +var os = require('os'); + +var PLUGIN_VERSION = '+'; +var _log = console.log.bind(console); + +function targetsAndroid (projectDir) { + var pkg = require(path.join(projectDir, 'package.json')); + if (!pkg.nativescript) { + throw new Error('Not a NativeScript project'); + } + + return ('tns-android' in pkg.nativescript); +} + +function buildGradleExists (platformsDir) { + return fs.existsSync(_getBuildGradlePath(platformsDir)); +} + +function checkForGoogleServicesJson (projectDir, resourcesDir) { + var androidIsTargeted = targetsAndroid(projectDir); + var resourcesPath = path.join(resourcesDir, 'Android', 'google-services.json'); + + if (androidIsTargeted && !fs.existsSync(resourcesPath)) { + _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 |!|'); + } +} + +function addOnPluginInstall (platformsDir) { + var path = _getBuildGradlePath(platformsDir); + if (buildGradleExists(platformsDir)) { + addIfNecessary(platformsDir); + } +} + +function addIfNecessary (platformsDir) { + _amendBuildGradle(platformsDir, function (pluginImported, pluginApplied, fileContents) { + var newContents = fileContents; + if (!pluginImported) { + newContents = _addPluginImport(newContents); + } + + if (!pluginApplied) { + newContents = _addPluginApplication(newContents); + } + return newContents; + }); +} + +function removeIfPresent (platformsDir) { + _amendBuildGradle(platformsDir, function (pluginImported, pluginApplied, fileContents) { + var newFileContents = fileContents; + if (pluginImported) { + newFileContents = _removePluginImport(newFileContents); + } + + if (pluginApplied) { + newFileContents = _removePluginApplication(newFileContents); + } + return newFileContents; + }); +} + +function setLogger (logFunc) { + _log = logFunc; +} + +// ============= private + +var _quotesRegExp = '["\']'; +var _versionRegExp = '[^\'"]+'; +var _pluginImportName = 'com.google.gms:google-services'; +var _pluginApplicationName = 'com.google.gms.google-services'; + +function _amendBuildGradle (platformsDir, applyAmendment) { + var path = _getBuildGradlePath(platformsDir); + + if (!buildGradleExists(platformsDir)) { + return _log('build.gradle file not found'); + } + + var fileContents = fs.readFileSync(path, 'utf8'); + var pluginImported = _checkForImport(fileContents); + var pluginApplied = _checkForApplication(fileContents); + var newContents = applyAmendment(pluginImported, pluginApplied, fileContents); + + fs.writeFileSync(path, newContents, 'utf8'); +} + +function _removePluginImport (buildGradleContents) { + var regExpStr = '\\s*classpath +' + _formNamePartOfRegExp(true) + '{0,0}:' + _versionRegExp + _quotesRegExp; + var regExp = new RegExp(regExpStr, 'i'); + return buildGradleContents.replace(regExp, ''); +} + +function _removePluginApplication (buildGradleContents) { + var regExpStr = '\\s*apply plugin: +' + _formNamePartOfRegExp(false); + var regExp = new RegExp(regExpStr, 'i'); + return buildGradleContents.replace(regExp, ''); +} + +function _addPluginImport (buildGradleContents) { + var androidGradle = 'com.android.tools.build:gradle'; + var insertBeforeDoubleQuotes = 'classpath "' + androidGradle; + var insertBeforeSingleQoutes = 'classpath \'' + androidGradle; + + var ind = buildGradleContents.indexOf(insertBeforeDoubleQuotes); + if (ind === -1) { + ind = buildGradleContents.indexOf(insertBeforeSingleQoutes); + } + + if (ind === -1) { + _log('build.gradle has unexpected contents -- please check it manually'); + return buildGradleContents; + } + + var result = buildGradleContents.substring(0, ind); + result += 'classpath "' + _pluginImportName + ':' + PLUGIN_VERSION + '"' + os.EOL; + result += '\t\t' + insertBeforeDoubleQuotes; + result += buildGradleContents.substring(ind + ('classpath "' + androidGradle).length); + return result; +} + +function _addPluginApplication (buildGradleContents) { + buildGradleContents += os.EOL + 'apply plugin: "' + _pluginApplicationName + '"' + os.EOL; + return buildGradleContents; +} + +function _formNamePartOfRegExp (useImportName) { + var name = useImportName ? _pluginImportName : _pluginApplicationName; + return _quotesRegExp + name + _quotesRegExp; +} + +function _checkForImport (buildGradleContents) { + var re = new RegExp('classpath +' + _formNamePartOfRegExp(true) + '{0,0}', 'i'); + return re.test(buildGradleContents); +} + +function _checkForApplication (buildGradleContents) { + var re = new RegExp('apply plugin: +' + _formNamePartOfRegExp(false), 'i'); + return re.test(buildGradleContents); +} + +function _getBuildGradlePath (platformsDir) { + return path.join(platformsDir, 'android', 'build.gradle'); +} + +// ============= end private + +module.exports = { + removeIfPresent: removeIfPresent, + setLogger: setLogger, + addIfNecessary: addIfNecessary, + targetsAndroid: targetsAndroid, + buildGradleExists: buildGradleExists, + addOnPluginInstall: addOnPluginInstall, + checkForGoogleServicesJson: checkForGoogleServicesJson +}; diff --git a/native-src/android/app/src/main/java/com/telerik/pushplugin/PushLifecycleCallbacks.java b/native-src/android/app/src/main/java/com/telerik/pushplugin/PushLifecycleCallbacks.java index 363c594..4da892f 100644 --- a/native-src/android/app/src/main/java/com/telerik/pushplugin/PushLifecycleCallbacks.java +++ b/native-src/android/app/src/main/java/com/telerik/pushplugin/PushLifecycleCallbacks.java @@ -42,7 +42,7 @@ public void onActivityPaused(Activity activity) { } public void onActivityResumed(Activity activity) { - Log.d(PushPlugin.TAG, "onActivityPaused: Application has been started"); + Log.d(PushPlugin.TAG, "onActivityResumed: Application has been started"); // the application has been resumed-> the push plugin is now in active/foreground state PushPlugin.isActive = true; diff --git a/native-src/android/app/src/main/java/com/telerik/pushplugin/PushPlugin.java b/native-src/android/app/src/main/java/com/telerik/pushplugin/PushPlugin.java index 21e396c..0ccf066 100644 --- a/native-src/android/app/src/main/java/com/telerik/pushplugin/PushPlugin.java +++ b/native-src/android/app/src/main/java/com/telerik/pushplugin/PushPlugin.java @@ -89,6 +89,7 @@ private static JsonObjectExtended convertMapToJson(Map data) { for (String key: data.keySet()) { json.put(key, JsonObjectExtended.wrap(data.get(key))); } + json.put("foreground", PushPlugin.isActive); } catch (JSONException ex) { Log.d(TAG, "Error thrown while parsing push notification data bundle to json: " + ex.getMessage()); } diff --git a/package.json b/package.json index 7cc4b6f..17cc103 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,21 @@ "platforms": { "ios": "2.4.0", "android": "2.4.1" - } - } + }, + "hooks": [ + { + "type": "before-prepare", + "script": "hooks/before-prepare.js", + "inject": true + } + ] + }, + "scripts": { + "postinstall": "node postinstall.js", + "preuninstall": "node preuninstall.js" + }, + "dependencies": { + "nativescript-hook": "0.2.1" + }, + "typings": "./definitions.d.ts" } diff --git a/platforms/android/pushplugin.aar b/platforms/android/pushplugin.aar index 9f507c0..ac9f792 100644 Binary files a/platforms/android/pushplugin.aar and b/platforms/android/pushplugin.aar differ diff --git a/postinstall.js b/postinstall.js new file mode 100644 index 0000000..3604e32 --- /dev/null +++ b/postinstall.js @@ -0,0 +1,7 @@ +require('nativescript-hook')(__dirname).postinstall(); +var path = require('path'); +var utils = require('./hooks/utils'); +var projDir = path.resolve(__dirname, '../../'); + +utils.checkForGoogleServicesJson(projDir, path.join(projDir, 'app', 'App_Resources')); +utils.addOnPluginInstall(path.join(projDir, 'platforms')); diff --git a/preuninstall.js b/preuninstall.js new file mode 100644 index 0000000..5865817 --- /dev/null +++ b/preuninstall.js @@ -0,0 +1,6 @@ +require('nativescript-hook')(__dirname).preuninstall(); +var path = require('path'); +var utils = require('./hooks/utils'); +var platformsDir = path.resolve(__dirname, '../../platforms'); + +utils.removeIfPresent(platformsDir); diff --git a/push-plugin.android.js b/push-plugin.android.js index 60c986c..b55e0ab 100644 --- a/push-plugin.android.js +++ b/push-plugin.android.js @@ -1,15 +1,19 @@ module.exports = (function () { var app = require('application'); - - (function() { - // Hook on the application events + var registerLifecycleEvents = function () { com.telerik.pushplugin.PushLifecycleCallbacks.registerCallbacks(app.android.nativeApp); - })(); + }; + + // Hook on the application events + if (app.android.nativeApp) { + registerLifecycleEvents(); + } else { + app.on(app.launchEvent, registerLifecycleEvents); + } var pluginObject = { register: function (options, successCallback, errorCallback) { com.telerik.pushplugin.PushPlugin.register(app.android.context, options.senderID, - //Success new com.telerik.pushplugin.PushPluginListener( { success: successCallback,