Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improved images and icons support #1

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ src/*.d.ts
!src/references.d.ts
demo/platforms
publish/package/
.DS_Store
168 changes: 104 additions & 64 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,13 +58,17 @@ You can pass several options to this function, everything is optional:
|`groupedMessages`| An array of atmost 5 messages that would be displayed using android's notification [inboxStyle](https://developer.android.com/reference/android/app/Notification.InboxStyle.html). Note: The array would be trimed from the top if the messages exceed five. Default not set |
|`groupSummary`| An [inboxStyle](https://developer.android.com/reference/android/app/Notification.InboxStyle.html) notification summary. Default empty|
|`ticker` |On Android you can show a different text in the statusbar, instead of the `body`. Default not set, so `body` is used.|
|`at` |A JavaScript Date object indicating when the notification should be shown. Default 'now'.|
|`at` |A JavaScript Date object indicating when the notification should be shown. Default no set (the notification will be shown immediately).|
|`badge` |On iOS (and some Android devices) you see a number on top of the app icon. On most Android devices you'll see this number in the notification center. Default not set (0).|
|`sound` |Notification sound. For custom notification sound (iOS only), copy the file to `App_Resources/iOS`. Set this to "default" (or do not set at all) in order to use default OS sound. Set this to `null` to suppress sound.|
|`interval` |Set to one of `second minute hour day week month quarter year` if you want a recurring notification.|
|`smallIcon` |On Android you can set a custom icon in the system tray. Pass in 'res://filename' (without the extension) which lives in App_Resouces/Android/drawable folders. If not passed, we look for a file named 'ic_stat_notify.png' in the App_Resources/Android/drawable folders. Default: the app icon.|
|`largeIcon` |Same as `smallIcon`, but this one is shown when you expand the notification center. The optional file we look for is not 'ic_stat_notify.png' but 'ic_notify.png'.|
|`smallIcon` | Custom icon to show in the system tray on Android, which lives in App_Resouces/Android/drawable folders. Example: 'res://filename.png'. Defaults to 'res://ic_stat_notify.png' or the app icon if not present. Android < Lollipop (21) only. |
|`smallSilhouetteIcon` | Same as 'smallIcon' but for Android >= Lollipop (21). Should be an alpha-only image. Defaults to 'res://ic_stat_notify_silhouette.png' or the app icon if not present. |
|`largeIcon` | Custom icon to show in the notification center on Android, which lives in App_Resouces/Android/drawable folders. Example: 'res://filename.png'. Defaults to 'res://ic_notify.png' or the app icon if not present. Android only < Lollipop (21). |
|`largeSilhouetteIcon` | Same as 'largeIcon' but for Android >= Lollipop (21). Should be an alpha-only image. Defaults to 'res://ic_notify_silhouette.png' or the app icon if not present. |
|`color` | Custom color for the notification icon and title that will be applied when the notification center is expanded. Android >= Lollipop (21) only. |
|`ongoing` |Default is (`false`). Set whether this is an `ongoing` notification. Ongoing notifications cannot be dismissed by the user, so your application must take care of canceling them. (**Android Only**) |
|`image` | Expandable notification image. |
|`channel` |Default is (`Channel`). Set the channel name for Android API >= 26, which is shown when the user longpresses a notification. (**Android Only**) |
|`forceShowWhenInForeground` |Default is `false`. Set to `true` to always show the notification. Note that on iOS < 10 this is ignored (the notification is not shown), and on newer Androids it's currently ignored as well (the notification always shows, per platform default). |
|`actions` |Add an array of `NotificationAction` objects (see below) to add buttons or text input to a notification. |
Expand All @@ -80,9 +84,6 @@ You can pass several options to this function, everything is optional:
|`submitLabel` |The submit button label for `type` = `input`.|
|`placeholder` |The placeholder text for `type` = `input`.|

Note that after a reboot the `smallIcon` and `largeIcon` are not restored but fall back to the default (app icon).
This is a known issue and can be fixed in a future version.


```js
LocalNotifications.schedule([{
Expand All @@ -99,40 +100,56 @@ This is a known issue and can be fixed in a future version.
channel: 'My Channel', // default: 'Channel'
sound: "customsound-ios.wav", // falls back to the default sound on Android
at: new Date(new Date().getTime() + (10 * 1000)) // 10 seconds from now
}]).then(
function() {
console.log("Notification scheduled");
},
function(error) {
console.log("scheduling error: " + error);
}
)
}]).then(() => {
console.log("Notification scheduled!");
}, (error) => {
console.log(`Scheduling error: ${ error }`);
})
```

### Notification icons

> __Background information:__ Local notifications may fail silently if you don't provide the notification icons in the correct dimensions. They may do work perfectly fine on one device but fail on the other. That's because android might fallback to your xxxhdpi launcher icon which is too big. This type of error is noticeable in logcat: `!!! FAILED BINDER TRANSACTION !!! (parcel size = 1435376)`
You might want to take a look at [Known Issues](#transactiontoolargeexception-failed-binder-transaction) as some affect the notification icons.


#### Spec for `options.smallIcon` / `options.smallSilhouetteIcon`

These options default to `res://ic_stat_notify.png` and `res://ic_stat_notify_silhouette.png` respectively or the app icon if not present.

#### Spec for ic_stat_notify.png (smallIcon)
`options.largeSilhouetteIcon` should be an alpha-only image and will be used in Android >= Lollipop (21).

[These](https://developer.android.com/guide/practices/ui_guidelines/icon_design_status_bar.html) are the official size guidelines for them:

| Density qualifier | px | dpi
| ------- | ------- | ---
| ldpi | 18 × 18 | 120
| mdpi | 24 × 24 | 160
| hdpi | 36 × 36 | 240
| xhdpi | 48 × 48 | 320
| xxhdpi | 72 × 72 | 480
| xxxhdpi | 96 × 96 | 640 aprox.

[Android API Guides → Status Bar Icons](https://developer.android.com/guide/practices/ui_guidelines/icon_design_status_bar.html)

#### Spec for ic_notify.png (largeIcon)
#### Spec for `options.largeIcon` / `options.largeSilhouetteIcon`

These options default to `res://ic_notify.png` and `res://ic_notify_silhouette.png` respectively or the app icon if not present.

`options.largeSilhouetteIcon` should be an alpha-only image and will be used in Android >= Lollipop (21).

Unfortunately it seems like there's no official guide for these. Anyways there's a [dimen](https://github.com/android/platform_frameworks_base/blob/2d5dbba/core/res/res/values/dimens.xml#L181) that's telling us the dp size which we can translate to the following spec:

| Density qualifier | px | dpi
| -- | -- | --
| ldpi | 48 x 48 | 120
| mdpi | 64 x 64 | 160
| hdpi | 96 x 96 | 240
| xhdpi | 128 x 128 | 320
| xxhdpi | 192 x 192 | 480
| --------- | --------- | ---
| ldpi | 48 × 48 | 120
| mdpi | 64 × 64 | 160
| hdpi | 96 × 96 | 240
| xhdpi | 128 × 128 | 320
| xxhdpi | 192 × 192 | 480
| xxxhdpi * | 256 × 256 | 640 aprox.

__Don't include xxxhdpi__
__* Might break your App. See [Known Issues](#transactiontoolargeexception-failed-binder-transaction).__

> __xxxhdpi__: Extra-extra-extra-high-density uses (__launcher icon only__, see the note in Supporting Multiple Screens); approximately 640dpi. Added in API Level 18
> Source: [Density Qualifier Docs](https://developer.android.com/guide/topics/resources/providing-resources.html#DensityQualifier)
__Source:__ [Density Qualifier Docs](https://developer.android.com/guide/topics/resources/providing-resources.html#DensityQualifier)

### addOnMessageReceivedCallback
Tapping a notification in the notification center will launch your app.
Expand All @@ -142,17 +159,13 @@ Use this function to have a callback invoked when a notification was used to lau
Note that on iOS it will even be triggered when your app is in the foreground and a notification is received.

```js
LocalNotifications.addOnMessageReceivedCallback(
function (notification) {
console.log("ID: " + notification.id);
console.log("Title: " + notification.title);
console.log("Body: " + notification.body);
}
).then(
function() {
console.log("Listener added");
}
)
LocalNotifications.addOnMessageReceivedCallback((notification) => {
console.log("ID: " + notification.id);
console.log("Title: " + notification.title);
console.log("Body: " + notification.body);
}).then(() => {
console.log("Listener added");
})
```

### getScheduledIds
Expand All @@ -161,33 +174,29 @@ If you want to know the ID's of all notifications which have been scheduled, do
Note that all functions have an error handler as well (see `schedule`), but to keep things readable we won't repeat ourselves.

```js
LocalNotifications.getScheduledIds().then(
function(ids) {
console.log("ID's: " + ids);
}
)
LocalNotifications.getScheduledIds().then((ids) => {
console.log("ID's: " + ids);
})
```

### cancel
If you want to cancel a previously scheduled notification (and you know its ID), you can cancel it:

```js
LocalNotifications.cancel(5 /* the ID */).then(
function(foundAndCanceled) {
if (foundAndCanceled) {
console.log("OK, it's gone!");
} else {
console.log("No ID 5 was scheduled");
}
}
)
LocalNotifications.cancel(5 /* the ID */).then((foundAndCanceled) => {
if (foundAndCanceled) {
console.log("OK, it's gone!");
} else {
console.log("No ID 5 was scheduled");
}
});
```

### cancelAll
If you just want to cancel all previously scheduled notifications, do this:

```js
LocalNotifications.cancelAll();
LocalNotifications.cancelAll();
```

### requestPermission
Expand All @@ -199,11 +208,9 @@ since an iOS can only request permission once. In which case the user needs to g
enable permissions for your app.

```js
LocalNotifications.requestPermission().then(
function(granted) {
console.log("Permission granted? " + granted);
}
)
LocalNotifications.requestPermission().then((granted) => {
console.log("Permission granted? " + granted);
});
```

### hasPermission
Expand All @@ -212,18 +219,51 @@ On Android you don't need permission, but on iOS you do. Android will simply ret
If the `requestPermission` or `schedule` functions previously ran you may want to check whether or not the user granted permission:

```js
LocalNotifications.hasPermission().then(
function(granted) {
console.log("Permission granted? " + granted);
}
)
LocalNotifications.hasPermission().then((granted) => {
console.log("Permission granted? " + granted);
});
```

## Help, my Android app is restarted
## Troubleshooting

### Help, my Android app is restarted
When your app is launched from a notification you may notice the app is not continuing from when you
put it in the background. To fix that, open `app/App_Resources/AndroidManifest.xml` and change the
`launchMode` of the NativeScript activity. For instance:

```xml
<activity android:launchMode="singleTop" />
```


## Known Issues

### `TransactionTooLargeException` / `!!! FAILED BINDER TRANSACTION !!!`

Due to the way the code in this plugin is organised on Android an [`Intent`](https://developer.android.com/reference/android/content/Intent) with a [`Bundle`](https://developer.android.com/reference/android/os/Bundle) containing a [`Notification`](https://developer.android.com/reference/android/app/Notification) is created everytime a delayed (`options.interval`) or recurrent notification (`option.at`) is created.

Before version `3.0.0`, this `Intent` will also be created (but not used) even if none of those 2 options are set.

The problem with this is that the notification will contain 2 properties that are [`Bitmap`s](https://developer.android.com/reference/android/graphics/Bitmap):

- `option.largeIcon` / `option.largeSilhouetteIcon`.
- `options.image`.

...and if these `Bitmap`s are too big, you will get a `TransactionTooLargeException` / `!!! FAILED BINDER TRANSACTION !!!` exception/error on the console/logcat and Local Notifications will fail silently:

!!! FAILED BINDER TRANSACTION !!! (parcel size = 1435376)

The correct way to fix this would be to create the `Notification` objects inside `NotificationPublisher`, which is the `BroadcastReceiver` that is notified when it's time to show a delayed/recurrent notification, but that involves a major rewrite of this plugin.

These are your options until that happens:

- Fix the plugin yourself and open a PR.
- Avoid using `option.interval` or `option.at` in conjunction with `options.image`, `option.largeIcon`, `option.largeSilhouetteIcon`.
- Avoid using `option.interval` or `option.at` in conjunction with `options.image` but use `option.largeIcon` and/or `option.largeSilhouetteIcon`, as long as you don't provide a `xxxhdpi` icon and stick to the sizes specified in the tables above.


### Notifications look different after a reboot on Android

Note that after a reboot the notification icons are not restored but fall back to the default (application icon). This is a known issue and can be fixed in a future version.

Also, notifications with grouped messages or images in Android will fall back to a normal (text-only) notification.
54 changes: 27 additions & 27 deletions demo/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
{
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"noEmitHelpers": true,
"noEmitOnError": true,
"lib": [
"es6",
"dom"
],
"baseUrl": ".",
"paths": {
"~/*": [
"app/*"
],
"*": [
"./node_modules/tns-core-modules/*",
"./node_modules/*"
]
}
},
"exclude": [
"node_modules",
"platforms"
]
}
"compilerOptions": {
"module": "commonjs",
"target": "es5",
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"noEmitHelpers": true,
"noEmitOnError": true,
"lib": [
"es6",
"dom"
],
"baseUrl": ".",
"paths": {
"~/*": [
"app/*"
],
"*": [
"./node_modules/tns-core-modules/*",
"./node_modules/*"
]
}
},
"exclude": [
"node_modules",
"platforms"
]
}