diff options
Diffstat (limited to 'plugins/phonegap-plugin-push/src')
11 files changed, 1491 insertions, 0 deletions
diff --git a/plugins/phonegap-plugin-push/src/android/com/adobe/phonegap/push/CordovaGCMBroadcastReceiver.java b/plugins/phonegap-plugin-push/src/android/com/adobe/phonegap/push/CordovaGCMBroadcastReceiver.java new file mode 100644 index 00000000..8bae76d9 --- /dev/null +++ b/plugins/phonegap-plugin-push/src/android/com/adobe/phonegap/push/CordovaGCMBroadcastReceiver.java @@ -0,0 +1,19 @@ +package com.adobe.phonegap.push; + +import android.content.Context; + +import com.google.android.gcm.GCMBroadcastReceiver; +import static com.google.android.gcm.GCMConstants.DEFAULT_INTENT_SERVICE_CLASS_NAME; + +/* + * Implementation of GCMBroadcastReceiver that hard-wires the intent service to be + * com.plugin.gcm.GCMIntentService, instead of your_package.GCMIntentService + */ +public class CordovaGCMBroadcastReceiver extends GCMBroadcastReceiver implements PushConstants { + + @Override + protected String getGCMIntentServiceClassName(Context context) { + return COM_ADOBE_PHONEGAP_PUSH + DEFAULT_INTENT_SERVICE_CLASS_NAME; + } + +}
\ No newline at end of file diff --git a/plugins/phonegap-plugin-push/src/android/com/adobe/phonegap/push/GCMIntentService.java b/plugins/phonegap-plugin-push/src/android/com/adobe/phonegap/push/GCMIntentService.java new file mode 100644 index 00000000..b22712d0 --- /dev/null +++ b/plugins/phonegap-plugin-push/src/android/com/adobe/phonegap/push/GCMIntentService.java @@ -0,0 +1,530 @@ +package com.adobe.phonegap.push; + +import android.annotation.SuppressLint; +import android.app.Notification; +import android.app.NotificationManager; +import android.app.PendingIntent; +import android.content.ContentResolver; +import android.content.Context; +import android.content.Intent; +import android.content.SharedPreferences; +import android.content.res.AssetManager; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Color; +import android.net.Uri; +import android.os.Bundle; +import android.support.v4.app.NotificationCompat; +import android.text.Html; +import android.util.Log; + +import com.google.android.gcm.GCMBaseIntentService; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Random; + +@SuppressLint("NewApi") +public class GCMIntentService extends GCMBaseIntentService implements PushConstants { + + private static final String LOG_TAG = "PushPlugin_GCMIntentService"; + private static HashMap<Integer, ArrayList<String>> messageMap = new HashMap<Integer, ArrayList<String>>(); + + public void setNotification(int notId, String message){ + ArrayList<String> messageList = messageMap.get(notId); + if(messageList == null) { + messageList = new ArrayList<String>(); + messageMap.put(notId, messageList); + } + + if(message.isEmpty()){ + messageList.clear(); + }else{ + messageList.add(message); + } + } + + public GCMIntentService() { + super("GCMIntentService"); + } + + @Override + public void onRegistered(Context context, String regId) { + + Log.v(LOG_TAG, "onRegistered: " + regId); + + try { + JSONObject json = new JSONObject().put(REGISTRATION_ID, regId); + + Log.v(LOG_TAG, "onRegistered: " + json.toString()); + + PushPlugin.sendEvent( json ); + } + catch(JSONException e) { + // No message to the user is sent, JSON failed + Log.e(LOG_TAG, "onRegistered: JSON exception"); + } + } + + @Override + public void onUnregistered(Context context, String regId) { + Log.d(LOG_TAG, "onUnregistered - regId: " + regId); + } + + @Override + protected void onMessage(Context context, Intent intent) { + Log.d(LOG_TAG, "onMessage - context: " + context); + + // Extract the payload from the message + Bundle extras = intent.getExtras(); + if (extras != null) { + // if we are in the foreground, just surface the payload, else post it to the statusbar + if (PushPlugin.isInForeground()) { + extras.putBoolean(FOREGROUND, true); + PushPlugin.sendExtras(extras); + } + else { + extras.putBoolean(FOREGROUND, false); + + // Send a notification if there is a message + String message = this.getMessageText(extras); + String title = getString(extras, TITLE, ""); + if ((message != null && message.length() != 0) || + (title != null && title.length() != 0)) { + createNotification(context, extras); + } + } + } + } + + public void createNotification(Context context, Bundle extras) { + NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); + String appName = getAppName(this); + String packageName = context.getPackageName(); + Resources resources = context.getResources(); + + int notId = parseInt(NOT_ID, extras); + Intent notificationIntent = new Intent(this, PushHandlerActivity.class); + notificationIntent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP | Intent.FLAG_ACTIVITY_CLEAR_TOP); + notificationIntent.putExtra(PUSH_BUNDLE, extras); + notificationIntent.putExtra(NOT_ID, notId); + + int requestCode = new Random().nextInt(); + PendingIntent contentIntent = PendingIntent.getActivity(this, requestCode, notificationIntent, PendingIntent.FLAG_UPDATE_CURRENT); + + NotificationCompat.Builder mBuilder = + new NotificationCompat.Builder(context) + .setWhen(System.currentTimeMillis()) + .setContentTitle(getString(extras, TITLE)) + .setTicker(getString(extras, TITLE)) + .setContentIntent(contentIntent) + .setAutoCancel(true); + + SharedPreferences prefs = context.getSharedPreferences(PushPlugin.COM_ADOBE_PHONEGAP_PUSH, Context.MODE_PRIVATE); + String localIcon = prefs.getString(ICON, null); + String localIconColor = prefs.getString(ICON_COLOR, null); + boolean soundOption = prefs.getBoolean(SOUND, true); + boolean vibrateOption = prefs.getBoolean(VIBRATE, true); + Log.d(LOG_TAG, "stored icon=" + localIcon); + Log.d(LOG_TAG, "stored iconColor=" + localIconColor); + Log.d(LOG_TAG, "stored sound=" + soundOption); + Log.d(LOG_TAG, "stored vibrate=" + vibrateOption); + + /* + * Notification Vibration + */ + + setNotificationVibration(extras, vibrateOption, mBuilder); + + /* + * Notification Icon Color + * + * Sets the small-icon background color of the notification. + * To use, add the `iconColor` key to plugin android options + * + */ + setNotificationIconColor(getString(extras,"color"), mBuilder, localIconColor); + + /* + * Notification Icon + * + * Sets the small-icon of the notification. + * + * - checks the plugin options for `icon` key + * - if none, uses the application icon + * + * The icon value must be a string that maps to a drawable resource. + * If no resource is found, falls + * + */ + setNotificationSmallIcon(context, extras, packageName, resources, mBuilder, localIcon); + + /* + * Notification Large-Icon + * + * Sets the large-icon of the notification + * + * - checks the gcm data for the `image` key + * - checks to see if remote image, loads it. + * - checks to see if assets image, Loads It. + * - checks to see if resource image, LOADS IT! + * - if none, we don't set the large icon + * + */ + setNotificationLargeIcon(extras, packageName, resources, mBuilder); + + /* + * Notification Sound + */ + if (soundOption) { + setNotificationSound(context, extras, mBuilder); + } + + /* + * LED Notification + */ + setNotificationLedColor(extras, mBuilder); + + /* + * Priority Notification + */ + setNotificationPriority(extras, mBuilder); + + /* + * Notification message + */ + setNotificationMessage(notId, extras, mBuilder); + + /* + * Notification count + */ + setNotificationCount(extras, mBuilder); + + /* + * Notication add actions + */ + createActions(extras, mBuilder, resources, packageName); + + mNotificationManager.notify(appName, notId, mBuilder.build()); + } + + private void createActions(Bundle extras, NotificationCompat.Builder mBuilder, Resources resources, String packageName) { + Log.d(LOG_TAG, "create actions"); + String actions = getString(extras, ACTIONS); + if (actions != null) { + try { + JSONArray actionsArray = new JSONArray(actions); + for (int i=0; i < actionsArray.length(); i++) { + Log.d(LOG_TAG, "adding action"); + JSONObject action = actionsArray.getJSONObject(i); + Log.d(LOG_TAG, "adding callback = " + action.getString(CALLBACK)); + Intent intent = new Intent(this, PushHandlerActivity.class); + intent.putExtra(CALLBACK, action.getString(CALLBACK)); + intent.putExtra(PUSH_BUNDLE, extras); + PendingIntent pIntent = PendingIntent.getActivity(this, i, intent, PendingIntent.FLAG_UPDATE_CURRENT); + + mBuilder.addAction(resources.getIdentifier(action.getString(ICON), DRAWABLE, packageName), + action.getString(TITLE), pIntent); + } + } catch(JSONException e) { + // nope + } + } + } + + private void setNotificationCount(Bundle extras, NotificationCompat.Builder mBuilder) { + String msgcnt = getString(extras, MSGCNT); + if (msgcnt == null) { + msgcnt = getString(extras, BADGE); + } + if (msgcnt != null) { + mBuilder.setNumber(Integer.parseInt(msgcnt)); + } + } + + private void setNotificationVibration(Bundle extras, Boolean vibrateOption, NotificationCompat.Builder mBuilder) { + String vibrationPattern = getString(extras, VIBRATION_PATTERN); + if (vibrationPattern != null) { + String[] items = vibrationPattern.replaceAll("\\[", "").replaceAll("\\]", "").split(","); + long[] results = new long[items.length]; + for (int i = 0; i < items.length; i++) { + try { + results[i] = Long.parseLong(items[i]); + } catch (NumberFormatException nfe) {} + } + mBuilder.setVibrate(results); + } else { + if (vibrateOption) { + mBuilder.setDefaults(Notification.DEFAULT_VIBRATE); + } + } + } + + private void setNotificationMessage(int notId, Bundle extras, NotificationCompat.Builder mBuilder) { + String message = getMessageText(extras); + + String style = getString(extras, STYLE, STYLE_TEXT); + if(STYLE_INBOX.equals(style)) { + setNotification(notId, message); + + mBuilder.setContentText(message); + + ArrayList<String> messageList = messageMap.get(notId); + Integer sizeList = messageList.size(); + if (sizeList > 1) { + String sizeListMessage = sizeList.toString(); + String stacking = sizeList + " more"; + if (getString(extras, SUMMARY_TEXT) != null) { + stacking = getString(extras, SUMMARY_TEXT); + stacking = stacking.replace("%n%", sizeListMessage); + } + NotificationCompat.InboxStyle notificationInbox = new NotificationCompat.InboxStyle() + .setBigContentTitle(getString(extras, TITLE)) + .setSummaryText(stacking); + + for (int i = messageList.size() - 1; i >= 0; i--) { + notificationInbox.addLine(Html.fromHtml(messageList.get(i))); + } + + mBuilder.setStyle(notificationInbox); + } else { + NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle(); + if (message != null) { + bigText.bigText(message); + bigText.setBigContentTitle(getString(extras, TITLE)); + mBuilder.setStyle(bigText); + } + } + } else if (STYLE_PICTURE.equals(style)) { + setNotification(notId, ""); + + NotificationCompat.BigPictureStyle bigPicture = new NotificationCompat.BigPictureStyle(); + bigPicture.bigPicture(getBitmapFromURL(getString(extras, PICTURE))); + bigPicture.setBigContentTitle(getString(extras, TITLE)); + bigPicture.setSummaryText(getString(extras, SUMMARY_TEXT)); + + mBuilder.setContentTitle(getString(extras, TITLE)); + mBuilder.setContentText(message); + + mBuilder.setStyle(bigPicture); + } else { + setNotification(notId, ""); + + NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle(); + + if (message != null) { + mBuilder.setContentText(Html.fromHtml(message)); + + bigText.bigText(message); + bigText.setBigContentTitle(getString(extras, TITLE)); + + String summaryText = getString(extras, SUMMARY_TEXT); + if (summaryText != null) { + bigText.setSummaryText(summaryText); + } + + mBuilder.setStyle(bigText); + } + /* + else { + mBuilder.setContentText("<missing message content>"); + } + */ + } + } + + private String getString(Bundle extras,String key) { + String message = extras.getString(key); + if (message == null) { + message = extras.getString(GCM_NOTIFICATION+"."+key); + } + return message; + } + + private String getString(Bundle extras,String key, String defaultString) { + String message = extras.getString(key); + if (message == null) { + message = extras.getString(GCM_NOTIFICATION+"."+key, defaultString); + } + return message; + } + + private String getMessageText(Bundle extras) { + String message = getString(extras, MESSAGE); + if (message == null) { + message = getString(extras, BODY); + } + return message; + } + + private void setNotificationSound(Context context, Bundle extras, NotificationCompat.Builder mBuilder) { + String soundname = getString(extras, SOUNDNAME); + if (soundname == null) { + soundname = getString(extras, SOUND); + } + if (soundname != null) { + Uri sound = Uri.parse(ContentResolver.SCHEME_ANDROID_RESOURCE + + "://" + context.getPackageName() + "/raw/" + soundname); + Log.d(LOG_TAG, sound.toString()); + mBuilder.setSound(sound); + } else { + mBuilder.setSound(android.provider.Settings.System.DEFAULT_NOTIFICATION_URI); + } + } + + private void setNotificationLedColor(Bundle extras, NotificationCompat.Builder mBuilder) { + String ledColor = getString(extras, LED_COLOR); + if (ledColor != null) { + // Converts parse Int Array from ledColor + String[] items = ledColor.replaceAll("\\[", "").replaceAll("\\]", "").split(","); + int[] results = new int[items.length]; + for (int i = 0; i < items.length; i++) { + try { + results[i] = Integer.parseInt(items[i]); + } catch (NumberFormatException nfe) {} + } + if (results.length == 4) { + mBuilder.setLights(Color.argb(results[0], results[1], results[2], results[3]), 500, 500); + } else { + Log.e(LOG_TAG, "ledColor parameter must be an array of length == 4 (ARGB)"); + } + } + } + + private void setNotificationPriority(Bundle extras, NotificationCompat.Builder mBuilder) { + String priorityStr = getString(extras, PRIORITY); + if (priorityStr != null) { + try { + Integer priority = Integer.parseInt(priorityStr); + if (priority >= NotificationCompat.PRIORITY_MIN && priority <= NotificationCompat.PRIORITY_MAX) { + mBuilder.setPriority(priority); + } else { + Log.e(LOG_TAG, "Priority parameter must be between -2 and 2"); + } + } catch (NumberFormatException e) { + e.printStackTrace(); + } + } + } + + private void setNotificationLargeIcon(Bundle extras, String packageName, Resources resources, NotificationCompat.Builder mBuilder) { + String gcmLargeIcon = getString(extras, IMAGE); // from gcm + if (gcmLargeIcon != null) { + if (gcmLargeIcon.startsWith("http://") || gcmLargeIcon.startsWith("https://")) { + mBuilder.setLargeIcon(getBitmapFromURL(gcmLargeIcon)); + Log.d(LOG_TAG, "using remote large-icon from gcm"); + } else { + AssetManager assetManager = getAssets(); + InputStream istr; + try { + istr = assetManager.open(gcmLargeIcon); + Bitmap bitmap = BitmapFactory.decodeStream(istr); + mBuilder.setLargeIcon(bitmap); + Log.d(LOG_TAG, "using assets large-icon from gcm"); + } catch (IOException e) { + int largeIconId = 0; + largeIconId = resources.getIdentifier(gcmLargeIcon, DRAWABLE, packageName); + if (largeIconId != 0) { + Bitmap largeIconBitmap = BitmapFactory.decodeResource(resources, largeIconId); + mBuilder.setLargeIcon(largeIconBitmap); + Log.d(LOG_TAG, "using resources large-icon from gcm"); + } else { + Log.d(LOG_TAG, "Not setting large icon"); + } + } + } + } + } + + private void setNotificationSmallIcon(Context context, Bundle extras, String packageName, Resources resources, NotificationCompat.Builder mBuilder, String localIcon) { + int iconId = 0; + String icon = getString(extras, ICON); + if (icon != null) { + iconId = resources.getIdentifier(icon, DRAWABLE, packageName); + Log.d(LOG_TAG, "using icon from plugin options"); + } + else if (localIcon != null) { + iconId = resources.getIdentifier(localIcon, DRAWABLE, packageName); + Log.d(LOG_TAG, "using icon from plugin options"); + } + if (iconId == 0) { + Log.d(LOG_TAG, "no icon resource found - using application icon"); + iconId = context.getApplicationInfo().icon; + } + mBuilder.setSmallIcon(iconId); + } + + private void setNotificationIconColor(String color, NotificationCompat.Builder mBuilder, String localIconColor) { + int iconColor = 0; + if (color != null) { + try { + iconColor = Color.parseColor(color); + } catch (IllegalArgumentException e) { + Log.e(LOG_TAG, "couldn't parse color from android options"); + } + } + else if (localIconColor != null) { + try { + iconColor = Color.parseColor(localIconColor); + } catch (IllegalArgumentException e) { + Log.e(LOG_TAG, "couldn't parse color from android options"); + } + } + if (iconColor != 0) { + mBuilder.setColor(iconColor); + } + } + + public Bitmap getBitmapFromURL(String strURL) { + try { + URL url = new URL(strURL); + HttpURLConnection connection = (HttpURLConnection) url.openConnection(); + connection.setDoInput(true); + connection.connect(); + InputStream input = connection.getInputStream(); + return BitmapFactory.decodeStream(input); + } catch (IOException e) { + e.printStackTrace(); + return null; + } + } + + private static String getAppName(Context context) { + CharSequence appName = context.getPackageManager().getApplicationLabel(context.getApplicationInfo()); + return (String)appName; + } + + @Override + public void onError(Context context, String errorId) { + Log.e(LOG_TAG, "onError - errorId: " + errorId); + // if we are in the foreground, just send the error + if (PushPlugin.isInForeground()) { + PushPlugin.sendError(errorId); + } + } + + private int parseInt(String value, Bundle extras) { + int retval = 0; + + try { + retval = Integer.parseInt(getString(extras, value)); + } + catch(NumberFormatException e) { + Log.e(LOG_TAG, "Number format exception - Error parsing " + value + ": " + e.getMessage()); + } + catch(Exception e) { + Log.e(LOG_TAG, "Number format exception - Error parsing " + value + ": " + e.getMessage()); + } + + return retval; + } +} diff --git a/plugins/phonegap-plugin-push/src/android/com/adobe/phonegap/push/PushConstants.java b/plugins/phonegap-plugin-push/src/android/com/adobe/phonegap/push/PushConstants.java new file mode 100644 index 00000000..09d7d4b9 --- /dev/null +++ b/plugins/phonegap-plugin-push/src/android/com/adobe/phonegap/push/PushConstants.java @@ -0,0 +1,44 @@ +package com.adobe.phonegap.push; + +public interface PushConstants { + public static final String COM_ADOBE_PHONEGAP_PUSH = "com.adobe.phonegap.push"; + public static final String REGISTRATION_ID = "registrationId"; + public static final String FOREGROUND = "foreground"; + public static final String TITLE = "title"; + public static final String NOT_ID = "notId"; + public static final String PUSH_BUNDLE = "pushBundle"; + public static final String ICON = "icon"; + public static final String ICON_COLOR = "iconColor"; + public static final String SOUND = "sound"; + public static final String VIBRATE = "vibrate"; + public static final String ACTIONS = "actions"; + public static final String CALLBACK = "callback"; + public static final String DRAWABLE = "drawable"; + public static final String MSGCNT = "msgcnt"; + public static final String VIBRATION_PATTERN = "vibrationPattern"; + public static final String STYLE = "style"; + public static final String SUMMARY_TEXT = "summaryText"; + public static final String PICTURE = "picture"; + public static final String GCM_NOTIFICATION = "gcm.notification"; + public static final String MESSAGE = "message"; + public static final String BODY = "body"; + public static final String SOUNDNAME = "soundname"; + public static final String LED_COLOR = "ledColor"; + public static final String PRIORITY = "priority"; + public static final String IMAGE = "image"; + public static final String STYLE_INBOX = "inbox"; + public static final String STYLE_PICTURE = "picture"; + public static final String STYLE_TEXT = "text"; + public static final String BADGE = "badge"; + public static final String INITIALIZE = "init"; + public static final String UNREGISTER = "unregister"; + public static final String EXIT = "exit"; + public static final String ANDROID = "android"; + public static final String SENDER_ID = "senderID"; + public static final String CLEAR_NOTIFICATIONS = "clearNotifications"; + public static final String COLDSTART = "coldstart"; + public static final String ADDITIONAL_DATA = "additionalData"; + public static final String COUNT = "count"; + public static final String FROM = "from"; + public static final String COLLAPSE_KEY = "collapse_key"; +} diff --git a/plugins/phonegap-plugin-push/src/android/com/adobe/phonegap/push/PushHandlerActivity.java b/plugins/phonegap-plugin-push/src/android/com/adobe/phonegap/push/PushHandlerActivity.java new file mode 100644 index 00000000..57d2be84 --- /dev/null +++ b/plugins/phonegap-plugin-push/src/android/com/adobe/phonegap/push/PushHandlerActivity.java @@ -0,0 +1,70 @@ +package com.adobe.phonegap.push; + +import android.app.Activity; +import android.app.NotificationManager; +import android.content.Context; +import android.content.Intent; +import android.content.pm.PackageManager; +import android.os.Bundle; +import android.util.Log; + +public class PushHandlerActivity extends Activity implements PushConstants { + private static String LOG_TAG = "PushPlugin_PushHandlerActivity"; + + /* + * this activity will be started if the user touches a notification that we own. + * We send it's data off to the push plugin for processing. + * If needed, we boot up the main activity to kickstart the application. + * @see android.app.Activity#onCreate(android.os.Bundle) + */ + @Override + public void onCreate(Bundle savedInstanceState) { + GCMIntentService gcm = new GCMIntentService(); + gcm.setNotification(getIntent().getIntExtra(NOT_ID, 0), ""); + super.onCreate(savedInstanceState); + Log.v(LOG_TAG, "onCreate"); + + boolean isPushPluginActive = PushPlugin.isActive(); + processPushBundle(isPushPluginActive); + + finish(); + + if (!isPushPluginActive) { + forceMainActivityReload(); + } + } + + /** + * Takes the pushBundle extras from the intent, + * and sends it through to the PushPlugin for processing. + */ + private void processPushBundle(boolean isPushPluginActive) { + Bundle extras = getIntent().getExtras(); + + if (extras != null) { + Bundle originalExtras = extras.getBundle(PUSH_BUNDLE); + + originalExtras.putBoolean(FOREGROUND, false); + originalExtras.putBoolean(COLDSTART, !isPushPluginActive); + originalExtras.putString(CALLBACK, getIntent().getExtras().getString("callback")); + + PushPlugin.sendExtras(originalExtras); + } + } + + /** + * Forces the main activity to re-launch if it's unloaded. + */ + private void forceMainActivityReload() { + PackageManager pm = getPackageManager(); + Intent launchIntent = pm.getLaunchIntentForPackage(getApplicationContext().getPackageName()); + startActivity(launchIntent); + } + + @Override + protected void onResume() { + super.onResume(); + final NotificationManager notificationManager = (NotificationManager) this.getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancelAll(); + } +}
\ No newline at end of file diff --git a/plugins/phonegap-plugin-push/src/android/com/adobe/phonegap/push/PushPlugin.java b/plugins/phonegap-plugin-push/src/android/com/adobe/phonegap/push/PushPlugin.java new file mode 100644 index 00000000..781ed2d1 --- /dev/null +++ b/plugins/phonegap-plugin-push/src/android/com/adobe/phonegap/push/PushPlugin.java @@ -0,0 +1,250 @@ +package com.adobe.phonegap.push; + +import android.app.NotificationManager; +import android.content.Context; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.util.Log; + +import com.google.android.gcm.GCMRegistrar; + +import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaInterface; +import org.apache.cordova.CordovaPlugin; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.PluginResult; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.Iterator; + +public class PushPlugin extends CordovaPlugin implements PushConstants { + + public static final String LOG_TAG = "PushPlugin"; + + private static CallbackContext pushContext; + private static CordovaWebView gWebView; + private static Bundle gCachedExtras = null; + private static boolean gForeground = false; + + /** + * Gets the application context from cordova's main activity. + * @return the application context + */ + private Context getApplicationContext() { + return this.cordova.getActivity().getApplicationContext(); + } + + @Override + public boolean execute(String action, JSONArray data, CallbackContext callbackContext) { + + boolean result = false; + + Log.v(LOG_TAG, "execute: action=" + action); + + if (INITIALIZE.equals(action)) { + pushContext = callbackContext; + JSONObject jo = null; + + Log.v(LOG_TAG, "execute: data=" + data.toString()); + + try { + jo = data.getJSONObject(0).getJSONObject(ANDROID); + + gWebView = this.webView; + Log.v(LOG_TAG, "execute: jo=" + jo.toString()); + + String senderID = jo.getString(SENDER_ID); + + Log.v(LOG_TAG, "execute: senderID=" + senderID); + + GCMRegistrar.register(getApplicationContext(), senderID); + result = true; + } catch (JSONException e) { + Log.e(LOG_TAG, "execute: Got JSON Exception " + e.getMessage()); + result = false; + callbackContext.error(e.getMessage()); + } + + if (jo != null) { + SharedPreferences sharedPref = getApplicationContext().getSharedPreferences(COM_ADOBE_PHONEGAP_PUSH, Context.MODE_PRIVATE); + SharedPreferences.Editor editor = sharedPref.edit(); + try { + editor.putString(ICON, jo.getString(ICON)); + } catch (JSONException e) { + Log.d(LOG_TAG, "no icon option"); + } + try { + editor.putString(ICON_COLOR, jo.getString(ICON_COLOR)); + } catch (JSONException e) { + Log.d(LOG_TAG, "no iconColor option"); + } + editor.putBoolean(SOUND, jo.optBoolean(SOUND, true)); + editor.putBoolean(VIBRATE, jo.optBoolean(VIBRATE, true)); + editor.putBoolean(CLEAR_NOTIFICATIONS, jo.optBoolean(CLEAR_NOTIFICATIONS, true)); + editor.commit(); + } + + if ( gCachedExtras != null) { + Log.v(LOG_TAG, "sending cached extras"); + sendExtras(gCachedExtras); + gCachedExtras = null; + } + + } else if (UNREGISTER.equals(action)) { + + GCMRegistrar.unregister(getApplicationContext()); + + Log.v(LOG_TAG, "UNREGISTER"); + result = true; + callbackContext.success(); + } else { + result = false; + Log.e(LOG_TAG, "Invalid action : " + action); + callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.INVALID_ACTION)); + } + + return result; + } + + public static void sendEvent(JSONObject _json) { + PluginResult pluginResult = new PluginResult(PluginResult.Status.OK, _json); + pluginResult.setKeepCallback(true); + if (pushContext != null) { + pushContext.sendPluginResult(pluginResult); + } + } + + public static void sendError(String message) { + PluginResult pluginResult = new PluginResult(PluginResult.Status.ERROR, message); + pluginResult.setKeepCallback(true); + if (pushContext != null) { + pushContext.sendPluginResult(pluginResult); + } + } + + /* + * Sends the pushbundle extras to the client application. + * If the client application isn't currently active, it is cached for later processing. + */ + public static void sendExtras(Bundle extras) { + if (extras != null) { + if (gWebView != null) { + sendEvent(convertBundleToJson(extras)); + } else { + Log.v(LOG_TAG, "sendExtras: caching extras to send at a later time."); + gCachedExtras = extras; + } + } + } + + @Override + public void initialize(CordovaInterface cordova, CordovaWebView webView) { + super.initialize(cordova, webView); + gForeground = true; + } + + @Override + public void onPause(boolean multitasking) { + super.onPause(multitasking); + gForeground = false; + + SharedPreferences prefs = getApplicationContext().getSharedPreferences(COM_ADOBE_PHONEGAP_PUSH, Context.MODE_PRIVATE); + if (prefs.getBoolean(CLEAR_NOTIFICATIONS, true)) { + final NotificationManager notificationManager = (NotificationManager) cordova.getActivity().getSystemService(Context.NOTIFICATION_SERVICE); + notificationManager.cancelAll(); + } + } + + @Override + public void onResume(boolean multitasking) { + super.onResume(multitasking); + gForeground = true; + } + + @Override + public void onDestroy() { + super.onDestroy(); + gForeground = false; + gWebView = null; + } + + /* + * serializes a bundle to JSON. + */ + private static JSONObject convertBundleToJson(Bundle extras) { + try { + JSONObject json = new JSONObject(); + JSONObject additionalData = new JSONObject(); + Iterator<String> it = extras.keySet().iterator(); + while (it.hasNext()) { + String key = it.next(); + Object value = extras.get(key); + + Log.d(LOG_TAG, "key = " + key); + if (key.startsWith(GCM_NOTIFICATION)) { + key = key.substring(GCM_NOTIFICATION.length()+1, key.length()); + } + + // System data from Android + if (key.equals(FROM) || key.equals(COLLAPSE_KEY)) { + additionalData.put(key, value); + } + else if (key.equals(FOREGROUND)) { + additionalData.put(key, extras.getBoolean(FOREGROUND)); + } + else if (key.equals(COLDSTART)){ + additionalData.put(key, extras.getBoolean(COLDSTART)); + } else if (key.equals(MESSAGE) || key.equals(BODY)) { + json.put(MESSAGE, value); + } else if (key.equals(TITLE)) { + json.put(TITLE, value); + } else if (key.equals(MSGCNT) || key.equals(BADGE)) { + json.put(COUNT, value); + } else if (key.equals(SOUNDNAME) || key.equals(SOUND)) { + json.put(SOUND, value); + } else if (key.equals(IMAGE)) { + json.put(IMAGE, value); + } else if (key.equals(CALLBACK)) { + json.put(CALLBACK, value); + } + else if ( value instanceof String ) { + String strValue = (String)value; + try { + // Try to figure out if the value is another JSON object + if (strValue.startsWith("{")) { + additionalData.put(key, new JSONObject(strValue)); + } + // Try to figure out if the value is another JSON array + else if (strValue.startsWith("[")) { + additionalData.put(key, new JSONArray(strValue)); + } + else { + additionalData.put(key, value); + } + } catch (Exception e) { + additionalData.put(key, value); + } + } + } // while + + json.put(ADDITIONAL_DATA, additionalData); + Log.v(LOG_TAG, "extrasToJSON: " + json.toString()); + + return json; + } + catch( JSONException e) { + Log.e(LOG_TAG, "extrasToJSON: JSON exception"); + } + return null; + } + + public static boolean isInForeground() { + return gForeground; + } + + public static boolean isActive() { + return gWebView != null; + } +}
\ No newline at end of file diff --git a/plugins/phonegap-plugin-push/src/android/libs/gcm.jar b/plugins/phonegap-plugin-push/src/android/libs/gcm.jar Binary files differnew file mode 100644 index 00000000..ac109a83 --- /dev/null +++ b/plugins/phonegap-plugin-push/src/android/libs/gcm.jar diff --git a/plugins/phonegap-plugin-push/src/ios/AppDelegate+notification.h b/plugins/phonegap-plugin-push/src/ios/AppDelegate+notification.h new file mode 100644 index 00000000..695e9384 --- /dev/null +++ b/plugins/phonegap-plugin-push/src/ios/AppDelegate+notification.h @@ -0,0 +1,22 @@ +// +// AppDelegate+notification.h +// pushtest +// +// Created by Robert Easterday on 10/26/12. +// +// + +#import "AppDelegate.h" + +@interface AppDelegate (notification) +- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; +- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error; +- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo; +- (void)applicationDidBecomeActive:(UIApplication *)application; +- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings; +- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void(^)())completionHandler; +- (id) getCommandInstance:(NSString*)className; + +@property (nonatomic, retain) NSDictionary *launchNotification; + +@end diff --git a/plugins/phonegap-plugin-push/src/ios/AppDelegate+notification.m b/plugins/phonegap-plugin-push/src/ios/AppDelegate+notification.m new file mode 100644 index 00000000..37241269 --- /dev/null +++ b/plugins/phonegap-plugin-push/src/ios/AppDelegate+notification.m @@ -0,0 +1,135 @@ +// +// AppDelegate+notification.m +// pushtest +// +// Created by Robert Easterday on 10/26/12. +// +// + +#import "AppDelegate+notification.h" +#import "PushPlugin.h" +#import <objc/runtime.h> + +static char launchNotificationKey; + +@implementation AppDelegate (notification) + +- (id) getCommandInstance:(NSString*)className +{ + return [self.viewController getCommandInstance:className]; +} + +// its dangerous to override a method from within a category. +// Instead we will use method swizzling. we set this up in the load call. ++ (void)load +{ + Method original, swizzled; + + original = class_getInstanceMethod(self, @selector(init)); + swizzled = class_getInstanceMethod(self, @selector(swizzled_init)); + method_exchangeImplementations(original, swizzled); +} + +- (AppDelegate *)swizzled_init +{ + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(createNotificationChecker:) + name:@"UIApplicationDidFinishLaunchingNotification" object:nil]; + + // This actually calls the original init method over in AppDelegate. Equivilent to calling super + // on an overrided method, this is not recursive, although it appears that way. neat huh? + return [self swizzled_init]; +} + +// This code will be called immediately after application:didFinishLaunchingWithOptions:. We need +// to process notifications in cold-start situations +- (void)createNotificationChecker:(NSNotification *)notification +{ + if (notification) + { + NSDictionary *launchOptions = [notification userInfo]; + if (launchOptions) + self.launchNotification = [launchOptions objectForKey: @"UIApplicationLaunchOptionsRemoteNotificationKey"]; + } +} + +- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"]; + [pushHandler didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; +} + +- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error { + PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"]; + [pushHandler didFailToRegisterForRemoteNotificationsWithError:error]; +} + +- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo { + NSLog(@"didReceiveNotification"); + + // Get application state for iOS4.x+ devices, otherwise assume active + UIApplicationState appState = UIApplicationStateActive; + if ([application respondsToSelector:@selector(applicationState)]) { + appState = application.applicationState; + } + + if (appState == UIApplicationStateActive) { + PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"]; + pushHandler.notificationMessage = userInfo; + pushHandler.isInline = YES; + [pushHandler notificationReceived]; + } else { + //save it for later + self.launchNotification = userInfo; + } +} + +- (void)applicationDidBecomeActive:(UIApplication *)application { + + NSLog(@"active"); + + //zero badge + application.applicationIconBadgeNumber = 0; + + if (self.launchNotification) { + PushPlugin *pushHandler = [self getCommandInstance:@"PushNotification"]; + pushHandler.isInline = NO; + pushHandler.notificationMessage = self.launchNotification; + self.launchNotification = nil; + [pushHandler performSelectorOnMainThread:@selector(notificationReceived) withObject:pushHandler waitUntilDone:NO]; + } +} + +- (void)application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings +{ + //register to receive notifications + [application registerForRemoteNotifications]; +} + +//For interactive notification only +- (void)application:(UIApplication *)application handleActionWithIdentifier:(NSString *)identifier forRemoteNotification:(NSDictionary *)userInfo completionHandler:(void(^)())completionHandler +{ + //handle the actions + if ([identifier isEqualToString:@"declineAction"]){ + } + else if ([identifier isEqualToString:@"answerAction"]){ + } +} + + +// The accessors use an Associative Reference since you can't define a iVar in a category +// http://developer.apple.com/library/ios/#documentation/cocoa/conceptual/objectivec/Chapters/ocAssociativeReferences.html +- (NSMutableArray *)launchNotification +{ + return objc_getAssociatedObject(self, &launchNotificationKey); +} + +- (void)setLaunchNotification:(NSDictionary *)aDictionary +{ + objc_setAssociatedObject(self, &launchNotificationKey, aDictionary, OBJC_ASSOCIATION_RETAIN_NONATOMIC); +} + +- (void)dealloc +{ + self.launchNotification = nil; // clear the association and release the object +} + +@end diff --git a/plugins/phonegap-plugin-push/src/ios/PushPlugin.h b/plugins/phonegap-plugin-push/src/ios/PushPlugin.h new file mode 100644 index 00000000..e363e2e5 --- /dev/null +++ b/plugins/phonegap-plugin-push/src/ios/PushPlugin.h @@ -0,0 +1,56 @@ +/* + Copyright 2009-2011 Urban Airship Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binaryform must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided withthe distribution. + + THIS SOFTWARE IS PROVIDED BY THE URBAN AIRSHIP INC``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + EVENT SHALL URBAN AIRSHIP INC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import <Foundation/Foundation.h> +#import <Cordova/CDV.h> +#import <Cordova/CDVPlugin.h> + +@interface PushPlugin : CDVPlugin +{ + NSDictionary *notificationMessage; + BOOL isInline; + NSString *notificationCallbackId; + NSString *callback; + + BOOL ready; +} + +@property (nonatomic, copy) NSString *callbackId; +@property (nonatomic, copy) NSString *notificationCallbackId; +@property (nonatomic, copy) NSString *callback; + +@property (nonatomic, strong) NSDictionary *notificationMessage; +@property BOOL isInline; + +- (void)init:(CDVInvokedUrlCommand*)command; +- (void)unregister:(CDVInvokedUrlCommand*)command; + +- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken; +- (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error; + +- (void)setNotificationMessage:(NSDictionary *)notification; +- (void)notificationReceived; + +@end diff --git a/plugins/phonegap-plugin-push/src/ios/PushPlugin.m b/plugins/phonegap-plugin-push/src/ios/PushPlugin.m new file mode 100644 index 00000000..fc5b8c2e --- /dev/null +++ b/plugins/phonegap-plugin-push/src/ios/PushPlugin.m @@ -0,0 +1,279 @@ +/* + Copyright 2009-2011 Urban Airship Inc. All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + + 2. Redistributions in binaryform must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided withthe distribution. + + THIS SOFTWARE IS PROVIDED BY THE URBAN AIRSHIP INC``AS IS'' AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO + EVENT SHALL URBAN AIRSHIP INC OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#import "PushPlugin.h" + +@implementation PushPlugin + +@synthesize notificationMessage; +@synthesize isInline; + +@synthesize callbackId; +@synthesize notificationCallbackId; +@synthesize callback; + + +- (void)unregister:(CDVInvokedUrlCommand*)command; +{ + self.callbackId = command.callbackId; + + [[UIApplication sharedApplication] unregisterForRemoteNotifications]; + [self successWithMessage:@"unregistered"]; +} + +- (void)init:(CDVInvokedUrlCommand*)command; +{ + NSLog(@"Push Plugin register called"); + self.callbackId = command.callbackId; + + NSMutableDictionary* options = [command.arguments objectAtIndex:0]; + NSMutableDictionary* iosOptions = [options objectForKey:@"ios"]; + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 + UIUserNotificationType UserNotificationTypes = UIUserNotificationTypeNone; +#endif + UIRemoteNotificationType notificationTypes = UIRemoteNotificationTypeNone; + + id badgeArg = [iosOptions objectForKey:@"badge"]; + id soundArg = [iosOptions objectForKey:@"sound"]; + id alertArg = [iosOptions objectForKey:@"alert"]; + + if (([badgeArg isKindOfClass:[NSString class]] && [badgeArg isEqualToString:@"true"]) || [badgeArg boolValue]) + { + notificationTypes |= UIRemoteNotificationTypeBadge; +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 + UserNotificationTypes |= UIUserNotificationTypeBadge; +#endif + } + + if (([soundArg isKindOfClass:[NSString class]] && [soundArg isEqualToString:@"true"]) || [soundArg boolValue]) + { + notificationTypes |= UIRemoteNotificationTypeSound; +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 + UserNotificationTypes |= UIUserNotificationTypeSound; +#endif + } + + if (([alertArg isKindOfClass:[NSString class]] && [alertArg isEqualToString:@"true"]) || [alertArg boolValue]) + { + notificationTypes |= UIRemoteNotificationTypeAlert; +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 + UserNotificationTypes |= UIUserNotificationTypeAlert; +#endif + } + + notificationTypes |= UIRemoteNotificationTypeNewsstandContentAvailability; +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 + UserNotificationTypes |= UIUserNotificationActivationModeBackground; +#endif + + if (notificationTypes == UIRemoteNotificationTypeNone) + NSLog(@"PushPlugin.register: Push notification type is set to none"); + + isInline = NO; + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 + if ([[UIApplication sharedApplication]respondsToSelector:@selector(registerUserNotificationSettings:)]) { + UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:UserNotificationTypes categories:nil]; + [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; + [[UIApplication sharedApplication] registerForRemoteNotifications]; + } else { + [[UIApplication sharedApplication] registerForRemoteNotificationTypes: + (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)]; + } +#else + [[UIApplication sharedApplication] registerForRemoteNotificationTypes: + (UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)]; +#endif + + if (notificationMessage) // if there is a pending startup notification + [self notificationReceived]; // go ahead and process it +} + +- (void)didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken { + NSLog(@"Push Plugin register success: %@", deviceToken); + + NSMutableDictionary *results = [NSMutableDictionary dictionary]; + NSString *token = [[[[deviceToken description] stringByReplacingOccurrencesOfString:@"<"withString:@""] + stringByReplacingOccurrencesOfString:@">" withString:@""] + stringByReplacingOccurrencesOfString: @" " withString: @""]; + [results setValue:token forKey:@"deviceToken"]; + +#if !TARGET_IPHONE_SIMULATOR + // Get Bundle Info for Remote Registration (handy if you have more than one app) + [results setValue:[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleDisplayName"] forKey:@"appName"]; + [results setValue:[[[NSBundle mainBundle] infoDictionary] objectForKey:@"CFBundleVersion"] forKey:@"appVersion"]; + + // Check what Notifications the user has turned on. We registered for all three, but they may have manually disabled some or all of them. +#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] compare:v options:NSNumericSearch] == NSOrderedAscending) + + NSUInteger rntypes; + if (!SYSTEM_VERSION_LESS_THAN(@"8.0")) { + rntypes = [[[UIApplication sharedApplication] currentUserNotificationSettings] types]; + } else { + rntypes = [[UIApplication sharedApplication] enabledRemoteNotificationTypes]; + } + + // Set the defaults to disabled unless we find otherwise... + NSString *pushBadge = @"disabled"; + NSString *pushAlert = @"disabled"; + NSString *pushSound = @"disabled"; + + // Check what Registered Types are turned on. This is a bit tricky since if two are enabled, and one is off, it will return a number 2... not telling you which + // one is actually disabled. So we are literally checking to see if rnTypes matches what is turned on, instead of by number. The "tricky" part is that the + // single notification types will only match if they are the ONLY one enabled. Likewise, when we are checking for a pair of notifications, it will only be + // true if those two notifications are on. This is why the code is written this way + if(rntypes & UIRemoteNotificationTypeBadge){ + pushBadge = @"enabled"; + } + if(rntypes & UIRemoteNotificationTypeAlert) { + pushAlert = @"enabled"; + } + if(rntypes & UIRemoteNotificationTypeSound) { + pushSound = @"enabled"; + } + + [results setValue:pushBadge forKey:@"pushBadge"]; + [results setValue:pushAlert forKey:@"pushAlert"]; + [results setValue:pushSound forKey:@"pushSound"]; + + // Get the users Device Model, Display Name, Token & Version Number + UIDevice *dev = [UIDevice currentDevice]; + [results setValue:dev.name forKey:@"deviceName"]; + [results setValue:dev.model forKey:@"deviceModel"]; + [results setValue:dev.systemVersion forKey:@"deviceSystemVersion"]; + + // Send result to trigger 'registration' event but keep callback + NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:1]; + [message setObject:token forKey:@"registrationId"]; + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:message]; + [pluginResult setKeepCallbackAsBool:YES]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; +#endif +} + +- (void)didFailToRegisterForRemoteNotificationsWithError:(NSError *)error +{ + NSLog(@"Push Plugin register failed"); + [self failWithMessage:@"" withError:error]; +} + +- (void)notificationReceived { + NSLog(@"Notification received"); + + if (notificationMessage && self.callbackId != nil) + { + NSMutableDictionary* message = [NSMutableDictionary dictionaryWithCapacity:4]; + NSMutableDictionary* additionalData = [NSMutableDictionary dictionaryWithCapacity:4]; + + + for (id key in notificationMessage) { + if ([key isEqualToString:@"aps"]) { + id aps = [notificationMessage objectForKey:@"aps"]; + + for(id key in aps) { + NSLog(@"Push Plugin key: %@", key); + id value = [aps objectForKey:key]; + + if ([key isEqualToString:@"alert"]) { + if ([value isKindOfClass:[NSDictionary class]]) { + for (id messageKey in value) { + id messageValue = [value objectForKey:messageKey]; + if ([messageKey isEqualToString:@"body"]) { + [message setObject:messageValue forKey:@"message"]; + } else if ([messageKey isEqualToString:@"title"]) { + [message setObject:messageValue forKey:@"title"]; + } else { + [additionalData setObject:messageValue forKey:messageKey]; + } + } + } + else { + [message setObject:value forKey:@"message"]; + } + } else if ([key isEqualToString:@"title"]) { + [message setObject:value forKey:@"title"]; + } else if ([key isEqualToString:@"badge"]) { + [message setObject:value forKey:@"count"]; + } else if ([key isEqualToString:@"sound"]) { + [message setObject:value forKey:@"sound"]; + } else if ([key isEqualToString:@"image"]) { + [message setObject:value forKey:@"image"]; + } else { + [additionalData setObject:value forKey:key]; + } + } + } else { + [additionalData setObject:[notificationMessage objectForKey:key] forKey:key]; + } + } + + if (isInline) { + [additionalData setObject:[NSNumber numberWithBool:YES] forKey:@"foreground"]; + } else { + [additionalData setObject:[NSNumber numberWithBool:NO] forKey:@"foreground"]; + } + + [message setObject:additionalData forKey:@"additionalData"]; + + // send notification message + CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:message]; + [pluginResult setKeepCallbackAsBool:YES]; + [self.commandDelegate sendPluginResult:pluginResult callbackId:self.callbackId]; + + self.notificationMessage = nil; + } +} + +- (void)setApplicationIconBadgeNumber:(CDVInvokedUrlCommand *)command +{ + NSMutableDictionary* options = [command.arguments objectAtIndex:0]; + int badge = [[options objectForKey:@"badge"] intValue] ?: 0; + + [[UIApplication sharedApplication] setApplicationIconBadgeNumber:badge]; + + NSString* message = [NSString stringWithFormat:@"app badge count set to %d", badge]; + CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message]; + [self.commandDelegate sendPluginResult:commandResult callbackId:command.callbackId]; +} + +-(void)successWithMessage:(NSString *)message +{ + if (self.callbackId != nil) + { + CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsString:message]; + [self.commandDelegate sendPluginResult:commandResult callbackId:self.callbackId]; + } +} + +-(void)failWithMessage:(NSString *)message withError:(NSError *)error +{ + NSString *errorMessage = (error) ? [NSString stringWithFormat:@"%@ - %@", message, [error localizedDescription]] : message; + CDVPluginResult *commandResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:errorMessage]; + + [self.commandDelegate sendPluginResult:commandResult callbackId:self.callbackId]; +} + +@end diff --git a/plugins/phonegap-plugin-push/src/windows/PushPluginProxy.js b/plugins/phonegap-plugin-push/src/windows/PushPluginProxy.js new file mode 100644 index 00000000..ca8087b2 --- /dev/null +++ b/plugins/phonegap-plugin-push/src/windows/PushPluginProxy.js @@ -0,0 +1,86 @@ +var myApp = {}; +var pushNotifications = Windows.Networking.PushNotifications; + +var createNotificationJSON = function (e) { + var result = { message: '' }; //Added to identify callback as notification type in the API in case where notification has no message + var notificationPayload; + + switch (e.notificationType) { + case pushNotifications.PushNotificationType.toast: + case pushNotifications.PushNotificationType.tile: + if (e.notificationType === pushNotifications.PushNotificationType.toast) { + notificationPayload = e.toastNotification.content; + } + else { + notificationPayload = e.tileNotification.content; + } + var texts = notificationPayload.getElementsByTagName("text"); + if (texts.length > 1) { + result.title = texts[0].innerText; + result.message = texts[1].innerText; + } + else if(texts.length === 1) { + result.message = texts[0].innerText; + } + var images = notificationPayload.getElementsByTagName("image"); + if (images.length > 0) { + result.image = images[0].getAttribute("src"); + } + var soundFile = notificationPayload.getElementsByTagName("audio"); + if (soundFile.length > 0) { + result.sound = soundFile[0].getAttribute("src"); + } + break; + + case pushNotifications.PushNotificationType.badge: + notificationPayload = e.badgeNotification.content; + result.count = notificationPayload.getElementsByTagName("badge")[0].getAttribute("value"); + break; + + case pushNotifications.PushNotificationType.raw: + result.message = e.rawNotification.content; + break; + } + + result.additionalData = {}; + result.additionalData.pushNotificationReceivedEventArgs = e; + return result; +} + +module.exports = { + init: function (onSuccess, onFail, args) { + + var onNotificationReceived = function (e) { + var result = createNotificationJSON(e); + onSuccess(result, { keepCallback: true }); + } + + try { + pushNotifications.PushNotificationChannelManager.createPushNotificationChannelForApplicationAsync().done( + function (channel) { + var result = {}; + result.registrationId = channel.uri; + myApp.channel = channel; + channel.addEventListener("pushnotificationreceived", onNotificationReceived); + myApp.notificationEvent = onNotificationReceived; + onSuccess(result, { keepCallback: true }); + }, function (error) { + onFail(error); + }); + } catch (ex) { + onFail(ex); + } + }, + unregister: function (onSuccess, onFail, args) { + try { + myApp.channel.removeEventListener("pushnotificationreceived", myApp.notificationEvent); + myApp.channel.close(); + onSuccess(); + } catch(ex) { + onFail(ex); + } + } +}; +require("cordova/exec/proxy").add("PushNotification", module.exports); + + |
