mirror of
https://github.com/afollestad/nock-nock.git
synced 2025-08-03 06:38:38 +00:00
Unit tests for NockNotificationManager
This commit is contained in:
parent
cf39207c08
commit
03c687def5
21 changed files with 333 additions and 163 deletions
|
@ -35,6 +35,7 @@ dependencies {
|
||||||
|
|
||||||
implementation 'com.afollestad.material-dialogs:core:' + versions.materialDialogs
|
implementation 'com.afollestad.material-dialogs:core:' + versions.materialDialogs
|
||||||
|
|
||||||
|
implementation 'com.jakewharton.timber:timber:' + versions.timber
|
||||||
testImplementation 'junit:junit:' + versions.junit
|
testImplementation 'junit:junit:' + versions.junit
|
||||||
testImplementation 'org.mockito:mockito-core:' + versions.mockito
|
testImplementation 'org.mockito:mockito-core:' + versions.mockito
|
||||||
testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:' + versions.mockitoKotlin
|
testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:' + versions.mockitoKotlin
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
package com.afollestad.nocknock
|
package com.afollestad.nocknock
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.util.Log
|
|
||||||
import com.afollestad.nocknock.di.AppComponent
|
import com.afollestad.nocknock.di.AppComponent
|
||||||
import com.afollestad.nocknock.di.DaggerAppComponent
|
import com.afollestad.nocknock.di.DaggerAppComponent
|
||||||
import com.afollestad.nocknock.engine.statuscheck.BootReceiver
|
import com.afollestad.nocknock.engine.statuscheck.BootReceiver
|
||||||
|
@ -18,19 +17,14 @@ import com.afollestad.nocknock.ui.viewsite.ViewSiteActivity
|
||||||
import com.afollestad.nocknock.utilities.Injector
|
import com.afollestad.nocknock.utilities.Injector
|
||||||
import com.afollestad.nocknock.utilities.ext.systemService
|
import com.afollestad.nocknock.utilities.ext.systemService
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
|
import timber.log.Timber
|
||||||
|
import timber.log.Timber.DebugTree
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import timber.log.Timber.d as log
|
||||||
|
|
||||||
/** @author Aidan Follestad (@afollestad) */
|
/** @author Aidan Follestad (@afollestad) */
|
||||||
class NockNockApp : Application(), Injector {
|
class NockNockApp : Application(), Injector {
|
||||||
|
|
||||||
companion object {
|
|
||||||
private fun log(message: String) {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.d("NockNockApp", message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private lateinit var appComponent: AppComponent
|
private lateinit var appComponent: AppComponent
|
||||||
@Inject lateinit var nockNotificationManager: NockNotificationManager
|
@Inject lateinit var nockNotificationManager: NockNotificationManager
|
||||||
|
|
||||||
|
@ -39,6 +33,10 @@ class NockNockApp : Application(), Injector {
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
Timber.plant(DebugTree())
|
||||||
|
}
|
||||||
|
|
||||||
val okHttpClient = OkHttpClient.Builder()
|
val okHttpClient = OkHttpClient.Builder()
|
||||||
.addNetworkInterceptor { chain ->
|
.addNetworkInterceptor { chain ->
|
||||||
val request = chain.request()
|
val request = chain.request()
|
||||||
|
|
|
@ -22,6 +22,7 @@ ext.versions = [
|
||||||
materialDialogs : '2.0.0-rc3',
|
materialDialogs : '2.0.0-rc3',
|
||||||
rxkPrefs : '1.2.0',
|
rxkPrefs : '1.2.0',
|
||||||
|
|
||||||
|
timber : '4.7.1',
|
||||||
junit : '4.12',
|
junit : '4.12',
|
||||||
mockito : '2.23.0',
|
mockito : '2.23.0',
|
||||||
mockitoKotlin : '2.0.0-RC1',
|
mockitoKotlin : '2.0.0-RC1',
|
||||||
|
|
|
@ -27,6 +27,7 @@ dependencies {
|
||||||
implementation 'com.google.dagger:dagger:' + versions.dagger
|
implementation 'com.google.dagger:dagger:' + versions.dagger
|
||||||
kapt 'com.google.dagger:dagger-compiler:' + versions.dagger
|
kapt 'com.google.dagger:dagger-compiler:' + versions.dagger
|
||||||
|
|
||||||
|
implementation 'com.jakewharton.timber:timber:' + versions.timber
|
||||||
testImplementation 'junit:junit:' + versions.junit
|
testImplementation 'junit:junit:' + versions.junit
|
||||||
testImplementation 'org.mockito:mockito-core:' + versions.mockito
|
testImplementation 'org.mockito:mockito-core:' + versions.mockito
|
||||||
testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:' + versions.mockitoKotlin
|
testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:' + versions.mockitoKotlin
|
||||||
|
|
|
@ -7,13 +7,13 @@ package com.afollestad.nocknock.engine.db
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.database.Cursor
|
import android.database.Cursor
|
||||||
import android.util.Log
|
|
||||||
import com.afollestad.nocknock.data.ServerModel
|
import com.afollestad.nocknock.data.ServerModel
|
||||||
import com.afollestad.nocknock.data.ServerModel.Companion.COLUMN_ID
|
import com.afollestad.nocknock.data.ServerModel.Companion.COLUMN_ID
|
||||||
import com.afollestad.nocknock.data.ServerModel.Companion.DEFAULT_SORT_ORDER
|
import com.afollestad.nocknock.data.ServerModel.Companion.DEFAULT_SORT_ORDER
|
||||||
import com.afollestad.nocknock.data.ServerModel.Companion.TABLE_NAME
|
import com.afollestad.nocknock.data.ServerModel.Companion.TABLE_NAME
|
||||||
import com.afollestad.nocknock.engine.BuildConfig
|
|
||||||
import com.afollestad.nocknock.utilities.ext.diffFrom
|
import com.afollestad.nocknock.utilities.ext.diffFrom
|
||||||
|
import timber.log.Timber.d as log
|
||||||
|
import timber.log.Timber.w as warn
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/** @author Aidan Follestad (@afollestad) */
|
/** @author Aidan Follestad (@afollestad) */
|
||||||
|
@ -35,21 +35,6 @@ interface ServerModelStore {
|
||||||
/** @author Aidan Follestad (@afollestad) */
|
/** @author Aidan Follestad (@afollestad) */
|
||||||
class RealServerModelStore @Inject constructor(app: Application) : ServerModelStore {
|
class RealServerModelStore @Inject constructor(app: Application) : ServerModelStore {
|
||||||
|
|
||||||
companion object {
|
|
||||||
private fun log(
|
|
||||||
message: String,
|
|
||||||
warning: Boolean = false
|
|
||||||
) {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
if (warning) {
|
|
||||||
Log.w("ServerModelStore", message)
|
|
||||||
} else {
|
|
||||||
Log.d("ServerModelStore", message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val dbHelper = ServerModelDbHelper(app)
|
private val dbHelper = ServerModelDbHelper(app)
|
||||||
|
|
||||||
override suspend fun get(id: Int?): List<ServerModel> {
|
override suspend fun get(id: Int?): List<ServerModel> {
|
||||||
|
@ -115,7 +100,7 @@ class RealServerModelStore @Inject constructor(app: Application) : ServerModelSt
|
||||||
val valuesDiff = oldValues.diffFrom(newValues)
|
val valuesDiff = oldValues.diffFrom(newValues)
|
||||||
|
|
||||||
if (valuesDiff.size() == 0) {
|
if (valuesDiff.size() == 0) {
|
||||||
log("Nothing has changed - nothing to update!", warning = true)
|
warn("Nothing has changed - nothing to update!")
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,8 +9,6 @@ import android.content.BroadcastReceiver
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.Intent.ACTION_BOOT_COMPLETED
|
import android.content.Intent.ACTION_BOOT_COMPLETED
|
||||||
import android.util.Log
|
|
||||||
import com.afollestad.nocknock.engine.BuildConfig
|
|
||||||
import com.afollestad.nocknock.utilities.ext.injector
|
import com.afollestad.nocknock.utilities.ext.injector
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
import kotlinx.coroutines.Dispatchers.Main
|
import kotlinx.coroutines.Dispatchers.Main
|
||||||
|
@ -18,18 +16,11 @@ import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import timber.log.Timber.d as log
|
||||||
|
|
||||||
/** @author Aidan Follestad (@afollestad) */
|
/** @author Aidan Follestad (@afollestad) */
|
||||||
class BootReceiver : BroadcastReceiver() {
|
class BootReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
companion object {
|
|
||||||
private fun log(message: String) {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.d("BootReceiver", message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Inject lateinit var checkStatusManager: CheckStatusManager
|
@Inject lateinit var checkStatusManager: CheckStatusManager
|
||||||
|
|
||||||
override fun onReceive(
|
override fun onReceive(
|
||||||
|
|
|
@ -8,7 +8,6 @@ package com.afollestad.nocknock.engine.statuscheck
|
||||||
import android.app.job.JobParameters
|
import android.app.job.JobParameters
|
||||||
import android.app.job.JobService
|
import android.app.job.JobService
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.util.Log
|
|
||||||
import com.afollestad.nocknock.data.ServerModel
|
import com.afollestad.nocknock.data.ServerModel
|
||||||
import com.afollestad.nocknock.data.ServerStatus
|
import com.afollestad.nocknock.data.ServerStatus
|
||||||
import com.afollestad.nocknock.data.ServerStatus.CHECKING
|
import com.afollestad.nocknock.data.ServerStatus.CHECKING
|
||||||
|
@ -21,7 +20,6 @@ import com.afollestad.nocknock.data.isPending
|
||||||
import com.afollestad.nocknock.engine.BuildConfig.APPLICATION_ID
|
import com.afollestad.nocknock.engine.BuildConfig.APPLICATION_ID
|
||||||
import com.afollestad.nocknock.engine.db.ServerModelStore
|
import com.afollestad.nocknock.engine.db.ServerModelStore
|
||||||
import com.afollestad.nocknock.notifications.NockNotificationManager
|
import com.afollestad.nocknock.notifications.NockNotificationManager
|
||||||
import com.afollestad.nocknock.utilities.BuildConfig
|
|
||||||
import com.afollestad.nocknock.utilities.ext.injector
|
import com.afollestad.nocknock.utilities.ext.injector
|
||||||
import com.afollestad.nocknock.utilities.js.JavaScript
|
import com.afollestad.nocknock.utilities.js.JavaScript
|
||||||
import kotlinx.coroutines.Dispatchers.IO
|
import kotlinx.coroutines.Dispatchers.IO
|
||||||
|
@ -32,6 +30,7 @@ import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.lang.System.currentTimeMillis
|
import java.lang.System.currentTimeMillis
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import timber.log.Timber.d as log
|
||||||
|
|
||||||
/** @author Aidan Follestad (@afollestad)*/
|
/** @author Aidan Follestad (@afollestad)*/
|
||||||
class CheckStatusJob : JobService() {
|
class CheckStatusJob : JobService() {
|
||||||
|
@ -41,12 +40,6 @@ class CheckStatusJob : JobService() {
|
||||||
const val ACTION_JOB_RUNNING = "$APPLICATION_ID.STATUS_JOB_RUNNING"
|
const val ACTION_JOB_RUNNING = "$APPLICATION_ID.STATUS_JOB_RUNNING"
|
||||||
const val KEY_UPDATE_MODEL = "site_model"
|
const val KEY_UPDATE_MODEL = "site_model"
|
||||||
const val KEY_SITE_ID = "site.id"
|
const val KEY_SITE_ID = "site.id"
|
||||||
|
|
||||||
private fun log(message: String) {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.d("CheckStatusJob", message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject lateinit var modelStore: ServerModelStore
|
@Inject lateinit var modelStore: ServerModelStore
|
||||||
|
|
|
@ -10,20 +10,19 @@ import android.app.job.JobInfo.NETWORK_TYPE_ANY
|
||||||
import android.app.job.JobScheduler
|
import android.app.job.JobScheduler
|
||||||
import android.app.job.JobScheduler.RESULT_SUCCESS
|
import android.app.job.JobScheduler.RESULT_SUCCESS
|
||||||
import android.os.PersistableBundle
|
import android.os.PersistableBundle
|
||||||
import android.util.Log
|
|
||||||
import com.afollestad.nocknock.data.ServerModel
|
import com.afollestad.nocknock.data.ServerModel
|
||||||
import com.afollestad.nocknock.data.ServerStatus.ERROR
|
import com.afollestad.nocknock.data.ServerStatus.ERROR
|
||||||
import com.afollestad.nocknock.data.ServerStatus.OK
|
import com.afollestad.nocknock.data.ServerStatus.OK
|
||||||
import com.afollestad.nocknock.engine.R
|
import com.afollestad.nocknock.engine.R
|
||||||
import com.afollestad.nocknock.engine.db.ServerModelStore
|
import com.afollestad.nocknock.engine.db.ServerModelStore
|
||||||
import com.afollestad.nocknock.engine.statuscheck.CheckStatusJob.Companion.KEY_SITE_ID
|
import com.afollestad.nocknock.engine.statuscheck.CheckStatusJob.Companion.KEY_SITE_ID
|
||||||
import com.afollestad.nocknock.utilities.BuildConfig
|
|
||||||
import com.afollestad.nocknock.utilities.providers.StringProvider
|
import com.afollestad.nocknock.utilities.providers.StringProvider
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.Request
|
import okhttp3.Request
|
||||||
import okhttp3.Response
|
import okhttp3.Response
|
||||||
import java.net.SocketTimeoutException
|
import java.net.SocketTimeoutException
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import timber.log.Timber.d as log
|
||||||
|
|
||||||
/** @author Aidan Follestad (@afollestad) */
|
/** @author Aidan Follestad (@afollestad) */
|
||||||
data class CheckResult(
|
data class CheckResult(
|
||||||
|
@ -56,14 +55,6 @@ class RealCheckStatusManager @Inject constructor(
|
||||||
private val siteStore: ServerModelStore
|
private val siteStore: ServerModelStore
|
||||||
) : CheckStatusManager {
|
) : CheckStatusManager {
|
||||||
|
|
||||||
companion object {
|
|
||||||
private fun log(message: String) {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.d("CheckStatusManager", message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override suspend fun ensureScheduledChecks() {
|
override suspend fun ensureScheduledChecks() {
|
||||||
val sites = siteStore.get()
|
val sites = siteStore.get()
|
||||||
if (sites.isEmpty()) {
|
if (sites.isEmpty()) {
|
||||||
|
|
|
@ -26,6 +26,7 @@ dependencies {
|
||||||
implementation 'com.google.dagger:dagger:' + versions.dagger
|
implementation 'com.google.dagger:dagger:' + versions.dagger
|
||||||
kapt 'com.google.dagger:dagger-compiler:' + versions.dagger
|
kapt 'com.google.dagger:dagger-compiler:' + versions.dagger
|
||||||
|
|
||||||
|
implementation 'com.jakewharton.timber:timber:' + versions.timber
|
||||||
testImplementation 'junit:junit:' + versions.junit
|
testImplementation 'junit:junit:' + versions.junit
|
||||||
testImplementation 'org.mockito:mockito-core:' + versions.mockito
|
testImplementation 'org.mockito:mockito-core:' + versions.mockito
|
||||||
testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:' + versions.mockitoKotlin
|
testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:' + versions.mockitoKotlin
|
||||||
|
|
|
@ -5,10 +5,6 @@
|
||||||
*/
|
*/
|
||||||
package com.afollestad.nocknock.notifications
|
package com.afollestad.nocknock.notifications
|
||||||
|
|
||||||
import android.app.NotificationChannel
|
|
||||||
import android.content.Context
|
|
||||||
import android.os.Build.VERSION_CODES
|
|
||||||
import androidx.annotation.RequiresApi
|
|
||||||
import androidx.core.app.NotificationManagerCompat.IMPORTANCE_DEFAULT
|
import androidx.core.app.NotificationManagerCompat.IMPORTANCE_DEFAULT
|
||||||
|
|
||||||
/** @author Aidan Follestad (@afollestad) */
|
/** @author Aidan Follestad (@afollestad) */
|
||||||
|
@ -25,14 +21,3 @@ enum class Channel(
|
||||||
importance = IMPORTANCE_DEFAULT
|
importance = IMPORTANCE_DEFAULT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @author Aidan Follestad (@afollestad) */
|
|
||||||
@RequiresApi(VERSION_CODES.O)
|
|
||||||
fun Channel.toNotificationChannel(context: Context): NotificationChannel {
|
|
||||||
val titleText = context.getString(this.title)
|
|
||||||
val descriptionText = context.getString(this.description)
|
|
||||||
return NotificationChannel(this.id, titleText, this.importance)
|
|
||||||
.apply {
|
|
||||||
description = descriptionText
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -6,20 +6,19 @@
|
||||||
package com.afollestad.nocknock.notifications
|
package com.afollestad.nocknock.notifications
|
||||||
|
|
||||||
import android.annotation.TargetApi
|
import android.annotation.TargetApi
|
||||||
import android.app.Application
|
|
||||||
import android.app.NotificationManager
|
import android.app.NotificationManager
|
||||||
import android.os.Build.VERSION_CODES
|
import android.os.Build.VERSION_CODES
|
||||||
import android.util.Log
|
|
||||||
import androidx.core.app.NotificationCompat.DEFAULT_VIBRATE
|
|
||||||
import com.afollestad.nocknock.data.ServerModel
|
import com.afollestad.nocknock.data.ServerModel
|
||||||
import com.afollestad.nocknock.notifications.Channel.CheckFailures
|
import com.afollestad.nocknock.notifications.Channel.CheckFailures
|
||||||
import com.afollestad.nocknock.utilities.providers.BitmapProvider
|
import com.afollestad.nocknock.utilities.providers.BitmapProvider
|
||||||
import com.afollestad.nocknock.utilities.providers.IntentProvider
|
import com.afollestad.nocknock.utilities.providers.IntentProvider
|
||||||
|
import com.afollestad.nocknock.utilities.providers.NotificationChannelProvider
|
||||||
|
import com.afollestad.nocknock.utilities.providers.NotificationProvider
|
||||||
import com.afollestad.nocknock.utilities.providers.RealIntentProvider.Companion.BASE_NOTIFICATION_REQUEST_CODE
|
import com.afollestad.nocknock.utilities.providers.RealIntentProvider.Companion.BASE_NOTIFICATION_REQUEST_CODE
|
||||||
import com.afollestad.nocknock.utilities.providers.StringProvider
|
import com.afollestad.nocknock.utilities.providers.StringProvider
|
||||||
import com.afollestad.nocknock.utilities.qualifiers.AppIconRes
|
import com.afollestad.nocknock.utilities.qualifiers.AppIconRes
|
||||||
import com.afollestad.nocknock.utilities.util.hasOreo
|
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
import timber.log.Timber.d as log
|
||||||
|
|
||||||
/** @author Aidan Follestad (@afollestad) */
|
/** @author Aidan Follestad (@afollestad) */
|
||||||
interface NockNotificationManager {
|
interface NockNotificationManager {
|
||||||
|
@ -37,22 +36,15 @@ interface NockNotificationManager {
|
||||||
|
|
||||||
/** @author Aidan Follestad (@afollestad) */
|
/** @author Aidan Follestad (@afollestad) */
|
||||||
class RealNockNotificationManager @Inject constructor(
|
class RealNockNotificationManager @Inject constructor(
|
||||||
private val app: Application,
|
|
||||||
@AppIconRes private val appIconRes: Int,
|
@AppIconRes private val appIconRes: Int,
|
||||||
private val stockManager: NotificationManager,
|
private val stockManager: NotificationManager,
|
||||||
private val bitmapProvider: BitmapProvider,
|
private val bitmapProvider: BitmapProvider,
|
||||||
private val stringProvider: StringProvider,
|
private val stringProvider: StringProvider,
|
||||||
private val intentProvider: IntentProvider
|
private val intentProvider: IntentProvider,
|
||||||
|
private val channelProvider: NotificationChannelProvider,
|
||||||
|
private val notificationProvider: NotificationProvider
|
||||||
) : NockNotificationManager {
|
) : NockNotificationManager {
|
||||||
|
|
||||||
companion object {
|
|
||||||
private fun log(message: String) {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.d("NockNotificationManager", message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private var isAppOpen = false
|
private var isAppOpen = false
|
||||||
|
|
||||||
override fun setIsAppOpen(open: Boolean) {
|
override fun setIsAppOpen(open: Boolean) {
|
||||||
|
@ -73,15 +65,14 @@ class RealNockNotificationManager @Inject constructor(
|
||||||
log("Posting status notification for site ${model.id}...")
|
log("Posting status notification for site ${model.id}...")
|
||||||
val intent = intentProvider.getPendingIntentForViewSite(model)
|
val intent = intentProvider.getPendingIntentForViewSite(model)
|
||||||
|
|
||||||
val newNotification = notification(app, CheckFailures) {
|
val newNotification = notificationProvider.create(
|
||||||
setContentTitle(model.name)
|
channelId = CheckFailures.id,
|
||||||
setContentText(stringProvider.get(R.string.something_wrong))
|
title = model.name,
|
||||||
setContentIntent(intent)
|
content = stringProvider.get(R.string.something_wrong),
|
||||||
setSmallIcon(R.drawable.ic_notification)
|
intent = intent,
|
||||||
setLargeIcon(bitmapProvider.get(appIconRes))
|
smallIcon = R.drawable.ic_notification,
|
||||||
setAutoCancel(true)
|
largeIcon = bitmapProvider.get(appIconRes)
|
||||||
setDefaults(DEFAULT_VIBRATE)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
stockManager.notify(model.url, model.notificationId(), newNotification)
|
stockManager.notify(model.url, model.notificationId(), newNotification)
|
||||||
log("Posted status notification for site ${model.notificationId()}.")
|
log("Posted status notification for site ${model.notificationId()}.")
|
||||||
|
@ -96,13 +87,13 @@ class RealNockNotificationManager @Inject constructor(
|
||||||
|
|
||||||
@TargetApi(VERSION_CODES.O)
|
@TargetApi(VERSION_CODES.O)
|
||||||
private fun createChannel(channel: Channel) {
|
private fun createChannel(channel: Channel) {
|
||||||
if (!hasOreo()) {
|
val notificationChannel = channelProvider.create(
|
||||||
log("Not running Android O, channels won't be created.")
|
id = channel.id,
|
||||||
return
|
title = stringProvider.get(channel.title),
|
||||||
}
|
description = stringProvider.get(channel.description),
|
||||||
|
importance = channel.importance
|
||||||
val notificationChannel = channel.toNotificationChannel(app)
|
)
|
||||||
stockManager.createNotificationChannel(notificationChannel)
|
notificationChannel?.let(stockManager::createNotificationChannel)
|
||||||
log("Created notification channel ${channel.id}")
|
log("Created notification channel ${channel.id}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed under Apache-2.0
|
|
||||||
*
|
|
||||||
* Designed and developed by Aidan Follestad (@afollestad)
|
|
||||||
*/
|
|
||||||
package com.afollestad.nocknock.notifications
|
|
||||||
|
|
||||||
import android.app.Notification
|
|
||||||
import android.content.Context
|
|
||||||
import androidx.core.app.NotificationCompat
|
|
||||||
|
|
||||||
typealias NotificationBuilder = NotificationCompat.Builder
|
|
||||||
typealias NotificationConstructor = NotificationBuilder.() -> Unit
|
|
||||||
|
|
||||||
/** @author Aidan Follestad (@afollestad) */
|
|
||||||
fun notification(
|
|
||||||
context: Context,
|
|
||||||
channel: Channel,
|
|
||||||
builder: NotificationConstructor
|
|
||||||
): Notification {
|
|
||||||
val newNotification = NotificationCompat.Builder(context, channel.id)
|
|
||||||
builder(newNotification)
|
|
||||||
return newNotification.build()
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
package com.afollestad.nocknock.notifications;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example local unit test, which will execute on the development machine (host).
|
|
||||||
*
|
|
||||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
|
||||||
*/
|
|
||||||
public class ExampleUnitTest {
|
|
||||||
@Test
|
|
||||||
public void addition_isCorrect() {
|
|
||||||
assertEquals(4, 2 + 2);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,151 @@
|
||||||
|
/*
|
||||||
|
* Licensed under Apache-2.0
|
||||||
|
*
|
||||||
|
* Designed and developed by Aidan Follestad (@afollestad)
|
||||||
|
*/
|
||||||
|
package com.afollestad.nocknock.notifications
|
||||||
|
|
||||||
|
import android.app.Notification
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import com.afollestad.nocknock.data.ServerModel
|
||||||
|
import com.afollestad.nocknock.data.ValidationMode.STATUS_CODE
|
||||||
|
import com.afollestad.nocknock.notifications.Channel.CheckFailures
|
||||||
|
import com.afollestad.nocknock.utilities.providers.BitmapProvider
|
||||||
|
import com.afollestad.nocknock.utilities.providers.IntentProvider
|
||||||
|
import com.afollestad.nocknock.utilities.providers.NotificationChannelProvider
|
||||||
|
import com.afollestad.nocknock.utilities.providers.NotificationProvider
|
||||||
|
import com.afollestad.nocknock.utilities.providers.RealIntentProvider.Companion.BASE_NOTIFICATION_REQUEST_CODE
|
||||||
|
import com.afollestad.nocknock.utilities.providers.StringProvider
|
||||||
|
import com.google.common.truth.Truth.assertThat
|
||||||
|
import com.nhaarman.mockitokotlin2.any
|
||||||
|
import com.nhaarman.mockitokotlin2.argumentCaptor
|
||||||
|
import com.nhaarman.mockitokotlin2.doAnswer
|
||||||
|
import com.nhaarman.mockitokotlin2.doReturn
|
||||||
|
import com.nhaarman.mockitokotlin2.mock
|
||||||
|
import com.nhaarman.mockitokotlin2.times
|
||||||
|
import com.nhaarman.mockitokotlin2.verify
|
||||||
|
import com.nhaarman.mockitokotlin2.verifyNoMoreInteractions
|
||||||
|
import com.nhaarman.mockitokotlin2.whenever
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
class NockNotificationManagerTest {
|
||||||
|
|
||||||
|
private val appIconRes = 1024
|
||||||
|
private val somethingWentWrong = "something went wrong"
|
||||||
|
|
||||||
|
private val stockManager = mock<NotificationManager>()
|
||||||
|
private val appIcon = mock<Bitmap>()
|
||||||
|
private val bitmapProvider = mock<BitmapProvider> {
|
||||||
|
on { get(appIconRes) } doReturn appIcon
|
||||||
|
}
|
||||||
|
private val stringProvider = mock<StringProvider> {
|
||||||
|
on { get(R.string.something_wrong) } doReturn somethingWentWrong
|
||||||
|
}
|
||||||
|
private val intentProvider = mock<IntentProvider>()
|
||||||
|
private val channelProvider = mock<NotificationChannelProvider>()
|
||||||
|
private val notificationProvider = mock<NotificationProvider>()
|
||||||
|
|
||||||
|
private val manager = RealNockNotificationManager(
|
||||||
|
appIconRes,
|
||||||
|
stockManager,
|
||||||
|
bitmapProvider,
|
||||||
|
stringProvider,
|
||||||
|
intentProvider,
|
||||||
|
channelProvider,
|
||||||
|
notificationProvider
|
||||||
|
)
|
||||||
|
|
||||||
|
@Before fun setup() {
|
||||||
|
whenever(channelProvider.create(any(), any(), any(), any())).doAnswer { inv ->
|
||||||
|
val id = inv.getArgument<String>(0)
|
||||||
|
val title = inv.getArgument<String>(1)
|
||||||
|
val description = inv.getArgument<String>(2)
|
||||||
|
val important = inv.getArgument<Int>(3)
|
||||||
|
return@doAnswer mock<NotificationChannel> {
|
||||||
|
on { this.id } doReturn id
|
||||||
|
on { this.name } doReturn title
|
||||||
|
on { this.description } doReturn description
|
||||||
|
on { this.importance } doReturn important
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun createChannels() {
|
||||||
|
whenever(stringProvider.get(any())).doReturn("")
|
||||||
|
val createdChannel = mock<NotificationChannel> {
|
||||||
|
on { this.id } doReturn CheckFailures.id
|
||||||
|
}
|
||||||
|
whenever(channelProvider.create(any(), any(), any(), any()))
|
||||||
|
.doReturn(createdChannel)
|
||||||
|
manager.createChannels()
|
||||||
|
|
||||||
|
val captor = argumentCaptor<NotificationChannel>()
|
||||||
|
verify(stockManager, times(1)).createNotificationChannel(captor.capture())
|
||||||
|
|
||||||
|
val channel = captor.allValues.single()
|
||||||
|
assertThat(channel.id).isEqualTo(CheckFailures.id)
|
||||||
|
|
||||||
|
verifyNoMoreInteractions(stockManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun postStatusNotification_appIsOpen() {
|
||||||
|
manager.setIsAppOpen(true)
|
||||||
|
manager.postStatusNotification(fakeModel())
|
||||||
|
|
||||||
|
verifyNoMoreInteractions(stockManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun postStatusNotification_appNotOpen() {
|
||||||
|
manager.setIsAppOpen(false)
|
||||||
|
val model = fakeModel()
|
||||||
|
|
||||||
|
val pendingIntent = mock<PendingIntent>()
|
||||||
|
whenever(intentProvider.getPendingIntentForViewSite(model))
|
||||||
|
.doReturn(pendingIntent)
|
||||||
|
|
||||||
|
val notification = mock<Notification>()
|
||||||
|
whenever(
|
||||||
|
notificationProvider.create(
|
||||||
|
CheckFailures.id,
|
||||||
|
model.name,
|
||||||
|
somethingWentWrong,
|
||||||
|
pendingIntent,
|
||||||
|
R.drawable.ic_notification,
|
||||||
|
appIcon
|
||||||
|
)
|
||||||
|
).doReturn(notification)
|
||||||
|
|
||||||
|
manager.postStatusNotification(model)
|
||||||
|
|
||||||
|
verify(stockManager).notify(
|
||||||
|
model.url,
|
||||||
|
BASE_NOTIFICATION_REQUEST_CODE + model.id,
|
||||||
|
notification
|
||||||
|
)
|
||||||
|
verifyNoMoreInteractions(stockManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun cancelStatusNotification() {
|
||||||
|
val model = fakeModel()
|
||||||
|
manager.cancelStatusNotification(model)
|
||||||
|
verify(stockManager).cancel(BASE_NOTIFICATION_REQUEST_CODE + model.id)
|
||||||
|
verifyNoMoreInteractions(stockManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test fun cancelStatusNotifications() {
|
||||||
|
manager.cancelStatusNotifications()
|
||||||
|
verify(stockManager).cancelAll()
|
||||||
|
verifyNoMoreInteractions(stockManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fakeModel() = ServerModel(
|
||||||
|
id = 1,
|
||||||
|
url = "https://hello.com",
|
||||||
|
name = "Testing",
|
||||||
|
validationMode = STATUS_CODE
|
||||||
|
)
|
||||||
|
}
|
|
@ -20,6 +20,7 @@ android {
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'androidx.annotation:annotation:' + versions.androidx
|
implementation 'androidx.annotation:annotation:' + versions.androidx
|
||||||
|
implementation 'androidx.appcompat:appcompat:' + versions.androidx
|
||||||
|
|
||||||
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:' + versions.kotlin
|
implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:' + versions.kotlin
|
||||||
api 'org.jetbrains.kotlinx:kotlinx-coroutines-core:' + versions.coroutines
|
api 'org.jetbrains.kotlinx:kotlinx-coroutines-core:' + versions.coroutines
|
||||||
|
|
|
@ -7,9 +7,15 @@ package com.afollestad.nocknock.utilities
|
||||||
|
|
||||||
import com.afollestad.nocknock.utilities.providers.BitmapProvider
|
import com.afollestad.nocknock.utilities.providers.BitmapProvider
|
||||||
import com.afollestad.nocknock.utilities.providers.IntentProvider
|
import com.afollestad.nocknock.utilities.providers.IntentProvider
|
||||||
|
import com.afollestad.nocknock.utilities.providers.NotificationChannelProvider
|
||||||
|
import com.afollestad.nocknock.utilities.providers.NotificationProvider
|
||||||
import com.afollestad.nocknock.utilities.providers.RealBitmapProvider
|
import com.afollestad.nocknock.utilities.providers.RealBitmapProvider
|
||||||
import com.afollestad.nocknock.utilities.providers.RealIntentProvider
|
import com.afollestad.nocknock.utilities.providers.RealIntentProvider
|
||||||
|
import com.afollestad.nocknock.utilities.providers.RealNotificationChannelProvider
|
||||||
|
import com.afollestad.nocknock.utilities.providers.RealNotificationProvider
|
||||||
|
import com.afollestad.nocknock.utilities.providers.RealSdkProvider
|
||||||
import com.afollestad.nocknock.utilities.providers.RealStringProvider
|
import com.afollestad.nocknock.utilities.providers.RealStringProvider
|
||||||
|
import com.afollestad.nocknock.utilities.providers.SdkProvider
|
||||||
import com.afollestad.nocknock.utilities.providers.StringProvider
|
import com.afollestad.nocknock.utilities.providers.StringProvider
|
||||||
import dagger.Binds
|
import dagger.Binds
|
||||||
import dagger.Module
|
import dagger.Module
|
||||||
|
@ -19,6 +25,12 @@ import javax.inject.Singleton
|
||||||
@Module
|
@Module
|
||||||
abstract class UtilitiesModule {
|
abstract class UtilitiesModule {
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
abstract fun provideSdkProvider(
|
||||||
|
sdkProvider: RealSdkProvider
|
||||||
|
): SdkProvider
|
||||||
|
|
||||||
@Binds
|
@Binds
|
||||||
@Singleton
|
@Singleton
|
||||||
abstract fun provideBitmapProvider(
|
abstract fun provideBitmapProvider(
|
||||||
|
@ -36,4 +48,16 @@ abstract class UtilitiesModule {
|
||||||
abstract fun provideIntentProvider(
|
abstract fun provideIntentProvider(
|
||||||
intentProvider: RealIntentProvider
|
intentProvider: RealIntentProvider
|
||||||
): IntentProvider
|
): IntentProvider
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
abstract fun provideChannelProvider(
|
||||||
|
channelProvider: RealNotificationChannelProvider
|
||||||
|
): NotificationChannelProvider
|
||||||
|
|
||||||
|
@Binds
|
||||||
|
@Singleton
|
||||||
|
abstract fun provideNotificationProvider(
|
||||||
|
notificationProvider: RealNotificationProvider
|
||||||
|
): NotificationProvider
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,6 @@
|
||||||
*/
|
*/
|
||||||
package com.afollestad.nocknock.utilities.js
|
package com.afollestad.nocknock.utilities.js
|
||||||
|
|
||||||
import android.util.Log
|
|
||||||
import com.afollestad.nocknock.utilities.BuildConfig
|
|
||||||
import org.mozilla.javascript.Context
|
import org.mozilla.javascript.Context
|
||||||
import org.mozilla.javascript.EvaluatorException
|
import org.mozilla.javascript.EvaluatorException
|
||||||
import org.mozilla.javascript.Function
|
import org.mozilla.javascript.Function
|
||||||
|
@ -62,9 +60,6 @@ object JavaScript {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log(
|
|
||||||
"Evaluated to $message ($success): $code"
|
|
||||||
)
|
|
||||||
return if (!success) message else null
|
return if (!success) message else null
|
||||||
} finally {
|
} finally {
|
||||||
Context.exit()
|
Context.exit()
|
||||||
|
@ -73,10 +68,4 @@ object JavaScript {
|
||||||
return e.message
|
return e.message
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun log(message: String) {
|
|
||||||
if (BuildConfig.DEBUG) {
|
|
||||||
Log.d("JavaScript", message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Licensed under Apache-2.0
|
||||||
|
*
|
||||||
|
* Designed and developed by Aidan Follestad (@afollestad)
|
||||||
|
*/
|
||||||
|
package com.afollestad.nocknock.utilities.providers
|
||||||
|
|
||||||
|
import android.annotation.TargetApi
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.os.Build.VERSION_CODES
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/** @author Aidan Follestad (@afollestad) */
|
||||||
|
interface NotificationChannelProvider {
|
||||||
|
|
||||||
|
/** @return null if the device doesn't have Android O. */
|
||||||
|
fun create(
|
||||||
|
id: String,
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
importance: Int
|
||||||
|
): NotificationChannel?
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @author Aidan Follestad (@afollestad) */
|
||||||
|
class RealNotificationChannelProvider @Inject constructor(
|
||||||
|
private val sdkProvider: SdkProvider
|
||||||
|
) : NotificationChannelProvider {
|
||||||
|
|
||||||
|
@TargetApi(VERSION_CODES.O)
|
||||||
|
override fun create(
|
||||||
|
id: String,
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
importance: Int
|
||||||
|
): NotificationChannel? {
|
||||||
|
if (!sdkProvider.hasOreo()) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return NotificationChannel(id, title, importance)
|
||||||
|
.apply {
|
||||||
|
this.description = description
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,52 @@
|
||||||
|
/*
|
||||||
|
* Licensed under Apache-2.0
|
||||||
|
*
|
||||||
|
* Designed and developed by Aidan Follestad (@afollestad)
|
||||||
|
*/
|
||||||
|
package com.afollestad.nocknock.utilities.providers
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.app.Notification
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.NotificationCompat.DEFAULT_VIBRATE
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/** @author Aidan Follestad (@afollestad) */
|
||||||
|
interface NotificationProvider {
|
||||||
|
|
||||||
|
fun create(
|
||||||
|
channelId: String,
|
||||||
|
title: String,
|
||||||
|
content: String,
|
||||||
|
intent: PendingIntent,
|
||||||
|
smallIcon: Int,
|
||||||
|
largeIcon: Bitmap
|
||||||
|
): Notification
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @author Aidan Follestad (@afollestad) */
|
||||||
|
class RealNotificationProvider @Inject constructor(
|
||||||
|
private val app: Application
|
||||||
|
) : NotificationProvider {
|
||||||
|
|
||||||
|
override fun create(
|
||||||
|
channelId: String,
|
||||||
|
title: String,
|
||||||
|
content: String,
|
||||||
|
intent: PendingIntent,
|
||||||
|
smallIcon: Int,
|
||||||
|
largeIcon: Bitmap
|
||||||
|
): Notification {
|
||||||
|
return NotificationCompat.Builder(app, channelId)
|
||||||
|
.setContentTitle(title)
|
||||||
|
.setContentText(content)
|
||||||
|
.setContentIntent(intent)
|
||||||
|
.setSmallIcon(smallIcon)
|
||||||
|
.setLargeIcon(largeIcon)
|
||||||
|
.setAutoCancel(true)
|
||||||
|
.setDefaults(DEFAULT_VIBRATE)
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* Licensed under Apache-2.0
|
||||||
|
*
|
||||||
|
* Designed and developed by Aidan Follestad (@afollestad)
|
||||||
|
*/
|
||||||
|
package com.afollestad.nocknock.utilities.providers
|
||||||
|
|
||||||
|
import android.os.Build.VERSION.SDK_INT
|
||||||
|
import android.os.Build.VERSION_CODES.O
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
/** @author Aidan Follestad (@afollestad) */
|
||||||
|
interface SdkProvider {
|
||||||
|
|
||||||
|
fun hasOreo(): Boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @author Aidan Follestad (@afollestad) */
|
||||||
|
class RealSdkProvider @Inject constructor() : SdkProvider {
|
||||||
|
|
||||||
|
override fun hasOreo() = SDK_INT >= O
|
||||||
|
}
|
|
@ -1,11 +0,0 @@
|
||||||
/*
|
|
||||||
* Licensed under Apache-2.0
|
|
||||||
*
|
|
||||||
* Designed and developed by Aidan Follestad (@afollestad)
|
|
||||||
*/
|
|
||||||
package com.afollestad.nocknock.utilities.util
|
|
||||||
|
|
||||||
import android.os.Build.VERSION.SDK_INT
|
|
||||||
import android.os.Build.VERSION_CODES.O
|
|
||||||
|
|
||||||
fun hasOreo() = SDK_INT >= O
|
|
Loading…
Add table
Add a link
Reference in a new issue