diff --git a/.classpath b/.classpath
new file mode 100644
index 0000000..2dee36d
--- /dev/null
+++ b/.classpath
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/.gitignore b/.gitignore
index 876179a..eb232e8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,4 +1,11 @@
-build
-dist
+.DS_Store
+.apt_generated
+.project
+.settings
+java-sources.txt
+tmp
+node_modules
libs
-build.properties
+bin
+build
+dist
\ No newline at end of file
diff --git a/README.md b/README.md
index 21bea4e..9cf71b2 100644
--- a/README.md
+++ b/README.md
@@ -9,7 +9,7 @@ Read the [documentation](https://github.com/morinel/gcmpush/blob/master/document
To build, create a `build.properties` file with the following content:
```
-titanium.platform=/Users/###USER###/Library/Application Support/Titanium/mobilesdk/osx/5.1.2.GA/android
+titanium.platform=/Users/###USER###/Library/Application Support/Titanium/mobilesdk/osx/5.4.0.GA/android
android.platform=/Users/###USER###/Library/Android/sdk/platforms/android-23
google.apis=/Users/###USER###/Library/Android/sdk/add-ons/addon-google_apis-google-23
android.ndk=/Users/###USER###/Library/Android/ndk
diff --git a/android/LICENSE b/android/LICENSE
new file mode 100644
index 0000000..7b3b6a0
--- /dev/null
+++ b/android/LICENSE
@@ -0,0 +1,202 @@
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "{}"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright 2015 Jeroen van Vianen
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
diff --git a/android/build.properties b/android/build.properties
new file mode 100644
index 0000000..cd0b1b6
--- /dev/null
+++ b/android/build.properties
@@ -0,0 +1,4 @@
+titanium.platform=/Users/manojkumar/Library/Application Support/Titanium/mobilesdk/osx/5.4.0.GA/android
+android.platform=/Users/manojkumar/Library/Android/sdk/platforms/android-23
+google.apis=/Applications/android-sdk/add-ons/addon-google_apis-google-23
+android.ndk=/Applications/android-ndk-r11b
\ No newline at end of file
diff --git a/android/build.xml b/android/build.xml
new file mode 100644
index 0000000..1634b7a
--- /dev/null
+++ b/android/build.xml
@@ -0,0 +1,56 @@
+
+
+ Ant build script for Titanium Android module gcmpush
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/android/lib/amazon-device-messaging-1.0.1.jar b/android/lib/amazon-device-messaging-1.0.1.jar
new file mode 100644
index 0000000..0fcef1d
Binary files /dev/null and b/android/lib/amazon-device-messaging-1.0.1.jar differ
diff --git a/lib/gcm-server.jar b/android/lib/gcm-server.jar
similarity index 100%
rename from lib/gcm-server.jar
rename to android/lib/gcm-server.jar
diff --git a/lib/google-play-services-gms.jar b/android/lib/google-play-services-gms.jar
similarity index 100%
rename from lib/google-play-services-gms.jar
rename to android/lib/google-play-services-gms.jar
diff --git a/lib/gson-2.3.1.jar b/android/lib/gson-2.3.1.jar
similarity index 100%
rename from lib/gson-2.3.1.jar
rename to android/lib/gson-2.3.1.jar
diff --git a/manifest b/android/manifest
old mode 100755
new mode 100644
similarity index 85%
rename from manifest
rename to android/manifest
index bc18887..fa4fe59
--- a/manifest
+++ b/android/manifest
@@ -2,7 +2,7 @@
# this is your module manifest and used by Titanium
# during compilation, packaging, distribution, etc.
#
-version: 1.7
+version: 1.8.0
apiversion: 3
description: Google Cloud Push for Titanium
author: Jeroen van Vianen
@@ -14,5 +14,5 @@ name: Gcm
moduleid: nl.vanvianen.android.gcm
guid: A2371685-B58E-42E4-8403-DF23A877FF0C
platform: android
-minsdk: 5.1.2.GA
-architectures: armeabi;armeabi-v7a;x86;x86_64
+minsdk: 5.4.0.GA
+architectures: armeabi armeabi-v7a x86
\ No newline at end of file
diff --git a/android/src/nl/vanvianen/android/gcm/ADMMessageHandler.java b/android/src/nl/vanvianen/android/gcm/ADMMessageHandler.java
new file mode 100644
index 0000000..2e74b7c
--- /dev/null
+++ b/android/src/nl/vanvianen/android/gcm/ADMMessageHandler.java
@@ -0,0 +1,65 @@
+package nl.vanvianen.android.gcm;
+
+import java.util.HashMap;
+
+import com.amazon.device.messaging.ADMMessageHandlerBase;
+
+import android.content.Intent;
+import android.util.Log;
+
+public class ADMMessageHandler extends ADMMessageHandlerBase {
+
+ // Standard Debugging variables
+ private static final String LCAT = "ADMMessageHandler";
+
+ public ADMMessageHandler() {
+ super(ADMMessageHandler.class.getName());
+ }
+
+ public static class ADMMessageReceiver extends
+ com.amazon.device.messaging.ADMMessageReceiver {
+ public ADMMessageReceiver() {
+ super(ADMMessageHandler.class);
+ }
+ }
+
+ public void onCreate() {
+ super.onCreate();
+ }
+
+ @Override
+ protected void onRegistered(final String registrationId) {
+ Log.d(LCAT, "Registered: " + registrationId);
+
+ if (GCMModule.getInstance() != null) {
+ GCMModule.getInstance().sendSuccess(registrationId);
+ }
+ }
+
+ @Override
+ protected void onUnregistered(final String registrationId) {
+ Log.i(LCAT, "Unregistered");
+
+ if (GCMModule.getInstance() != null) {
+ GCMModule.getInstance().fireEvent(GCMModule.UNREGISTER_EVENT,
+ new HashMap());
+ }
+ }
+
+ @Override
+ protected void onRegistrationError(final String errorId) {
+ Log.d(LCAT, "RegistrationError: " + errorId);
+
+ if (GCMModule.getInstance() != null) {
+ GCMModule.getInstance().sendError(
+ "ADM registration failed with code " + errorId);
+ }
+ }
+
+ @Override
+ protected void onMessage(final Intent intent) {
+ Log.d(LCAT, "Push notification received");
+
+ NotificationBuilder.build(this, intent);
+ }
+}
diff --git a/src/nl/vanvianen/android/gcm/AppStateListener.java b/android/src/nl/vanvianen/android/gcm/AppStateListener.java
similarity index 100%
rename from src/nl/vanvianen/android/gcm/AppStateListener.java
rename to android/src/nl/vanvianen/android/gcm/AppStateListener.java
diff --git a/src/nl/vanvianen/android/gcm/GCMBroadcastReceiver.java b/android/src/nl/vanvianen/android/gcm/GCMBroadcastReceiver.java
similarity index 100%
rename from src/nl/vanvianen/android/gcm/GCMBroadcastReceiver.java
rename to android/src/nl/vanvianen/android/gcm/GCMBroadcastReceiver.java
diff --git a/android/src/nl/vanvianen/android/gcm/GCMIntentService.java b/android/src/nl/vanvianen/android/gcm/GCMIntentService.java
new file mode 100755
index 0000000..057503b
--- /dev/null
+++ b/android/src/nl/vanvianen/android/gcm/GCMIntentService.java
@@ -0,0 +1,76 @@
+/**
+ * Copyright 2015 Jeroen van Vianen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package nl.vanvianen.android.gcm;
+
+import android.content.Context;
+import android.content.Intent;
+import com.google.android.gcm.GCMBaseIntentService;
+import org.appcelerator.kroll.common.Log;
+
+import java.util.HashMap;
+
+public class GCMIntentService extends GCMBaseIntentService {
+
+ private static final String LCAT = "GCMIntentService";
+
+ public GCMIntentService() {
+ super("");
+ }
+
+ @Override
+ public void onRegistered(Context context, String registrationId) {
+ Log.d(LCAT, "Registered: " + registrationId);
+
+ GCMModule.getInstance().sendSuccess(registrationId);
+ }
+
+ @Override
+ public void onUnregistered(Context context, String registrationId) {
+ Log.d(LCAT, "Unregistered");
+
+ GCMModule.getInstance().fireEvent(GCMModule.UNREGISTER_EVENT,
+ new HashMap());
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ protected void onMessage(Context context, Intent intent) {
+ Log.d(LCAT, "Push notification received");
+
+ NotificationBuilder.build(this, intent);
+ }
+
+ @Override
+ public void onError(Context context, String errorId) {
+ Log.e(LCAT, "Error: " + errorId);
+
+ if (GCMModule.getInstance() != null) {
+ GCMModule.getInstance().sendError(errorId);
+ }
+ }
+
+ @Override
+ public boolean onRecoverableError(Context context, String errorId) {
+ Log.e(LCAT, "RecoverableError: " + errorId);
+
+ if (GCMModule.getInstance() != null) {
+ GCMModule.getInstance().sendError(errorId);
+ }
+
+ return true;
+ }
+}
diff --git a/android/src/nl/vanvianen/android/gcm/GCMModule.java b/android/src/nl/vanvianen/android/gcm/GCMModule.java
new file mode 100644
index 0000000..bf4ff2b
--- /dev/null
+++ b/android/src/nl/vanvianen/android/gcm/GCMModule.java
@@ -0,0 +1,428 @@
+/**
+ * Copyright 2015 Jeroen van Vianen
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package nl.vanvianen.android.gcm;
+
+import android.app.Activity;
+import android.app.NotificationManager;
+import android.os.AsyncTask;
+import com.google.android.gcm.GCMRegistrar;
+import com.google.android.gms.gcm.GcmPubSub;
+import com.google.android.gms.gcm.GoogleCloudMessaging;
+import com.google.android.gms.iid.InstanceID;
+import com.google.gson.Gson;
+import org.appcelerator.kroll.KrollDict;
+import org.appcelerator.kroll.KrollFunction;
+import org.appcelerator.kroll.KrollModule;
+import org.appcelerator.kroll.annotations.Kroll;
+import org.appcelerator.kroll.common.Log;
+import org.appcelerator.titanium.TiApplication;
+import org.json.JSONObject;
+
+import java.util.HashMap;
+import java.util.Map;
+
+@Kroll.module(name = "Gcm", id = "nl.vanvianen.android.gcm")
+public class GCMModule extends KrollModule {
+ // Standard Debugging variables
+ private static final String LCAT = "GCMModule";
+
+ private static GCMModule instance = null;
+ private static AppStateListener appStateListener = null;
+ private static Object adm = null;
+ private static boolean IsADMSupported = false;
+
+ /* Callbacks for push notifications */
+ private KrollFunction successCallback = null;
+ private KrollFunction errorCallback = null;
+ private KrollFunction messageCallback = null;
+
+ /* Callbacks for topics */
+ private KrollFunction successTopicCallback = null;
+ private KrollFunction errorTopicCallback = null;
+ private KrollFunction topicCallback = null;
+
+ public static final String LAST_DATA = "nl.vanvianen.android.gcm.last_data";
+ public static final String NOTIFICATION_SETTINGS = "nl.vanvianen.android.gcm.notification_settings";
+ public static final String UNREGISTER_EVENT = "unregister";
+
+ public GCMModule() {
+ super();
+ instance = this;
+ if (appStateListener == null) {
+ appStateListener = new AppStateListener();
+ TiApplication.addActivityTransitionListener(appStateListener);
+ }
+
+ try {
+ Class.forName("com.amazon.device.messaging.ADM");
+ IsADMSupported = true;
+ adm = (Object) new com.amazon.device.messaging.ADM(
+ TiApplication.getInstance());
+ Log.i(LCAT, "adm supported");
+ } catch (Exception e) {
+ Log.e(LCAT, e.getMessage());
+ e.printStackTrace();
+ }
+ }
+
+ public boolean isInForeground() {
+ return AppStateListener.oneActivityIsResumed;
+ }
+
+ @Kroll.method
+ public boolean isADMSupported() {
+ return IsADMSupported;
+ }
+
+ @Kroll.method
+ @SuppressWarnings("unchecked")
+ public void registerPush(HashMap options) {
+
+ Log.d(LCAT, "registerPush called");
+
+ Map notificationSettings = (Map) options
+ .get("notificationSettings");
+ successCallback = (KrollFunction) options.get("success");
+ errorCallback = (KrollFunction) options.get("error");
+ messageCallback = (KrollFunction) options.get("callback");
+
+ /* Store notification settings in global Ti.App properties */
+ JSONObject json = new JSONObject(notificationSettings);
+ TiApplication.getInstance().getAppProperties()
+ .setString(GCMModule.NOTIFICATION_SETTINGS, json.toString());
+
+ String registrationId;
+
+ if (isADMSupported()) {
+
+ registrationId = getRegistrationId();
+ if (registrationId != null && registrationId.length() > 0) {
+ sendSuccess(registrationId);
+ } else {
+ ((com.amazon.device.messaging.ADM) adm).startRegister();
+ }
+
+ } else {
+ String senderId = (String) options.get("senderId");
+ if (senderId != null) {
+ GCMRegistrar.register(TiApplication.getInstance(), senderId);
+
+ registrationId = getRegistrationId();
+ if (registrationId != null && registrationId.length() > 0) {
+ sendSuccess(registrationId);
+ }
+ } else {
+ sendError(errorCallback,
+ "No GCM senderId specified; get it from the Google Play Developer Console");
+ }
+ }
+ }
+
+ @Kroll.method
+ public void unregister() {
+ Log.d(LCAT, "unregister called (" + (instance != null) + ")");
+
+ try {
+ if (isADMSupported()) {
+ ((com.amazon.device.messaging.ADM) adm).startUnregister();
+ } else {
+ GCMRegistrar.unregister(TiApplication.getInstance());
+ }
+ } catch (Exception ex) {
+ Log.e(LCAT, "Cannot unregister from push: " + ex.getMessage());
+ }
+ }
+
+ @Kroll.method
+ @Kroll.getProperty
+ public String getRegistrationId() {
+ Log.d(LCAT, "get registrationId property");
+ if (isADMSupported()) {
+ return ((com.amazon.device.messaging.ADM) adm).getRegistrationId();
+ } else {
+ return GCMRegistrar.getRegistrationId(TiApplication.getInstance());
+ }
+ }
+
+ @Kroll.method
+ public void subscribe(final HashMap options) {
+ Log.d(LCAT, "subscribe called");
+
+ // subscripe to a topic
+ final String senderId = (String) options.get("senderId");
+ final String topic = (String) options.get("topic");
+
+ if (options.get("success") != null) {
+ successTopicCallback = (KrollFunction) options.get("success");
+ }
+ if (options.get("error") != null) {
+ errorTopicCallback = (KrollFunction) options.get("error");
+ }
+ if (options.get("callback") != null) {
+ topicCallback = (KrollFunction) options.get("callback");
+ }
+
+ if (topic == null || !topic.startsWith("/topics/")) {
+ sendError(errorTopicCallback,
+ "No or invalid topic specified, should start with /topics/");
+ }
+
+ if (senderId != null) {
+ new AsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ try {
+ String token = getToken(senderId);
+ GcmPubSub.getInstance(TiApplication.getInstance())
+ .subscribe(token, topic, null);
+
+ if (successTopicCallback != null) {
+ // send success callback
+ HashMap data = new HashMap();
+ data.put("success", true);
+ data.put("topic", topic);
+ successTopicCallback.callAsync(getKrollObject(),
+ data);
+ }
+ } catch (Exception ex) {
+ // error
+ Log.e(LCAT, "Error " + ex.toString());
+ if (errorTopicCallback != null) {
+ // send error callback
+ HashMap data = new HashMap();
+ data.put("success", false);
+ data.put("topic", topic);
+ data.put("error", ex.toString());
+ errorCallback.callAsync(getKrollObject(), data);
+ }
+ }
+ return null;
+ }
+ }.execute();
+ } else {
+ sendError(errorTopicCallback,
+ "No GCM senderId specified; get it from the Google Play Developer Console");
+ }
+ }
+
+ @Kroll.method
+ public void unsubscribe(final HashMap options) {
+ // unsubscripe from a topic
+ final String senderId = (String) options.get("senderId");
+ final String topic = (String) options.get("topic");
+ final KrollFunction callback = (KrollFunction) options.get("callback");
+
+ if (topic == null || !topic.startsWith("/topics/")) {
+ Log.e(LCAT,
+ "No or invalid topic specified, should start with /topics/");
+ }
+
+ if (senderId != null) {
+ new AsyncTask() {
+ @Override
+ protected Void doInBackground(Void... params) {
+ try {
+ String token = getToken(senderId);
+ if (token != null) {
+ GcmPubSub.getInstance(TiApplication.getInstance())
+ .unsubscribe(token, topic);
+
+ if (callback != null) {
+ // send success callback
+ HashMap data = new HashMap();
+ data.put("success", true);
+ data.put("topic", topic);
+ data.put("token", token);
+ callback.callAsync(getKrollObject(), data);
+ }
+ } else {
+ sendError(callback,
+ "Cannot unsubscribe from topic " + topic);
+ }
+ } catch (Exception ex) {
+ sendError(callback, "Cannot unsubscribe from topic "
+ + topic + ": " + ex.getMessage());
+ }
+ return null;
+ }
+ }.execute();
+ }
+ }
+
+ public String getToken(String senderId) {
+ // get token and return it
+ try {
+ InstanceID instanceID = InstanceID.getInstance(TiApplication
+ .getInstance());
+ return instanceID.getToken(senderId,
+ GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
+ } catch (Exception ex) {
+ return null;
+ }
+ }
+
+ @Kroll.method
+ @Kroll.getProperty
+ @SuppressWarnings("unchecked")
+ public KrollDict getLastData() {
+ Map map = new Gson().fromJson(TiApplication.getInstance()
+ .getAppProperties().getString(LAST_DATA, null), Map.class);
+ return map != null ? new KrollDict(map) : null;
+ }
+
+ @Kroll.method
+ public void clearLastData() {
+ TiApplication.getInstance().getAppProperties()
+ .removeProperty(LAST_DATA);
+ }
+
+ /**
+ * Cancel a notification by the id given in the payload.
+ *
+ * @param notificationId
+ */
+ @Kroll.method
+ public void cancelNotificationById(int notificationId) {
+ try {
+ NotificationManager notificationManager = (NotificationManager) TiApplication
+ .getInstance().getApplicationContext()
+ .getSystemService(TiApplication.NOTIFICATION_SERVICE);
+ notificationManager.cancel(notificationId);
+ Log.i(LCAT, "Notification " + notificationId
+ + " cleared successfully");
+ } catch (Exception ex) {
+ Log.e(LCAT, "Cannot cancel notification:" + notificationId
+ + " Error: " + ex.getMessage());
+ }
+ }
+
+ @Kroll.method
+ @Kroll.getProperty
+ @SuppressWarnings("unchecked")
+ public KrollDict getNotificationSettings() {
+ Log.d(LCAT, "Getting notification settings");
+ Map map = new Gson().fromJson(
+ TiApplication.getInstance().getAppProperties()
+ .getString(GCMModule.NOTIFICATION_SETTINGS, null),
+ Map.class);
+ return map != null ? new KrollDict(map) : null;
+ }
+
+ @Kroll.method
+ @Kroll.setProperty
+ @SuppressWarnings("unchecked")
+ public void setNotificationSettings(Map notificationSettings) {
+ Log.d(LCAT, "Setting notification settings");
+ JSONObject json = new JSONObject(notificationSettings);
+ TiApplication.getInstance().getAppProperties()
+ .setString(GCMModule.NOTIFICATION_SETTINGS, json.toString());
+ }
+
+ public void sendSuccess(String registrationId) {
+ if (successCallback != null) {
+ HashMap data = new HashMap();
+ data.put("success", true);
+ data.put("registrationId", registrationId);
+ successCallback.callAsync(getKrollObject(), data);
+ }
+ }
+
+ public void sendError(String error) {
+ sendError(errorCallback, error);
+ }
+
+ public void sendError(KrollFunction callback, String error) {
+ Log.e(LCAT, error);
+ if (callback != null) {
+ HashMap data = new HashMap();
+ data.put("success", false);
+ data.put("error", error);
+
+ callback.callAsync(getKrollObject(), data);
+ }
+ }
+
+ public void sendMessage(HashMap messageData) {
+ if (messageCallback != null) {
+ HashMap data = new HashMap();
+ data.put("data", messageData);
+ data.put("inBackground", !isInForeground());
+
+ messageCallback.call(getKrollObject(), data);
+ } else {
+ Log.e(LCAT, "No callback specified for push notification");
+ }
+ }
+
+ public void sendTopicMessage(HashMap messageData) {
+ if (topicCallback != null) {
+ HashMap data = new HashMap();
+ data.put("data", messageData);
+ data.put("inBackground", !isInForeground());
+
+ topicCallback.call(getKrollObject(), data);
+ } else {
+ Log.e(LCAT, "No callback specified for topic subscribe");
+ }
+ }
+
+ @Kroll.onAppCreate
+ public static void onAppCreate(TiApplication app) {
+ Log.d(LCAT, "onAppCreate " + app + " (" + (instance != null) + ")");
+ }
+
+ @Override
+ protected void initActivity(Activity activity) {
+ Log.d(LCAT, "initActivity " + activity + " (" + (instance != null)
+ + ")");
+ super.initActivity(activity);
+ }
+
+ @Override
+ public void onResume(Activity activity) {
+ Log.d(LCAT, "onResume " + activity + " (" + (instance != null) + ")");
+ super.onResume(activity);
+ }
+
+ @Override
+ public void onPause(Activity activity) {
+ Log.d(LCAT, "onPause " + activity + " (" + (instance != null) + ")");
+ super.onPause(activity);
+ }
+
+ @Override
+ public void onDestroy(Activity activity) {
+ Log.d(LCAT, "onDestroy " + activity + " (" + (instance != null) + ")");
+ super.onDestroy(activity);
+ }
+
+ @Override
+ public void onStart(Activity activity) {
+ Log.d(LCAT, "onStart " + activity + " (" + (instance != null) + ")");
+ super.onStart(activity);
+ }
+
+ @Override
+ public void onStop(Activity activity) {
+ Log.d(LCAT, "onStop " + activity + " (" + (instance != null) + ")");
+ super.onStop(activity);
+ }
+
+ public static GCMModule getInstance() {
+ return instance;
+ }
+}
\ No newline at end of file
diff --git a/android/src/nl/vanvianen/android/gcm/NotificationBuilder.java b/android/src/nl/vanvianen/android/gcm/NotificationBuilder.java
new file mode 100644
index 0000000..40adbd0
--- /dev/null
+++ b/android/src/nl/vanvianen/android/gcm/NotificationBuilder.java
@@ -0,0 +1,511 @@
+package nl.vanvianen.android.gcm;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.appcelerator.kroll.common.Log;
+import org.appcelerator.titanium.TiApplication;
+import org.appcelerator.titanium.util.TiRHelper;
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.net.Uri;
+import android.support.v4.app.NotificationCompat;
+
+import com.google.gson.Gson;
+
+public class NotificationBuilder {
+
+ private static final String LCAT = "NotificationHelper";
+
+ private static final String DEFAULT_TITLE_KEY = "title";
+ private static final String DEFAULT_MESSAGE_KEY = "message";
+ private static final String DEFAULT_TICKER_KEY = "ticker";
+
+ public static void build(Context context, Intent intent) {
+
+ boolean isTopic = false;
+
+ HashMap data = new HashMap();
+ for (String key : intent.getExtras().keySet()) {
+ Object value = intent.getExtras().get(key);
+ Log.d(LCAT, "Message key: \"" + key + "\" value: \"" + value + "\"");
+
+ if (key.equals("from") && value instanceof String
+ && ((String) value).startsWith("/topics/")) {
+ isTopic = true;
+ }
+
+ String eventKey = key.startsWith("data.") ? key.substring(5) : key;
+ data.put(eventKey, intent.getExtras().get(key));
+
+ if (value instanceof String && ((String) value).startsWith("{")) {
+ Log.d(LCAT, "Parsing JSON string...");
+ try {
+ JSONObject json = new JSONObject((String) value);
+
+ Iterator keys = json.keys();
+ while (keys.hasNext()) {
+ String jKey = keys.next();
+ String jValue = json.getString(jKey);
+ Log.d(LCAT, "JSON key: \"" + jKey + "\" value: \""
+ + jValue + "\"");
+
+ data.put(jKey, jValue);
+ }
+ } catch (JSONException ex) {
+ Log.d(LCAT, "JSON error: " + ex.getMessage());
+ }
+ }
+ }
+
+ /*
+ * Store data to be retrieved when resuming app as a JSON object,
+ * serialized as a String, otherwise
+ * Ti.App.Properties.getString(GCMModule.LAST_DATA) doesn't work.
+ */
+ JSONObject json = new JSONObject(data);
+ TiApplication.getInstance().getAppProperties()
+ .setString(GCMModule.LAST_DATA, json.toString());
+
+ /* Get settings from notification object */
+ int smallIcon = 0;
+ int largeIcon = 0;
+ String sound = null;
+ boolean vibrate = false;
+ boolean insistent = false;
+ String group = null;
+ boolean localOnly = true;
+ int priority = 0;
+ boolean bigText = false;
+ int notificationId = 1;
+
+ Integer ledOn = null;
+ Integer ledOff = null;
+
+ String titleKey = DEFAULT_TITLE_KEY;
+ String messageKey = DEFAULT_MESSAGE_KEY;
+ String tickerKey = DEFAULT_TICKER_KEY;
+ String title = null;
+ String message = null;
+ String ticker = null;
+
+ boolean backgroundOnly = false;
+
+ Map notificationSettings = new Gson().fromJson(
+ TiApplication.getInstance().getAppProperties()
+ .getString(GCMModule.NOTIFICATION_SETTINGS, null),
+ Map.class);
+ if (notificationSettings != null) {
+ if (notificationSettings.get("smallIcon") instanceof String) {
+ smallIcon = getResource("drawable",
+ (String) notificationSettings.get("smallIcon"));
+ } else {
+ Log.e(LCAT, "Invalid setting smallIcon, should be String");
+ }
+
+ if (notificationSettings.get("largeIcon") instanceof String) {
+ largeIcon = getResource("drawable",
+ (String) notificationSettings.get("largeIcon"));
+ } else {
+ Log.e(LCAT, "Invalid setting largeIcon, should be String");
+ }
+
+ if (notificationSettings.get("sound") != null) {
+ if (notificationSettings.get("sound") instanceof String) {
+ sound = (String) notificationSettings.get("sound");
+ } else {
+ Log.e(LCAT, "Invalid setting sound, should be String");
+ }
+ }
+
+ if (notificationSettings.get("vibrate") != null) {
+ if (notificationSettings.get("vibrate") instanceof Boolean) {
+ vibrate = (Boolean) notificationSettings.get("vibrate");
+ } else {
+ Log.e(LCAT, "Invalid setting vibrate, should be Boolean");
+ }
+ }
+
+ if (notificationSettings.get("insistent") != null) {
+ if (notificationSettings.get("insistent") instanceof Boolean) {
+ insistent = (Boolean) notificationSettings.get("insistent");
+ } else {
+ Log.e(LCAT, "Invalid setting insistent, should be Boolean");
+ }
+ }
+
+ if (notificationSettings.get("group") != null) {
+ if (notificationSettings.get("group") instanceof String) {
+ group = (String) notificationSettings.get("group");
+ } else {
+ Log.e(LCAT, "Invalid setting group, should be String");
+ }
+ }
+
+ if (notificationSettings.get("localOnly") != null) {
+ if (notificationSettings.get("localOnly") instanceof Boolean) {
+ localOnly = (Boolean) notificationSettings.get("localOnly");
+ } else {
+ Log.e(LCAT, "Invalid setting localOnly, should be Boolean");
+ }
+ }
+
+ if (notificationSettings.get("priority") != null) {
+ if (notificationSettings.get("priority") instanceof Integer) {
+ priority = (Integer) notificationSettings.get("priority");
+ } else if (notificationSettings.get("priority") instanceof Double) {
+ priority = ((Double) notificationSettings.get("priority"))
+ .intValue();
+ } else {
+ Log.e(LCAT,
+ "Invalid setting priority, should be an integer, between PRIORITY_MIN ("
+ + NotificationCompat.PRIORITY_MIN
+ + ") and PRIORITY_MAX ("
+ + NotificationCompat.PRIORITY_MAX + ")");
+ }
+ }
+
+ if (notificationSettings.get("bigText") != null) {
+ if (notificationSettings.get("bigText") instanceof Boolean) {
+ bigText = (Boolean) notificationSettings.get("bigText");
+ } else {
+ Log.e(LCAT, "Invalid setting bigText, should be Boolean");
+ }
+ }
+
+ if (notificationSettings.get("titleKey") != null) {
+ if (notificationSettings.get("titleKey") instanceof String) {
+ titleKey = (String) notificationSettings.get("titleKey");
+ } else {
+ Log.e(LCAT, "Invalid setting titleKey, should be String");
+ }
+ }
+
+ if (notificationSettings.get("messageKey") != null) {
+ if (notificationSettings.get("messageKey") instanceof String) {
+ messageKey = (String) notificationSettings
+ .get("messageKey");
+ } else {
+ Log.e(LCAT, "Invalid setting messageKey, should be String");
+ }
+ }
+
+ if (notificationSettings.get("tickerKey") != null) {
+ if (notificationSettings.get("tickerKey") instanceof String) {
+ tickerKey = (String) notificationSettings.get("tickerKey");
+ } else {
+ Log.e(LCAT, "Invalid setting tickerKey, should be String");
+ }
+ }
+
+ if (notificationSettings.get("title") != null) {
+ if (notificationSettings.get("title") instanceof String) {
+ title = (String) notificationSettings.get("title");
+ } else {
+ Log.e(LCAT, "Invalid setting title, should be String");
+ }
+ }
+
+ if (notificationSettings.get("message") != null) {
+ if (notificationSettings.get("message") instanceof String) {
+ message = (String) notificationSettings.get("message");
+ } else {
+ Log.e(LCAT, "Invalid setting message, should be String");
+ }
+ }
+
+ if (notificationSettings.get("ticker") != null) {
+ if (notificationSettings.get("ticker") instanceof String) {
+ ticker = (String) notificationSettings.get("ticker");
+ } else {
+ Log.e(LCAT, "Invalid setting ticker, should be String");
+ }
+ }
+
+ if (notificationSettings.get("ledOn") != null) {
+ if (notificationSettings.get("ledOn") instanceof Integer) {
+ ledOn = (Integer) notificationSettings.get("ledOn");
+ if (ledOn < 0) {
+ Log.e(LCAT, "Invalid setting ledOn, should be positive");
+ ledOn = null;
+ }
+ } else {
+ Log.e(LCAT, "Invalid setting ledOn, should be Integer");
+ }
+ }
+
+ if (notificationSettings.get("ledOff") != null) {
+ if (notificationSettings.get("ledOff") instanceof Integer) {
+ ledOff = (Integer) notificationSettings.get("ledOff");
+ if (ledOff < 0) {
+ Log.e(LCAT,
+ "Invalid setting ledOff, should be positive");
+ ledOff = null;
+ }
+ } else {
+ Log.e(LCAT, "Invalid setting ledOff, should be Integer");
+ }
+ }
+
+ if (notificationSettings.get("backgroundOnly") != null) {
+ if (notificationSettings.get("backgroundOnly") instanceof Boolean) {
+ backgroundOnly = (Boolean) notificationSettings
+ .get("backgroundOnly");
+ } else {
+ Log.e(LCAT,
+ "Invalid setting backgroundOnly, should be Boolean");
+ }
+ }
+
+ if (notificationSettings.get("notificationId") != null) {
+ if (notificationSettings.get("notificationId") instanceof Integer) {
+ notificationId = (Integer) notificationSettings
+ .get("notificationId");
+ } else {
+ Log.e(LCAT,
+ "Invalid setting notificationId, should be Integer");
+ }
+ }
+
+ } else {
+ Log.d(LCAT, "No notification settings found");
+ }
+
+ /* If icon not found, default to appicon */
+ if (smallIcon == 0) {
+ smallIcon = getResource("drawable", "appicon");
+ }
+
+ /* If large icon not found, default to icon */
+ if (largeIcon == 0) {
+ largeIcon = smallIcon;
+ }
+
+ /* Create intent to (re)start the app's root activity */
+ String pkg = TiApplication.getInstance().getApplicationContext()
+ .getPackageName();
+ Intent launcherIntent = TiApplication.getInstance()
+ .getApplicationContext().getPackageManager()
+ .getLaunchIntentForPackage(pkg);
+ launcherIntent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
+ launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
+
+ /*
+ * Grab notification content from data according to provided keys if not
+ * already set
+ */
+ if (title == null && titleKey != null) {
+ title = (String) data.get(titleKey);
+ }
+ if (message == null && messageKey != null) {
+ message = (String) data.get(messageKey);
+ }
+ if (ticker == null && tickerKey != null) {
+ ticker = (String) data.get(tickerKey);
+ }
+
+ Log.i(LCAT, "Title: " + title);
+ Log.i(LCAT, "Message: " + message);
+ Log.i(LCAT, "Ticker: " + ticker);
+
+ /* Check for app state */
+ if (GCMModule.getInstance() != null) {
+ /* Send data to app */
+ if (isTopic) {
+ GCMModule.getInstance().sendTopicMessage(data);
+ } else {
+ GCMModule.getInstance().sendMessage(data);
+ }
+ /*
+ * Do not create notification if backgroundOnly and app is in
+ * foreground
+ */
+ if (backgroundOnly && GCMModule.getInstance().isInForeground()) {
+ Log.d(LCAT,
+ "Notification received in foreground, no need for notification.");
+ return;
+ }
+ }
+
+ if (message == null) {
+ Log.d(LCAT,
+ "Message received but no 'message' specified in push notification payload, so will make this silent");
+ } else {
+ Log.d(LCAT, "Creating notification...");
+
+ Bitmap bitmap = BitmapFactory.decodeResource(
+ context.getResources(), largeIcon);
+ if (bitmap == null) {
+ Log.d(LCAT, "No large icon found");
+ }
+
+ NotificationCompat.Builder builder = new NotificationCompat.Builder(
+ context)
+ .setContentTitle(title)
+ .setContentText(message)
+ .setTicker(ticker)
+ .setContentIntent(
+ PendingIntent
+ .getActivity(context, 0, launcherIntent,
+ PendingIntent.FLAG_ONE_SHOT))
+ .setSmallIcon(smallIcon).setLargeIcon(bitmap);
+
+ /*
+ * Name of group to group similar notifications together, can also
+ * be set in the push notification payload
+ */
+ if (data.get("group") != null) {
+ group = (String) data.get("group");
+ }
+ if (group != null) {
+ builder.setGroup(group);
+ }
+ Log.i(LCAT, "Group: " + group);
+
+ /*
+ * Whether notification should be for this device only or bridged to
+ * other devices, can also be set in the push notification payload
+ */
+ if (data.get("localOnly") != null) {
+ localOnly = Boolean.valueOf((String) data.get("localOnly"));
+ }
+ builder.setLocalOnly(localOnly);
+ Log.i(LCAT, "LocalOnly: " + localOnly);
+
+ /*
+ * Specify notification priority, can also be set in the push
+ * notification payload
+ */
+ if (data.get("priority") != null) {
+ priority = Integer.parseInt((String) data.get("priority"));
+ }
+ if (priority >= NotificationCompat.PRIORITY_MIN
+ && priority <= NotificationCompat.PRIORITY_MAX) {
+ builder.setPriority(priority);
+ Log.i(LCAT, "Priority: " + priority);
+ } else {
+ Log.e(LCAT, "Ignored invalid priority " + priority);
+ }
+
+ /*
+ * Specify whether bigtext should be used, can also be set in the
+ * push notification payload
+ */
+ if (data.get("bigText") != null) {
+ bigText = Boolean.valueOf((String) data.get("bigText"));
+ }
+ if (bigText) {
+ builder.setStyle(new NotificationCompat.BigTextStyle()
+ .bigText(message));
+ }
+ Log.i(LCAT, "bigText: " + bigText);
+
+ Notification notification = builder.build();
+
+ /* Sound, can also be set in the push notification payload */
+ if (data.get("sound") != null) {
+ Log.d(LCAT, "Sound specified in notification");
+ sound = (String) data.get("sound");
+ }
+
+ if ("default".equals(sound)) {
+ Log.i(LCAT, "Sound: default sound");
+ notification.defaults |= Notification.DEFAULT_SOUND;
+ } else if (sound != null) {
+ Log.i(LCAT, "Sound " + sound);
+ notification.sound = Uri.parse("android.resource://" + pkg
+ + "/" + getResource("raw", sound));
+ }
+
+ /* Vibrate, can also be set in the push notification payload */
+ if (data.get("vibrate") != null) {
+ vibrate = Boolean.valueOf((String) data.get("vibrate"));
+ }
+ if (vibrate) {
+ notification.defaults |= Notification.DEFAULT_VIBRATE;
+ }
+ Log.i(LCAT, "Vibrate: " + vibrate);
+
+ /* Insistent, can also be set in the push notification payload */
+ if ("true".equals(data.get("insistent"))) {
+ insistent = true;
+ }
+ if (insistent) {
+ notification.flags |= Notification.FLAG_INSISTENT;
+ }
+ Log.i(LCAT, "Insistent: " + insistent);
+
+ /*
+ * notificationId, set in push payload to specify multiple
+ * notifications should be shown. If not specified, subsequent
+ * notifications "override / overwrite" the older ones
+ */
+ if (data.get("notificationId") != null) {
+ if (data.get("notificationId") instanceof Integer) {
+ notificationId = (Integer) data.get("notificationId");
+ } else if (data.get("notificationId") instanceof String) {
+ try {
+ notificationId = Integer.parseInt((String) data
+ .get("notificationId"));
+ } catch (NumberFormatException ex) {
+ Log.e(LCAT,
+ "Invalid setting notificationId, should be Integer");
+ }
+ } else {
+ Log.e(LCAT,
+ "Invalid setting notificationId, should be Integer");
+ }
+ }
+ Log.i(LCAT, "Notification ID: " + notificationId);
+
+ /* Specify LED flashing */
+ if (ledOn != null || ledOff != null) {
+ notification.flags |= Notification.FLAG_SHOW_LIGHTS;
+ if (ledOn != null) {
+ notification.ledOnMS = ledOn;
+ }
+ if (ledOff != null) {
+ notification.ledOffMS = ledOff;
+ }
+ } else {
+ notification.defaults |= Notification.DEFAULT_LIGHTS;
+ }
+
+ notification.flags |= Notification.FLAG_AUTO_CANCEL;
+
+ ((NotificationManager) context
+ .getSystemService(Context.NOTIFICATION_SERVICE)).notify(
+ notificationId, notification);
+ }
+ }
+
+ private static int getResource(String type, String name) {
+ int icon = 0;
+ if (name != null) {
+ /* Remove extension from icon */
+ int index = name.lastIndexOf(".");
+ if (index > 0) {
+ name = name.substring(0, index);
+ }
+ try {
+ icon = TiRHelper.getApplicationResource(type + "." + name);
+ } catch (TiRHelper.ResourceNotFoundException ex) {
+ Log.e(LCAT, type + "." + name
+ + " not found; make sure it's in platform/android/res/"
+ + type);
+ }
+ }
+
+ return icon;
+ }
+
+}
diff --git a/android/timodule.xml b/android/timodule.xml
new file mode 100644
index 0000000..f828076
--- /dev/null
+++ b/android/timodule.xml
@@ -0,0 +1,51 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/build.properties b/build.properties
deleted file mode 100644
index c196372..0000000
--- a/build.properties
+++ /dev/null
@@ -1,4 +0,0 @@
-titanium.platform=/Users/jeroen/Library/Application Support/Titanium/mobilesdk/osx/5.3.0.GA/android
-android.platform=/Users/jeroen/Library/android-sdk-macosx/platforms/android-14
-google.apis=/Users/jeroen/Library/android-sdk-macosx/add-ons/addon-google_apis-google-15
-android.ndk=/Users/jeroen/Library/android-ndk-r12b
diff --git a/build.xml b/build.xml
deleted file mode 100644
index f015cba..0000000
--- a/build.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
- Ant build script for Titanium Android module gcm
-
-
-
-
-
-
-
diff --git a/documentation/index.md b/documentation/index.md
index d6c6ec8..cb20563 100644
--- a/documentation/index.md
+++ b/documentation/index.md
@@ -11,6 +11,23 @@ A Titanium module for registering a device with Google Cloud Messaging and handl
This module does not require any tiapp.xml properties, all configuration is done in Javascript.
+In order to configure ADM properly, you must include this [plugin](https://github.com/morinel/gcmpush/tree/master/plugins/gcmpush) as shown below and replace the conent of [api_key.txt](https://github.com/morinel/gcmpush/blob/master/platform/android/assets/api_key.txt) with your api key.
+
+```xml
+
+ ti.alloy
+ gcmpush
+
+```
+
+Then you must install the dependent node modules of this plugin by running `npm install` from plugin's root directory.
+
+This plugin must know the module version you are using, so you must specify the `version` property on the module tag in `tiapp.xml`
+
+```xml
+nl.vanvianen.android.gcm
+```
+
## Example server-side code to send a push notification ##
Use the following dependency:
diff --git a/example/app.js b/example/app.js
index 6ee1f03..8d2cfa5 100644
--- a/example/app.js
+++ b/example/app.js
@@ -7,6 +7,14 @@ if (lastData) {
gcm.clearLastData();
}
+/**
+ * If you are running on a amazon kindle fire device that supports ADM,
+ * then this module will automatically initate ADM instead of GCM.
+ */
+if (gcm.isADMSupported()) {
+ // write any adm specific code here
+}
+
gcm.registerPush({
/* The Sender ID from Google Developers Console, see https://console.developers.google.com/project/XXXXXXXX/apiui/credential */
/* It's the same as your project id */
diff --git a/hooks/README b/hooks/README
deleted file mode 100644
index 66b10a8..0000000
--- a/hooks/README
+++ /dev/null
@@ -1 +0,0 @@
-These files are not yet supported as of 1.4.0 but will be in a near future release.
diff --git a/hooks/add.py b/hooks/add.py
deleted file mode 100644
index 04e1c1d..0000000
--- a/hooks/add.py
+++ /dev/null
@@ -1,35 +0,0 @@
-#!/usr/bin/env python
-#
-# This is the module project add hook that will be
-# called when your module is added to a project
-#
-import os, sys
-
-def dequote(s):
- if s[0:1] == '"':
- return s[1:-1]
- return s
-
-def main(args,argc):
- # You will get the following command line arguments
- # in the following order:
- #
- # project_dir = the full path to the project root directory
- # project_type = the type of project (desktop, mobile, ipad)
- # project_name = the name of the project
- #
- project_dir = dequote(os.path.expanduser(args[1]))
- project_type = dequote(args[2])
- project_name = dequote(args[3])
-
- # TODO: write your add hook here (optional)
-
-
- # exit
- sys.exit(0)
-
-
-
-if __name__ == '__main__':
- main(sys.argv,len(sys.argv))
-
diff --git a/hooks/install.py b/hooks/install.py
deleted file mode 100644
index b423fe9..0000000
--- a/hooks/install.py
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env python
-#
-# This is the module install hook that will be
-# called when your module is first installed
-#
-import os, sys
-
-def main(args,argc):
-
- # TODO: write your install hook here (optional)
-
- # exit
- sys.exit(0)
-
-
-
-if __name__ == '__main__':
- main(sys.argv,len(sys.argv))
-
diff --git a/hooks/remove.py b/hooks/remove.py
deleted file mode 100644
index f92a234..0000000
--- a/hooks/remove.py
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env python
-#
-# This is the module project remove hook that will be
-# called when your module is remove from a project
-#
-import os, sys
-
-def dequote(s):
- if s[0:1] == '"':
- return s[1:-1]
- return s
-
-def main(args,argc):
- # You will get the following command line arguments
- # in the following order:
- #
- # project_dir = the full path to the project root directory
- # project_type = the type of project (desktop, mobile, ipad)
- # project_name = the name of the project
- #
- project_dir = dequote(os.path.expanduser(args[1]))
- project_type = dequote(args[2])
- project_name = dequote(args[3])
-
- # TODO: write your remove hook here (optional)
-
- # exit
- sys.exit(0)
-
-
-
-if __name__ == '__main__':
- main(sys.argv,len(sys.argv))
-
diff --git a/hooks/uninstall.py b/hooks/uninstall.py
deleted file mode 100644
index a7ffd91..0000000
--- a/hooks/uninstall.py
+++ /dev/null
@@ -1,18 +0,0 @@
-#!/usr/bin/env python
-#
-# This is the module uninstall hook that will be
-# called when your module is uninstalled
-#
-import os, sys
-
-def main(args,argc):
-
- # TODO: write your uninstall hook here (optional)
-
- # exit
- sys.exit(0)
-
-
-if __name__ == '__main__':
- main(sys.argv,len(sys.argv))
-
diff --git a/platform/README b/platform/README
deleted file mode 100644
index 7ac991c..0000000
--- a/platform/README
+++ /dev/null
@@ -1,3 +0,0 @@
-You can place platform-specific files here in sub-folders named "android" and/or "iphone", just as you can with normal Titanium Mobile SDK projects. Any folders and files you place here will be merged with the platform-specific files in a Titanium Mobile project that uses this module.
-
-When a Titanium Mobile project that uses this module is built, the files from this platform/ folder will be treated the same as files (if any) from the Titanium Mobile project's platform/ folder.
diff --git a/platform/android/assets/api_key.txt b/platform/android/assets/api_key.txt
new file mode 100644
index 0000000..ecc9d7a
--- /dev/null
+++ b/platform/android/assets/api_key.txt
@@ -0,0 +1 @@
+Replace this line with your API Key.
\ No newline at end of file
diff --git a/plugins/gcmpush/hooks/gcmpush.js b/plugins/gcmpush/hooks/gcmpush.js
new file mode 100644
index 0000000..1024229
--- /dev/null
+++ b/plugins/gcmpush/hooks/gcmpush.js
@@ -0,0 +1,67 @@
+exports.cliVersion = ">=3.X";
+
+var LCAT = "GCMPush",
+ fs = require("fs"),
+ path = require("path"),
+ AndroidManifest = require("androidmanifest");
+
+exports.init = function(logger, config, cli, nodeappc) {
+
+ var platform = cli.argv.platform,
+ projectDir = cli.argv["project-dir"];
+
+ if (platform === "android") {
+
+ /**
+ * delete amazon-device-messaging stub library from libs
+ * ToDo: handle with Ant at module build time
+ */
+ cli.on("build.pre.compile", function(data, done) {
+ var moduleId = "nl.vanvianen.android.gcm",
+ modules = cli.tiapp.modules,
+ modulePath;
+ for (var i in modules) {
+ var module = modules[i];
+ if (module.id === moduleId) {
+ modulePath = path.join(projectDir, "modules/android", moduleId, module.version, "lib");
+ break;
+ }
+ }
+ var jars = fs.readdirSync(modulePath);
+ for (var i in jars) {
+ var jar = jars[i];
+ if (jar.indexOf("amazon-device-messaging") >= 0) {
+ fs.unlinkSync(path.join(modulePath, jar));
+ break;
+ }
+ }
+ done();
+ });
+
+ /**
+ * TIMOB-12591
+ */
+ cli.on("build.android.copyResource", function(data, done) {
+ var apiKeyFile = path.join(projectDir, "build/android/assets/api_key.txt"),
+ binApiKeyFile = path.join(projectDir, "build/android/bin/assets/api_key.txt");
+ if (fs.existsSync(apiKeyFile) && !fs.existsSync(binApiKeyFile)) {
+ fs.createReadStream(apiKeyFile).pipe(fs.createWriteStream(binApiKeyFile));
+ }
+ done();
+ });
+
+ /**
+ * Appc cli eliminates custom tags
+ */
+ cli.on("build.android.writeAndroidManifest", function(data, done) {
+ var manifestFilePath = this.androidManifestFile,
+ manifest = new AndroidManifest().readFile(manifestFilePath);
+
+ manifest.$("manifest").attr("xmlns:amazon", "http://schemas.amazon.com/apk/res/android");
+ manifest.$("application").append("");
+ manifest.writeFile(manifestFilePath);
+
+ done();
+ });
+ }
+};
diff --git a/plugins/gcmpush/package.json b/plugins/gcmpush/package.json
new file mode 100644
index 0000000..801ba3c
--- /dev/null
+++ b/plugins/gcmpush/package.json
@@ -0,0 +1,10 @@
+{
+ "id": "gcmpush",
+ "name": "gcmpush",
+ "author": "Manoj Kumar",
+ "version": "1.0.0",
+ "copyright": "Copyright (c) 2016",
+ "dependencies": {
+ "androidmanifest": "^2.0.0"
+ }
+}
diff --git a/src/nl/vanvianen/android/gcm/GCMIntentService.java b/src/nl/vanvianen/android/gcm/GCMIntentService.java
deleted file mode 100755
index 87082ea..0000000
--- a/src/nl/vanvianen/android/gcm/GCMIntentService.java
+++ /dev/null
@@ -1,508 +0,0 @@
-/**
- * Copyright 2015 Jeroen van Vianen
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package nl.vanvianen.android.gcm;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.content.Context;
-import android.content.Intent;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.net.Uri;
-import android.support.v4.app.NotificationCompat;
-import com.google.android.gcm.GCMBaseIntentService;
-import com.google.gson.Gson;
-import org.appcelerator.kroll.common.Log;
-import org.appcelerator.titanium.TiApplication;
-import org.appcelerator.titanium.util.TiRHelper;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.concurrent.atomic.AtomicInteger;
-
-public class GCMIntentService extends GCMBaseIntentService {
-
- private static final String LCAT = "GCMIntentService";
-
- private static final String UNREGISTER_EVENT = "unregister";
-
- private static final String DEFAULT_TITLE_KEY = "title";
- private static final String DEFAULT_MESSAGE_KEY = "message";
- private static final String DEFAULT_TICKER_KEY = "ticker";
-
- private final static AtomicInteger notificationCounter = new AtomicInteger(0);
-
- public GCMIntentService() {
- super("");
- }
-
- @Override
- public void onRegistered(Context context, String registrationId) {
- Log.d(LCAT, "Registered: " + registrationId);
-
- GCMModule.getInstance().sendSuccess(registrationId);
- }
-
- @Override
- public void onUnregistered(Context context, String registrationId) {
- Log.d(LCAT, "Unregistered");
-
- GCMModule.getInstance().fireEvent(UNREGISTER_EVENT, new HashMap());
- }
-
- private int getResource(String type, String name) {
- int icon = 0;
- if (name != null) {
- /* Remove extension from icon */
- int index = name.lastIndexOf(".");
- if (index > 0) {
- name = name.substring(0, index);
- }
- try {
- icon = TiRHelper.getApplicationResource(type + "." + name);
- } catch (TiRHelper.ResourceNotFoundException ex) {
- Log.e(LCAT, type + "." + name + " not found; make sure it's in platform/android/res/" + type);
- }
- }
-
- return icon;
- }
-
- @Override
- @SuppressWarnings("unchecked")
- protected void onMessage(Context context, Intent intent) {
- Log.d(LCAT, "Push notification received");
-
- boolean isTopic = false;
-
- HashMap data = new HashMap();
- for (String key : intent.getExtras().keySet()) {
- Object value = intent.getExtras().get(key);
- Log.d(LCAT, "Message key: \"" + key + "\" value: \"" + value + "\"");
-
- if (key.equals("from") && value instanceof String && ((String) value).startsWith("/topics/")) {
- isTopic = true;
- }
-
- String eventKey = key.startsWith("data.") ? key.substring(5) : key;
- data.put(eventKey, intent.getExtras().get(key));
-
- if (value instanceof String && ((String) value).startsWith("{")) {
- Log.d(LCAT, "Parsing JSON string...");
- try {
- JSONObject json = new JSONObject((String) value);
-
- Iterator keys = json.keys();
- while (keys.hasNext()) {
- String jKey = keys.next();
- String jValue = json.getString(jKey);
- Log.d(LCAT, "JSON key: \"" + jKey + "\" value: \"" + jValue + "\"");
-
- data.put(jKey, jValue);
- }
- } catch(JSONException ex) {
- Log.d(LCAT, "JSON error: " + ex.getMessage());
- }
- }
- }
-
- /* Store data to be retrieved when resuming app as a JSON object, serialized as a String, otherwise
- * Ti.App.Properties.getString(GCMModule.LAST_DATA) doesn't work. */
- JSONObject json = new JSONObject(data);
- TiApplication.getInstance().getAppProperties().setString(GCMModule.LAST_DATA, json.toString());
-
- /* Get settings from notification object */
- int smallIcon = 0;
- int largeIcon = 0;
- String sound = null;
- boolean vibrate = false;
- boolean insistent = false;
- String group = null;
- boolean localOnly = true;
- int priority = 0;
- boolean bigText = false;
- int notificationId = 1;
-
- Integer ledOn = null;
- Integer ledOff = null;
-
- String titleKey = DEFAULT_TITLE_KEY;
- String messageKey = DEFAULT_MESSAGE_KEY;
- String tickerKey = DEFAULT_TICKER_KEY;
- String title = null;
- String message = null;
- String ticker = null;
-
- boolean backgroundOnly = false;
-
- Map notificationSettings = new Gson().fromJson(TiApplication.getInstance().getAppProperties().getString(GCMModule.NOTIFICATION_SETTINGS, null), Map.class);
- if (notificationSettings != null) {
- if (notificationSettings.get("smallIcon") instanceof String) {
- smallIcon = getResource("drawable", (String) notificationSettings.get("smallIcon"));
- } else {
- Log.e(LCAT, "Invalid setting smallIcon, should be String");
- }
-
- if (notificationSettings.get("largeIcon") instanceof String) {
- largeIcon = getResource("drawable", (String) notificationSettings.get("largeIcon"));
- } else {
- Log.e(LCAT, "Invalid setting largeIcon, should be String");
- }
-
- if (notificationSettings.get("sound") != null) {
- if (notificationSettings.get("sound") instanceof String) {
- sound = (String) notificationSettings.get("sound");
- } else {
- Log.e(LCAT, "Invalid setting sound, should be String");
- }
- }
-
- if (notificationSettings.get("vibrate") != null) {
- if (notificationSettings.get("vibrate") instanceof Boolean) {
- vibrate = (Boolean) notificationSettings.get("vibrate");
- } else {
- Log.e(LCAT, "Invalid setting vibrate, should be Boolean");
- }
- }
-
- if (notificationSettings.get("insistent") != null) {
- if (notificationSettings.get("insistent") instanceof Boolean) {
- insistent = (Boolean) notificationSettings.get("insistent");
- } else {
- Log.e(LCAT, "Invalid setting insistent, should be Boolean");
- }
- }
-
- if (notificationSettings.get("group") != null) {
- if (notificationSettings.get("group") instanceof String) {
- group = (String) notificationSettings.get("group");
- } else {
- Log.e(LCAT, "Invalid setting group, should be String");
- }
- }
-
- if (notificationSettings.get("localOnly") != null) {
- if (notificationSettings.get("localOnly") instanceof Boolean) {
- localOnly = (Boolean) notificationSettings.get("localOnly");
- } else {
- Log.e(LCAT, "Invalid setting localOnly, should be Boolean");
- }
- }
-
- if (notificationSettings.get("priority") != null) {
- if (notificationSettings.get("priority") instanceof Integer) {
- priority = (Integer) notificationSettings.get("priority");
- } else if (notificationSettings.get("priority") instanceof Double) {
- priority = ((Double) notificationSettings.get("priority")).intValue();
- } else {
- Log.e(LCAT, "Invalid setting priority, should be an integer, between PRIORITY_MIN (" + NotificationCompat.PRIORITY_MIN + ") and PRIORITY_MAX (" + NotificationCompat.PRIORITY_MAX + ")");
- }
- }
-
- if (notificationSettings.get("bigText") != null) {
- if (notificationSettings.get("bigText") instanceof Boolean) {
- bigText = (Boolean) notificationSettings.get("bigText");
- } else {
- Log.e(LCAT, "Invalid setting bigText, should be Boolean");
- }
- }
-
- if (notificationSettings.get("titleKey") != null) {
- if (notificationSettings.get("titleKey") instanceof String) {
- titleKey = (String) notificationSettings.get("titleKey");
- } else {
- Log.e(LCAT, "Invalid setting titleKey, should be String");
- }
- }
-
- if (notificationSettings.get("messageKey") != null) {
- if (notificationSettings.get("messageKey") instanceof String) {
- messageKey = (String) notificationSettings.get("messageKey");
- } else {
- Log.e(LCAT, "Invalid setting messageKey, should be String");
- }
- }
-
- if (notificationSettings.get("tickerKey") != null) {
- if (notificationSettings.get("tickerKey") instanceof String) {
- tickerKey = (String) notificationSettings.get("tickerKey");
- } else {
- Log.e(LCAT, "Invalid setting tickerKey, should be String");
- }
- }
-
- if (notificationSettings.get("title") != null) {
- if (notificationSettings.get("title") instanceof String) {
- title = (String) notificationSettings.get("title");
- } else {
- Log.e(LCAT, "Invalid setting title, should be String");
- }
- }
-
- if (notificationSettings.get("message") != null) {
- if (notificationSettings.get("message") instanceof String) {
- message = (String) notificationSettings.get("message");
- } else {
- Log.e(LCAT, "Invalid setting message, should be String");
- }
- }
-
- if (notificationSettings.get("ticker") != null) {
- if (notificationSettings.get("ticker") instanceof String) {
- ticker = (String) notificationSettings.get("ticker");
- } else {
- Log.e(LCAT, "Invalid setting ticker, should be String");
- }
- }
-
- if (notificationSettings.get("ledOn") != null) {
- if (notificationSettings.get("ledOn") instanceof Integer) {
- ledOn = (Integer) notificationSettings.get("ledOn");
- if (ledOn < 0) {
- Log.e(LCAT, "Invalid setting ledOn, should be positive");
- ledOn = null;
- }
- } else {
- Log.e(LCAT, "Invalid setting ledOn, should be Integer");
- }
- }
-
- if (notificationSettings.get("ledOff") != null) {
- if (notificationSettings.get("ledOff") instanceof Integer) {
- ledOff = (Integer) notificationSettings.get("ledOff");
- if (ledOff < 0) {
- Log.e(LCAT, "Invalid setting ledOff, should be positive");
- ledOff = null;
- }
- } else {
- Log.e(LCAT, "Invalid setting ledOff, should be Integer");
- }
- }
-
- if (notificationSettings.get("backgroundOnly") != null) {
- if (notificationSettings.get("backgroundOnly") instanceof Boolean) {
- backgroundOnly = (Boolean) notificationSettings.get("backgroundOnly");
- } else {
- Log.e(LCAT, "Invalid setting backgroundOnly, should be Boolean");
- }
- }
-
- if (notificationSettings.get("notificationId") != null) {
- if (notificationSettings.get("notificationId") instanceof Integer) {
- notificationId = (Integer) notificationSettings.get("notificationId");
- } else {
- Log.e(LCAT, "Invalid setting notificationId, should be Integer");
- }
- }
-
- } else {
- Log.d(LCAT, "No notification settings found");
- }
-
- /* If icon not found, default to appicon */
- if (smallIcon == 0) {
- smallIcon = getResource("drawable", "appicon");
- }
-
- /* If large icon not found, default to icon */
- if (largeIcon == 0) {
- largeIcon = smallIcon;
- }
-
- /* Create intent to (re)start the app's root activity */
- String pkg = TiApplication.getInstance().getApplicationContext().getPackageName();
- Intent launcherIntent = TiApplication.getInstance().getApplicationContext().getPackageManager().getLaunchIntentForPackage(pkg);
- launcherIntent.setFlags(Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- launcherIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-
- /* Grab notification content from data according to provided keys if not already set */
- if (title == null && titleKey != null) {
- title = (String) data.get(titleKey);
- }
- if (message == null && messageKey != null) {
- message = (String) data.get(messageKey);
- }
- if (ticker == null && tickerKey != null) {
- ticker = (String) data.get(tickerKey);
- }
-
- Log.i(LCAT, "Title: " + title);
- Log.i(LCAT, "Message: " + message);
- Log.i(LCAT, "Ticker: " + ticker);
-
- /* Check for app state */
- if (GCMModule.getInstance() != null) {
- /* Send data to app */
- if (isTopic) {
- GCMModule.getInstance().sendTopicMessage(data);
- } else {
- GCMModule.getInstance().sendMessage(data);
- }
- /* Do not create notification if backgroundOnly and app is in foreground */
- if (backgroundOnly && GCMModule.getInstance().isInForeground()) {
- Log.d(LCAT, "Notification received in foreground, no need for notification.");
- return;
- }
- }
-
- if (message == null) {
- Log.d(LCAT, "Message received but no 'message' specified in push notification payload, so will make this silent");
- } else {
- Log.d(LCAT, "Creating notification...");
-
- Bitmap bitmap = BitmapFactory.decodeResource(getResources(), largeIcon);
- if (bitmap == null) {
- Log.d(LCAT, "No large icon found");
- }
-
- NotificationCompat.Builder builder = new NotificationCompat.Builder(context)
- .setContentTitle(title)
- .setContentText(message)
- .setTicker(ticker)
- .setContentIntent(PendingIntent.getActivity(this, 0, launcherIntent, PendingIntent.FLAG_ONE_SHOT))
- .setSmallIcon(smallIcon)
- .setLargeIcon(bitmap);
-
- /* Name of group to group similar notifications together, can also be set in the push notification payload */
- if (data.get("group") != null) {
- group = (String) data.get("group");
- }
- if (group != null) {
- builder.setGroup(group);
- }
- Log.i(LCAT, "Group: " + group);
-
- /* Whether notification should be for this device only or bridged to other devices, can also be set in the push notification payload */
- if (data.get("localOnly") != null) {
- localOnly = Boolean.valueOf((String) data.get("localOnly"));
- }
- builder.setLocalOnly(localOnly);
- Log.i(LCAT, "LocalOnly: " + localOnly);
-
- /* Specify notification priority, can also be set in the push notification payload */
- if (data.get("priority") != null) {
- priority = Integer.parseInt((String) data.get("priority"));
- }
- if (priority >= NotificationCompat.PRIORITY_MIN && priority <= NotificationCompat.PRIORITY_MAX) {
- builder.setPriority(priority);
- Log.i(LCAT, "Priority: " + priority);
- } else {
- Log.e(LCAT, "Ignored invalid priority " + priority);
- }
-
- /* Specify whether bigtext should be used, can also be set in the push notification payload */
- if (data.get("bigText") != null) {
- bigText = Boolean.valueOf((String) data.get("bigText"));
- }
- if (bigText) {
- builder.setStyle(new NotificationCompat.BigTextStyle().bigText(message));
- }
- Log.i(LCAT, "bigText: " + bigText);
-
- Notification notification = builder.build();
-
- /* Sound, can also be set in the push notification payload */
- if (data.get("sound") != null) {
- Log.d(LCAT, "Sound specified in notification");
- sound = (String) data.get("sound");
- }
-
- if ("default".equals(sound)) {
- Log.i(LCAT, "Sound: default sound");
- notification.defaults |= Notification.DEFAULT_SOUND;
- } else if (sound != null) {
- Log.i(LCAT, "Sound " + sound);
- notification.sound = Uri.parse("android.resource://" + pkg + "/" + getResource("raw", sound));
- }
-
- /* Vibrate, can also be set in the push notification payload */
- if (data.get("vibrate") != null) {
- vibrate = Boolean.valueOf((String) data.get("vibrate"));
- }
- if (vibrate) {
- notification.defaults |= Notification.DEFAULT_VIBRATE;
- }
- Log.i(LCAT, "Vibrate: " + vibrate);
-
- /* Insistent, can also be set in the push notification payload */
- if ("true".equals(data.get("insistent"))) {
- insistent = true;
- }
- if (insistent) {
- notification.flags |= Notification.FLAG_INSISTENT;
- }
- Log.i(LCAT, "Insistent: " + insistent);
-
- /* notificationId, set in push payload to specify multiple notifications should be shown. If not specified, subsequent notifications "override / overwrite" the older ones */
- if (data.get("notificationId") != null) {
- if (data.get("notificationId") instanceof Integer) {
- notificationId = (Integer) data.get("notificationId");
- } else if (data.get("notificationId") instanceof String) {
- try {
- notificationId = Integer.parseInt((String) data.get("notificationId"));
- } catch (NumberFormatException ex) {
- Log.e(LCAT, "Invalid setting notificationId, should be Integer");
- }
- } else {
- Log.e(LCAT, "Invalid setting notificationId, should be Integer");
- }
- }
- Log.i(LCAT, "Notification ID: " + notificationId);
-
- /* Specify LED flashing */
- if (ledOn != null || ledOff != null) {
- notification.flags |= Notification.FLAG_SHOW_LIGHTS;
- if (ledOn != null) {
- notification.ledOnMS = ledOn;
- }
- if (ledOff != null) {
- notification.ledOffMS = ledOff;
- }
- } else {
- notification.defaults |= Notification.DEFAULT_LIGHTS;
- }
-
- notification.flags |= Notification.FLAG_AUTO_CANCEL;
-
- ((NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE)).notify(notificationId, notification);
- }
- }
-
- @Override
- public void onError(Context context, String errorId) {
- Log.e(LCAT, "Error: " + errorId);
-
- if (GCMModule.getInstance() != null) {
- GCMModule.getInstance().sendError(errorId);
- }
- }
-
- @Override
- public boolean onRecoverableError(Context context, String errorId) {
- Log.e(LCAT, "RecoverableError: " + errorId);
-
- if (GCMModule.getInstance() != null) {
- GCMModule.getInstance().sendError(errorId);
- }
-
- return true;
- }
-}
diff --git a/src/nl/vanvianen/android/gcm/GCMModule.java b/src/nl/vanvianen/android/gcm/GCMModule.java
deleted file mode 100644
index d5c24b9..0000000
--- a/src/nl/vanvianen/android/gcm/GCMModule.java
+++ /dev/null
@@ -1,367 +0,0 @@
-/**
- * Copyright 2015 Jeroen van Vianen
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package nl.vanvianen.android.gcm;
-
-import android.app.Activity;
-import android.app.NotificationManager;
-import android.os.AsyncTask;
-import com.google.android.gcm.GCMRegistrar;
-import com.google.android.gms.gcm.GcmPubSub;
-import com.google.android.gms.gcm.GoogleCloudMessaging;
-import com.google.android.gms.iid.InstanceID;
-import com.google.gson.Gson;
-import org.appcelerator.kroll.KrollDict;
-import org.appcelerator.kroll.KrollFunction;
-import org.appcelerator.kroll.KrollModule;
-import org.appcelerator.kroll.annotations.Kroll;
-import org.appcelerator.kroll.common.Log;
-import org.appcelerator.titanium.TiApplication;
-import org.json.JSONObject;
-
-import java.util.HashMap;
-import java.util.Map;
-
-@Kroll.module(name = "Gcm", id = "nl.vanvianen.android.gcm")
-public class GCMModule extends KrollModule {
- // Standard Debugging variables
- private static final String LCAT = "GCMModule";
-
- private static GCMModule instance = null;
- private static AppStateListener appStateListener = null;
-
- /* Callbacks for push notifications */
- private KrollFunction successCallback = null;
- private KrollFunction errorCallback = null;
- private KrollFunction messageCallback = null;
-
- /* Callbacks for topics */
- private KrollFunction successTopicCallback = null;
- private KrollFunction errorTopicCallback = null;
- private KrollFunction topicCallback = null;
-
-
- public static final String LAST_DATA = "nl.vanvianen.android.gcm.last_data";
- public static final String NOTIFICATION_SETTINGS = "nl.vanvianen.android.gcm.notification_settings";
-
- public GCMModule() {
- super();
- instance = this;
- if (appStateListener == null) {
- appStateListener = new AppStateListener();
- TiApplication.addActivityTransitionListener(appStateListener);
- }
-
- }
-
- public boolean isInForeground() {
- return AppStateListener.oneActivityIsResumed;
- }
-
- @Kroll.method
- @SuppressWarnings("unchecked")
- public void registerPush(HashMap options) {
-
- Log.d(LCAT, "registerPush called");
-
- String senderId = (String) options.get("senderId");
- Map notificationSettings = (Map) options.get("notificationSettings");
- successCallback = (KrollFunction) options.get("success");
- errorCallback = (KrollFunction) options.get("error");
- messageCallback = (KrollFunction) options.get("callback");
-
- /* Store notification settings in global Ti.App properties */
- JSONObject json = new JSONObject(notificationSettings);
- TiApplication.getInstance().getAppProperties().setString(GCMModule.NOTIFICATION_SETTINGS, json.toString());
-
- if (senderId != null) {
- GCMRegistrar.register(TiApplication.getInstance(), senderId);
-
- String registrationId = getRegistrationId();
- if (registrationId != null && registrationId.length() > 0) {
- sendSuccess(registrationId);
- }
- } else {
- sendError(errorCallback, "No GCM senderId specified; get it from the Google Play Developer Console");
- }
- }
-
- @Kroll.method
- public void unregister() {
- Log.d(LCAT, "unregister called (" + (instance != null) + ")");
- try {
- GCMRegistrar.unregister(TiApplication.getInstance());
- } catch (Exception ex) {
- Log.e(LCAT, "Cannot unregister from push: " + ex.getMessage());
- }
- }
-
-
- @Kroll.method
- @Kroll.getProperty
- public String getRegistrationId() {
- Log.d(LCAT, "get registrationId property");
- return GCMRegistrar.getRegistrationId(TiApplication.getInstance());
- }
-
-
- @Kroll.method
- public void subscribe(final HashMap options) {
- Log.d(LCAT, "subscribe called");
-
- // subscripe to a topic
- final String senderId = (String) options.get("senderId");
- final String topic = (String) options.get("topic");
-
- if (options.get("success") != null) {
- successTopicCallback = (KrollFunction) options.get("success");
- }
- if (options.get("error") != null) {
- errorTopicCallback = (KrollFunction) options.get("error");
- }
- if (options.get("callback") != null) {
- topicCallback = (KrollFunction) options.get("callback");
- }
-
- if (topic == null || !topic.startsWith("/topics/")) {
- sendError(errorTopicCallback, "No or invalid topic specified, should start with /topics/");
- }
-
- if (senderId != null) {
- new AsyncTask() {
- @Override
- protected Void doInBackground(Void... params) {
- try {
- String token = getToken(senderId);
- GcmPubSub.getInstance(TiApplication.getInstance()).subscribe(token, topic, null);
-
- if (successTopicCallback != null) {
- // send success callback
- HashMap data = new HashMap();
- data.put("success", true);
- data.put("topic", topic);
- successTopicCallback.callAsync(getKrollObject(), data);
- }
- } catch (Exception ex) {
- // error
- Log.e(LCAT, "Error " + ex.toString());
- if (errorTopicCallback != null) {
- // send error callback
- HashMap data = new HashMap();
- data.put("success", false);
- data.put("topic", topic);
- data.put("error", ex.toString());
- errorCallback.callAsync(getKrollObject(), data);
- }
- }
- return null;
- }
- }.execute();
- } else {
- sendError(errorTopicCallback, "No GCM senderId specified; get it from the Google Play Developer Console");
- }
- }
-
- @Kroll.method
- public void unsubscribe(final HashMap options) {
- // unsubscripe from a topic
- final String senderId = (String) options.get("senderId");
- final String topic = (String) options.get("topic");
- final KrollFunction callback = (KrollFunction) options.get("callback");
-
- if (topic == null || !topic.startsWith("/topics/")) {
- Log.e(LCAT, "No or invalid topic specified, should start with /topics/");
- }
-
- if (senderId != null) {
- new AsyncTask() {
- @Override
- protected Void doInBackground(Void... params) {
- try {
- String token = getToken(senderId);
- if (token != null) {
- GcmPubSub.getInstance(TiApplication.getInstance()).unsubscribe(token, topic);
-
- if (callback != null) {
- // send success callback
- HashMap data = new HashMap();
- data.put("success", true);
- data.put("topic", topic);
- data.put("token", token);
- callback.callAsync(getKrollObject(), data);
- }
- } else {
- sendError(callback, "Cannot unsubscribe from topic " + topic);
- }
- } catch (Exception ex) {
- sendError(callback, "Cannot unsubscribe from topic " + topic + ": " + ex.getMessage());
- }
- return null;
- }
- }.execute();
- }
- }
-
- public String getToken(String senderId){
- // get token and return it
- try {
- InstanceID instanceID = InstanceID.getInstance(TiApplication.getInstance());
- return instanceID.getToken(senderId, GoogleCloudMessaging.INSTANCE_ID_SCOPE, null);
- } catch (Exception ex) {
- return null;
- }
- }
-
- @Kroll.method
- @Kroll.getProperty
- @SuppressWarnings("unchecked")
- public KrollDict getLastData() {
- Map map = new Gson().fromJson(TiApplication.getInstance().getAppProperties().getString(LAST_DATA, null), Map.class);
- return map != null ? new KrollDict(map) : null;
- }
-
- @Kroll.method
- public void clearLastData() {
- TiApplication.getInstance().getAppProperties().removeProperty(LAST_DATA);
- }
-
- /**
- * Cancel a notification by the id given in the payload.
- * @param notificationId
- */
- @Kroll.method
- public void cancelNotificationById(int notificationId) {
- try {
- NotificationManager notificationManager = (NotificationManager) TiApplication.getInstance().getApplicationContext().getSystemService(TiApplication.NOTIFICATION_SERVICE);
- notificationManager.cancel(notificationId);
- Log.i(LCAT, "Notification " + notificationId + " cleared successfully");
- } catch (Exception ex) {
- Log.e(LCAT, "Cannot cancel notification:" + notificationId + " Error: " + ex.getMessage());
- }
- }
-
- @Kroll.method
- @Kroll.getProperty
- @SuppressWarnings("unchecked")
- public KrollDict getNotificationSettings() {
- Log.d(LCAT, "Getting notification settings");
- Map map = new Gson().fromJson(TiApplication.getInstance().getAppProperties().getString(GCMModule.NOTIFICATION_SETTINGS, null), Map.class);
- return map != null ? new KrollDict(map) : null;
- }
-
- @Kroll.method
- @Kroll.setProperty
- @SuppressWarnings("unchecked")
- public void setNotificationSettings(Map notificationSettings) {
- Log.d(LCAT, "Setting notification settings");
- JSONObject json = new JSONObject(notificationSettings);
- TiApplication.getInstance().getAppProperties().setString(GCMModule.NOTIFICATION_SETTINGS, json.toString());
- }
-
- public void sendSuccess(String registrationId) {
- if (successCallback != null) {
- HashMap data = new HashMap();
- data.put("success", true);
- data.put("registrationId", registrationId);
- successCallback.callAsync(getKrollObject(), data);
- }
- }
-
- public void sendError(String error) {
- sendError(errorCallback, error);
- }
-
-
- public void sendError(KrollFunction callback, String error) {
- Log.e(LCAT, error);
- if (callback != null) {
- HashMap data = new HashMap();
- data.put("success", false);
- data.put("error", error);
-
- callback.callAsync(getKrollObject(), data);
- }
- }
-
- public void sendMessage(HashMap messageData) {
- if (messageCallback != null) {
- HashMap data = new HashMap();
- data.put("data", messageData);
- data.put("inBackground", !isInForeground());
-
- messageCallback.call(getKrollObject(), data);
- } else {
- Log.e(LCAT, "No callback specified for push notification");
- }
- }
-
- public void sendTopicMessage(HashMap messageData) {
- if (topicCallback != null) {
- HashMap data = new HashMap();
- data.put("data", messageData);
- data.put("inBackground", !isInForeground());
-
- topicCallback.call(getKrollObject(), data);
- } else {
- Log.e(LCAT, "No callback specified for topic subscribe");
- }
- }
-
- @Kroll.onAppCreate
- public static void onAppCreate(TiApplication app) {
- Log.d(LCAT, "onAppCreate " + app + " (" + (instance != null) + ")");
- }
-
- @Override
- protected void initActivity(Activity activity) {
- Log.d(LCAT, "initActivity " + activity + " (" + (instance != null) + ")");
- super.initActivity(activity);
- }
-
- @Override
- public void onResume(Activity activity) {
- Log.d(LCAT, "onResume " + activity + " (" + (instance != null) + ")");
- super.onResume(activity);
- }
-
- @Override
- public void onPause(Activity activity) {
- Log.d(LCAT, "onPause " + activity + " (" + (instance != null) + ")");
- super.onPause(activity);
- }
-
- @Override
- public void onDestroy(Activity activity) {
- Log.d(LCAT, "onDestroy " + activity + " (" + (instance != null) + ")");
- super.onDestroy(activity);
- }
-
- @Override
- public void onStart(Activity activity) {
- Log.d(LCAT, "onStart " + activity + " (" + (instance != null) + ")");
- super.onStart(activity);
- }
-
- @Override
- public void onStop(Activity activity) {
- Log.d(LCAT, "onStop " + activity + " (" + (instance != null) + ")");
- super.onStop(activity);
- }
-
- public static GCMModule getInstance() {
- return instance;
- }
-}
diff --git a/timodule.xml b/timodule.xml
deleted file mode 100644
index 0543d18..0000000
--- a/timodule.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-