Fix StatusUpdateIntentReceiverTest

This commit is contained in:
Aidan Follestad 2018-12-06 18:10:51 -08:00
parent 1e92644904
commit 98327c8c5b
8 changed files with 72 additions and 27 deletions

View file

@ -15,11 +15,10 @@
*/
package com.afollestad.nocknock.broadcasts
import android.app.Application
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import androidx.lifecycle.Lifecycle.Event.ON_DESTROY
import androidx.lifecycle.Lifecycle.Event.ON_PAUSE
import androidx.lifecycle.Lifecycle.Event.ON_RESUME
import androidx.lifecycle.LifecycleObserver
@ -27,13 +26,15 @@ import androidx.lifecycle.OnLifecycleEvent
import com.afollestad.nocknock.data.model.Site
import com.afollestad.nocknock.engine.validation.ValidationJob.Companion.ACTION_STATUS_UPDATE
import com.afollestad.nocknock.engine.validation.ValidationJob.Companion.KEY_UPDATE_MODEL
import com.afollestad.nocknock.utilities.providers.IntentProvider
typealias SiteCallback = (Site) -> Unit
/** @author Aidan Follestad (@afollestad) */
class StatusUpdateIntentReceiver(
private val app: Application,
private val callback: SiteCallback
private val context: Context,
private val intentProvider: IntentProvider,
private var callback: SiteCallback?
) : LifecycleObserver {
internal val intentReceiver = object : BroadcastReceiver() {
@ -44,19 +45,24 @@ class StatusUpdateIntentReceiver(
if (intent.action == ACTION_STATUS_UPDATE) {
val model = intent.getSerializableExtra(KEY_UPDATE_MODEL) as? Site
?: return
callback(model)
callback?.invoke(model)
}
}
}
@OnLifecycleEvent(ON_RESUME)
fun onResume() {
val filter = IntentFilter().apply {
addAction(ACTION_STATUS_UPDATE)
}
app.registerReceiver(intentReceiver, filter)
val filter = intentProvider.createFilter(ACTION_STATUS_UPDATE)
context.registerReceiver(intentReceiver, filter)
}
@OnLifecycleEvent(ON_PAUSE)
fun onPause() = app.unregisterReceiver(intentReceiver)
fun onPause() {
context.unregisterReceiver(intentReceiver)
}
@OnLifecycleEvent(ON_DESTROY)
fun onDestroy() {
callback = null
}
}

View file

@ -30,6 +30,7 @@ import com.afollestad.nocknock.broadcasts.StatusUpdateIntentReceiver
import com.afollestad.nocknock.data.model.Site
import com.afollestad.nocknock.dialogs.AboutDialog
import com.afollestad.nocknock.notifications.NockNotificationManager
import com.afollestad.nocknock.utilities.providers.IntentProvider
import com.afollestad.nocknock.viewcomponents.ext.showOrHide
import kotlinx.android.synthetic.main.activity_main.fab
import kotlinx.android.synthetic.main.activity_main.list
@ -43,12 +44,14 @@ import org.koin.androidx.viewmodel.ext.android.viewModel
class MainActivity : AppCompatActivity() {
private val notificationManager by inject<NockNotificationManager>()
private val intentProvider by inject<IntentProvider>()
internal val viewModel by viewModel<MainViewModel>()
private lateinit var adapter: ServerAdapter
private val statusUpdateReceiver =
StatusUpdateIntentReceiver(application) {
StatusUpdateIntentReceiver(application, intentProvider) {
viewModel.postSiteUpdate(it)
}

View file

@ -25,6 +25,7 @@ import com.afollestad.nocknock.R
import com.afollestad.nocknock.broadcasts.StatusUpdateIntentReceiver
import com.afollestad.nocknock.data.model.Site
import com.afollestad.nocknock.data.model.ValidationMode
import com.afollestad.nocknock.utilities.providers.IntentProvider
import com.afollestad.nocknock.viewcomponents.ext.attachLiveData
import com.afollestad.nocknock.viewcomponents.ext.dimenFloat
import com.afollestad.nocknock.viewcomponents.ext.onScroll
@ -48,6 +49,7 @@ import kotlinx.android.synthetic.main.activity_viewsite.textNextCheck
import kotlinx.android.synthetic.main.activity_viewsite.textUrlWarning
import kotlinx.android.synthetic.main.activity_viewsite.toolbar
import kotlinx.android.synthetic.main.activity_viewsite.validationModeDescription
import org.koin.android.ext.android.inject
import org.koin.androidx.viewmodel.ext.android.viewModel
/** @author Aidan Follestad (@afollestad) */
@ -55,8 +57,9 @@ class ViewSiteActivity : AppCompatActivity() {
internal val viewModel by viewModel<ViewSiteViewModel>()
private val intentProvider by inject<IntentProvider>()
private val statusUpdateReceiver =
StatusUpdateIntentReceiver(application) {
StatusUpdateIntentReceiver(application, intentProvider) {
viewModel.setModel(it)
}

View file

@ -15,7 +15,9 @@
*/
package com.afollestad.nocknock
import android.app.PendingIntent
import android.content.Intent
import android.content.IntentFilter
import com.afollestad.nocknock.data.AppDatabase
import com.afollestad.nocknock.data.SiteDao
import com.afollestad.nocknock.data.SiteSettingsDao
@ -27,6 +29,9 @@ import com.afollestad.nocknock.data.model.Status.OK
import com.afollestad.nocknock.data.model.ValidationMode
import com.afollestad.nocknock.data.model.ValidationMode.STATUS_CODE
import com.afollestad.nocknock.data.model.ValidationResult
import com.afollestad.nocknock.utilities.providers.CanNotifyModel
import com.afollestad.nocknock.utilities.providers.IntentProvider
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.doAnswer
import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.isA
@ -130,3 +135,21 @@ fun mockDatabase(): AppDatabase {
on { validationResultsDao() } doReturn resultsDao
}
}
fun mockIntentProvider() = object : IntentProvider {
override fun createFilter(vararg actions: String): IntentFilter {
return mock {
on { this.getAction(any()) } doAnswer { inv ->
val index = inv.getArgument<Int>(0)
return@doAnswer actions[index]
}
on { this.actionsIterator() } doReturn actions.iterator()
on { this.countActions() } doReturn actions.size
}
}
override fun getPendingIntentForViewSite(model: CanNotifyModel): PendingIntent {
// basically no-op right now
return mock()
}
}

View file

@ -21,9 +21,11 @@ import com.afollestad.nocknock.MOCK_MODEL_2
import com.afollestad.nocknock.engine.validation.ValidationJob.Companion.ACTION_STATUS_UPDATE
import com.afollestad.nocknock.engine.validation.ValidationJob.Companion.KEY_UPDATE_MODEL
import com.afollestad.nocknock.fakeIntent
import com.afollestad.nocknock.mockIntentProvider
import com.google.common.truth.Truth.assertThat
import com.nhaarman.mockitokotlin2.argumentCaptor
import com.nhaarman.mockitokotlin2.doReturn
import com.nhaarman.mockitokotlin2.eq
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.times
import com.nhaarman.mockitokotlin2.verify
@ -34,9 +36,10 @@ import org.junit.Test
class StatusUpdateIntentReceiverTest {
private val app = mock<Application>()
private val intentProvider = mockIntentProvider()
private val callback = mock<SiteCallback>()
private val receiver = StatusUpdateIntentReceiver(app, callback)
private val receiver = StatusUpdateIntentReceiver(app, intentProvider, callback)
@Test fun onReceive() {
val badIntent = fakeIntent("Hello World")
@ -54,7 +57,7 @@ class StatusUpdateIntentReceiverTest {
receiver.onResume()
val filterCaptor = argumentCaptor<IntentFilter>()
verify(app).registerReceiver(receiver.intentReceiver, filterCaptor.capture())
verify(app).registerReceiver(eq(receiver.intentReceiver), filterCaptor.capture())
val actionIterator = filterCaptor.firstValue.actionsIterator()
assertThat(actionIterator.hasNext()).isTrue()

View file

@ -19,21 +19,24 @@ import android.app.PendingIntent
import android.app.PendingIntent.FLAG_CANCEL_CURRENT
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import java.io.Serializable
/** @author Aidan Follestad (@afollestad) */
interface CanNotifyModel : Serializable {
fun notiId(): Int
fun notifyId(): Int
fun notiName(): String
fun notifyName(): String
fun notiTag(): String
fun notifyTag(): String
}
/** @author Aidan Follestad (@afollestad) */
interface IntentProvider {
fun createFilter(vararg actions: String): IntentFilter
fun getPendingIntentForViewSite(
model: CanNotifyModel
): PendingIntent
@ -50,11 +53,15 @@ class RealIntentProvider(
const val KEY_VIEW_NOTIFICATION_MODEL = "model"
}
override fun createFilter(vararg actions: String) = IntentFilter().apply {
actions.forEach { addAction(it) }
}
override fun getPendingIntentForViewSite(model: CanNotifyModel): PendingIntent {
val openIntent = getIntentForViewSite(model)
return PendingIntent.getActivity(
context,
BASE_NOTIFICATION_REQUEST_CODE + model.notiId(),
BASE_NOTIFICATION_REQUEST_CODE + model.notifyId(),
openIntent,
FLAG_CANCEL_CURRENT
)

View file

@ -41,11 +41,11 @@ data class Site(
constructor() : this(0, "", "", null, null)
override fun notiId(): Int = id.toInt()
override fun notifyId(): Int = id.toInt()
override fun notiName(): String = name
override fun notifyName(): String = name
override fun notiTag(): String = url
override fun notifyTag(): String = url
fun intervalText(): String {
requireNotNull(settings) { "Settings not queried." }

View file

@ -66,29 +66,29 @@ class RealNockNotificationManager(
override fun postStatusNotification(model: CanNotifyModel) {
if (isAppOpen) {
// Don't show notifications while the app is open
log("App is open, status notification for site ${model.notiId()} won't be posted.")
log("App is open, status notification for site ${model.notifyId()} won't be posted.")
return
}
log("Posting status notification for site ${model.notiId()}...")
log("Posting status notification for site ${model.notifyId()}...")
val intent = intentProvider.getPendingIntentForViewSite(model)
val newNotification = notificationProvider.create(
channelId = CheckFailures.id,
title = model.notiName(),
title = model.notifyName(),
content = stringProvider.get(R.string.something_wrong),
intent = intent,
smallIcon = R.drawable.ic_notification,
largeIcon = bitmapProvider.get(appIconRes)
)
stockManager.notify(model.notiTag(), model.notificationId(), newNotification)
stockManager.notify(model.notifyTag(), model.notificationId(), newNotification)
log("Posted status notification for site ${model.notificationId()}.")
}
override fun cancelStatusNotification(model: CanNotifyModel) {
stockManager.cancel(model.notificationId())
log("Cancelled status notification for site ${model.notiId()}.")
log("Cancelled status notification for site ${model.notifyId()}.")
}
override fun cancelStatusNotifications() = stockManager.cancelAll()
@ -105,5 +105,5 @@ class RealNockNotificationManager(
log("Created notification channel ${channel.id}")
}
private fun CanNotifyModel.notificationId() = BASE_NOTIFICATION_REQUEST_CODE + this.notiId()
private fun CanNotifyModel.notificationId() = BASE_NOTIFICATION_REQUEST_CODE + this.notifyId()
}