diff --git a/app/src/main/java/org/mozilla/fenix/ext/TextView.kt b/app/src/main/java/org/mozilla/fenix/ext/TextView.kt new file mode 100644 index 000000000..262f3ce7e --- /dev/null +++ b/app/src/main/java/org/mozilla/fenix/ext/TextView.kt @@ -0,0 +1,19 @@ +/* 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.ext + +import android.text.style.UnderlineSpan +import android.widget.TextView +import androidx.core.text.toSpannable + +/** + * Adds an underline effect to the text displayed in the TextView. + */ +fun TextView.addUnderline() { + val currentText = text + text = currentText.toSpannable().apply { + setSpan(UnderlineSpan(), 0, currentText.length, 0) + } +} diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/PrivateBrowsingDescriptionViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/PrivateBrowsingDescriptionViewHolder.kt index 31a1facb1..4cc6a0d85 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/PrivateBrowsingDescriptionViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/PrivateBrowsingDescriptionViewHolder.kt @@ -4,13 +4,12 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders -import android.text.SpannableString import android.text.method.LinkMovementMethod -import android.text.style.UnderlineSpan import android.view.View import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.private_browsing_description.view.* import org.mozilla.fenix.R +import org.mozilla.fenix.ext.addUnderline import org.mozilla.fenix.home.sessioncontrol.TabSessionInteractor class PrivateBrowsingDescriptionViewHolder( @@ -24,13 +23,9 @@ class PrivateBrowsingDescriptionViewHolder( view.private_session_description.text = resources.getString( R.string.private_browsing_placeholder_description_2, appName ) - val commonMythsText = view.private_session_common_myths.text.toString() - val textWithLink = SpannableString(commonMythsText).apply { - setSpan(UnderlineSpan(), 0, commonMythsText.length, 0) - } with(view.private_session_common_myths) { movementMethod = LinkMovementMethod.getInstance() - text = textWithLink + addUnderline() setOnClickListener { interactor.onPrivateBrowsingLearnMoreClicked() } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingIcon.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingIcon.kt index 5ea1b7446..62badd38a 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingIcon.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingIcon.kt @@ -6,8 +6,8 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding import android.widget.TextView import androidx.annotation.DrawableRes -import androidx.appcompat.content.res.AppCompatResources import mozilla.components.support.ktx.android.content.getColorFromAttr +import mozilla.components.support.ktx.android.content.getDrawableWithTint import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelative import org.mozilla.fenix.R import org.mozilla.fenix.ext.setBounds @@ -16,9 +16,9 @@ import org.mozilla.fenix.ext.setBounds * Sets the drawableStart of a header in an onboarding card. */ fun TextView.setOnboardingIcon(@DrawableRes id: Int) { - val icon = AppCompatResources.getDrawable(context, id) - val size = context.resources.getDimensionPixelSize(R.dimen.onboarding_header_icon_height_width) - icon?.setBounds(size) - icon?.setTint(context.getColorFromAttr(R.attr.onboardingSelected)) + val icon = context.getDrawableWithTint(id, context.getColorFromAttr(R.attr.onboardingSelected))?.apply { + val size = context.resources.getDimensionPixelSize(R.dimen.onboarding_header_icon_height_width) + setBounds(size) + } putCompoundDrawablesRelative(start = icon) } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingManualSignInViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingManualSignInViewHolder.kt index 219553d49..e4fa6d5a3 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingManualSignInViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingManualSignInViewHolder.kt @@ -5,11 +5,11 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding import android.view.View -import androidx.appcompat.content.res.AppCompatResources import androidx.core.content.ContextCompat import androidx.navigation.Navigation import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.onboarding_manual_signin.view.* +import mozilla.components.support.ktx.android.content.getDrawableWithTint import mozilla.components.support.ktx.android.view.putCompoundDrawablesRelativeWithIntrinsicBounds import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event @@ -34,8 +34,10 @@ class OnboardingManualSignInViewHolder(view: View) : RecyclerView.ViewHolder(vie val appName = context.getString(R.string.app_name) headerText.text = context.getString(R.string.onboarding_firefox_account_header, appName) - val icon = AppCompatResources.getDrawable(context, R.drawable.ic_onboarding_firefox_accounts) - icon?.setTint(ContextCompat.getColor(context, R.color.white_color)) + val icon = context.getDrawableWithTint( + R.drawable.ic_onboarding_firefox_accounts, + ContextCompat.getColor(context, R.color.white_color) + ) headerText.putCompoundDrawablesRelativeWithIntrinsicBounds(start = icon) } diff --git a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingWhatsNewViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingWhatsNewViewHolder.kt index b37181533..e6cd4567c 100644 --- a/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingWhatsNewViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingWhatsNewViewHolder.kt @@ -4,13 +4,12 @@ package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding -import android.text.SpannableString -import android.text.style.UnderlineSpan import android.view.View import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.onboarding_whats_new.view.* import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event +import org.mozilla.fenix.ext.addUnderline import org.mozilla.fenix.ext.components import org.mozilla.fenix.home.sessioncontrol.OnboardingInteractor @@ -25,12 +24,7 @@ class OnboardingWhatsNewViewHolder( val appName = view.context.getString(R.string.app_name) view.description_text.text = view.context.getString(R.string.onboarding_whats_new_description, appName) - val getAnswersText = view.get_answers.text.toString() - val textWithLink = SpannableString(getAnswersText).apply { - setSpan(UnderlineSpan(), 0, getAnswersText.length, 0) - } - - view.get_answers.text = textWithLink + view.get_answers.addUnderline() view.get_answers.setOnClickListener { interactor.onWhatsNewGetAnswersClicked() view.context.components.analytics.metrics.track(Event.OnboardingWhatsNew) diff --git a/app/src/main/java/org/mozilla/fenix/home/tips/ButtonTipViewHolder.kt b/app/src/main/java/org/mozilla/fenix/home/tips/ButtonTipViewHolder.kt index a487f6655..1711ff071 100644 --- a/app/src/main/java/org/mozilla/fenix/home/tips/ButtonTipViewHolder.kt +++ b/app/src/main/java/org/mozilla/fenix/home/tips/ButtonTipViewHolder.kt @@ -1,7 +1,5 @@ package org.mozilla.fenix.home.tips -import android.text.SpannableString -import android.text.style.UnderlineSpan import android.view.View import androidx.recyclerview.widget.RecyclerView import kotlinx.android.synthetic.main.button_tip_item.view.* @@ -11,6 +9,7 @@ import org.mozilla.fenix.R import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.tips.Tip import org.mozilla.fenix.components.tips.TipType +import org.mozilla.fenix.ext.addUnderline import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.settings import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor @@ -36,12 +35,7 @@ class ButtonTipViewHolder( if (tip.learnMoreURL == null) { tip_learn_more.visibility = View.GONE } else { - val learnMoreText = context.getString(R.string.search_suggestions_onboarding_learn_more_link) - val textWithLink = SpannableString(learnMoreText).apply { - setSpan(UnderlineSpan(), 0, learnMoreText.length, 0) - } - - tip_learn_more.text = textWithLink + tip_learn_more.addUnderline() tip_learn_more.setOnClickListener { (context as HomeActivity).openToBrowserAndLoad( diff --git a/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsView.kt b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsView.kt index 4223fdbf6..3f83360f4 100644 --- a/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsView.kt +++ b/app/src/main/java/org/mozilla/fenix/settings/logins/SavedLoginsView.kt @@ -4,9 +4,7 @@ package org.mozilla.fenix.settings.logins -import android.text.SpannableString import android.text.method.LinkMovementMethod -import android.text.style.UnderlineSpan import android.view.LayoutInflater import android.view.ViewGroup import android.widget.FrameLayout @@ -14,8 +12,8 @@ import androidx.core.view.isVisible import androidx.recyclerview.widget.LinearLayoutManager import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.component_saved_logins.view.* -import kotlinx.android.synthetic.main.component_saved_logins.view.progress_bar import org.mozilla.fenix.R +import org.mozilla.fenix.ext.addUnderline import org.mozilla.fenix.utils.Settings /** @@ -39,13 +37,9 @@ class SavedLoginsView( itemAnimator = null } - val learnMoreText = view.saved_passwords_empty_learn_more.text.toString() - val textWithLink = SpannableString(learnMoreText).apply { - setSpan(UnderlineSpan(), 0, learnMoreText.length, 0) - } with(view.saved_passwords_empty_learn_more) { movementMethod = LinkMovementMethod.getInstance() - text = textWithLink + addUnderline() setOnClickListener { interactor.onLearnMore() } } diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotectionexceptions/ExceptionsView.kt b/app/src/main/java/org/mozilla/fenix/trackingprotectionexceptions/ExceptionsView.kt index 090d9c303..fcc58f7ce 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotectionexceptions/ExceptionsView.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotectionexceptions/ExceptionsView.kt @@ -5,17 +5,16 @@ package org.mozilla.fenix.trackingprotectionexceptions import android.text.method.LinkMovementMethod -import android.text.style.UnderlineSpan import android.view.LayoutInflater import android.view.ViewGroup import android.widget.FrameLayout -import androidx.core.text.toSpannable import androidx.core.view.isVisible import androidx.recyclerview.widget.LinearLayoutManager import kotlinx.android.extensions.LayoutContainer import kotlinx.android.synthetic.main.component_exceptions.* import mozilla.components.concept.engine.content.blocking.TrackingProtectionException import org.mozilla.fenix.R +import org.mozilla.fenix.ext.addUnderline /** * Interface for the ExceptionsViewInteractor. This interface is implemented by objects that want @@ -62,10 +61,7 @@ class ExceptionsView( } with(exceptions_learn_more) { - val learnMoreText = text - text = learnMoreText.toSpannable().apply { - setSpan(UnderlineSpan(), 0, learnMoreText.length, 0) - } + addUnderline() movementMethod = LinkMovementMethod.getInstance() setOnClickListener { interactor.onLearnMore() } diff --git a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/PrivateBrowsingDescriptionViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/PrivateBrowsingDescriptionViewHolderTest.kt new file mode 100644 index 000000000..e2c54a205 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/PrivateBrowsingDescriptionViewHolderTest.kt @@ -0,0 +1,39 @@ +/* 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.sessioncontrol.viewholders + +import android.view.LayoutInflater +import android.view.View +import io.mockk.mockk +import io.mockk.verify +import kotlinx.android.synthetic.main.private_browsing_description.view.* +import mozilla.components.support.test.robolectric.testContext +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import org.mozilla.fenix.home.sessioncontrol.TabSessionInteractor + +@RunWith(FenixRobolectricTestRunner::class) +class PrivateBrowsingDescriptionViewHolderTest { + + private lateinit var view: View + private lateinit var interactor: TabSessionInteractor + + @Before + fun setup() { + view = LayoutInflater.from(testContext) + .inflate(PrivateBrowsingDescriptionViewHolder.LAYOUT_ID, null) + interactor = mockk(relaxed = true) + } + + @Test + fun `call interactor on click`() { + PrivateBrowsingDescriptionViewHolder(view, interactor) + + view.private_session_common_myths.performClick() + verify { interactor.onPrivateBrowsingLearnMoreClicked() } + } +} diff --git a/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingWhatsNewViewHolderTest.kt b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingWhatsNewViewHolderTest.kt new file mode 100644 index 000000000..b1e128432 --- /dev/null +++ b/app/src/test/java/org/mozilla/fenix/home/sessioncontrol/viewholders/onboarding/OnboardingWhatsNewViewHolderTest.kt @@ -0,0 +1,74 @@ +/* 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.sessioncontrol.viewholders.onboarding + +import android.content.res.Resources +import android.text.Spanned +import android.view.LayoutInflater +import android.view.View +import androidx.core.text.HtmlCompat +import androidx.core.text.HtmlCompat.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE +import io.mockk.every +import io.mockk.mockk +import io.mockk.mockkStatic +import io.mockk.unmockkStatic +import io.mockk.verify +import kotlinx.android.synthetic.main.onboarding_whats_new.view.* +import mozilla.components.support.ktx.android.content.res.resolveAttribute +import mozilla.components.support.test.robolectric.testContext +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mozilla.fenix.R +import org.mozilla.fenix.helpers.FenixRobolectricTestRunner +import org.mozilla.fenix.home.sessioncontrol.OnboardingInteractor + +@RunWith(FenixRobolectricTestRunner::class) +class OnboardingWhatsNewViewHolderTest { + + private lateinit var view: View + private lateinit var interactor: OnboardingInteractor + + @Before + fun setup() { + mockkStatic("mozilla.components.support.ktx.android.content.res.ThemeKt") + view = LayoutInflater.from(testContext) + .inflate(OnboardingWhatsNewViewHolder.LAYOUT_ID, null) + interactor = mockk(relaxed = true) + + every { + any().resolveAttribute(R.attr.onboardingSelected) + } returns R.color.onboarding_illustration_selected_normal_theme + } + + @After + fun teardown() { + unmockkStatic("mozilla.components.support.ktx.android.content.res.ThemeKt") + } + + @Test + fun `sets and styles strings`() { + OnboardingWhatsNewViewHolder(view, interactor) + + assertEquals( + "Have questions about the redesigned Firefox Preview? Want to know what’s changed?", + view.description_text.text + ) + + val getAnswersHtml = HtmlCompat.toHtml(view.get_answers.text as Spanned, TO_HTML_PARAGRAPH_LINES_CONSECUTIVE) + assertTrue(getAnswersHtml, "Get answers here" in getAnswersHtml) + } + + @Test + fun `call interactor on click`() { + OnboardingWhatsNewViewHolder(view, interactor) + + view.get_answers.performClick() + verify { interactor.onWhatsNewGetAnswersClicked() } + } +}