1
0
Fork 0

For #6153 - Add powered by notification

master
Tiger Oakes 2019-10-24 20:55:24 -07:00 committed by Emily Kager
parent 6a6c4f75dd
commit 9456998f9f
5 changed files with 171 additions and 2 deletions

View File

@ -14,8 +14,8 @@ import mozilla.components.browser.menu.item.SimpleBrowserMenuItem
import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager
import org.mozilla.fenix.R
import org.mozilla.fenix.theme.ThemeManager
import org.mozilla.fenix.components.toolbar.ToolbarMenu
import org.mozilla.fenix.theme.ThemeManager
class CustomTabToolbarMenu(
private val context: Context,
@ -148,7 +148,7 @@ class CustomTabToolbarMenu(
SimpleBrowserMenuItem(
{
val appName = context.getString(R.string.app_name)
context.getString(R.string.browser_menu_powered_by, appName).toUpperCase()
context.getString(R.string.browser_menu_powered_by2, appName).toUpperCase()
}(),
ToolbarMenu.CAPTION_TEXT_SIZE,
ThemeManager.resolveAttribute(R.attr.primaryText, context)

View File

@ -46,6 +46,7 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), BackHandler {
private val customTabsIntegration = ViewBoundFeatureWrapper<CustomTabsIntegration>()
private val hideToolbarFeature = ViewBoundFeatureWrapper<WebAppHideToolbarFeature>()
@Suppress("LongMethod")
override fun initializeUI(view: View): Session? {
return super.initializeUI(view)?.also {
val activity = requireActivity()
@ -100,6 +101,14 @@ class ExternalAppBrowserFragment : BaseBrowserFragment(), BackHandler {
manifest
)
)
} else {
viewLifecycleOwner.lifecycle.addObserver(
PoweredByNotification(
activity.applicationContext,
requireComponents.core.store,
customTabSessionId
)
)
}
}

View File

@ -0,0 +1,96 @@
/* 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.customtabs
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context
import android.os.Build
import android.os.Build.VERSION.SDK_INT
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationCompat.BADGE_ICON_NONE
import androidx.core.app.NotificationManagerCompat
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.OnLifecycleEvent
import mozilla.components.browser.state.selector.findCustomTab
import mozilla.components.browser.state.state.ExternalAppType
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.support.base.ids.cancel
import mozilla.components.support.base.ids.notify
import org.mozilla.fenix.R
/**
* Displays a "Powered by Firefox Preview" notification when a Trusted Web Activity is running.
*/
class PoweredByNotification(
private val applicationContext: Context,
private val store: BrowserStore,
private val customTabId: String
) : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
fun onResume() {
if (store.state.findCustomTab(customTabId)?.config?.externalAppType === ExternalAppType.TRUSTED_WEB_ACTIVITY) {
NotificationManagerCompat.from(applicationContext)
.notify(applicationContext, NOTIFICATION_TAG, buildNotification())
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
fun onPause() {
NotificationManagerCompat.from(applicationContext)
.cancel(applicationContext, NOTIFICATION_TAG)
}
/**
* Build the notification with site controls to be displayed while the web app is active.
*/
private fun buildNotification(): Notification {
val channelId = ensureChannelExists()
with(applicationContext) {
val appName = getString(R.string.app_name)
return NotificationCompat.Builder(this, channelId)
.setSmallIcon(R.drawable.ic_status_logo)
.setContentTitle(getString(R.string.browser_menu_powered_by2, appName))
.setBadgeIconType(BADGE_ICON_NONE)
.setColor(ContextCompat.getColor(this, R.color.primary_text_light_theme))
.setPriority(NotificationCompat.PRIORITY_MIN)
.setShowWhen(false)
.setOngoing(true)
.build()
}
}
/**
* Make sure a notification channel for the powered by notifications exists.
*
* Returns the channel id to be used for notifications.
*/
private fun ensureChannelExists(): String {
if (SDK_INT >= Build.VERSION_CODES.O) {
val notificationManager: NotificationManager = applicationContext.getSystemService()!!
val channel = NotificationChannel(
NOTIFICATION_CHANNEL_ID,
applicationContext.getString(R.string.mozac_feature_pwa_site_controls_notification_channel),
NotificationManager.IMPORTANCE_MIN
)
notificationManager.createNotificationChannel(channel)
}
return NOTIFICATION_CHANNEL_ID
}
companion object {
private const val NOTIFICATION_CHANNEL_ID = "Powered By"
private const val NOTIFICATION_TAG = "PoweredBy"
}
}

View File

@ -91,6 +91,9 @@
<!-- Browser menu text shown in custom tabs to indicate this is a Fenix tab
The first parameter is the name of the app defined in app_name (for example: Fenix) -->
<string name="browser_menu_powered_by">POWERED BY %1$s</string>
<!-- Browser menu text shown in custom tabs to indicate this is a Fenix tab
The first parameter is the name of the app defined in app_name (for example: Fenix) -->
<string name="browser_menu_powered_by2">Powered by %1$s</string>
<!-- Search Fragment -->
<!-- Button in the search view that lets a user search by scanning a QR code -->
@ -639,6 +642,8 @@
<string name="notification_pbm_action_open">Open</string>
<!-- Notification action to delete all current private browsing sessions AND switch to Fenix (bring it to the foreground) -->
<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>
<!-- 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 -->

View File

@ -0,0 +1,59 @@
/* 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.customtabs
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.CustomTabConfig
import mozilla.components.browser.state.state.ExternalAppType
import mozilla.components.browser.state.state.createCustomTab
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.support.test.mock
import mozilla.components.support.test.robolectric.testContext
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.TestApplication
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(application = TestApplication::class)
class PoweredByNotificationTest {
@Test
fun `register receiver on resume`() {
val config = CustomTabConfig(externalAppType = ExternalAppType.TRUSTED_WEB_ACTIVITY)
val store = BrowserStore(
BrowserState(
customTabs = listOf(
createCustomTab("https://mozilla.org", config = config)
)
)
)
val feature = PoweredByNotification(testContext, store, "session-id")
feature.onResume()
}
@Test
fun `don't register receiver if not in a TWA`() {
val config = CustomTabConfig(externalAppType = ExternalAppType.PROGRESSIVE_WEB_APP)
val store = BrowserStore(
BrowserState(
customTabs = listOf(
createCustomTab("https://mozilla.org", config = config)
)
)
)
val feature = PoweredByNotification(testContext, store, "session-id")
feature.onResume()
}
@Test
fun `unregister receiver on pause`() {
val feature = PoweredByNotification(testContext, mock(), "session-id")
feature.onPause()
}
}