parent
c9ea048431
commit
dce16964c0
@ -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.components.tips
|
||||
|
||||
import org.mozilla.fenix.FeatureFlags
|
||||
|
||||
sealed class TipType {
|
||||
data class Button(val text: String, val action: () -> Unit) : TipType()
|
||||
}
|
||||
|
||||
open class Tip(
|
||||
val type: TipType,
|
||||
val identifier: String,
|
||||
val title: String,
|
||||
val description: String,
|
||||
val learnMoreURL: String?
|
||||
)
|
||||
|
||||
interface TipProvider {
|
||||
val tip: Tip?
|
||||
val shouldDisplay: Boolean
|
||||
}
|
||||
|
||||
interface TipManager {
|
||||
fun getTip(): Tip?
|
||||
}
|
||||
|
||||
class FenixTipManager(
|
||||
private val providers: List<TipProvider>
|
||||
) : TipManager {
|
||||
override fun getTip(): Tip? {
|
||||
if (!FeatureFlags.tips) { return null }
|
||||
return providers
|
||||
.firstOrNull { it.shouldDisplay }
|
||||
?.tip
|
||||
}
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
/* 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.components.tips.providers
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.metrics.MozillaProductDetector
|
||||
import org.mozilla.fenix.components.metrics.MozillaProductDetector.MozillaProducts.FENIX
|
||||
import org.mozilla.fenix.components.metrics.MozillaProductDetector.MozillaProducts.FENIX_NIGHTLY
|
||||
import org.mozilla.fenix.components.metrics.MozillaProductDetector.MozillaProducts.FIREFOX_NIGHTLY
|
||||
import org.mozilla.fenix.components.tips.Tip
|
||||
import org.mozilla.fenix.components.tips.TipProvider
|
||||
import org.mozilla.fenix.components.tips.TipType
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.settings.SupportUtils
|
||||
|
||||
/**
|
||||
* Tip explaining to users the migration of Fenix channels
|
||||
*/
|
||||
class MigrationTipProvider(private val context: Context) : TipProvider {
|
||||
|
||||
override val tip: Tip? =
|
||||
when (context.packageName) {
|
||||
FENIX.productName -> firefoxPreviewMovedTip()
|
||||
FIREFOX_NIGHTLY.productName -> getNightlyMigrationTip()
|
||||
FENIX_NIGHTLY.productName -> getNightlyMigrationTip()
|
||||
else -> null
|
||||
}
|
||||
|
||||
override val shouldDisplay: Boolean = context.settings().shouldDisplayFenixMovingTip()
|
||||
|
||||
private fun firefoxPreviewMovedTip(): Tip =
|
||||
Tip(
|
||||
type = TipType.Button(
|
||||
text = context.getString(R.string.tip_firefox_preview_moved_button),
|
||||
action = ::getFirefoxMovedButtonAction
|
||||
),
|
||||
identifier = getIdentifier(),
|
||||
title = context.getString(R.string.tip_firefox_preview_moved_header),
|
||||
description = context.getString(R.string.tip_firefox_preview_moved_description),
|
||||
learnMoreURL = SupportUtils.getGenericSumoURLForTopic(SupportUtils.SumoTopic.FENIX_MOVING)
|
||||
)
|
||||
|
||||
private fun firefoxPreviewMovedPreviewInstalledTip(): Tip =
|
||||
Tip(
|
||||
type = TipType.Button(
|
||||
text = context.getString(R.string.tip_firefox_preview_moved_button_preview_installed),
|
||||
action = ::getFirefoxMovedButtonAction
|
||||
),
|
||||
identifier = getIdentifier(),
|
||||
title = context.getString(R.string.tip_firefox_preview_moved_header_preview_installed),
|
||||
description = context.getString(R.string.tip_firefox_preview_moved_description_preview_installed),
|
||||
learnMoreURL = SupportUtils.getGenericSumoURLForTopic(SupportUtils.SumoTopic.FENIX_MOVING)
|
||||
)
|
||||
|
||||
private fun firefoxPreviewMovedPreviewNotInstalledTip(): Tip =
|
||||
Tip(
|
||||
type = TipType.Button(
|
||||
text = context.getString(R.string.tip_firefox_preview_moved_button_preview_not_installed),
|
||||
action = ::getFirefoxMovedButtonAction
|
||||
),
|
||||
identifier = getIdentifier(),
|
||||
title = context.getString(R.string.tip_firefox_preview_moved_header_preview_not_installed),
|
||||
description = context.getString(R.string.tip_firefox_preview_moved_description_preview_not_installed),
|
||||
learnMoreURL = SupportUtils.getGenericSumoURLForTopic(SupportUtils.SumoTopic.FENIX_MOVING)
|
||||
)
|
||||
|
||||
private fun getNightlyMigrationTip(): Tip? {
|
||||
return if (MozillaProductDetector.packageIsInstalled(context, FENIX.productName)) {
|
||||
firefoxPreviewMovedPreviewInstalledTip()
|
||||
} else {
|
||||
firefoxPreviewMovedPreviewNotInstalledTip()
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFirefoxMovedButtonAction() {
|
||||
when (context.packageName) {
|
||||
FENIX.productName -> context.startActivity(
|
||||
Intent(Intent.ACTION_VIEW, Uri.parse(SupportUtils.FIREFOX_BETA_PLAY_STORE_URL))
|
||||
)
|
||||
FIREFOX_NIGHTLY.productName -> getNightlyMigrationAction()
|
||||
FENIX_NIGHTLY.productName -> getNightlyMigrationAction()
|
||||
else -> { }
|
||||
}
|
||||
}
|
||||
|
||||
private fun getNightlyMigrationAction() {
|
||||
return if (MozillaProductDetector.packageIsInstalled(context, FENIX.productName)) {
|
||||
context.startActivity(context.packageManager.getLaunchIntentForPackage(FENIX.productName))
|
||||
} else {
|
||||
context.startActivity(Intent(
|
||||
Intent.ACTION_VIEW, Uri.parse(SupportUtils.FIREFOX_NIGHTLY_PLAY_STORE_URL)
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
private fun getIdentifier(): String {
|
||||
return when (context.packageName) {
|
||||
FENIX.productName -> context.getString(R.string.pref_key_migrating_from_fenix_tip)
|
||||
FIREFOX_NIGHTLY.productName -> context.getString(R.string.pref_key_migrating_from_firefox_nightly_tip)
|
||||
FENIX_NIGHTLY.productName -> context.getString(R.string.pref_key_migrating_from_fenix_nightly_tip)
|
||||
else -> { "" }
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,78 @@
|
||||
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.*
|
||||
import org.mozilla.fenix.BrowserDirection
|
||||
import org.mozilla.fenix.HomeActivity
|
||||
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.components
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.home.sessioncontrol.SessionControlInteractor
|
||||
|
||||
class ButtonTipViewHolder(
|
||||
val view: View,
|
||||
val interactor: SessionControlInteractor
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
var tip: Tip? = null
|
||||
|
||||
fun bind(tip: Tip) {
|
||||
require(tip.type is TipType.Button)
|
||||
|
||||
this.tip = tip
|
||||
|
||||
view.apply {
|
||||
context.components.analytics.metrics.track(Event.TipDisplayed(tip.identifier))
|
||||
|
||||
tip_header_text.text = tip.title
|
||||
tip_description_text.text = tip.description
|
||||
tip_button.text = tip.type.text
|
||||
|
||||
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.setOnClickListener {
|
||||
(context as HomeActivity).openToBrowserAndLoad(
|
||||
searchTermOrURL = tip.learnMoreURL,
|
||||
newTab = true,
|
||||
from = BrowserDirection.FromHome
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
tip_button.setOnClickListener {
|
||||
tip.type.action.invoke()
|
||||
context.components.analytics.metrics.track(
|
||||
Event.TipPressed(tip.identifier)
|
||||
)
|
||||
}
|
||||
|
||||
tip_close.setOnClickListener {
|
||||
context.components.analytics.metrics.track(Event.TipClosed(tip.identifier))
|
||||
|
||||
context.settings().preferences
|
||||
.edit()
|
||||
.putBoolean(tip.identifier, false)
|
||||
.apply()
|
||||
|
||||
interactor.onCloseTip(tip)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.button_tip_item
|
||||
}
|
||||
}
|
@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/tip_card"
|
||||
android:background="@drawable/cfr_background_gradient"
|
||||
style="@style/OnboardingCardLight"
|
||||
android:padding="0dp"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tip_header_text"
|
||||
android:layout_width="0dp"
|
||||
android:maxLines="2"
|
||||
android:lineSpacingExtra="2dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textColor="@color/primary_text_dark_theme"
|
||||
android:textAppearance="@style/HeaderTextStyle"
|
||||
android:gravity="center_vertical"
|
||||
tools:text="Header text"
|
||||
android:layout_margin="16dp"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/tip_close"
|
||||
android:tint="@color/primary_text_dark_theme"
|
||||
app:srcCompat="@drawable/ic_close"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:background="?selectableItemBackgroundBorderless"
|
||||
android:contentDescription="@string/create_collection_close"
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="48dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tip_description_text"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:lineSpacingExtra="2dp"
|
||||
android:textAppearance="@style/Body14TextStyle"
|
||||
android:textColor="@color/primary_text_dark_theme"
|
||||
tools:text="Tip description"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/tip_header_text"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tip_learn_more"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/search_suggestions_onboarding_learn_more_link"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="@color/primary_text_dark_theme"
|
||||
app:layout_constraintEnd_toEndOf="@id/tip_description_text"
|
||||
app:layout_constraintStart_toStartOf="@id/tip_description_text"
|
||||
app:layout_constraintTop_toBottomOf="@id/tip_description_text"
|
||||
tools:textColor="@color/accent_high_contrast_private_theme"/>
|
||||
|
||||
<Button
|
||||
style="@style/NeutralButton"
|
||||
android:id="@+id/tip_button"
|
||||
android:layout_marginTop="8dp"
|
||||
tools:text="Call to action"
|
||||
android:layout_height="36dp"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:layout_marginBottom="16dp"
|
||||
app:layout_constraintTop_toBottomOf="@id/tip_learn_more"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
Loading…
Reference in New Issue