For #21732 - Adds inactive tabs survey on disable + telemetry

upstream-sync
codrut.topliceanu 3 years ago committed by mergify[bot]
parent 6d62aed35f
commit bba787e87e

@ -1720,6 +1720,38 @@ preferences:
notification_emails: notification_emails:
- android-probes@mozilla.com - android-probes@mozilla.com
expires: "2022-11-01" expires: "2022-11-01"
inactive_tabs_survey_opened:
type: event
description: >
A survey for asking the user why she intends to turn off the
inactive tabs feature is shown.
bugs:
- https://github.com/mozilla-mobile/fenix/issues/21732
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/21862#issuecomment-949598042
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: "2022-02-01"
turn_off_inactive_tabs_survey:
type: event
description: >
The user has disabled inactive tabs feature and responded
to our request for feedback.
extra_keys:
feedback:
description: |
The user's feedback regarding inactive tabs feature.
bugs:
- https://github.com/mozilla-mobile/fenix/issues/21732
data_reviews:
- https://github.com/mozilla-mobile/fenix/pull/21862#issuecomment-946977614
data_sensitivity:
- interaction
notification_emails:
- android-probes@mozilla.com
expires: "2022-02-01"
search.default_engine: search.default_engine:
code: code:

@ -20,6 +20,7 @@ import org.mozilla.fenix.GleanMetrics.Events
import org.mozilla.fenix.GleanMetrics.Logins import org.mozilla.fenix.GleanMetrics.Logins
import org.mozilla.fenix.GleanMetrics.Onboarding import org.mozilla.fenix.GleanMetrics.Onboarding
import org.mozilla.fenix.GleanMetrics.Pocket import org.mozilla.fenix.GleanMetrics.Pocket
import org.mozilla.fenix.GleanMetrics.Preferences
import org.mozilla.fenix.GleanMetrics.ProgressiveWebApp import org.mozilla.fenix.GleanMetrics.ProgressiveWebApp
import org.mozilla.fenix.GleanMetrics.SearchShortcuts import org.mozilla.fenix.GleanMetrics.SearchShortcuts
import org.mozilla.fenix.GleanMetrics.TabsTray import org.mozilla.fenix.GleanMetrics.TabsTray
@ -203,6 +204,12 @@ sealed class Event {
data class TabsTrayCloseInactiveTab(val amountClosed: Int = 1) : Event() data class TabsTrayCloseInactiveTab(val amountClosed: Int = 1) : Event()
object TabsTrayOpenInactiveTab : Event() object TabsTrayOpenInactiveTab : Event()
object InactiveTabsSurveyOpened : Event()
data class InactiveTabsOffSurvey(val feedback: String) : Event() {
override val extras: Map<Preferences.turnOffInactiveTabsSurveyKeys, String>
get() = mapOf(Preferences.turnOffInactiveTabsSurveyKeys.feedback to feedback.lowercase(Locale.ROOT))
}
object ProgressiveWebAppOpenFromHomescreenTap : Event() object ProgressiveWebAppOpenFromHomescreenTap : Event()
object ProgressiveWebAppInstallAsShortcut : Event() object ProgressiveWebAppInstallAsShortcut : Event()

@ -37,6 +37,7 @@ import org.mozilla.fenix.GleanMetrics.Metrics
import org.mozilla.fenix.GleanMetrics.Onboarding import org.mozilla.fenix.GleanMetrics.Onboarding
import org.mozilla.fenix.GleanMetrics.Pings import org.mozilla.fenix.GleanMetrics.Pings
import org.mozilla.fenix.GleanMetrics.Pocket import org.mozilla.fenix.GleanMetrics.Pocket
import org.mozilla.fenix.GleanMetrics.Preferences
import org.mozilla.fenix.GleanMetrics.ProgressiveWebApp import org.mozilla.fenix.GleanMetrics.ProgressiveWebApp
import org.mozilla.fenix.GleanMetrics.ReaderMode import org.mozilla.fenix.GleanMetrics.ReaderMode
import org.mozilla.fenix.GleanMetrics.RecentBookmarks import org.mozilla.fenix.GleanMetrics.RecentBookmarks
@ -626,6 +627,13 @@ private val Event.wrapper: EventWrapper<*>?
is Event.TabsTrayOpenInactiveTab -> EventWrapper<NoExtraKeys>( is Event.TabsTrayOpenInactiveTab -> EventWrapper<NoExtraKeys>(
{ TabsTray.openInactiveTab.add() } { TabsTray.openInactiveTab.add() }
) )
is Event.InactiveTabsSurveyOpened -> EventWrapper<NoExtraKeys>(
{ Preferences.inactiveTabsSurveyOpened.record(it) }
)
is Event.InactiveTabsOffSurvey -> EventWrapper(
{ Preferences.turnOffInactiveTabsSurvey.record(it) },
{ Preferences.turnOffInactiveTabsSurveyKeys.valueOf(it) }
)
is Event.AutoPlaySettingVisited -> EventWrapper<NoExtraKeys>( is Event.AutoPlaySettingVisited -> EventWrapper<NoExtraKeys>(
{ Autoplay.visitedSetting.record(it) } { Autoplay.visitedSetting.record(it) }
) )

@ -4,8 +4,12 @@
package org.mozilla.fenix.settings package org.mozilla.fenix.settings
import android.content.res.Configuration
import android.os.Bundle import android.os.Bundle
import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup
import android.widget.RadioButton
import androidx.preference.PreferenceCategory import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreference import androidx.preference.SwitchPreference
@ -14,14 +18,18 @@ import org.mozilla.fenix.R
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.components.metrics.Event.TabViewSettingChanged import org.mozilla.fenix.components.metrics.Event.TabViewSettingChanged
import org.mozilla.fenix.components.metrics.Event.TabViewSettingChanged.Type import org.mozilla.fenix.components.metrics.Event.TabViewSettingChanged.Type
import org.mozilla.fenix.databinding.SurveyInactiveTabsDisableBinding
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.metrics
import org.mozilla.fenix.ext.settings import org.mozilla.fenix.ext.settings
import org.mozilla.fenix.ext.showToolbar import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.utils.view.addToRadioGroup import org.mozilla.fenix.utils.view.addToRadioGroup
import java.util.Locale
/** /**
* Lets the user customize auto closing tabs. * Lets the user customize auto closing tabs.
*/ */
@Suppress("TooManyFunctions")
class TabsSettingsFragment : PreferenceFragmentCompat() { class TabsSettingsFragment : PreferenceFragmentCompat() {
private lateinit var listRadioButton: RadioButtonPreference private lateinit var listRadioButton: RadioButtonPreference
private lateinit var gridRadioButton: RadioButtonPreference private lateinit var gridRadioButton: RadioButtonPreference
@ -32,6 +40,9 @@ class TabsSettingsFragment : PreferenceFragmentCompat() {
private lateinit var inactiveTabsCategory: PreferenceCategory private lateinit var inactiveTabsCategory: PreferenceCategory
private lateinit var inactiveTabs: SwitchPreference private lateinit var inactiveTabs: SwitchPreference
private lateinit var searchTermTabGroups: SwitchPreference private lateinit var searchTermTabGroups: SwitchPreference
private val shouldShowInactiveTabsTurnOffSurvey
get() = requireContext().settings().isTelemetryEnabled &&
requireContext().settings().shouldShowInactiveTabsTurnOffSurvey
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.tabs_preferences, rootKey) setPreferencesFromResource(R.xml.tabs_preferences, rootKey)
@ -45,6 +56,7 @@ class TabsSettingsFragment : PreferenceFragmentCompat() {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
showToolbar(getString(R.string.preferences_tabs)) showToolbar(getString(R.string.preferences_tabs))
setupPreferences() setupPreferences()
} }
@ -68,8 +80,25 @@ class TabsSettingsFragment : PreferenceFragmentCompat() {
radioOneDay = requirePreference(R.string.pref_key_close_tabs_after_one_day) radioOneDay = requirePreference(R.string.pref_key_close_tabs_after_one_day)
inactiveTabs = requirePreference<SwitchPreference>(R.string.pref_key_inactive_tabs).also { inactiveTabs = requirePreference<SwitchPreference>(R.string.pref_key_inactive_tabs).also {
it.isChecked = it.context.settings().inactiveTabsAreEnabled it.isChecked = requireContext().settings().inactiveTabsAreEnabled
it.onPreferenceChangeListener = SharedPreferenceUpdater() it.setOnPreferenceChangeListener { preference, newValue ->
if (shouldShowInactiveTabsTurnOffSurvey && newValue == false) {
// The first time the user tries to disable the feature show a little survey for her motives.
val inactiveTabsSurveyBinding = SurveyInactiveTabsDisableBinding.inflate(
LayoutInflater.from(context),
view as ViewGroup,
true
)
setupSurvey(inactiveTabsSurveyBinding)
requireContext().metrics.track(Event.InactiveTabsSurveyOpened)
// Don't update the preference as a direct action of user tapping the switch.
// Only disable the feature after the user selects an option in the survey or expressly closes it.
false
} else {
SharedPreferenceUpdater().onPreferenceChange(preference, newValue)
}
}
} }
inactiveTabsCategory = requirePreference<PreferenceCategory>(R.string.pref_key_inactive_tabs_category).also { inactiveTabsCategory = requirePreference<PreferenceCategory>(R.string.pref_key_inactive_tabs_category).also {
@ -88,6 +117,66 @@ class TabsSettingsFragment : PreferenceFragmentCompat() {
setupRadioGroups() setupRadioGroups()
} }
private fun setupSurvey(inactiveTabsSurveyBinding: SurveyInactiveTabsDisableBinding) {
inactiveTabsSurveyBinding.closeSurvey.setOnClickListener {
finishInactiveTabsSurvey(inactiveTabsSurveyBinding)
// Register that user closed this survey without picking any option.
requireContext().metrics.track(
Event.InactiveTabsOffSurvey("none")
)
}
// A map is needed to help retrieve the correct string on SEND.
// These values are also sent to Glean which will truncate anything over 100 UTF8 characters.
val radioButtonsMap: Map<Int, Int> = mapOf(
R.id.rb_do_not_understand to R.string.inactive_tabs_survey_do_not_understand,
R.id.rb_do_it_myself to R.string.inactive_tabs_survey_do_it_myself,
R.id.rb_time_too_long to R.string.inactive_tabs_survey_time_too_long_option,
R.id.rb_time_too_short to R.string.inactive_tabs_survey_time_too_short_option,
)
// Sets the Radio buttons' text
radioButtonsMap.forEach {
inactiveTabsSurveyBinding.surveyGroup.findViewById<RadioButton>(it.key)?.text =
requireContext().getText(it.value)
}
inactiveTabsSurveyBinding.sendButton.setOnClickListener {
val checkedRadioButtonId = inactiveTabsSurveyBinding.surveyGroup.checkedRadioButtonId
// If no option has been selected the button does not need to do anything.
if (checkedRadioButtonId != -1) {
finishInactiveTabsSurvey(inactiveTabsSurveyBinding)
// Using the stringId of the selected option an event is sent using English.
radioButtonsMap[checkedRadioButtonId]?.let { stringId ->
requireContext().metrics.track(
Event.InactiveTabsOffSurvey(getDefaultString(stringId))
)
}
}
}
}
/**
* Set the inactive tabs survey completed and the feature disabled.
*/
private fun finishInactiveTabsSurvey(inactiveTabsSurveyBinding: SurveyInactiveTabsDisableBinding) {
inactiveTabsSurveyBinding.surveyContainer.visibility = View.GONE
requireContext().settings().shouldShowInactiveTabsTurnOffSurvey = false
requireContext().settings().inactiveTabsAreEnabled = false
requirePreference<SwitchPreference>(R.string.pref_key_inactive_tabs).isChecked = false
}
/**
* Get the "en-US" string value for the indicated [resourceId].
*/
private fun getDefaultString(resourceId: Int): String {
val config = Configuration(requireContext().resources.configuration)
config.setLocale(Locale.ENGLISH)
return requireContext().createConfigurationContext(config).getText(resourceId).toString()
}
private fun setupRadioGroups() { private fun setupRadioGroups() {
addToRadioGroup( addToRadioGroup(
listRadioButton, listRadioButton,

@ -867,6 +867,14 @@ class Settings(private val appContext: Context) : PreferencesHolder {
default = true default = true
) )
/**
* Should we display a feedback request to the user when he turns off the Inactive Tabs feature
*/
var shouldShowInactiveTabsTurnOffSurvey by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_should_show_inactive_tabs_turn_off_survey),
default = true
)
fun getSitePermissionsPhoneFeatureAction( fun getSitePermissionsPhoneFeatureAction(
feature: PhoneFeature, feature: PhoneFeature,
default: Action = Action.ASK_TO_ALLOW default: Action = Action.ASK_TO_ALLOW

@ -0,0 +1,114 @@
<?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"
android:id="@+id/survey_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="16dp"
android:background="@drawable/onboarding_card_background_light">
<TextView
android:id="@+id/survey_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="18dp"
android:layout_marginEnd="16dp"
android:text="@string/inactive_tabs_survey_header"
android:textAppearance="@style/Body12TextStyle"
android:textColor="#592ACB"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintEnd_toStartOf="@+id/close_survey"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/survey_subtitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:layout_marginEnd="16dp"
android:text="@string/inactive_tabs_survey_content"
android:textAppearance="@style/Body14TextStyle"
android:textSize="16sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/survey_title" />
<RadioGroup
android:id="@+id/survey_group"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="18dp"
android:layout_marginTop="10dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="18dp"
app:layout_constraintBottom_toTopOf="@+id/send_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/survey_subtitle"
app:layout_constraintVertical_bias="0.0">
<RadioButton
android:id="@+id/rb_do_not_understand"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/Body14TextStyle" />
<RadioButton
android:id="@+id/rb_do_it_myself"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/Body14TextStyle" />
<RadioButton
android:id="@+id/rb_time_too_long"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/Body14TextStyle" />
<RadioButton
android:id="@+id/rb_time_too_short"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="@style/Body14TextStyle" />
</RadioGroup>
<ImageButton
android:id="@+id/close_survey"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?selectableItemBackgroundBorderless"
android:contentDescription="@string/inactive_tabs_survey_close_button_content_description"
android:paddingStart="18dp"
android:paddingTop="20dp"
android:paddingEnd="18dp"
android:paddingBottom="20dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_close" />
<Button
android:id="@+id/send_button"
style="@style/MetropolisButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:background="@color/toolbar_menu_transparent"
android:text="@string/inactive_tabs_survey_send_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

@ -358,4 +358,7 @@
<!-- App Spinners colors --> <!-- App Spinners colors -->
<color name="spinner_selected_item">#1415141A</color> <color name="spinner_selected_item">#1415141A</color>
<!-- Toolbar menu icon colors -->
<color name="toolbar_menu_transparent">@android:color/transparent</color>
</resources> </resources>

@ -212,6 +212,9 @@
<!-- A value of `true` means the jump back in onboarding popup has not been shown yet --> <!-- A value of `true` means the jump back in onboarding popup has not been shown yet -->
<string name="pref_key_should_show_jump_back_in_tabs_popup" translatable="false">pref_key_should_show_jump_back_in_tabs_popup</string> <string name="pref_key_should_show_jump_back_in_tabs_popup" translatable="false">pref_key_should_show_jump_back_in_tabs_popup</string>
<!-- A value of `true` means the inactive tabs turn off - survey hasn't been shown yet-->
<string name="pref_key_should_show_inactive_tabs_turn_off_survey" translatable="false">pref_key_should_show_inactive_tabs_turn_off_survey</string>
<string name="pref_key_migrating_from_fenix_nightly_tip" translatable="false">pref_key_migrating_from_fenix_nightly_tip</string> <string name="pref_key_migrating_from_fenix_nightly_tip" translatable="false">pref_key_migrating_from_fenix_nightly_tip</string>
<string name="pref_key_migrating_from_firefox_nightly_tip" translatable="false">pref_key_migrating_from_firefox_nightly_tip</string> <string name="pref_key_migrating_from_firefox_nightly_tip" translatable="false">pref_key_migrating_from_firefox_nightly_tip</string>

@ -1966,19 +1966,21 @@
<!-- Inactive tabs survey --> <!-- Inactive tabs survey -->
<!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. --> <!-- Header text for the inactive tabs survey asking for feedback to improve the inactive tabs feature. -->
<string name="inactive_tabs_survey_header" tools:ignore="UnusedResources">Please help us to improve</string> <string name="inactive_tabs_survey_header">Help improve Firefox</string>
<!-- Content text for the inactive tabs survey asking the primary survey feedback question. --> <!-- Content text for the inactive tabs survey asking the primary survey feedback question. -->
<string name="inactive_tabs_survey_content" tools:ignore="UnusedResources">Why did you disable inactive tabs?</string> <string name="inactive_tabs_survey_content">Why did you disable inactive tabs?</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. --> <!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_not_interested_option" tools:ignore="UnusedResources">Not interested in the feature</string> <string name="inactive_tabs_survey_do_not_understand">I dont understand how it works</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. --> <!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_long_option" tools:ignore="UnusedResources">Time to inactive is too long</string> <string name="inactive_tabs_survey_do_it_myself">I like to clear out old tabs myself</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. --> <!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_short_option" tools:ignore="UnusedResources">Time to inactive is too short</string> <string name="inactive_tabs_survey_time_too_long_option">The two-week time period is too long</string>
<!-- One of the feedback option that can be selected as a responses to the inactive tabs survey question. -->
<string name="inactive_tabs_survey_time_too_short_option">The two-week time period is too short</string>
<!-- Confirmation button text to submit the feedback for the inactive tabs survey. --> <!-- Confirmation button text to submit the feedback for the inactive tabs survey. -->
<string name="inactive_tabs_survey_send_button" tools:ignore="UnusedResources">Send</string> <string name="inactive_tabs_survey_send_button">Send</string>
<!-- Content description for inactive tabs survey close button --> <!-- Content description for inactive tabs survey close button -->
<string name="inactive_tabs_survey_close_button_content_description" tools:ignore="UnusedResources">Close</string> <string name="inactive_tabs_survey_close_button_content_description">Close</string>
<!-- Default browser experiment --> <!-- Default browser experiment -->
<string name="default_browser_experiment_card_text">Set links from websites, emails, and messages to open automatically in Firefox.</string> <string name="default_browser_experiment_card_text">Set links from websites, emails, and messages to open automatically in Firefox.</string>

Loading…
Cancel
Save