[fenix] Use AC version of PrivateNotificationService (https://github.com/mozilla-mobile/fenix/pull/12459)
parent
bdf01c141b
commit
1f7bb1af2e
@ -1,51 +0,0 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
package org.mozilla.fenix.session
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.cancel
|
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import mozilla.components.browser.state.selector.privateTabs
|
|
||||||
import mozilla.components.lib.state.ext.flowScoped
|
|
||||||
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
|
|
||||||
import org.mozilla.fenix.ext.components
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This observer starts and stops the service to show a notification
|
|
||||||
* indicating that a private tab is open.
|
|
||||||
*/
|
|
||||||
class NotificationSessionObserver(
|
|
||||||
private val applicationContext: Context,
|
|
||||||
private val notificationService: SessionNotificationService.Companion = SessionNotificationService
|
|
||||||
) {
|
|
||||||
|
|
||||||
private var scope: CoroutineScope? = null
|
|
||||||
|
|
||||||
@ExperimentalCoroutinesApi
|
|
||||||
fun start() {
|
|
||||||
scope = applicationContext.components.core.store.flowScoped { flow ->
|
|
||||||
flow.map { state -> state.privateTabs.isNotEmpty() }
|
|
||||||
.ifChanged()
|
|
||||||
.collect { hasPrivateTabs ->
|
|
||||||
if (hasPrivateTabs) {
|
|
||||||
notificationService.start(applicationContext, isStartedFromPrivateShortcut)
|
|
||||||
} else if (SessionNotificationService.started) {
|
|
||||||
notificationService.stop(applicationContext)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun stop() {
|
|
||||||
scope?.cancel()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
var isStartedFromPrivateShortcut = false
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,66 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.fenix.session
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import mozilla.components.browser.state.store.BrowserStore
|
||||||
|
import mozilla.components.feature.privatemode.notification.AbstractPrivateNotificationService
|
||||||
|
import org.mozilla.fenix.HomeActivity
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.components.metrics.Event
|
||||||
|
import org.mozilla.fenix.ext.components
|
||||||
|
import org.mozilla.fenix.ext.metrics
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Manages notifications for private tabs.
|
||||||
|
*
|
||||||
|
* Private tab notifications solve two problems for us:
|
||||||
|
* 1 - They allow users to interact with us from outside of the app (example: by closing all
|
||||||
|
* private tabs).
|
||||||
|
* 2 - The notification will keep our process alive, allowing us to keep private tabs in memory.
|
||||||
|
*
|
||||||
|
* As long as a session is active this service will keep its notification alive.
|
||||||
|
*/
|
||||||
|
class PrivateNotificationService : AbstractPrivateNotificationService() {
|
||||||
|
|
||||||
|
override val store: BrowserStore by lazy { components.core.store }
|
||||||
|
|
||||||
|
override fun NotificationCompat.Builder.buildNotification() {
|
||||||
|
setSmallIcon(R.drawable.ic_pbm_notification)
|
||||||
|
setContentTitle(getString(R.string.app_name_private_4, getString(R.string.app_name)))
|
||||||
|
setContentText(getString(R.string.notification_pbm_delete_text_2))
|
||||||
|
color = ContextCompat.getColor(this@PrivateNotificationService, R.color.pbm_notification_color)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun erasePrivateTabs() {
|
||||||
|
metrics.track(Event.PrivateBrowsingNotificationTapped)
|
||||||
|
|
||||||
|
val homeScreenIntent = Intent(this, HomeActivity::class.java).apply {
|
||||||
|
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
||||||
|
putExtra(HomeActivity.PRIVATE_BROWSING_MODE, isStartedFromPrivateShortcut)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (VisibilityLifecycleCallback.finishAndRemoveTaskIfInBackground(this)) {
|
||||||
|
// Set start mode to be in background (recents screen)
|
||||||
|
homeScreenIntent.apply {
|
||||||
|
putExtra(HomeActivity.START_IN_RECENTS_SCREEN, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
startActivity(homeScreenIntent)
|
||||||
|
super.erasePrivateTabs()
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Global used by [HomeActivity] to figure out if normal mode or private mode
|
||||||
|
* should be used after closing all private tabs.
|
||||||
|
*/
|
||||||
|
var isStartedFromPrivateShortcut = false
|
||||||
|
}
|
||||||
|
}
|
@ -1,174 +0,0 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
package org.mozilla.fenix.session
|
|
||||||
|
|
||||||
import android.app.Notification
|
|
||||||
import android.app.NotificationChannel
|
|
||||||
import android.app.NotificationManager
|
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.app.Service
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import android.os.Build
|
|
||||||
import android.os.IBinder
|
|
||||||
import androidx.core.app.NotificationCompat
|
|
||||||
import androidx.core.content.ContextCompat
|
|
||||||
import androidx.core.content.getSystemService
|
|
||||||
import mozilla.components.browser.session.SessionManager
|
|
||||||
import mozilla.components.support.utils.ThreadUtils
|
|
||||||
import org.mozilla.fenix.HomeActivity
|
|
||||||
import org.mozilla.fenix.R
|
|
||||||
import org.mozilla.fenix.components.metrics.Event
|
|
||||||
import org.mozilla.fenix.ext.components
|
|
||||||
import org.mozilla.fenix.ext.metrics
|
|
||||||
import org.mozilla.fenix.ext.sessionsOfType
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Manages notifications for private tabs.
|
|
||||||
*
|
|
||||||
* Private tab notifications solve two problems for us:
|
|
||||||
* 1 - They allow users to interact with us from outside of the app (example: by closing all
|
|
||||||
* private tabs).
|
|
||||||
* 2 - The notification will keep our process alive, allowing us to keep private tabs in memory.
|
|
||||||
*
|
|
||||||
* As long as a session is active this service will keep its notification alive.
|
|
||||||
*/
|
|
||||||
class SessionNotificationService : Service() {
|
|
||||||
|
|
||||||
private var isStartedFromPrivateShortcut: Boolean = false
|
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
|
||||||
val action = intent.action ?: return START_NOT_STICKY
|
|
||||||
|
|
||||||
when (action) {
|
|
||||||
ACTION_START -> {
|
|
||||||
isStartedFromPrivateShortcut = intent.getBooleanExtra(STARTED_FROM_PRIVATE_SHORTCUT, false)
|
|
||||||
createNotificationChannelIfNeeded()
|
|
||||||
startForeground(NOTIFICATION_ID, buildNotification())
|
|
||||||
}
|
|
||||||
|
|
||||||
ACTION_ERASE -> {
|
|
||||||
metrics.track(Event.PrivateBrowsingNotificationTapped)
|
|
||||||
|
|
||||||
val homeScreenIntent = Intent(this, HomeActivity::class.java)
|
|
||||||
val intentFlags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
|
|
||||||
homeScreenIntent.apply {
|
|
||||||
setFlags(intentFlags)
|
|
||||||
putExtra(HomeActivity.PRIVATE_BROWSING_MODE, isStartedFromPrivateShortcut)
|
|
||||||
}
|
|
||||||
if (VisibilityLifecycleCallback.finishAndRemoveTaskIfInBackground(this)) {
|
|
||||||
// Set start mode to be in background (recents screen)
|
|
||||||
homeScreenIntent.apply {
|
|
||||||
putExtra(HomeActivity.START_IN_RECENTS_SCREEN, true)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
startActivity(homeScreenIntent)
|
|
||||||
components.core.sessionManager.removeAndCloseAllPrivateSessions()
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> throw IllegalStateException("Unknown intent: $intent")
|
|
||||||
}
|
|
||||||
|
|
||||||
return START_NOT_STICKY
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onTaskRemoved(rootIntent: Intent) {
|
|
||||||
components.core.sessionManager.removeAndCloseAllPrivateSessions()
|
|
||||||
|
|
||||||
stopForeground(true)
|
|
||||||
stopSelf()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun buildNotification(): Notification {
|
|
||||||
return NotificationCompat.Builder(this, NOTIFICATION_CHANNEL_ID)
|
|
||||||
.setOngoing(true)
|
|
||||||
.setSmallIcon(R.drawable.ic_pbm_notification)
|
|
||||||
.setContentTitle(getString(R.string.app_name_private_4, getString(R.string.app_name)))
|
|
||||||
.setContentText(getString(R.string.notification_pbm_delete_text_2))
|
|
||||||
.setContentIntent(createNotificationIntent())
|
|
||||||
.setVisibility(NotificationCompat.VISIBILITY_SECRET)
|
|
||||||
.setShowWhen(false)
|
|
||||||
.setLocalOnly(true)
|
|
||||||
.setColor(ContextCompat.getColor(this, R.color.pbm_notification_color))
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createNotificationIntent(): PendingIntent {
|
|
||||||
val intent = Intent(this, SessionNotificationService::class.java)
|
|
||||||
intent.action = ACTION_ERASE
|
|
||||||
|
|
||||||
return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_ONE_SHOT)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun createNotificationChannelIfNeeded() {
|
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
|
|
||||||
// Notification channels are only available on Android O or higher.
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val notificationManager = getSystemService<NotificationManager>() ?: return
|
|
||||||
|
|
||||||
val notificationChannelName = getString(R.string.notification_pbm_channel_name)
|
|
||||||
|
|
||||||
val channel = NotificationChannel(
|
|
||||||
NOTIFICATION_CHANNEL_ID, notificationChannelName, NotificationManager.IMPORTANCE_MIN
|
|
||||||
)
|
|
||||||
channel.importance = NotificationManager.IMPORTANCE_LOW
|
|
||||||
channel.enableLights(false)
|
|
||||||
channel.enableVibration(false)
|
|
||||||
channel.setShowBadge(false)
|
|
||||||
|
|
||||||
notificationManager.createNotificationChannel(channel)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun SessionManager.removeAndCloseAllPrivateSessions() {
|
|
||||||
sessionsOfType(private = true).forEach { remove(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onBind(intent: Intent): IBinder? {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
private const val NOTIFICATION_ID = 83
|
|
||||||
private const val NOTIFICATION_CHANNEL_ID = "browsing-session"
|
|
||||||
private const val STARTED_FROM_PRIVATE_SHORTCUT = "STARTED_FROM_PRIVATE_SHORTCUT"
|
|
||||||
|
|
||||||
private const val ACTION_START = "start"
|
|
||||||
private const val ACTION_ERASE = "erase"
|
|
||||||
internal var started = false
|
|
||||||
|
|
||||||
internal fun start(
|
|
||||||
context: Context,
|
|
||||||
startedFromPrivateShortcut: Boolean
|
|
||||||
) {
|
|
||||||
val intent = Intent(context, SessionNotificationService::class.java)
|
|
||||||
intent.action = ACTION_START
|
|
||||||
intent.putExtra(STARTED_FROM_PRIVATE_SHORTCUT, startedFromPrivateShortcut)
|
|
||||||
|
|
||||||
// From Focus #2901: The application is crashing due to the service not calling `startForeground`
|
|
||||||
// before it times out. This is a speculative fix to decrease the time between these two
|
|
||||||
// calls by running this after potentially expensive calls in FocusApplication.onCreate and
|
|
||||||
// BrowserFragment.inflateView by posting it to the end of the main thread.
|
|
||||||
ThreadUtils.postToMainThread(Runnable {
|
|
||||||
context.startService(intent)
|
|
||||||
})
|
|
||||||
|
|
||||||
started = true
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun stop(context: Context) {
|
|
||||||
val intent = Intent(context, SessionNotificationService::class.java)
|
|
||||||
|
|
||||||
// We want to make sure we always call stop after start. So we're
|
|
||||||
// putting these actions on the same sequential run queue.
|
|
||||||
ThreadUtils.postToMainThread(Runnable {
|
|
||||||
context.stopService(intent)
|
|
||||||
})
|
|
||||||
|
|
||||||
started = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,87 +0,0 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
package org.mozilla.fenix.session
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import io.mockk.Called
|
|
||||||
import io.mockk.MockKAnnotations
|
|
||||||
import io.mockk.confirmVerified
|
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.impl.annotations.MockK
|
|
||||||
import io.mockk.verify
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.runBlocking
|
|
||||||
import mozilla.components.browser.state.action.CustomTabListAction
|
|
||||||
import mozilla.components.browser.state.action.TabListAction
|
|
||||||
import mozilla.components.browser.state.state.createCustomTab
|
|
||||||
import mozilla.components.browser.state.state.createTab
|
|
||||||
import mozilla.components.browser.state.store.BrowserStore
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.mozilla.fenix.ext.components
|
|
||||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
|
||||||
|
|
||||||
@ExperimentalCoroutinesApi
|
|
||||||
@RunWith(FenixRobolectricTestRunner::class)
|
|
||||||
class NotificationSessionObserverTest {
|
|
||||||
|
|
||||||
private lateinit var observer: NotificationSessionObserver
|
|
||||||
private lateinit var store: BrowserStore
|
|
||||||
@MockK private lateinit var context: Context
|
|
||||||
@MockK(relaxed = true) private lateinit var notificationService: SessionNotificationService.Companion
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun before() {
|
|
||||||
MockKAnnotations.init(this)
|
|
||||||
store = BrowserStore()
|
|
||||||
every { context.components.core.store } returns store
|
|
||||||
observer = NotificationSessionObserver(context, notificationService)
|
|
||||||
NotificationSessionObserver.isStartedFromPrivateShortcut = false
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `GIVEN session is private and non-custom WHEN it is added THEN notification service should be started`() = runBlocking {
|
|
||||||
val privateSession = createTab("https://firefox.com", private = true)
|
|
||||||
|
|
||||||
store.dispatch(TabListAction.AddTabAction(privateSession)).join()
|
|
||||||
|
|
||||||
observer.start()
|
|
||||||
verify(exactly = 1) { notificationService.start(context, false) }
|
|
||||||
confirmVerified(notificationService)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `GIVEN session is not private WHEN it is added THEN notification service should not be started`() = runBlocking {
|
|
||||||
val normalSession = createTab("https://firefox.com")
|
|
||||||
val customSession = createCustomTab("https://firefox.com")
|
|
||||||
|
|
||||||
observer.start()
|
|
||||||
verify { notificationService wasNot Called }
|
|
||||||
|
|
||||||
store.dispatch(TabListAction.AddTabAction(normalSession)).join()
|
|
||||||
verify(exactly = 0) { notificationService.start(context, false) }
|
|
||||||
|
|
||||||
store.dispatch(CustomTabListAction.AddCustomTabAction(customSession)).join()
|
|
||||||
verify(exactly = 0) { notificationService.start(context, false) }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `GIVEN session is custom tab WHEN it is added THEN notification service should not be started`() = runBlocking {
|
|
||||||
val privateCustomSession = createCustomTab("https://firefox.com").let {
|
|
||||||
it.copy(content = it.content.copy(private = true))
|
|
||||||
}
|
|
||||||
val customSession = createCustomTab("https://firefox.com")
|
|
||||||
|
|
||||||
observer.start()
|
|
||||||
verify { notificationService wasNot Called }
|
|
||||||
|
|
||||||
store.dispatch(CustomTabListAction.AddCustomTabAction(privateCustomSession)).join()
|
|
||||||
verify(exactly = 0) { notificationService.start(context, false) }
|
|
||||||
|
|
||||||
store.dispatch(CustomTabListAction.AddCustomTabAction(customSession)).join()
|
|
||||||
verify(exactly = 0) { notificationService.start(context, false) }
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,64 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.fenix.session
|
||||||
|
|
||||||
|
import android.content.ComponentName
|
||||||
|
import android.content.Intent
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import mozilla.components.feature.privatemode.notification.AbstractPrivateNotificationService.Companion.ACTION_ERASE
|
||||||
|
import mozilla.components.support.test.robolectric.testContext
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mozilla.fenix.HomeActivity
|
||||||
|
import org.mozilla.fenix.HomeActivity.Companion.PRIVATE_BROWSING_MODE
|
||||||
|
import org.mozilla.fenix.ext.components
|
||||||
|
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||||
|
import org.robolectric.Robolectric
|
||||||
|
import org.robolectric.Shadows.shadowOf
|
||||||
|
import org.robolectric.android.controller.ServiceController
|
||||||
|
|
||||||
|
@RunWith(FenixRobolectricTestRunner::class)
|
||||||
|
class PrivateNotificationServiceTest {
|
||||||
|
|
||||||
|
private lateinit var controller: ServiceController<PrivateNotificationService>
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
val store = testContext.components.core.store
|
||||||
|
every { store.dispatch(any()) } returns mockk()
|
||||||
|
|
||||||
|
controller = Robolectric.buildService(
|
||||||
|
PrivateNotificationService::class.java,
|
||||||
|
Intent(ACTION_ERASE)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `service opens home activity with PBM flag set to true`() {
|
||||||
|
PrivateNotificationService.isStartedFromPrivateShortcut = true
|
||||||
|
val service = shadowOf(controller.get())
|
||||||
|
controller.startCommand(0, 0)
|
||||||
|
|
||||||
|
val intent = service.nextStartedActivity
|
||||||
|
assertEquals(ComponentName(testContext, HomeActivity::class.java), intent.component)
|
||||||
|
assertEquals(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK, intent.flags)
|
||||||
|
assertEquals(true, intent.extras?.getBoolean(PRIVATE_BROWSING_MODE))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `service opens home activity with PBM flag set to false`() {
|
||||||
|
PrivateNotificationService.isStartedFromPrivateShortcut = false
|
||||||
|
val service = shadowOf(controller.get())
|
||||||
|
controller.startCommand(0, 0)
|
||||||
|
|
||||||
|
val intent = service.nextStartedActivity
|
||||||
|
assertEquals(ComponentName(testContext, HomeActivity::class.java), intent.component)
|
||||||
|
assertEquals(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK, intent.flags)
|
||||||
|
assertEquals(false, intent.extras?.getBoolean(PRIVATE_BROWSING_MODE))
|
||||||
|
}
|
||||||
|
}
|
@ -1,27 +0,0 @@
|
|||||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
||||||
|
|
||||||
package org.mozilla.fenix.session
|
|
||||||
|
|
||||||
import mozilla.components.support.test.robolectric.testContext
|
|
||||||
import org.junit.Assert.assertFalse
|
|
||||||
import org.junit.Assert.assertTrue
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
|
||||||
|
|
||||||
@RunWith(FenixRobolectricTestRunner::class)
|
|
||||||
class SessionNotificationServiceTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `Service keeps tracked of started state`() {
|
|
||||||
assertFalse(SessionNotificationService.started)
|
|
||||||
|
|
||||||
SessionNotificationService.start(testContext, false)
|
|
||||||
assertTrue(SessionNotificationService.started)
|
|
||||||
|
|
||||||
SessionNotificationService.stop(testContext)
|
|
||||||
assertFalse(SessionNotificationService.started)
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue