For #3700 - Add Setting to Delete Data on "Quit" menu action
parent
ddc1b2e648
commit
e3209dcc84
@ -0,0 +1,105 @@
|
|||||||
|
/* 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.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.preference.CheckBoxPreference
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
import androidx.preference.SwitchPreference
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.ext.getPreferenceKey
|
||||||
|
import org.mozilla.fenix.utils.Settings
|
||||||
|
|
||||||
|
class DeleteBrowsingDataOnQuitFragment : PreferenceFragmentCompat() {
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
setPreferencesFromResource(R.xml.delete_browsing_data_quit_preferences, rootKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Suppress("ComplexMethod")
|
||||||
|
override fun onResume() {
|
||||||
|
super.onResume()
|
||||||
|
activity?.title = getString(R.string.preferences_delete_browsing_data_on_quit)
|
||||||
|
(activity as AppCompatActivity).supportActionBar?.show()
|
||||||
|
|
||||||
|
val checkboxUpdater = object : SharedPreferenceUpdater() {
|
||||||
|
override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean {
|
||||||
|
super.onPreferenceChange(preference, newValue)
|
||||||
|
if (!Settings.getInstance(preference.context).shouldDeleteAnyDataOnQuit()) {
|
||||||
|
findPreference<SwitchPreference>(
|
||||||
|
getPreferenceKey(R.string.pref_key_delete_browsing_data_on_quit)
|
||||||
|
)?.apply {
|
||||||
|
isChecked = false
|
||||||
|
}
|
||||||
|
Settings.getInstance(preference.context).preferences.edit().putBoolean(
|
||||||
|
getString(R.string.pref_key_delete_browsing_data_on_quit),
|
||||||
|
false
|
||||||
|
).apply()
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val switchUpdater = object : SharedPreferenceUpdater() {
|
||||||
|
override fun onPreferenceChange(preference: Preference, newValue: Any?): Boolean {
|
||||||
|
setAllCheckboxes(newValue as Boolean)
|
||||||
|
return super.onPreferenceChange(preference, newValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
findPreference<CheckBoxPreference>(getPreferenceKey(R.string.pref_key_delete_open_tabs_on_quit))?.apply {
|
||||||
|
onPreferenceChangeListener = checkboxUpdater
|
||||||
|
}
|
||||||
|
findPreference<CheckBoxPreference>(getPreferenceKey(R.string.pref_key_delete_browsing_history_on_quit))?.apply {
|
||||||
|
onPreferenceChangeListener = checkboxUpdater
|
||||||
|
}
|
||||||
|
findPreference<CheckBoxPreference>(getPreferenceKey(R.string.pref_key_delete_caches_on_quit))?.apply {
|
||||||
|
onPreferenceChangeListener = checkboxUpdater
|
||||||
|
}
|
||||||
|
findPreference<CheckBoxPreference>(getPreferenceKey(R.string.pref_key_delete_permissions_on_quit))?.apply {
|
||||||
|
onPreferenceChangeListener = checkboxUpdater
|
||||||
|
}
|
||||||
|
findPreference<CheckBoxPreference>(getPreferenceKey(R.string.pref_key_delete_cookies_on_quit))?.apply {
|
||||||
|
onPreferenceChangeListener = checkboxUpdater
|
||||||
|
}
|
||||||
|
|
||||||
|
// Delete Browsing Data on Quit Switch
|
||||||
|
val deleteOnQuitKey = getPreferenceKey(R.string.pref_key_delete_browsing_data_on_quit)
|
||||||
|
findPreference<SwitchPreference>(deleteOnQuitKey)?.apply {
|
||||||
|
onPreferenceChangeListener = switchUpdater
|
||||||
|
isChecked = Settings.getInstance(context!!).shouldDeleteBrowsingDataOnQuit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setAllCheckboxes(newValue: Boolean) {
|
||||||
|
val openTabs =
|
||||||
|
findPreference<CheckBoxPreference>(getPreferenceKey(R.string.pref_key_delete_open_tabs_on_quit))
|
||||||
|
val history =
|
||||||
|
findPreference<CheckBoxPreference>(getPreferenceKey(R.string.pref_key_delete_browsing_history_on_quit))
|
||||||
|
val cache =
|
||||||
|
findPreference<CheckBoxPreference>(getPreferenceKey(R.string.pref_key_delete_caches_on_quit))
|
||||||
|
val permissions =
|
||||||
|
findPreference<CheckBoxPreference>(getPreferenceKey(R.string.pref_key_delete_permissions_on_quit))
|
||||||
|
val cookies =
|
||||||
|
findPreference<CheckBoxPreference>(getPreferenceKey(R.string.pref_key_delete_cookies_on_quit))
|
||||||
|
|
||||||
|
openTabs?.isChecked = newValue
|
||||||
|
history?.isChecked = newValue
|
||||||
|
cache?.isChecked = newValue
|
||||||
|
permissions?.isChecked = newValue
|
||||||
|
cookies?.isChecked = newValue
|
||||||
|
|
||||||
|
Settings.getInstance(context!!).preferences.edit().putBoolean(openTabs?.key, newValue)
|
||||||
|
.apply()
|
||||||
|
Settings.getInstance(context!!).preferences.edit().putBoolean(history?.key, newValue)
|
||||||
|
.apply()
|
||||||
|
Settings.getInstance(context!!).preferences.edit().putBoolean(cache?.key, newValue).apply()
|
||||||
|
Settings.getInstance(context!!).preferences.edit().putBoolean(permissions?.key, newValue)
|
||||||
|
.apply()
|
||||||
|
Settings.getInstance(context!!).preferences.edit().putBoolean(cookies?.key, newValue)
|
||||||
|
.apply()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
/* 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.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.mozilla.fenix.ext.asActivity
|
||||||
|
import org.mozilla.fenix.settings.DefaultDeleteBrowsingDataController
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Deletes selected browsing data and finishes the activity
|
||||||
|
*/
|
||||||
|
fun Context.deleteAndQuit(coroutineScope: CoroutineScope) {
|
||||||
|
coroutineScope.launch {
|
||||||
|
runBlocking {
|
||||||
|
val controller =
|
||||||
|
DefaultDeleteBrowsingDataController(this@deleteAndQuit, coroutineContext)
|
||||||
|
if (Settings.getInstance(this@deleteAndQuit).deleteCacheOnQuit) {
|
||||||
|
controller.deleteCachedFiles()
|
||||||
|
}
|
||||||
|
if (Settings.getInstance(this@deleteAndQuit).deleteTabsOnQuit) {
|
||||||
|
controller.deleteTabs()
|
||||||
|
}
|
||||||
|
if (Settings.getInstance(this@deleteAndQuit).deletePermissionsOnQuit) {
|
||||||
|
launch(Dispatchers.IO) {
|
||||||
|
controller.deleteSitePermissions()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Settings.getInstance(this@deleteAndQuit).deleteCookiesOnQuit) {
|
||||||
|
controller.deleteCookies()
|
||||||
|
}
|
||||||
|
if (Settings.getInstance(this@deleteAndQuit).deleteHistoryOnQuit) {
|
||||||
|
controller.deleteHistoryAndDOMStorages()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this@deleteAndQuit.asActivity()?.finish()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,13 @@
|
|||||||
|
<?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:fillColor="?primaryText"
|
||||||
|
android:pathData="M12,1a11,11 0,1 0,11 11A11,11 0,0 0,12 1zM12,21a9,9 0,1 1,9 -9,9 9,0 0,1 -9,9zM16.71,7.29a1,1 0,0 0,-1.42 0L12,10.59l-3.29,-3.3a1,1 0,1 0,-1.42 1.42l3.3,3.29 -3.3,3.29a1,1 0,0 0,0 1.42,1 1,0 0,0 1.42,0l3.29,-3.3 3.29,3.3a1,1 0,0 0,1.42 0,1 1,0 0,0 0,-1.42L13.41,12l3.3,-3.29a1,1 0,0 0,0 -1.42z" />
|
||||||
|
</vector>
|
@ -0,0 +1,50 @@
|
|||||||
|
<?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:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="64dp"
|
||||||
|
android:background="?android:selectableItemBackground"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||||
|
android:paddingEnd="?android:attr/scrollbarSize">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@android:id/widget_frame"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="vertical"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintStart_toStartOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@android:id/title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:textSize="16sp"
|
||||||
|
app:layout_constraintBottom_toTopOf="@android:id/summary"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@android:id/widget_frame"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
tools:text="Delete browsing data category" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@android:id/summary"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"
|
||||||
|
app:layout_constraintStart_toEndOf="@android:id/widget_frame"
|
||||||
|
app:layout_constraintTop_toBottomOf="@android:id/title"
|
||||||
|
app:layout_constraintVertical_chainStyle="packed"
|
||||||
|
tools:text="Delete browsing data summary" />
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
@ -0,0 +1,40 @@
|
|||||||
|
<?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"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
<SwitchPreference
|
||||||
|
android:defaultValue="false"
|
||||||
|
android:key="@string/pref_key_delete_browsing_data_on_quit"
|
||||||
|
android:summary="@string/preference_summary_delete_browsing_data_on_quit_2"
|
||||||
|
android:title="@string/preferences_delete_browsing_data_on_quit" />
|
||||||
|
<PreferenceCategory
|
||||||
|
android:dependency="@string/pref_key_delete_browsing_data_on_quit"
|
||||||
|
android:key="@string/pref_key_delete_browsing_data_on_quit_categories"
|
||||||
|
app:allowDividerAbove="false"
|
||||||
|
app:iconSpaceReserved="false">
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="@string/pref_key_delete_open_tabs_on_quit"
|
||||||
|
android:layout="@layout/delete_browsing_category_checkbox"
|
||||||
|
android:title="@string/preferences_delete_browsing_data_tabs_title" />
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="@string/pref_key_delete_browsing_history_on_quit"
|
||||||
|
android:layout="@layout/delete_browsing_category_checkbox"
|
||||||
|
android:title="@string/preferences_delete_browsing_data_on_quit_browsing_history" />
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="@string/pref_key_delete_cookies_on_quit"
|
||||||
|
android:layout="@layout/delete_browsing_category_checkbox"
|
||||||
|
android:summary="@string/preferences_delete_browsing_data_cookies_subtitle"
|
||||||
|
android:title="@string/preferences_delete_browsing_data_cookies" />
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="@string/pref_key_delete_caches_on_quit"
|
||||||
|
android:layout="@layout/delete_browsing_category_checkbox"
|
||||||
|
android:summary="@string/preferences_delete_browsing_data_cached_files_subtitle"
|
||||||
|
android:title="@string/preferences_delete_browsing_data_cached_files" />
|
||||||
|
<CheckBoxPreference
|
||||||
|
android:key="@string/pref_key_delete_permissions_on_quit"
|
||||||
|
android:layout="@layout/delete_browsing_category_checkbox"
|
||||||
|
android:title="@string/preferences_delete_browsing_data_site_permissions" />
|
||||||
|
</PreferenceCategory>
|
||||||
|
</androidx.preference.PreferenceScreen>
|
@ -0,0 +1,140 @@
|
|||||||
|
/* 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("DEPRECATION")
|
||||||
|
|
||||||
|
package org.mozilla.fenix.utils
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||||
|
import kotlinx.coroutines.newSingleThreadContext
|
||||||
|
import kotlinx.coroutines.test.resetMain
|
||||||
|
import kotlinx.coroutines.test.runBlockingTest
|
||||||
|
import kotlinx.coroutines.test.setMain
|
||||||
|
import mozilla.components.browser.storage.sync.PlacesHistoryStorage
|
||||||
|
import mozilla.components.concept.engine.Engine
|
||||||
|
import mozilla.components.feature.tabs.TabsUseCases
|
||||||
|
import mozilla.components.support.test.robolectric.testContext
|
||||||
|
import org.junit.After
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mozilla.fenix.HomeActivity
|
||||||
|
import org.mozilla.fenix.TestApplication
|
||||||
|
import org.mozilla.fenix.components.PermissionStorage
|
||||||
|
import org.mozilla.fenix.ext.asActivity
|
||||||
|
import org.mozilla.fenix.ext.clearAndCommit
|
||||||
|
import org.mozilla.fenix.ext.components
|
||||||
|
import org.robolectric.annotation.Config
|
||||||
|
|
||||||
|
@ObsoleteCoroutinesApi
|
||||||
|
@ExperimentalCoroutinesApi
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
@Config(application = TestApplication::class)
|
||||||
|
class DeleteAndQuitTest {
|
||||||
|
|
||||||
|
private val mainThreadSurrogate = newSingleThreadContext("UI thread")
|
||||||
|
|
||||||
|
private var context: HomeActivity = mockk(relaxed = true)
|
||||||
|
lateinit var settings: Settings
|
||||||
|
private val tabUseCases: TabsUseCases = mockk(relaxed = true)
|
||||||
|
private val historyStorage: PlacesHistoryStorage = mockk(relaxed = true)
|
||||||
|
private val permissionStorage: PermissionStorage = mockk(relaxed = true)
|
||||||
|
private val engine: Engine = mockk(relaxed = true)
|
||||||
|
private val removeAllTabsUseCases: TabsUseCases.RemoveAllTabsUseCase = mockk(relaxed = true)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setUp() {
|
||||||
|
settings = Settings.getInstance(testContext).apply {
|
||||||
|
clear()
|
||||||
|
}
|
||||||
|
|
||||||
|
Dispatchers.setMain(mainThreadSurrogate)
|
||||||
|
|
||||||
|
every { context.components.core.historyStorage } returns historyStorage
|
||||||
|
every { context.components.core.permissionStorage } returns permissionStorage
|
||||||
|
every { context.components.useCases.tabsUseCases } returns tabUseCases
|
||||||
|
every { tabUseCases.removeAllTabs } returns removeAllTabsUseCases
|
||||||
|
every { context.components.core.engine } returns engine
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
fun tearDown() {
|
||||||
|
Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher
|
||||||
|
mainThreadSurrogate.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun Settings.clear() {
|
||||||
|
preferences.clearAndCommit()
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `delete only tabs and quit`() = runBlockingTest {
|
||||||
|
// When
|
||||||
|
settings.deleteTabsOnQuit = true
|
||||||
|
|
||||||
|
context.deleteAndQuit(this)
|
||||||
|
|
||||||
|
verify {
|
||||||
|
removeAllTabsUseCases.invoke()
|
||||||
|
context.asActivity()?.finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
verify(exactly = 0) {
|
||||||
|
historyStorage
|
||||||
|
|
||||||
|
engine.clearData(
|
||||||
|
Engine.BrowsingData.select(
|
||||||
|
Engine.BrowsingData.COOKIES
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
permissionStorage.deleteAllSitePermissions()
|
||||||
|
|
||||||
|
engine.clearData(Engine.BrowsingData.allCaches())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `delete everything and quit`() = runBlockingTest {
|
||||||
|
// When
|
||||||
|
settings.deleteTabsOnQuit = true
|
||||||
|
settings.deletePermissionsOnQuit = true
|
||||||
|
settings.deleteHistoryOnQuit = true
|
||||||
|
settings.deleteCookiesOnQuit = true
|
||||||
|
settings.deleteCacheOnQuit = true
|
||||||
|
|
||||||
|
context.deleteAndQuit(this)
|
||||||
|
|
||||||
|
verify(exactly = 1) {
|
||||||
|
engine.clearData(Engine.BrowsingData.allCaches())
|
||||||
|
|
||||||
|
removeAllTabsUseCases.invoke()
|
||||||
|
|
||||||
|
engine.clearData(
|
||||||
|
Engine.BrowsingData.select(Engine.BrowsingData.ALL_SITE_SETTINGS)
|
||||||
|
)
|
||||||
|
|
||||||
|
permissionStorage.deleteAllSitePermissions()
|
||||||
|
|
||||||
|
engine.clearData(
|
||||||
|
Engine.BrowsingData.select(
|
||||||
|
Engine.BrowsingData.COOKIES,
|
||||||
|
Engine.BrowsingData.AUTH_SESSIONS
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
engine.clearData(Engine.BrowsingData.select(Engine.BrowsingData.DOM_STORAGES))
|
||||||
|
|
||||||
|
historyStorage
|
||||||
|
|
||||||
|
context.asActivity()?.finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue