1
0
Fork 0

Extract TP onboarding popup (#6700)

master
Tiger Oakes 2019-11-21 14:23:35 -08:00 committed by Roger Yang
parent 0d6ea08de7
commit bd475d54e5
3 changed files with 190 additions and 71 deletions

View File

@ -5,17 +5,12 @@
package org.mozilla.fenix.browser
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.Button
import android.widget.ImageView
import android.widget.PopupWindow
import android.widget.RadioButton
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.content.ContextCompat
@ -24,7 +19,6 @@ import androidx.transition.TransitionInflater
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.fragment_browser.view.*
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.tracking_protection_onboarding_popup.view.*
import kotlinx.coroutines.ExperimentalCoroutinesApi
import mozilla.components.browser.session.Session
import mozilla.components.feature.contextmenu.ContextMenuCandidate
@ -35,21 +29,19 @@ import mozilla.components.feature.tabs.WindowFeature
import mozilla.components.lib.state.ext.consumeFrom
import mozilla.components.support.base.feature.BackHandler
import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
import org.jetbrains.anko.dimen
import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.R
import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.TabCollectionStorage
import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.getDimenInDip
import org.mozilla.fenix.ext.increaseTapArea
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.home.sessioncontrol.SessionControlChange
import org.mozilla.fenix.home.sessioncontrol.TabCollection
import org.mozilla.fenix.mvi.getManagedEmitter
import org.mozilla.fenix.trackingprotection.TrackingProtectionOverlay
/**
* Fragment used for browsing the web within the main app.
@ -127,17 +119,16 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
override fun onStart() {
super.onStart()
subscribeToTabCollections()
getSessionById()?.register(toolbarSessionObserver, this, autoPause = true)
}
private val toolbarSessionObserver = object : Session.Observer {
override fun onLoadingStateChanged(session: Session, loading: Boolean) {
if (!loading &&
shouldShowTrackingProtectionOnboarding(session)
) {
showTrackingProtectionOnboarding()
}
}
val toolbarSessionObserver = TrackingProtectionOverlay(
context = requireContext(),
settings = requireContext().settings(),
toolbar = browserToolbarView.view,
trackingProtectionIcon = browserToolbarView
.view
.findViewById<AppCompatImageView>(R.id.mozac_browser_toolbar_tracking_protection_indicator)
)
getSessionById()?.register(toolbarSessionObserver, this, autoPause = true)
}
override fun onResume() {
@ -254,54 +245,6 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
}
}
private fun showTrackingProtectionOnboarding() {
context?.let {
val layout = LayoutInflater.from(it)
.inflate(R.layout.tracking_protection_onboarding_popup, null)
layout.onboarding_message.text =
it.getString(R.string.etp_onboarding_message_2, getString(R.string.app_name))
val trackingOnboarding = PopupWindow(
layout,
it.dimen(R.dimen.tp_onboarding_width),
WindowManager.LayoutParams.WRAP_CONTENT
).apply {
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
isOutsideTouchable = true
isFocusable = true
elevation = view!!.resources.getDimension(R.dimen.mozac_browser_menu_elevation)
animationStyle = R.style.Mozac_Browser_Menu_Animation_OverflowMenuBottom
}
val closeButton = layout.findViewById<ImageView>(R.id.close_onboarding)
closeButton.increaseTapArea(BUTTON_INCREASE_DPS)
closeButton.setOnClickListener {
trackingOnboarding.dismiss()
}
val tpIcon =
browserToolbarView
.view
.findViewById<AppCompatImageView>(R.id.mozac_browser_toolbar_tracking_protection_indicator)
// Measure layout view
val spec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)
layout.measure(spec, spec)
val containerHeight = layout.measuredHeight
val triangleHeight = it.getDimenInDip(R.dimen.tp_onboarding_triangle_height).toInt()
val xOffset = it.dimen(R.dimen.tp_onboarding_x_offset)
// Positioning the popup above the tp anchor.
val yOffset =
-containerHeight - (browserToolbarView.view.height / THREE * 2) + triangleHeight
trackingOnboarding.showAsDropDown(tpIcon, xOffset, yOffset)
it.settings().incrementTrackingProtectionOnboardingCount()
}
}
override fun getContextMenuCandidates(
context: Context,
view: View
@ -316,10 +259,6 @@ class BrowserFragment : BaseBrowserFragment(), BackHandler {
)
)
private fun shouldShowTrackingProtectionOnboarding(session: Session) =
context?.settings()?.shouldShowTrackingProtectionOnboarding ?: false &&
session.trackerBlockingEnabled && session.trackersBlocked.isNotEmpty()
companion object {
private const val THREE = 3
private const val BUTTON_INCREASE_DPS = 12

View File

@ -0,0 +1,89 @@
/* 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.trackingprotection
import android.content.Context
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.view.LayoutInflater
import android.view.View
import android.view.View.MeasureSpec
import android.view.WindowManager
import android.widget.ImageView
import android.widget.PopupWindow
import kotlinx.android.synthetic.main.tracking_protection_onboarding_popup.view.*
import mozilla.components.browser.session.Session
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.getDimenInDip
import org.mozilla.fenix.ext.increaseTapArea
import org.mozilla.fenix.utils.Settings
/**
* Displays an overlay above the tracking protection button in the browser toolbar
* to onboard the user about tracking protection.
*/
class TrackingProtectionOverlay(
private val context: Context,
private val settings: Settings,
private val toolbar: View,
private val trackingProtectionIcon: View
) : Session.Observer {
override fun onLoadingStateChanged(session: Session, loading: Boolean) {
if (!loading && shouldShowTrackingProtectionOnboarding(session)) {
showTrackingProtectionOnboarding()
}
}
private fun shouldShowTrackingProtectionOnboarding(session: Session) =
settings.shouldShowTrackingProtectionOnboarding &&
session.trackerBlockingEnabled &&
session.trackersBlocked.isNotEmpty()
@Suppress("MagicNumber", "InflateParams")
private fun showTrackingProtectionOnboarding() {
val layout = LayoutInflater.from(context)
.inflate(R.layout.tracking_protection_onboarding_popup, null)
layout.onboarding_message.text =
context.getString(R.string.etp_onboarding_message_2, context.getString(R.string.app_name))
val trackingOnboarding = PopupWindow(
layout,
context.resources.getDimensionPixelSize(R.dimen.tp_onboarding_width),
WindowManager.LayoutParams.WRAP_CONTENT
).apply {
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
isOutsideTouchable = true
isFocusable = true
elevation = context.resources.getDimension(R.dimen.mozac_browser_menu_elevation)
animationStyle = R.style.Mozac_Browser_Menu_Animation_OverflowMenuBottom
}
val closeButton = layout.findViewById<ImageView>(R.id.close_onboarding)
closeButton.increaseTapArea(BUTTON_INCREASE_DPS)
closeButton.setOnClickListener {
trackingOnboarding.dismiss()
}
// Measure layout view
val spec = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)
layout.measure(spec, spec)
val containerHeight = layout.measuredHeight
val triangleHeight = context.getDimenInDip(R.dimen.tp_onboarding_triangle_height).toInt()
val xOffset = context.resources.getDimensionPixelSize(R.dimen.tp_onboarding_x_offset)
// Positioning the popup above the tp anchor.
val yOffset = -containerHeight - (toolbar.height / 3 * 2) + triangleHeight
trackingOnboarding.showAsDropDown(trackingProtectionIcon, xOffset, yOffset)
settings.incrementTrackingProtectionOnboardingCount()
}
private companion object {
private const val BUTTON_INCREASE_DPS = 12
}
}

View File

@ -0,0 +1,91 @@
/* 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.trackingprotection
import android.content.Context
import android.view.View
import io.mockk.every
import io.mockk.mockk
import io.mockk.spyk
import io.mockk.verify
import mozilla.components.browser.session.Session
import mozilla.components.support.test.robolectric.testContext
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.TestApplication
import org.mozilla.fenix.utils.Settings
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
@RunWith(RobolectricTestRunner::class)
@Config(application = TestApplication::class)
class TrackingProtectionOverlayTest {
private lateinit var context: Context
private lateinit var settings: Settings
private lateinit var toolbar: View
private lateinit var icon: View
private lateinit var session: Session
private lateinit var overlay: TrackingProtectionOverlay
@Before
fun setup() {
context = spyk(testContext)
settings = mockk(relaxed = true)
toolbar = mockk(relaxed = true)
icon = mockk(relaxed = true)
session = mockk(relaxed = true)
overlay = TrackingProtectionOverlay(context, settings, toolbar, icon)
}
@Test
fun `no-op when loading`() {
every { settings.shouldShowTrackingProtectionOnboarding } returns true
every { session.trackerBlockingEnabled } returns true
every { session.trackersBlocked } returns listOf(mockk())
overlay.onLoadingStateChanged(session, loading = true)
verify(exactly = 0) { settings.incrementTrackingProtectionOnboardingCount() }
}
@Test
fun `no-op when should not show onboarding`() {
every { settings.shouldShowTrackingProtectionOnboarding } returns false
overlay.onLoadingStateChanged(session, loading = false)
verify(exactly = 0) { settings.incrementTrackingProtectionOnboardingCount() }
}
@Test
fun `no-op when tracking protection disabled`() {
every { settings.shouldShowTrackingProtectionOnboarding } returns true
every { session.trackerBlockingEnabled } returns false
overlay.onLoadingStateChanged(session, loading = false)
verify(exactly = 0) { settings.incrementTrackingProtectionOnboardingCount() }
}
@Test
fun `no-op when no trackers blocked`() {
every { settings.shouldShowTrackingProtectionOnboarding } returns true
every { session.trackerBlockingEnabled } returns true
every { session.trackersBlocked } returns emptyList()
overlay.onLoadingStateChanged(session, loading = false)
verify(exactly = 0) { settings.incrementTrackingProtectionOnboardingCount() }
}
@Test
fun `show onboarding when trackers are blocked`() {
every { settings.shouldShowTrackingProtectionOnboarding } returns true
every { session.trackerBlockingEnabled } returns true
every { session.trackersBlocked } returns listOf(mockk())
overlay.onLoadingStateChanged(session, loading = false)
verify { settings.incrementTrackingProtectionOnboardingCount() }
}
}