Bug 1819431 - Reimplement default browser notification with Nimbus Messaging equivalent (#1031)
* Bug 1819431 - Added default-browser-notification * Bug 1819431 - Remove DefaultBrowserNotificationWorker and friends * Bug 1819431 - Add tests for all messages * Bug 1819431 - Remove settings for default browser notification * Bug 1819431 - Remove metrics for default browser notification --------- Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>fenix/112.0
parent
d6230dfa43
commit
110d24a4a8
@ -0,0 +1,119 @@
|
||||
/* 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.ui
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.gleanplumb.CustomAttributeProvider
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.TestHelper
|
||||
import org.mozilla.fenix.nimbus.FxNimbus
|
||||
import org.mozilla.fenix.nimbus.Messaging
|
||||
|
||||
/**
|
||||
* This test is to test the integrity of messages hardcoded in the FML.
|
||||
*
|
||||
* It tests if the trigger expressions are valid, all the fields are complete
|
||||
* and a simple check if they are localized (don't contain `_`).
|
||||
*/
|
||||
class NimbusMessagingMessageTest {
|
||||
private lateinit var feature: Messaging
|
||||
private lateinit var mDevice: UiDevice
|
||||
|
||||
private lateinit var context: Context
|
||||
|
||||
private val storage
|
||||
get() = context.components.analytics.messagingStorage
|
||||
|
||||
@get:Rule
|
||||
val activityTestRule =
|
||||
HomeActivityIntentTestRule.withDefaultSettingsOverrides(skipOnboarding = true)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
context = TestHelper.appContext
|
||||
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
feature = FxNimbus.features.messaging.value()
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if all messages in the FML are internally consistent with the
|
||||
* rest of the FML. This check is done in the `NimbusMessagingStorage`
|
||||
* class.
|
||||
*/
|
||||
@Test
|
||||
fun testAllMessageIntegrity() = runTest {
|
||||
val messages = storage.getMessages()
|
||||
val rawMessages = feature.messages
|
||||
assertTrue(rawMessages.isNotEmpty())
|
||||
|
||||
if (messages.size != rawMessages.size) {
|
||||
val expected = rawMessages.keys.toHashSet()
|
||||
val observed = messages.map { it.id }.toHashSet()
|
||||
val missing = expected - observed
|
||||
fail("Problem with message(s) in FML: $missing")
|
||||
}
|
||||
assertEquals(messages.size, rawMessages.size)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the messages' triggers are well formed JEXL.
|
||||
*/
|
||||
@Test
|
||||
fun testAllMessageTriggers() = runTest {
|
||||
val nimbus = context.components.analytics.experiments
|
||||
val helper = nimbus.createMessageHelper(
|
||||
CustomAttributeProvider.getCustomAttributes(context),
|
||||
)
|
||||
val messages = storage.getMessages()
|
||||
messages.forEach { message ->
|
||||
storage.isMessageEligible(message, helper)
|
||||
if (storage.malFormedMap.isNotEmpty()) {
|
||||
fail("${message.id} has a problem with its JEXL trigger: ${storage.malFormedMap.keys}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkIsLocalized(string: String) {
|
||||
assertFalse(string.isBlank())
|
||||
// The check will almost always succeed, since the generated code
|
||||
// will not compile if this is true, and there is no resource available.
|
||||
assertFalse(string.matches(Regex("[a-z][_a-z\\d]*")))
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the messages are localized.
|
||||
*/
|
||||
@Test
|
||||
fun testAllMessagesAreLocalized() {
|
||||
feature.messages.values.forEach { message ->
|
||||
message.buttonLabel?.let(::checkIsLocalized)
|
||||
message.title?.let(::checkIsLocalized)
|
||||
checkIsLocalized(message.text)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testIndividualMessagesAreValid() {
|
||||
val expectedMessages = listOf(
|
||||
"default-browser",
|
||||
"default-browser-notification",
|
||||
)
|
||||
val rawMessages = feature.messages
|
||||
for (id in expectedMessages) {
|
||||
assertTrue(rawMessages.containsKey(id))
|
||||
}
|
||||
}
|
||||
}
|
@ -1,107 +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.onboarding
|
||||
|
||||
import android.app.Notification
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import androidx.core.app.NotificationManagerCompat
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.OneTimeWorkRequest
|
||||
import androidx.work.WorkManager
|
||||
import androidx.work.Worker
|
||||
import androidx.work.WorkerParameters
|
||||
import mozilla.components.service.glean.private.NoExtras
|
||||
import mozilla.components.support.base.ids.SharedIdsHelper
|
||||
import org.mozilla.fenix.GleanMetrics.Events
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.utils.IntentUtils
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
import org.mozilla.fenix.utils.createBaseNotification
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class DefaultBrowserNotificationWorker(
|
||||
context: Context,
|
||||
workerParameters: WorkerParameters,
|
||||
) : Worker(context, workerParameters) {
|
||||
|
||||
override fun doWork(): Result {
|
||||
val channelId = ensureMarketingChannelExists(applicationContext)
|
||||
|
||||
NotificationManagerCompat.from(applicationContext)
|
||||
.notify(
|
||||
NOTIFICATION_TAG,
|
||||
DEFAULT_BROWSER_NOTIFICATION_ID,
|
||||
buildNotification(channelId),
|
||||
)
|
||||
|
||||
Events.defaultBrowserNotifShown.record(NoExtras())
|
||||
|
||||
// default browser notification should only happen once
|
||||
applicationContext.settings().defaultBrowserNotificationDisplayed = true
|
||||
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the default browser notification.
|
||||
*/
|
||||
private fun buildNotification(channelId: String): Notification {
|
||||
val intent = Intent(applicationContext, HomeActivity::class.java)
|
||||
intent.putExtra(INTENT_DEFAULT_BROWSER_NOTIFICATION, true)
|
||||
|
||||
val pendingIntent = PendingIntent.getActivity(
|
||||
applicationContext,
|
||||
SharedIdsHelper.getNextIdForTag(applicationContext, NOTIFICATION_PENDING_INTENT_TAG),
|
||||
intent,
|
||||
IntentUtils.defaultIntentPendingFlags,
|
||||
)
|
||||
|
||||
with(applicationContext) {
|
||||
val appName = getString(R.string.app_name)
|
||||
return createBaseNotification(
|
||||
this,
|
||||
channelId,
|
||||
getString(R.string.notification_default_browser_title, appName),
|
||||
getString(R.string.notification_default_browser_text, appName),
|
||||
pendingIntent,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val NOTIFICATION_PENDING_INTENT_TAG = "org.mozilla.fenix.default.browser"
|
||||
private const val INTENT_DEFAULT_BROWSER_NOTIFICATION = "org.mozilla.fenix.default.browser.intent"
|
||||
private const val NOTIFICATION_TAG = "org.mozilla.fenix.default.browser.tag"
|
||||
private const val NOTIFICATION_WORK_NAME = "org.mozilla.fenix.default.browser.work"
|
||||
private const val NOTIFICATION_DELAY = Settings.THREE_DAYS_MS
|
||||
|
||||
fun isDefaultBrowserNotificationIntent(intent: Intent) =
|
||||
intent.extras?.containsKey(INTENT_DEFAULT_BROWSER_NOTIFICATION) ?: false
|
||||
|
||||
fun setDefaultBrowserNotificationIfNeeded(context: Context) {
|
||||
val instanceWorkManager = WorkManager.getInstance(context)
|
||||
|
||||
if (!context.settings().shouldShowDefaultBrowserNotification()) {
|
||||
// cancel notification work if already default browser
|
||||
instanceWorkManager.cancelUniqueWork(NOTIFICATION_WORK_NAME)
|
||||
return
|
||||
}
|
||||
|
||||
val notificationWork = OneTimeWorkRequest.Builder(DefaultBrowserNotificationWorker::class.java)
|
||||
.setInitialDelay(NOTIFICATION_DELAY, TimeUnit.MILLISECONDS)
|
||||
.build()
|
||||
|
||||
instanceWorkManager.beginUniqueWork(
|
||||
NOTIFICATION_WORK_NAME,
|
||||
ExistingWorkPolicy.KEEP,
|
||||
notificationWork,
|
||||
).enqueue()
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue