[fenix] Closes https://github.com/mozilla-mobile/fenix/issues/19846: Show Default Browser Notification if browser is not default

pull/600/head
Roger Yang 3 years ago committed by mergify[bot]
parent 2631d2b252
commit 048abd7f4c

@ -86,6 +86,7 @@ import org.mozilla.fenix.ext.setNavigationIcon
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.home.HomeFragmentDirections
import org.mozilla.fenix.home.intent.CrashReporterIntentProcessor
import org.mozilla.fenix.home.intent.DefaultBrowserIntentProcessor
import org.mozilla.fenix.home.intent.OpenBrowserIntentProcessor
import org.mozilla.fenix.home.intent.OpenSpecificTabIntentProcessor
import org.mozilla.fenix.home.intent.SpeechProcessingIntentProcessor
@ -94,6 +95,7 @@ import org.mozilla.fenix.library.bookmarks.BookmarkFragmentDirections
import org.mozilla.fenix.library.bookmarks.DesktopFolders
import org.mozilla.fenix.library.history.HistoryFragmentDirections
import org.mozilla.fenix.library.recentlyclosed.RecentlyClosedFragmentDirections
import org.mozilla.fenix.onboarding.DefaultBrowserNotificationWorker
import org.mozilla.fenix.perf.Performance
import org.mozilla.fenix.perf.PerformanceInflater
import org.mozilla.fenix.perf.ProfilerMarkers
@ -158,7 +160,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
SpeechProcessingIntentProcessor(this, components.core.store, components.analytics.metrics),
StartSearchIntentProcessor(components.analytics.metrics),
OpenBrowserIntentProcessor(this, ::getIntentSessionId),
OpenSpecificTabIntentProcessor(this)
OpenSpecificTabIntentProcessor(this),
DefaultBrowserIntentProcessor(this)
)
}
@ -337,6 +340,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
}
settings().wasDefaultBrowserOnLastResume = settings().isDefaultBrowser()
DefaultBrowserNotificationWorker.setDefaultBrowserNotificationIfNeeded(applicationContext)
}
}

@ -0,0 +1,34 @@
/* 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.home.intent
import android.content.Intent
import androidx.navigation.NavController
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.ext.openSetDefaultBrowserOption
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.onboarding.DefaultBrowserNotificationWorker.Companion.isDefaultBrowserNotificationIntent
/**
* When the default browser notification is tapped we need to launch [openSetDefaultBrowserOption]
*
* This should only happens once in a user's lifetime since once the user taps on the default browser
* notification, [settings.shouldShowDefaultBrowserNotification] will return false
*/
class DefaultBrowserIntentProcessor(
private val activity: HomeActivity
) : HomeIntentProcessor {
override fun process(intent: Intent, navController: NavController, out: Intent): Boolean {
return if (isDefaultBrowserNotificationIntent(intent)) {
activity.openSetDefaultBrowserOption()
activity.settings().defaultBrowserNotificationDisplayed = true
true
} else {
false
}
}
}

@ -0,0 +1,126 @@
/* 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.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.WorkManager
import androidx.work.Worker
import androidx.work.WorkerParameters
import java.util.concurrent.TimeUnit
import mozilla.components.support.base.ids.SharedIdsHelper
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.utils.Settings
class DefaultBrowserNotificationWorker(
context: Context,
workerParameters: WorkerParameters
) : Worker(context, workerParameters) {
override fun doWork(): Result {
ensureChannelExists()
NotificationManagerCompat.from(applicationContext)
.notify(NOTIFICATION_TAG, NOTIFICATION_ID, buildNotification())
return Result.success()
}
/**
* Build the default browser notification.
*/
private fun buildNotification(): Notification {
val channelId = ensureChannelExists()
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,
0
)
with(applicationContext) {
val appName = getString(R.string.app_name)
return NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_status_logo)
.setContentTitle(
applicationContext.getString(R.string.notification_default_browser_text, appName))
.setBadgeIconType(NotificationCompat.BADGE_ICON_SMALL)
.setColor(ContextCompat.getColor(this, R.color.primary_text_light_theme))
.setPriority(NotificationCompat.PRIORITY_DEFAULT)
.setShowWhen(false)
.setContentIntent(pendingIntent)
.setAutoCancel(true)
.build()
}
}
/**
* Make sure a notification channel for default browser notification exists.
*
* Returns the channel id to be used for notifications.
*/
private fun ensureChannelExists(): String {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager: NotificationManager =
applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
val channel = NotificationChannel(
NOTIFICATION_CHANNEL_ID,
applicationContext.getString(R.string.notification_marketing_channel_name),
NotificationManager.IMPORTANCE_DEFAULT
)
notificationManager.createNotificationChannel(channel)
}
return NOTIFICATION_CHANNEL_ID
}
companion object {
private const val NOTIFICATION_CHANNEL_ID = "org.mozilla.fenix.default.browser.channel"
private const val NOTIFICATION_ID = 1
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.ONE_DAY_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.REPLACE,
notificationWork
).enqueue()
}
}
}

@ -484,6 +484,15 @@ class Settings(private val appContext: Context) : PreferencesHolder {
return browsers.isDefaultBrowser
}
var defaultBrowserNotificationDisplayed by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_should_show_default_browser_notification),
default = false
)
fun shouldShowDefaultBrowserNotification(): Boolean {
return !defaultBrowserNotificationDisplayed && !isDefaultBrowser()
}
val shouldUseAutoBatteryTheme by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_auto_battery_theme),
default = false

@ -67,6 +67,7 @@
<string name="pref_key_last_review_prompt_shown_time" translatable="false">pref_key_last_review_prompt_shown_time</string>
<string name="pref_key_last_browse_activity_time" translatable="false">pref_key_last_browse_activity_time</string>
<string name="pref_key_last_cfr_shown_time" translatable="false">pref_key_last_cfr_shown_time</string>
<string name="pref_key_should_show_default_browser_notification" translatable="false">pref_key_should_show_default_browser_notification</string>
<!-- Data Choices -->
<string name="pref_key_telemetry" translatable="false">pref_key_telemetry</string>

@ -1022,6 +1022,12 @@
<string name="notification_pbm_action_delete_and_open">Delete and Open</string>
<!-- Name of the "Powered by Fenix" notification channel. Displayed in the "App notifications" system settings for the app -->
<string name="notification_powered_by_channel_name">Powered By</string>
<!-- Name of the marketing notification channel. Displayed in the "App notifications" system settings for the app -->
<string name="notification_marketing_channel_name">Marketing</string>
<!-- Text shown in the notification that pops up to remind the user to set fenix as default browser. -->
<string name="notification_default_browser_text">Make %1$s your default browser</string>
<!-- Snackbar -->
<!-- Text shown in snackbar when user deletes a collection -->
<string name="snackbar_collection_deleted">Collection deleted</string>
<!-- Text shown in snackbar when user renames a collection -->

@ -0,0 +1,52 @@
/* 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.home.intent
import android.content.Intent
import androidx.navigation.NavController
import io.mockk.Called
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertFalse
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
class DefaultBrowserIntentProcessorTest {
@Test
fun `do not process blank intents`() {
val navController: NavController = mockk()
val out: Intent = mockk()
val result = DefaultBrowserIntentProcessor(mockk()).process(Intent(), navController, out)
assertFalse(result)
verify { navController wasNot Called }
verify { out wasNot Called }
}
@Test
fun `process default browser notification intents`() {
val navController: NavController = mockk(relaxed = true)
val out: Intent = mockk()
val activity: HomeActivity = mockk()
val intent = Intent().apply {
putExtra("org.mozilla.fenix.default.browser.intent", true)
}
every { activity.startActivity(any()) } returns Unit
every { activity.applicationContext } returns testContext
val result = DefaultBrowserIntentProcessor(activity).process(intent, navController, out)
assert(result)
verify { navController wasNot Called }
verify { out wasNot Called }
}
}

@ -745,4 +745,24 @@ class SettingsTest {
assertFalse(localSetting.shouldStartOnHome())
}
@Test
fun `GIVEN shownDefaultBrowserNotification and isDefaultBrowser WHEN calling shouldShowDefaultBrowserNotification THEN return correct value`() {
val localSetting = spyk(settings)
every { localSetting.isDefaultBrowser() } returns false
localSetting.defaultBrowserNotificationDisplayed = false
assert(localSetting.shouldShowDefaultBrowserNotification())
localSetting.defaultBrowserNotificationDisplayed = true
assertFalse(localSetting.shouldShowDefaultBrowserNotification())
every { localSetting.isDefaultBrowser() } returns true
localSetting.defaultBrowserNotificationDisplayed = false
assertFalse(localSetting.shouldShowDefaultBrowserNotification())
localSetting.defaultBrowserNotificationDisplayed = true
assertFalse(localSetting.shouldShowDefaultBrowserNotification())
}
}

Loading…
Cancel
Save