From 6d5c04c1db21d81fb7d379d4313fa7461eef943f Mon Sep 17 00:00:00 2001 From: Karn Saheb Date: Wed, 2 Jan 2019 10:02:43 -0500 Subject: [PATCH] Updated advanced topic examples + deprecated NotifyCreator#cancel() (#24) - The Advanced Topics document has been updated with examples of click and clear handling, as well as examples of notification actions and stackable notifications. - Additionally, the 'NotifyCreator#cancel()' function has been deprecated in favor of the static implementation 'Notify#cancelNotification()'. This is because the NotifyCreator class should act primarily as the notification construction interface and should not expose functions which manage notification states or attributes post-construction. --- README.md | 13 --- docs/advanced.md | 98 ++++++++++++++++++- .../src/main/java/io/karn/notify/Notify.kt | 14 +-- .../main/java/io/karn/notify/NotifyCreator.kt | 10 +- .../test/java/io/karn/notify/NotifyTest.kt | 3 +- .../main/java/presentation/MainActivity.kt | 4 +- 6 files changed, 115 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 63cb1b8..a74ca1e 100644 --- a/README.md +++ b/README.md @@ -55,19 +55,6 @@ If you run into a case in which the library does not provide the requisite build > **Tip:** Advanced usage topics are documented [here](./docs/advanced.md). -#### NOTIFICATION ANATOMY - -![Anatomy](./docs/assets/anatomy.svg) - -| ID | Name | Description | -| --- | --- | --- | -| 1 | Icon | Set using the `Header#icon` field. | -| 2 | App Name | Application name, immutable. | -| 3 | Header Text | Optional description text. Set using the `Header#headerText` field. | -| 4 | Timestamp | Timestamp of the notification. | -| 5 | Expand Icon | Indicates that the notification is expandable. | -| 6 | Content | The "meat" of the notification set using of of the `NotifyCreator#as[Type]((Type) -> Unit)` scoped functions. | -| 7 | Actions | Set using the `NotifyCreator#actions((ArrayList) -> Unit)` scoped function. | #### CONTRIBUTING There are many ways to [contribute](./.github/CONTRIBUTING.md), you can diff --git a/docs/advanced.md b/docs/advanced.md index 4909f60..6fb1d77 100644 --- a/docs/advanced.md +++ b/docs/advanced.md @@ -1,10 +1,104 @@ ## Advanced Usage > **Note:** This page is still a work-in-progress. You can help complete the documentation by contributing to the project. +#### NOTIFICATION ANATOMY + +![Anatomy](./docs/assets/anatomy.svg) + +| ID | Name | Description | +| --- | --- | --- | +| 1 | Icon | Set using the `Header#icon` field. | +| 2 | App Name | Application name, immutable. | +| 3 | Header Text | Optional description text. Set using the `Header#headerText` field. | +| 4 | Timestamp | Timestamp of the notification. | +| 5 | Expand Icon | Indicates that the notification is expandable. | +| 6 | Content | The "meat" of the notification set using of of the `NotifyCreator#as[Type]((Type) -> Unit)` scoped functions. | +| 7 | Actions | Set using the `NotifyCreator#actions((ArrayList) -> Unit)` scoped function. | #### RESPONDING TO CLICKS -The `Payload.Meta` object provides `clickIntent` and `clearIntent` members which when not `null` will be fired when clicked or dismissed. + +The `Payload.Meta` object provides `clickIntent` and `clearIntent` members which when not `null`, will be fired when clicked or dismissed. + +```Kotlin +Notify + .with(context) + .meta { // this: Payload.Meta + // Launch the MainActivity once the notification is clicked. + clickIntent = PendingIntent.getActivity(this@MainActivity, + 0, + Intent(this@MainActivity, MainActivity::class.java), + 0) + // Start a service which clears the badge count once the notification is dismissed. + clearIntent = PendingIntent.getService(this@MainActivity, + 0, + Intent(this@MainActivity, MyNotificationService::class.java) + .putExtra("action", "clear_badges"), + 0) + } + .content { // this: Payload.Content.Default + title = "New dessert menu" + text = "The Cheesecake Factory has a new dessert for you to try!" + } + .show() +``` + + +#### ACTIONS + +Similarly, we can construct actions which can be used to quickly perform tasks without opening your app. The actions are the collection of buttons that are shown below the notification to quickly perform tasks. + +```Kotlin +Notify + .with(context) + .content { // this: Payload.Content.Default + title = "New dessert menu" + text = "The Cheesecake Factory has a new dessert for you to try!" + } + .actions { // this: ArrayList + add(Action( + // The icon corresponding to the action. + R.drawable.ic_app_icon, + // The text corresponding to the action -- this is what shows . + "Clear", + // Swap this PendingIntent for whatever Intent is to be processed when the action is clicked. + PendingIntent.getService(this@MainActivity, + 0, + Intent(this@MainActivity, MyNotificationService::class.java) + .putExtra("action", "clear_badges"), + 0) + )) + } + .show() +``` + #### STACKABLE NOTIFICATIONS -#### ACTIONS \ No newline at end of file +Notify provides a solution to the idea of grouping notifications into a "stack". I.e the notifications are grouped into a single notification when there is more than one of the same type as defined by `stackable.key`. +This is a particularly effective method of reducing the clutter of the notification tray while also providing the relevant information. + +```Kotlin +Notify + .with(this) + .content { // this: Payload.Content.Default + title = "New dessert menu" + text = "The Cheesecake Factory has a new dessert for you to try!" + } + // Define the notification as being stackable. This block should be the same for all notifications which + // are to be grouped together. + .stackable { // this: Payload.Stackable + // In particular, this key should be the same. The properties of this stackable notification as + // taken from the latest stackable notification's stackable block. + key = "test_key" + // This is the summary of this notification as it appears when it is as part of a stacked notification. This + // String value is what is shown as a single line in the stacked notification. + summaryContent = "test summary content" + // The number of notifications with the same key is passed as the 'count' argument. We happen not to + // use it, but it is there if needed. + summaryTitle = { count -> "Summary title" } + // ... here as well, but we instead choose to use to to update the summary for when the notification + // is collapsed. + summaryDescription = { count -> count.toString() + " new notifications." } + } + .show() +``` \ No newline at end of file diff --git a/library/src/main/java/io/karn/notify/Notify.kt b/library/src/main/java/io/karn/notify/Notify.kt index 3bb851e..541c12d 100644 --- a/library/src/main/java/io/karn/notify/Notify.kt +++ b/library/src/main/java/io/karn/notify/Notify.kt @@ -78,6 +78,13 @@ class Notify internal constructor(internal var context: Context) { fun with(context: Context): NotifyCreator { return NotifyCreator(Notify(context), defaultConfig) } + + /** + * Cancel an existing notification with a particular id. + */ + fun cancelNotification(id: Int) { + return NotificationInterop.cancelNotification(Notify.defaultConfig.notificationManager!!, id) + } } init { @@ -111,11 +118,4 @@ class Notify internal constructor(internal var context: Context) { internal fun show(builder: NotificationCompat.Builder): Int { return NotificationInterop.showNotification(Notify.defaultConfig.notificationManager!!, builder) } - - /** - * Cancel an existing notification with a particular id. - */ - internal fun cancel(id: Int) { - return NotificationInterop.cancelNotification(Notify.defaultConfig.notificationManager!!, id) - } } diff --git a/library/src/main/java/io/karn/notify/NotifyCreator.kt b/library/src/main/java/io/karn/notify/NotifyCreator.kt index 4cc898c..7530fe6 100644 --- a/library/src/main/java/io/karn/notify/NotifyCreator.kt +++ b/library/src/main/java/io/karn/notify/NotifyCreator.kt @@ -148,7 +148,15 @@ class NotifyCreator internal constructor(private val notify: Notify, config: Not return notify.show(asBuilder()) } + /** + * Cancel an existing notification given an ID. + * + * @deprecated Choose to instead use the static function {@see Notify#cancelNotification()} which provides the correct + * encapsulation of the this `cancel` function. + */ + @Deprecated(message = "Exposes function under the incorrect API -- NotifyCreator is reserved strictly for notification construction.", + replaceWith = ReplaceWith("Notify.cancelNotification(id)", "io.karn.notify.Notify")) fun cancel(id: Int) { - return notify.cancel(id) + return Notify.cancelNotification(id) } } diff --git a/library/src/test/java/io/karn/notify/NotifyTest.kt b/library/src/test/java/io/karn/notify/NotifyTest.kt index 69c50be..8fc099d 100644 --- a/library/src/test/java/io/karn/notify/NotifyTest.kt +++ b/library/src/test/java/io/karn/notify/NotifyTest.kt @@ -87,8 +87,7 @@ class NotifyTest : NotifyTestBase() { Assert.assertEquals(1, NotificationInterop.getActiveNotifications(shadowNotificationManager).size) - Notify.with(this.context) - .cancel(notificationId) + Notify.cancelNotification(notificationId) Assert.assertEquals(0, NotificationInterop.getActiveNotifications(shadowNotificationManager).size) } diff --git a/sample/src/main/java/presentation/MainActivity.kt b/sample/src/main/java/presentation/MainActivity.kt index d221221..596405c 100644 --- a/sample/src/main/java/presentation/MainActivity.kt +++ b/sample/src/main/java/presentation/MainActivity.kt @@ -3,9 +3,9 @@ package presentation import android.graphics.BitmapFactory import android.graphics.Color import android.os.Bundle -import androidx.core.app.NotificationCompat -import androidx.appcompat.app.AppCompatActivity import android.view.View +import androidx.appcompat.app.AppCompatActivity +import androidx.core.app.NotificationCompat import io.karn.notify.Notify import io.karn.notify.sample.R import java.util.*