diff --git a/docs/API.md b/docs/API.md index 56e6b32dd..1d6374939 100644 --- a/docs/API.md +++ b/docs/API.md @@ -73,6 +73,7 @@ All iOS boolean options can also be specified as `string` | `ios.sound` | `boolean` | `false` | Optional. If `true` the device plays a sound on receipt of notification. **Note:** the value you set this option to the first time you call the init method will be how the application always acts. Once this is set programmatically in the init method it can only be changed manually by the user in Settings>Notifications>`App Name`. This is normal iOS behaviour. | | `ios.clearBadge` | `boolean` | `false` | Optional. If `true` the badge will be cleared on app startup. | | `ios.categories` | `Object` | `{}` | Optional. The data required in order to enable Action Buttons for iOS. See [Action Buttons on iOS](https://github.com/phonegap/phonegap-plugin-push/blob/master/docs/PAYLOAD.md#action-buttons-1) for more details. | +| `ios.critical` | `boolean` | `false` | Optional. If `true` the device can show up critical alerts. (Possible since iOS 12 with a special entitlement) **Note:** the value you set this option to the first time you call the init method will be how the application always acts. Once this is set programmatically in the init method it can only be changed manually by the user in Settings>Notifications>`App Name`. This is normal iOS behaviour. | #### iOS GCM support diff --git a/docs/PAYLOAD.md b/docs/PAYLOAD.md index d06085340..463259a8e 100644 --- a/docs/PAYLOAD.md +++ b/docs/PAYLOAD.md @@ -167,6 +167,37 @@ The JSON message can contain the following fields, see [Apple developer docs](ht } ``` +### Speical Format for Critical Alerts + +Since iOS 12 it's possible to send critical alerts to the users device. A critical alert will popup and play sound even when device is in DND Mode or muted. This functionallity is mainly to be used by health Apps to inform about critical states. +Instead of the name of the sound you have to send a dictionary containing further information about critical + +```json +{ + "aps": { + "alert": { + // alternatively just a string: "Your Message", + "title": "A short string describing the purpose of the notification", + "body": "The text of the alert message", + // localization of message is possible + "launch-image": "The filename of an image file in the app bundle, with or without the filename extension. The image is used as the launch image when users tap the action button or move the action slider" + }, + "badge": 5, // Number to show at App icon + "content-available": "0", // configure background updates, see below + "category": "identifier", // Provide this key with a string value that represents the notification’s type + "thread-id": "id", // Provide this key with a string value that represents the app-specific identifier for grouping notifications + "sound": { + "critical" : 1, // When 1, the notification is handled as a critical one. The sound is played aloud even when device is in dnd mode or muted + "name" : "default", // play default sound, or custom sound, see [iOS Sound](#sound-1) section + "volume" : 1.0 // Optional: Volume. Value can between 0.0 (silent) and 1.0 (full volume) + } + }, + "notId": 1, + "custom_key1": "value1", + "custom_key2": "value2" +} +``` + ### Using AWS-SNS with APNS This is the JSON-encoded format you can send via AWS-SNS's web UI: diff --git a/src/android/com/adobe/phonegap/push/FCMService.java b/src/android/com/adobe/phonegap/push/FCMService.java index db1738a72..124681bd0 100644 --- a/src/android/com/adobe/phonegap/push/FCMService.java +++ b/src/android/com/adobe/phonegap/push/FCMService.java @@ -29,6 +29,11 @@ import android.text.Html; import android.text.Spanned; import android.util.Log; +import android.media.MediaPlayer; +import android.os.Vibrator; +import android.media.AudioManager; +import android.media.AudioAttributes; +import android.os.VibrationEffect; import com.google.firebase.messaging.FirebaseMessagingService; import com.google.firebase.messaging.RemoteMessage; @@ -53,6 +58,10 @@ public class FCMService extends FirebaseMessagingService implements PushConstant private static final String LOG_TAG = "Push_FCMService"; private static HashMap> messageMap = new HashMap>(); + + public FCMService() { + + } public void setNotification(int notId, String message) { ArrayList messageList = messageMap.get(notId); @@ -415,6 +424,8 @@ public void createNotification(Context context, Bundle extras) { } else { mBuilder = new NotificationCompat.Builder(context); } + + mBuilder.setCategory(NotificationCompat.CATEGORY_ALARM); mBuilder.setWhen(System.currentTimeMillis()).setContentTitle(fromHtml(extras.getString(TITLE))) .setTicker(fromHtml(extras.getString(TITLE))).setContentIntent(contentIntent).setDeleteIntent(deleteIntent) @@ -425,6 +436,8 @@ public void createNotification(Context context, Bundle extras) { String localIconColor = prefs.getString(ICON_COLOR, null); boolean soundOption = prefs.getBoolean(SOUND, true); boolean vibrateOption = prefs.getBoolean(VIBRATE, true); + boolean isAlarm = Boolean.parseBoolean(extras.getString(ISALARM, "false")); + Log.d(LOG_TAG, "stored icon=" + localIcon); Log.d(LOG_TAG, "stored iconColor=" + localIconColor); Log.d(LOG_TAG, "stored sound=" + soundOption); @@ -434,8 +447,16 @@ public void createNotification(Context context, Bundle extras) { * Notification Vibration */ - setNotificationVibration(extras, vibrateOption, mBuilder); + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) + { + setNotificationVibrationExternal(extras, vibrateOption, mBuilder); + } else { + setNotificationVibration(extras, vibrateOption, mBuilder); + } + + + /* * Notification Icon Color * @@ -477,7 +498,17 @@ public void createNotification(Context context, Bundle extras) { * Notification Sound */ if (soundOption) { - setNotificationSound(context, extras, mBuilder); + + /* Check is isAlarm Notification */ + + Log.d(LOG_TAG, "isAlarm = " + String.valueOf(isAlarm)); + + if(!isAlarm) + { + + } + + //setNotificationSound(context, extras, mBuilder); } /* @@ -516,6 +547,68 @@ public void createNotification(Context context, Bundle extras) { createActions(extras, mBuilder, resources, packageName, notId); mNotificationManager.notify(appName, notId, mBuilder.build()); + + if (soundOption) { + + String soundname = extras.getString(SOUNDNAME); + if (soundname == null) { + soundname = extras.getString(SOUND); + } + + MediaPlayer mMediaPlayer; + try { + + mMediaPlayer = new MediaPlayer(); + + if (SOUND_RINGTONE.equals(soundname)) { + mMediaPlayer.setDataSource(this.getApplicationContext(), android.provider.Settings.System.DEFAULT_RINGTONE_URI); + } else if (soundname != null && !soundname.contentEquals(SOUND_DEFAULT)) { + Uri sound = Uri + .parse(ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + context.getPackageName() + "/raw/" + soundname); + Log.d(LOG_TAG, sound.toString()); + mMediaPlayer.setDataSource(this.getApplicationContext(), sound); + } else { + mMediaPlayer.setDataSource(this.getApplicationContext(), android.provider.Settings.System.DEFAULT_NOTIFICATION_URI); + } + + if(isAlarm) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + { + AudioAttributes audioAttributes = new AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_ALARM) + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .build(); + mMediaPlayer.setAudioAttributes(audioAttributes); + } else { + mMediaPlayer.setAudioStreamType(AudioManager.STREAM_ALARM); + } + } else { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) + { + AudioAttributes audioAttributes = new AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_NOTIFICATION_EVENT) + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .build(); + mMediaPlayer.setAudioAttributes(audioAttributes); + } else { + mMediaPlayer.setAudioStreamType(AudioManager.STREAM_NOTIFICATION); + } + } + + + mMediaPlayer.setLooping(false); + mMediaPlayer.prepareAsync(); + mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() { + @Override + public void onPrepared(MediaPlayer mp) { + mMediaPlayer.start(); + } + }); + } catch (SecurityException | IOException e) { + + } + } + } private void updateIntent(Intent intent, String callback, Bundle extras, boolean foreground, int notId) { @@ -633,6 +726,23 @@ private void setVisibility(Context context, Bundle extras, NotificationCompat.Bu } } + private void setNotificationVibrationExternal(Bundle extras, Boolean vibrateOption, NotificationCompat.Builder mBuilder) { + String vibrationPattern = extras.getString(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].trim()); + } catch (NumberFormatException nfe) { + } + } + + Vibrator vibrator = (Vibrator)this.getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE); + vibrator.vibrate(VibrationEffect.createWaveform(results, -1)); + } + } + private void setNotificationVibration(Bundle extras, Boolean vibrateOption, NotificationCompat.Builder mBuilder) { String vibrationPattern = extras.getString(VIBRATION_PATTERN); if (vibrationPattern != null) { @@ -728,6 +838,7 @@ private void setNotificationMessage(int notId, Bundle extras, NotificationCompat } } + private void setNotificationSound(Context context, Bundle extras, NotificationCompat.Builder mBuilder) { String soundname = extras.getString(SOUNDNAME); if (soundname == null) { @@ -947,4 +1058,4 @@ private boolean isAvailableSender(String from) { return from.equals(savedSenderID) || from.startsWith("/topics/"); } -} +} \ No newline at end of file diff --git a/src/android/com/adobe/phonegap/push/PushConstants.java b/src/android/com/adobe/phonegap/push/PushConstants.java index 9b4656a00..cd40ad6a9 100644 --- a/src/android/com/adobe/phonegap/push/PushConstants.java +++ b/src/android/com/adobe/phonegap/push/PushConstants.java @@ -13,6 +13,7 @@ public interface PushConstants { public static final String SOUND = "sound"; public static final String SOUND_DEFAULT = "default"; public static final String SOUND_RINGTONE = "ringtone"; + public static final String SOUND_NONE = "none"; public static final String VIBRATE = "vibrate"; public static final String ACTIONS = "actions"; public static final String CALLBACK = "callback"; @@ -102,4 +103,5 @@ public interface PushConstants { public static final String ONGOING = "ongoing"; public static final String LIST_CHANNELS = "listChannels"; public static final String CLEAR_NOTIFICATION = "clearNotification"; + public static final String ISALARM = "isAlarm"; } diff --git a/src/android/com/adobe/phonegap/push/PushPlugin.java b/src/android/com/adobe/phonegap/push/PushPlugin.java index c1f0a10c6..f4baa7f0b 100644 --- a/src/android/com/adobe/phonegap/push/PushPlugin.java +++ b/src/android/com/adobe/phonegap/push/PushPlugin.java @@ -114,6 +114,8 @@ private void createChannel(JSONObject channel) throws JSONException { .setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE).build(); if (SOUND_RINGTONE.equals(sound)) { mChannel.setSound(android.provider.Settings.System.DEFAULT_RINGTONE_URI, audioAttributes); + } else if (SOUND_NONE.equals(sound)) { + mChannel.setSound(null, null); } else if (sound != null && sound.isEmpty()) { // Disable sound for this notification channel if an empty string is passed. // https://stackoverflow.com/a/47144981/6194193 diff --git a/src/ios/PushPlugin.m b/src/ios/PushPlugin.m index 9aca1d0d9..145a50b6b 100644 --- a/src/ios/PushPlugin.m +++ b/src/ios/PushPlugin.m @@ -202,6 +202,7 @@ - (void)init:(CDVInvokedUrlCommand*)command; id badgeArg = [iosOptions objectForKey:@"badge"]; id soundArg = [iosOptions objectForKey:@"sound"]; id alertArg = [iosOptions objectForKey:@"alert"]; + id criticalArg = [iosOptions objectForKey:@"critical"]; id clearBadgeArg = [iosOptions objectForKey:@"clearBadge"]; if (([badgeArg isKindOfClass:[NSString class]] && [badgeArg isEqualToString:@"true"]) || [badgeArg boolValue]) @@ -218,6 +219,11 @@ - (void)init:(CDVInvokedUrlCommand*)command; { authorizationOptions |= UNAuthorizationOptionAlert; } + + if ((([criticalArg isKindOfClass:[NSString class]] && [criticalArg isEqualToString:@"true"]) || [criticalArg boolValue]) && @available(iOS 12.0, *)) + { + authorizationOptions |= UNAuthorizationOptionCriticalAlert; + } if (clearBadgeArg == nil || ([clearBadgeArg isKindOfClass:[NSString class]] && [clearBadgeArg isEqualToString:@"false"]) || ![clearBadgeArg boolValue]) { NSLog(@"PushPlugin.register: setting badge to false");