Commit 2d67e4b7 authored by ekager's avatar ekager Committed by Emily Kager
Browse files

For #4118 - Creates setting for auto closing tabs

parent 7c7aa468
......@@ -69,6 +69,7 @@ class SettingsBasicsTest {
verifyBasicsHeading()
verifySearchEngineButton()
verifyDefaultBrowserItem()
verifyCloseTabsItem()
// drill down to submenu
}.openSearchSubMenu {
verifyDefaultSearchEngineHeader()
......@@ -168,6 +169,17 @@ class SettingsBasicsTest {
}
}
@Test
fun changeCloseTabsSetting() {
// Goes through the settings and verified the close tabs setting options.
homeScreen {
}.openThreeDotMenu {
}.openSettings {
}.openCloseTabsSubMenu {
verifyOptions()
}
}
@Test
fun changeAccessibiltySettings() {
// Goes through the settings and changes the default text on a webpage, then verifies if the text has changed.
......
......@@ -51,6 +51,7 @@ class SettingsRobot {
fun verifyAccessibilityButton() = assertAccessibilityButton()
fun verifySetAsDefaultBrowserButton() = assertSetAsDefaultBrowserButton()
fun verifyDefaultBrowserItem() = assertDefaultBrowserItem()
fun verifyCloseTabsItem() = assertCloseTabsItem()
fun verifyDefaultBrowserIsDisaled() = assertDefaultBrowserIsDisabled()
fun clickDefaultBrowserSwitch() = toggleDefaultBrowserSwitch()
fun verifyAndroidDefaultAppsMenuAppears() = assertAndroidDefaultAppsMenuAppears()
......@@ -134,6 +135,15 @@ class SettingsRobot {
return SettingsSubMenuThemeRobot.Transition()
}
fun openCloseTabsSubMenu(interact: SettingsSubMenuTabsRobot.() -> Unit): SettingsSubMenuTabsRobot.Transition {
fun closeTabsButton() = onView(withText("Close tabs"))
closeTabsButton().click()
SettingsSubMenuTabsRobot().interact()
return SettingsSubMenuTabsRobot.Transition()
}
fun openAccessibilitySubMenu(interact: SettingsSubMenuAccessibilityRobot.() -> Unit): SettingsSubMenuAccessibilityRobot.Transition {
fun accessibilityButton() = onView(withText("Accessibility"))
......@@ -237,8 +247,11 @@ private fun assertSettingsView() {
}
// GENERAL SECTION
private fun assertGeneralHeading() = onView(withText("General"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
private fun assertGeneralHeading() {
scrollToElementByText("General")
onView(withText("General"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun assertSearchEngineButton() {
mDevice.wait(Until.findObject(By.text("Search")), waitingTime)
......@@ -284,8 +297,15 @@ private fun assertDefaultBrowserItem() {
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
private fun assertCloseTabsItem() {
mDevice.wait(Until.findObject(By.text("Close tabs")), waitingTime)
onView(withText("Close tabs"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
// PRIVACY SECTION
private fun assertPrivacyHeading() {
scrollToElementByText("Privacy and security")
onView(withText("Privacy and security"))
.check(matches(withEffectiveVisibility(Visibility.VISIBLE)))
}
......
/* 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/. */
@file:Suppress("TooManyFunctions")
package org.mozilla.fenix.ui.robots
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.action.ViewActions
import androidx.test.espresso.assertion.ViewAssertions
import androidx.test.espresso.matcher.ViewMatchers
import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.uiautomator.UiDevice
import org.hamcrest.CoreMatchers.allOf
/**
* Implementation of Robot Pattern for the settings Tabs sub menu.
*/
class SettingsSubMenuTabsRobot {
fun verifyOptions() = assertOptions()
class Transition {
val mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
fun goBack(interact: SettingsRobot.() -> Unit): SettingsRobot.Transition {
mDevice.waitForIdle()
goBackButton().perform(ViewActions.click())
SettingsRobot().interact()
return SettingsRobot.Transition()
}
}
}
private fun assertOptions() {
afterOneDayToggle()
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
manualToggle()
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
afterOneWeekToggle()
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
afterOneMonthToggle()
.check(ViewAssertions.matches(ViewMatchers.withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
}
private fun manualToggle() = onView(withText("Manually"))
private fun afterOneDayToggle() = onView(withText("After one day"))
private fun afterOneWeekToggle() = onView(withText("After one week"))
private fun afterOneMonthToggle() = onView(withText("After one month"))
private fun goBackButton() =
onView(allOf(ViewMatchers.withContentDescription("Navigate up")))
......@@ -281,6 +281,14 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
}
settings().wasDefaultBrowserOnLastResume = settings().isDefaultBrowser()
if (!settings().manuallyCloseTabs) {
components.core.store.state.tabs.filter {
(System.currentTimeMillis() - it.lastAccess) > settings().getTabTimeout()
}.forEach {
components.useCases.tabsUseCases.removeTab(it.id)
}
}
}
}
......
/* 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.settings
import android.os.Bundle
import androidx.preference.PreferenceFragmentCompat
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.showToolbar
import org.mozilla.fenix.utils.view.addToRadioGroup
/**
* Lets the user customize auto closing tabs.
*/
class CloseTabsSettingsFragment : PreferenceFragmentCompat() {
private lateinit var radioManual: RadioButtonPreference
private lateinit var radioOneDay: RadioButtonPreference
private lateinit var radioOneWeek: RadioButtonPreference
private lateinit var radioOneMonth: RadioButtonPreference
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.close_tabs_preferences, rootKey)
}
override fun onResume() {
super.onResume()
showToolbar(getString(R.string.preferences_close_tabs))
setupPreferences()
}
private fun setupPreferences() {
radioManual = requirePreference(R.string.pref_key_close_tabs_manually)
radioOneDay = requirePreference(R.string.pref_key_close_tabs_after_one_day)
radioOneWeek = requirePreference(R.string.pref_key_close_tabs_after_one_week)
radioOneMonth = requirePreference(R.string.pref_key_close_tabs_after_one_month)
setupRadioGroups()
}
private fun setupRadioGroups() {
addToRadioGroup(
radioManual,
radioOneDay,
radioOneMonth,
radioOneWeek
)
}
}
......@@ -170,6 +170,10 @@ class SettingsFragment : PreferenceFragmentCompat() {
}
}
val tabSettingsPreference =
requirePreference<Preference>(R.string.pref_key_close_tabs)
tabSettingsPreference.summary = context?.settings()?.getTabTimeoutString()
setupPreferences()
if (shouldUpdateAccountUIState) {
......@@ -192,6 +196,9 @@ class SettingsFragment : PreferenceFragmentCompat() {
resources.getString(R.string.pref_key_sign_in) -> {
SettingsFragmentDirections.actionSettingsFragmentToTurnOnSyncFragment()
}
resources.getString(R.string.pref_key_close_tabs) -> {
SettingsFragmentDirections.actionSettingsFragmentToCloseTabsSettingsFragment()
}
resources.getString(R.string.pref_key_search_settings) -> {
SettingsFragmentDirections.actionSettingsFragmentToSearchEngineFragment()
}
......@@ -301,7 +308,8 @@ class SettingsFragment : PreferenceFragmentCompat() {
requirePreference<Preference>(R.string.pref_key_external_download_manager)
val preferenceLeakCanary = findPreference<Preference>(leakKey)
val preferenceRemoteDebugging = findPreference<Preference>(debuggingKey)
val preferenceMakeDefaultBrowser = requirePreference<Preference>(R.string.pref_key_make_default_browser)
val preferenceMakeDefaultBrowser =
requirePreference<Preference>(R.string.pref_key_make_default_browser)
if (!Config.channel.isReleased) {
preferenceLeakCanary?.setOnPreferenceChangeListener { _, newValue ->
......
......@@ -31,6 +31,7 @@ import org.mozilla.fenix.home.HomeFragment
interface TabTrayController {
fun onNewTabTapped(private: Boolean)
fun onTabTrayDismissed()
fun handleTabSettingsClicked()
fun onShareTabsClicked(private: Boolean)
fun onSyncedTabClicked(syncTab: SyncTab)
fun onSaveToCollectionClicked(selectedTabs: Set<Tab>)
......@@ -88,6 +89,10 @@ class DefaultTabTrayController(
)
}
override fun handleTabSettingsClicked() {
navController.navigate(TabTrayDialogFragmentDirections.actionGlobalCloseTabSettingsFragment())
}
override fun onTabTrayDismissed() {
dismissTabTray()
}
......
......@@ -24,6 +24,11 @@ interface TabTrayInteractor {
*/
fun onShareTabsClicked(private: Boolean)
/**
* Called when user clicks the tab settings button.
*/
fun onTabSettingsClicked()
/**
* Called when user clicks button to save selected tabs to a collection.
*/
......@@ -83,6 +88,10 @@ class TabTrayFragmentInteractor(private val controller: TabTrayController) : Tab
controller.onTabTrayDismissed()
}
override fun onTabSettingsClicked() {
controller.handleTabSettingsClicked()
}
override fun onShareTabsClicked(private: Boolean) {
controller.onShareTabsClicked(private)
}
......
......@@ -214,6 +214,7 @@ class TabTrayView(
is TabTrayItemMenu.Item.ShareAllTabs -> interactor.onShareTabsClicked(
isPrivateModeSelected
)
is TabTrayItemMenu.Item.OpenTabSettings -> interactor.onTabSettingsClicked()
is TabTrayItemMenu.Item.SaveToCollection -> interactor.onEnterMultiselect()
is TabTrayItemMenu.Item.CloseAllTabs -> interactor.onCloseAllTabsClicked(
isPrivateModeSelected
......@@ -574,6 +575,7 @@ class TabTrayItemMenu(
sealed class Item {
object ShareAllTabs : Item()
object OpenTabSettings : Item()
object SaveToCollection : Item()
object CloseAllTabs : Item()
}
......@@ -598,6 +600,13 @@ class TabTrayItemMenu(
onItemTapped.invoke(Item.ShareAllTabs)
},
SimpleBrowserMenuItem(
context.getString(R.string.tab_tray_menu_tab_settings),
textColorResource = R.color.primary_text_normal_theme
) {
onItemTapped.invoke(Item.OpenTabSettings)
},
SimpleBrowserMenuItem(
context.getString(R.string.tab_tray_menu_item_close),
textColorResource = R.color.primary_text_normal_theme
......
......@@ -62,6 +62,10 @@ class Settings(private val appContext: Context) : PreferencesHolder {
private const val CFR_COUNT_CONDITION_FOCUS_NOT_INSTALLED = 3
private const val MIN_DAYS_SINCE_FEEDBACK_PROMPT = 120
const val ONE_DAY_MS = 60 * 60 * 24 * 1000L
const val ONE_WEEK_MS = 60 * 60 * 24 * 7 * 1000L
const val ONE_MONTH_MS = (60 * 60 * 24 * 365 * 1000L) / 12
private fun Action.toInt() = when (this) {
Action.BLOCKED -> BLOCKED_INT
Action.ASK_TO_ALLOW -> ASK_TO_ALLOW_INT
......@@ -324,6 +328,48 @@ class Settings(private val appContext: Context) : PreferencesHolder {
default = false
)
var manuallyCloseTabs by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_close_tabs_manually),
default = true
)
var closeTabsAfterOneDay by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_close_tabs_after_one_day),
default = false
)
var closeTabsAfterOneWeek by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_close_tabs_after_one_week),
default = false
)
var closeTabsAfterOneMonth by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_close_tabs_after_one_month),
default = false
)
fun getTabTimeout(): Long = when {
closeTabsAfterOneDay -> ONE_DAY_MS
closeTabsAfterOneWeek -> ONE_WEEK_MS
closeTabsAfterOneMonth -> ONE_MONTH_MS
else -> System.currentTimeMillis()
}
fun getTabTimeoutString(): String = when {
closeTabsAfterOneDay -> {
appContext.getString(R.string.close_tabs_after_one_day)
}
closeTabsAfterOneWeek -> {
appContext.getString(R.string.close_tabs_after_one_week)
}
closeTabsAfterOneMonth -> {
appContext.getString(R.string.close_tabs_after_one_week)
}
else -> {
appContext.getString(R.string.close_tabs_manually)
}
}
val shouldUseDarkTheme by booleanPreference(
appContext.getPreferenceKey(R.string.pref_key_dark_theme),
default = false
......
<?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/. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:pathData="M15.22 22H5.78A3.78 3.78 0 0 1 2 18.22V8.78C2 6.69 3.7 5 5.78 5h9.44C17.31 5 19 6.7 19 8.78v9.44c0 2-1.7 3.78-3.78 3.78zM5.86 7C4.83 7 4 7.83 4 8.86v9.28C4 19.17 4.83 20 5.86 20h9.28c1 0 1.86-0.83 1.86-1.86V8.86C17 8 16.17 7 15 7H6zM6 4c0-1.1 1-2 2-2h8a6 6 0 0 1 6 6v0L22 16a2 2 0 0 1-2 2L20 8V8a4 4 0 0 0-4-4H6z"
android:fillColor="?primaryText" />
</vector>
......@@ -68,7 +68,8 @@
android:id="@+id/action_global_historyFragment"
app:destination="@id/historyFragment" />
<action android:id="@+id/action_global_downloadsFragment"
<action
android:id="@+id/action_global_downloadsFragment"
app:destination="@id/downloadsFragment" />
<action
android:id="@+id/action_global_accountProblemFragment"
......@@ -109,6 +110,9 @@
<action
android:id="@+id/action_global_tabHistoryDialogFragment"
app:destination="@id/tabHistoryDialogFragment" />
<action
android:id="@+id/action_global_closeTabSettingsFragment"
app:destination="@id/closeTabsSettingsFragment" />
<dialog
android:id="@+id/tabTrayDialogFragment"
......@@ -526,7 +530,18 @@
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
<action
android:id="@+id/action_settingsFragment_to_closeTabsSettingsFragment"
app:destination="@id/closeTabsSettingsFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
</fragment>
<fragment
android:id="@+id/closeTabsSettingsFragment"
android:name="org.mozilla.fenix.settings.CloseTabsSettingsFragment"
android:label="@string/preferences_close_tabs" />
<fragment
android:id="@+id/dataChoicesFragment"
android:name="org.mozilla.fenix.settings.DataChoicesFragment"
......@@ -808,7 +823,8 @@
android:name="org.mozilla.fenix.tabhistory.TabHistoryDialogFragment"
tools:layout="@layout/fragment_tab_history_dialog" />
<navigation android:id="@+id/site_permissions_exceptions_graph"
<navigation
android:id="@+id/site_permissions_exceptions_graph"
app:startDestination="@id/sitePermissionsExceptionsFragment">
<fragment
......@@ -847,7 +863,8 @@
</fragment>
</navigation>
<navigation android:id="@+id/addons_management_graph"
<navigation
android:id="@+id/addons_management_graph"
app:startDestination="@id/addonsManagementFragment">
<fragment
android:id="@+id/addonsManagementFragment"
......@@ -914,7 +931,8 @@
</fragment>
</navigation>
<navigation android:id="@+id/search_engine_graph"
<navigation
android:id="@+id/search_engine_graph"
app:startDestination="@id/searchEngineFragment">
<fragment
android:id="@+id/searchEngineFragment"
......
......@@ -206,4 +206,10 @@
<string name="pref_key_login_exceptions" translatable="false">pref_key_login_exceptions</string>
<string name="pref_key_show_collections_placeholder_home" translatable="false">pref_key_show_collections_home</string>
<string name="pref_key_close_tabs" translatable="false">pref_key_close_tabs</string>
<string name="pref_key_close_tabs_manually" translatable="false">pref_key_close_tabs_manually</string>
<string name="pref_key_close_tabs_after_one_day" translatable="false">pref_key_close_tabs_after_one_day</string>
<string name="pref_key_close_tabs_after_one_week" translatable="false">pref_key_close_tabs_after_one_week</string>
<string name="pref_key_close_tabs_after_one_month" translatable="false">pref_key_close_tabs_after_one_month</string>
</resources>
<?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.preference.PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<org.mozilla.fenix.settings.RadioButtonPreference
android:defaultValue="true"
android:key="@string/pref_key_close_tabs_manually"
android:title="@string/close_tabs_manually" />
<org.mozilla.fenix.settings.RadioButtonPreference
android:defaultValue="false"
android:key="@string/pref_key_close_tabs_after_one_day"
android:title="@string/close_tabs_after_one_day" />
<org.mozilla.fenix.settings.RadioButtonPreference
android:defaultValue="false"
android:key="@string/pref_key_close_tabs_after_one_week"
android:title="@string/close_tabs_after_one_week" />
<org.mozilla.fenix.settings.RadioButtonPreference
android:defaultValue="false"
android:key="@string/pref_key_close_tabs_after_one_month"
android:title="@string/close_tabs_after_one_month" />
</androidx.preference.PreferenceScreen>
......@@ -77,6 +77,11 @@
android:icon="@drawable/ic_internet"
android:key="@string/pref_key_make_default_browser"
android:title="@string/preferences_set_as_default_browser" />
<androidx.preference.Preference
android:icon="@drawable/ic_multiple_tabs"
android:key="@string/pref_key_close_tabs"
android:title="@string/preferences_close_tabs" />
</androidx.preference.PreferenceCategory>
<androidx.preference.PreferenceCategory
......
......@@ -100,6 +100,17 @@ class DefaultTabTrayControllerTest {
)
}
@Test
fun handleTabSettingsClicked() {
controller.handleTabSettingsClicked()
verify {
navController.navigate(
TabTrayDialogFragmentDirections.actionGlobalCloseTabSettingsFragment()
)
}
}
@Test
fun onNewTabTapped() {
controller.onNewTabTapped(private = false)
......
......@@ -22,6 +22,15 @@ class TabTrayFragmentInteractorTest {
verify { controller.onNewTabTapped(false) }
}
@Test
fun onTabSettingsClicked() {
interactor.onTabSettingsClicked()
verify {
controller.handleTabSettingsClicked()
}
}
@Test
fun onTabTrayDismissed() {
interactor.onTabTrayDismissed()
......
......@@ -189,6 +189,47 @@ class SettingsTest {
assertTrue(settings.shouldUseLightTheme)
}
@Test
fun shouldManuallyCloseTabs() {
// When just created
// Then
assertTrue(settings.manuallyCloseTabs)
// When
settings.manuallyCloseTabs = false
// Then
assertFalse(settings.shouldUseLightTheme)
}
@Test
fun getTabTimeout() {
// When just created
// Then
assertTrue(settings.manuallyCloseTabs)
// When
settings.manuallyCloseTabs = false
settings.closeTabsAfterOneDay = true
// Then
assertEquals(settings.getTabTimeout(), Settings.ONE_DAY_MS)
// When
settings.closeTabsAfterOneDay = false
settings.closeTabsAfterOneWeek = true
// Then
assertEquals(settings.getTabTimeout(), Settings.ONE_WEEK_MS)
// When
settings.closeTabsAfterOneWeek = false
settings.closeTabsAfterOneMonth = true
// Then
assertEquals(settings.getTabTimeout(), Settings.ONE_MONTH_MS)
}
@Test
fun shouldUseAutoSize() {
// When just created
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish ed