Skip to content

Commit

Permalink
Merge pull request #6 from Karn/develop
Browse files Browse the repository at this point in the history
Release 0.0.3
  • Loading branch information
Karn authored Apr 30, 2018
2 parents 9d40f23 + 421a2f8 commit db4b3ed
Show file tree
Hide file tree
Showing 18 changed files with 549 additions and 79 deletions.
8 changes: 8 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
language: android

script:
- ./gradlew build jacocoTestReport
- ./gradlew connectedCheck
after_success:
- bash <(curl -s https://codecov.io/bash)

before_install:
- yes | sdkmanager "platforms;android-27"

branches:
only:
- master
Expand Down
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Simplified notification delivery for Android.
[![Kotlin](https://img.shields.io/badge/Kotlin-1.2.21-blue.svg?style=flat-square)](http://kotlinlang.org)
[![RxJava](https://img.shields.io/badge/Support-27.1.0-6ab344.svg?style=flat-square)](https://github.com/ReactiveX/RxJava/releases/tag/v2.1.10)
[![Build Status](https://img.shields.io/travis/Karn/notify.svg?style=flat-square)](https://travis-ci.org/Karn/notify)
[![Codecov](https://img.shields.io/codecov/c/github/karn/notify/master.svg?style=flat-square)](https://codecov.io/gh/Karn/notify)
[![GitHub (pre-)release](https://img.shields.io/github/release/karn/notify/all.svg?style=flat-square)
](./../../releases)

Expand Down Expand Up @@ -47,6 +48,21 @@ Notify

![Basic usecase](./docs/assets/default.svg)

If you run into a case in which the library does not provide the requisite builder functions you can get the `NotificationCompat.Builder` object and continue to use it as you would normally by calling `Creator#asBuilder()`.

###### NOTIFICATION ANATOMY

![Anatory](./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 `Creator#as[Type]((Type) -> Unit)` scoped functions. |
| 7 | Actions | Set using the `Creator#actions((ArrayList<Action>) -> Unit)` scoped function. |

###### CONTRIBUTING
There are many ways to [contribute](./.github/CONTRIBUTING.md), you can
Expand Down
6 changes: 6 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ buildscript {
ext {
kotlin_version = '1.2.21'
support_version = '27.1.1'

dokka_version = '0.9.16'
}

repositories {
Expand All @@ -22,6 +24,10 @@ buildscript {
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
classpath 'com.github.dcendents:android-maven-gradle-plugin:2.0'
// Code coverage
classpath 'org.jacoco:org.jacoco.core:0.8.0'
// Library documentation
classpath "org.jetbrains.dokka:dokka-android-gradle-plugin:$dokka_version"
}
}

Expand Down
140 changes: 140 additions & 0 deletions docs/assets/anatomy.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
70 changes: 70 additions & 0 deletions library/build.gradle
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'org.jetbrains.dokka-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'jacoco'

android {
compileSdkVersion 27
Expand All @@ -17,6 +19,9 @@ android {
}

buildTypes {
debug {
testCoverageEnabled true
}
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
Expand Down Expand Up @@ -58,3 +63,68 @@ dependencies {
testImplementation 'junit:junit:4.12'
testImplementation "org.robolectric:robolectric:3.8"
}

jacoco {
toolVersion = '0.8.0'
}

tasks.withType(Test) {
jacoco.includeNoLocationClasses = true
//travis has some strict RAM restrictions, can't afford to exceed this
maxHeapSize = "1024m"
jvmArgs "-Xmx1024m"
}

/**
* Adapted from https://medium.com/@rafael_toledo/unified-code-coverage-for-android-revisited-44789c9b722f
*/
task jacocoTestReport(type: JacocoReport, dependsOn: ['testDebugUnitTest', 'createDebugCoverageReport']) {
group = "Reporting"
description = "Generate Jacoco coverage reports for Debug build"

reports {
xml.enabled = true
html.enabled = true
}

// what to exclude from coverage report
// UI, "noise", generated classes, platform classes, etc.
def excludes = [
'**/R.class',
'**/R$*.class',
'**/*$ViewInjector*.*',
'**/BuildConfig.*',
'**/Manifest*.*',
'**/*Test*.*',
'android/**/*.*',
'**/*Fragment.*',
'**/*Activity.*'
]
// generated classes
classDirectories = fileTree(
dir: "$buildDir/intermediates/classes/debug",
excludes: excludes
) + fileTree(
dir: "$buildDir/tmp/kotlin-classes/debug",
excludes: excludes
)

// sources
sourceDirectories = files([
android.sourceSets.main.java.srcDirs,
"$project.projectDir/src/main/java"
])
executionData = fileTree(dir: project.buildDir, includes: ['jacoco/testDebugUnitTest.exec'])
}

dokka {
outputFormat = 'html'
outputDirectory = "$buildDir/javadoc"

// Use to include or exclude non public members.
includeNonPublic = false

// Do not create index pages for empty packages
skipEmptyPackages = true
noStdlibLink = true
}
94 changes: 71 additions & 23 deletions library/src/main/java/io/karn/notify/Creator.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,73 +6,121 @@ import io.karn.notify.entities.NotifyConfig
import io.karn.notify.entities.Payload
import io.karn.notify.entities.RawNotification

class Creator(private val notify: Notify, config: NotifyConfig = NotifyConfig()) {
/**
* Fluent API for creating a Notification object.
*/
class Creator internal constructor(private val notify: Notify, config: NotifyConfig = NotifyConfig()) {

private var meta = Payload.Meta()
private var header = config.header
private var content: Payload.Content = Payload.Content.Default()
private var actions: ArrayList<Action>? = null
private var stackable: Payload.Stackable? = null

fun meta(meta: Payload.Meta.() -> Unit): Creator {
meta(this.meta)
/**
* Scoped function for modifying the Metadata of a notification, such as click intents,
* notification category, and priority among other options.
*/
fun meta(init: Payload.Meta.() -> Unit): Creator {
this.meta.init()

return this
}

fun header(header: Payload.Header.() -> Unit): Creator {
header(this.header)
/**
* Scoped function for modifying the Header of a notification. Specifically, it allows the
* modification of the notificationIcon, color, the headerText (optional text next to the
* appName), and finally the channel of the notification if targeting Android O.
*/
fun header(init: Payload.Header.() -> Unit): Creator {
this.header.init()

return this
}

fun content(block: Payload.Content.Default.() -> Unit): Creator {
/**
* Scoped function for modifying the content of a 'Default' notification.
*/
fun content(init: Payload.Content.Default.() -> Unit): Creator {
this.content = Payload.Content.Default()
block(this.content as Payload.Content.Default)
(this.content as Payload.Content.Default).init()
return this
}

fun asTextList(block: Payload.Content.TextList.() -> Unit): Creator {
/**
* Scoped function for modifying the content of a 'TextList' notification.
*/
fun asTextList(init: Payload.Content.TextList.() -> Unit): Creator {
this.content = Payload.Content.TextList()
block(this.content as Payload.Content.TextList)
(this.content as Payload.Content.TextList).init()
return this
}

fun asBigText(block: Payload.Content.BigText.() -> Unit): Creator {
/**
* Scoped function for modifying the content of a 'BigText' notification.
*/
fun asBigText(init: Payload.Content.BigText.() -> Unit): Creator {
this.content = Payload.Content.BigText()
block(this.content as Payload.Content.BigText)
(this.content as Payload.Content.BigText).init()
return this
}

fun asBigPicture(block: Payload.Content.BigPicture.() -> Unit): Creator {
/**
* Scoped function for modifying the content of a 'BigPicture' notification.
*/
fun asBigPicture(init: Payload.Content.BigPicture.() -> Unit): Creator {
this.content = Payload.Content.BigPicture()
block(this.content as Payload.Content.BigPicture)
(this.content as Payload.Content.BigPicture).init()
return this
}

fun asMessage(block: Payload.Content.Message.() -> Unit): Creator {
/**
* Scoped function for modifying the content of a 'Message' notification.
*/
fun asMessage(init: Payload.Content.Message.() -> Unit): Creator {
this.content = Payload.Content.Message()
block(this.content as Payload.Content.Message)
(this.content as Payload.Content.Message).init()
return this
}

fun actions(block: ArrayList<Action>.() -> Unit): Creator {
/**
* Scoped function for modifying the 'Actions' of a notification. The transformation
* relies on adding standard notification Action objects.
*/
fun actions(init: ArrayList<Action>.() -> Unit): Creator {
this.actions = ArrayList()
block(this.actions as ArrayList<Action>)
(this.actions as ArrayList<Action>).init()
return this
}

fun stackable(block: Payload.Stackable.() -> Unit): Creator {
/**
* Scoped function for modifying the behaviour of 'Stacked' notifications. The transformation
* relies on the 'summaryText' of a stackable notification.
*/
fun stackable(init: Payload.Stackable.() -> Unit): Creator {
this.stackable = Payload.Stackable()
block(this.stackable as Payload.Stackable)
(this.stackable as Payload.Stackable).init()
return this
}

fun getBuilder(): NotificationCompat.Builder {
return notify.getBuilder(RawNotification(meta, header, content, stackable, actions))
/**
* Return the standard {@see NotificationCompat.Builder} after applying fluent API
* transformations (if any) from the {@see Creator} builder object.
*/
fun asBuilder(): NotificationCompat.Builder {
return notify.asBuilder(RawNotification(meta, header, content, stackable, actions))
}

fun send(): Int {
return notify.send(getBuilder())
/**
* Delegate a {@see Notification.Builder} object to the Notify NotificationInterop class which
* builds and displays the notification.
*
* This is a terminal operation.
*
* @return An integer corresponding to the ID of the system notification. Any updates should use
* this returned integer to make updates or to cancel the notification.
*/
fun show(): Int {
return notify.show(asBuilder())
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import android.text.Html
import io.karn.notify.entities.Payload
import io.karn.notify.entities.RawNotification

internal object NotificationInterlop {
internal object NotificationInterop {

@RequiresApi(Build.VERSION_CODES.O)
fun registerChannel(context: Context, channelKey: String, channelName: String, channelDescription: String, importance: Int = NotificationManager.IMPORTANCE_DEFAULT) {
Expand Down Expand Up @@ -126,6 +126,11 @@ internal object NotificationInterlop {
.setContentIntent(payload.meta.clickIntent)
// Set the handler in the event that the notification is dismissed.
.setDeleteIntent(payload.meta.clearIntent)
// The category of the notification which allows android to prioritize the
// notification as required.
.setCategory(payload.meta.category)
// Manual specification of the priority.
.setPriority(payload.meta.priority)

// Standard notifications have the collapsed title and text.
if (payload.content is Payload.Content.Standard) {
Expand All @@ -144,6 +149,11 @@ internal object NotificationInterlop {
var style: NotificationCompat.Style?

if (stackable != null) {
if (stackable.key.isBlank()) {
throw IllegalArgumentException("Specified a stackable notification but did" +
" not provide a valid stack key.")
}

builder.setContentIntent(stackable.clickIntent)
.extend(NotifyExtender()
.setKey(stackable.key)
Expand Down Expand Up @@ -175,7 +185,7 @@ internal object NotificationInterlop {
builder.setContentText(Utils.getAsSecondaryFormattedText((content.text
?: "").toString()))

val bigText: CharSequence = Html.fromHtml("<font color='#3D3D3D'>" + (content.expandedText
val bigText: CharSequence = Html.fromHtml("<font color='#3D3D3D'>" + (content.collapsedText
?: content.title
?: "")
.toString() + "</font><br>" + content.bigText?.replace("\n".toRegex(), "<br>"))
Expand All @@ -191,7 +201,7 @@ internal object NotificationInterlop {

NotificationCompat.BigPictureStyle()
// This is the second line in the 'expanded' notification.
.setSummaryText(content.expandedText ?: content.text)
.setSummaryText(content.collapsedText ?: content.text)
// This is the picture below.
.bigPicture(content.image)

Expand Down
Loading

0 comments on commit db4b3ed

Please sign in to comment.