For #7094 - Adds save login exceptions
parent
69020a1f26
commit
9ed85510ae
|
@ -501,6 +501,7 @@ dependencies {
|
|||
implementation Deps.mozilla_feature_toolbar
|
||||
implementation Deps.mozilla_feature_tabs
|
||||
implementation Deps.mozilla_feature_findinpage
|
||||
implementation Deps.mozilla_feature_logins
|
||||
implementation Deps.mozilla_feature_site_permissions
|
||||
implementation Deps.mozilla_feature_readerview
|
||||
implementation Deps.mozilla_feature_tab_collections
|
||||
|
|
|
@ -170,6 +170,7 @@ class SettingsPrivacyTest {
|
|||
verifyDefaultView()
|
||||
verifyDefaultValueSyncLogins()
|
||||
verifyDefaultValueAutofillLogins()
|
||||
verifyDefaultValueExceptions()
|
||||
}.openSavedLogins {
|
||||
verifySavedLoginsView()
|
||||
tapSetupLater()
|
||||
|
@ -209,13 +210,13 @@ class SettingsPrivacyTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
fun doNotSaveLoginFromPromptTest() {
|
||||
fun neverSaveLoginFromPromptTest() {
|
||||
val saveLoginTest = TestAssetHelper.getSaveLoginAsset(mockWebServer)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(saveLoginTest.url) {
|
||||
verifySaveLoginPromptIsShown()
|
||||
// Don't save the login
|
||||
// Don't save the login, add to exceptions
|
||||
saveLoginFromPrompt("Never save")
|
||||
}.openTabDrawer {
|
||||
}.openHomeScreen {
|
||||
|
@ -228,7 +229,11 @@ class SettingsPrivacyTest {
|
|||
verifySavedLoginsView()
|
||||
tapSetupLater()
|
||||
// Verify that the login list is empty
|
||||
verifyNotSavedLoginFromPromt()
|
||||
verifyNotSavedLoginFromPrompt()
|
||||
}.goBack {
|
||||
}.openLoginExceptions {
|
||||
// Verify localhost was added to exceptions list
|
||||
verifyLocalhostExceptionAdded()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,6 +38,8 @@ class SettingsSubMenuLoginsAndPasswordRobot {
|
|||
mDevice.waitNotNull(Until.findObjects(By.text("On")), TestAssetHelper.waitingTime)
|
||||
}
|
||||
|
||||
fun verifyDefaultValueExceptions() = assertDefaultValueExceptions()
|
||||
|
||||
fun verifyDefaultValueAutofillLogins() = assertDefaultValueAutofillLogins()
|
||||
|
||||
fun verifyDefaultValueSyncLogins() = assertDefaultValueSyncLogins()
|
||||
|
@ -60,6 +62,14 @@ class SettingsSubMenuLoginsAndPasswordRobot {
|
|||
return SettingsSubMenuLoginsAndPasswordsSavedLoginsRobot.Transition()
|
||||
}
|
||||
|
||||
fun openLoginExceptions(interact: SettingsSubMenuLoginsAndPasswordsSavedLoginsRobot.() -> Unit): SettingsSubMenuLoginsAndPasswordsSavedLoginsRobot.Transition {
|
||||
fun loginExceptionsButton() = onView(ViewMatchers.withText("Exceptions"))
|
||||
loginExceptionsButton().click()
|
||||
|
||||
SettingsSubMenuLoginsAndPasswordsSavedLoginsRobot().interact()
|
||||
return SettingsSubMenuLoginsAndPasswordsSavedLoginsRobot.Transition()
|
||||
}
|
||||
|
||||
fun openSyncLogins(interact: SettingsTurnOnSyncRobot.() -> Unit): SettingsTurnOnSyncRobot.Transition {
|
||||
fun syncLoginsButton() = onView(ViewMatchers.withText("Sync logins"))
|
||||
syncLoginsButton().click()
|
||||
|
@ -92,5 +102,8 @@ private fun assertDefaultView() = onView(ViewMatchers.withText("Sync logins"))
|
|||
private fun assertDefaultValueAutofillLogins() = onView(ViewMatchers.withText("Autofill"))
|
||||
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
||||
|
||||
private fun assertDefaultValueExceptions() = onView(ViewMatchers.withText("Exceptions"))
|
||||
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
||||
|
||||
private fun assertDefaultValueSyncLogins() = onView(ViewMatchers.withText("Sign in to Sync"))
|
||||
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
||||
|
|
|
@ -13,6 +13,7 @@ import androidx.test.espresso.matcher.ViewMatchers.withEffectiveVisibility
|
|||
import androidx.test.uiautomator.By
|
||||
import androidx.test.uiautomator.Until
|
||||
import org.hamcrest.CoreMatchers
|
||||
import org.hamcrest.CoreMatchers.containsString
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.helpers.ext.waitNotNull
|
||||
|
||||
|
@ -24,16 +25,23 @@ class SettingsSubMenuLoginsAndPasswordsSavedLoginsRobot {
|
|||
fun verifySavedLoginsView() = assertSavedLoginsView()
|
||||
|
||||
fun verifySavedLoginsAfterSync() {
|
||||
mDevice.waitNotNull(Until.findObjects(By.text("https://accounts.google.com")), TestAssetHelper.waitingTime)
|
||||
mDevice.waitNotNull(
|
||||
Until.findObjects(By.text("https://accounts.google.com")),
|
||||
TestAssetHelper.waitingTime
|
||||
)
|
||||
assertSavedLoginAppears()
|
||||
}
|
||||
|
||||
fun tapSetupLater() = onView(ViewMatchers.withText("Later")).perform(ViewActions.click())
|
||||
|
||||
fun verifySavedLoginFromPrompt() = mDevice.waitNotNull(Until.findObjects(By.text("test@example.com")))
|
||||
fun verifySavedLoginFromPrompt() =
|
||||
mDevice.waitNotNull(Until.findObjects(By.text("test@example.com")))
|
||||
|
||||
fun verifyNotSavedLoginFromPromt() = onView(ViewMatchers.withText("test@example.com"))
|
||||
.check(ViewAssertions.doesNotExist())
|
||||
fun verifyNotSavedLoginFromPrompt() = onView(ViewMatchers.withText("test@example.com"))
|
||||
.check(ViewAssertions.doesNotExist())
|
||||
|
||||
fun verifyLocalhostExceptionAdded() = onView(ViewMatchers.withText(containsString("localhost")))
|
||||
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
||||
|
||||
class Transition {
|
||||
fun goBack(interact: SettingsSubMenuLoginsAndPasswordRobot.() -> Unit): SettingsSubMenuLoginsAndPasswordRobot.Transition {
|
||||
|
@ -46,9 +54,10 @@ class SettingsSubMenuLoginsAndPasswordsSavedLoginsRobot {
|
|||
}
|
||||
|
||||
private fun goBackButton() =
|
||||
onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up")))
|
||||
onView(CoreMatchers.allOf(ViewMatchers.withContentDescription("Navigate up")))
|
||||
|
||||
private fun assertSavedLoginsView() = onView(ViewMatchers.withText("Secure your logins and passwords"))
|
||||
private fun assertSavedLoginsView() =
|
||||
onView(ViewMatchers.withText("Secure your logins and passwords"))
|
||||
.check(matches(withEffectiveVisibility(ViewMatchers.Visibility.VISIBLE)))
|
||||
|
||||
private fun assertSavedLoginAppears() = onView(ViewMatchers.withText("https://accounts.google.com"))
|
||||
|
|
|
@ -21,7 +21,7 @@ enum class BrowserDirection(@IdRes val fragmentId: Int) {
|
|||
FromSyncedTabs(R.id.syncedTabsFragment),
|
||||
FromBookmarks(R.id.bookmarkFragment),
|
||||
FromHistory(R.id.historyFragment),
|
||||
FromExceptions(R.id.exceptionsFragment),
|
||||
FromTrackingProtectionExceptions(R.id.trackingProtectionExceptionsFragment),
|
||||
FromAbout(R.id.aboutFragment),
|
||||
FromTrackingProtection(R.id.trackingProtectionFragment),
|
||||
FromSavedLoginsFragment(R.id.savedLoginsFragment),
|
||||
|
|
|
@ -62,7 +62,7 @@ import org.mozilla.fenix.browser.browsingmode.BrowsingModeManager
|
|||
import org.mozilla.fenix.browser.browsingmode.DefaultBrowsingModeManager
|
||||
import org.mozilla.fenix.components.metrics.BreadcrumbsRecorder
|
||||
import org.mozilla.fenix.components.metrics.Event
|
||||
import org.mozilla.fenix.exceptions.ExceptionsFragmentDirections
|
||||
import org.mozilla.fenix.trackingprotectionexceptions.TrackingProtectionExceptionsFragmentDirections
|
||||
import org.mozilla.fenix.ext.alreadyOnDestination
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.nav
|
||||
|
@ -498,8 +498,8 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
|
|||
BookmarkFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
||||
BrowserDirection.FromHistory ->
|
||||
HistoryFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
||||
BrowserDirection.FromExceptions ->
|
||||
ExceptionsFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
||||
BrowserDirection.FromTrackingProtectionExceptions ->
|
||||
TrackingProtectionExceptionsFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
||||
BrowserDirection.FromAbout ->
|
||||
AboutFragmentDirections.actionGlobalBrowser(customTabSessionId)
|
||||
BrowserDirection.FromTrackingProtection ->
|
||||
|
|
|
@ -443,6 +443,7 @@ abstract class BaseBrowserFragment : Fragment(), UserInteractionHandler, Session
|
|||
isSaveLoginEnabled = {
|
||||
context.settings().shouldPromptToSaveLogins
|
||||
},
|
||||
loginExceptionStorage = context.components.core.loginExceptionStorage,
|
||||
shareDelegate = object : ShareDelegate {
|
||||
override fun showShareSheet(
|
||||
context: Context,
|
||||
|
|
|
@ -30,6 +30,7 @@ import mozilla.components.concept.engine.mediaquery.PreferredColorScheme
|
|||
import mozilla.components.concept.fetch.Client
|
||||
import mozilla.components.feature.customtabs.store.CustomTabsServiceStore
|
||||
import mozilla.components.feature.downloads.DownloadMiddleware
|
||||
import mozilla.components.feature.logins.exceptions.LoginExceptionStorage
|
||||
import mozilla.components.feature.media.RecordingDevicesNotificationFeature
|
||||
import mozilla.components.feature.media.middleware.MediaMiddleware
|
||||
import mozilla.components.feature.pwa.ManifestStorage
|
||||
|
@ -251,6 +252,8 @@ class Core(private val context: Context) {
|
|||
|
||||
val webAppManifestStorage by lazy { ManifestStorage(context) }
|
||||
|
||||
val loginExceptionStorage by lazy { LoginExceptionStorage(context) }
|
||||
|
||||
/**
|
||||
* Shared Preferences that encrypt/decrypt using Android KeyStore and lib-dataprotect for 23+
|
||||
* only on Nightly/Debug for now, otherwise simply stored.
|
||||
|
|
|
@ -0,0 +1,41 @@
|
|||
/* 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.loginexceptions
|
||||
|
||||
import mozilla.components.feature.logins.exceptions.LoginException
|
||||
import mozilla.components.lib.state.Action
|
||||
import mozilla.components.lib.state.State
|
||||
import mozilla.components.lib.state.Store
|
||||
|
||||
/**
|
||||
* The [Store] for holding the [ExceptionsFragmentState] and applying [ExceptionsFragmentAction]s.
|
||||
*/
|
||||
class ExceptionsFragmentStore(initialState: ExceptionsFragmentState) :
|
||||
Store<ExceptionsFragmentState, ExceptionsFragmentAction>(initialState, ::exceptionsStateReducer)
|
||||
|
||||
/**
|
||||
* Actions to dispatch through the `ExceptionsStore` to modify `ExceptionsState` through the reducer.
|
||||
*/
|
||||
sealed class ExceptionsFragmentAction : Action {
|
||||
data class Change(val list: List<LoginException>) : ExceptionsFragmentAction()
|
||||
}
|
||||
|
||||
/**
|
||||
* The state for the Exceptions Screen
|
||||
* @property items List of exceptions to display
|
||||
*/
|
||||
data class ExceptionsFragmentState(val items: List<LoginException>) : State
|
||||
|
||||
/**
|
||||
* The ExceptionsState Reducer.
|
||||
*/
|
||||
private fun exceptionsStateReducer(
|
||||
state: ExceptionsFragmentState,
|
||||
action: ExceptionsFragmentAction
|
||||
): ExceptionsFragmentState {
|
||||
return when (action) {
|
||||
is ExceptionsFragmentAction.Change -> state.copy(items = action.list)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
/* 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.loginexceptions
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import mozilla.components.feature.logins.exceptions.LoginException
|
||||
import org.mozilla.fenix.loginexceptions.viewholders.LoginExceptionsDeleteButtonViewHolder
|
||||
import org.mozilla.fenix.loginexceptions.viewholders.LoginExceptionsHeaderViewHolder
|
||||
import org.mozilla.fenix.loginexceptions.viewholders.LoginExceptionsListItemViewHolder
|
||||
|
||||
sealed class AdapterItem {
|
||||
object DeleteButton : AdapterItem()
|
||||
object Header : AdapterItem()
|
||||
data class Item(val item: LoginException) : AdapterItem()
|
||||
}
|
||||
|
||||
/**
|
||||
* Adapter for a list of sites that are exempted from saving logins,
|
||||
* along with controls to remove the exception.
|
||||
*/
|
||||
class LoginExceptionsAdapter(
|
||||
private val interactor: LoginExceptionsInteractor
|
||||
) : ListAdapter<AdapterItem, RecyclerView.ViewHolder>(DiffCallback) {
|
||||
|
||||
/**
|
||||
* Change the list of items that are displayed.
|
||||
* Header and footer items are added to the list as well.
|
||||
*/
|
||||
fun updateData(exceptions: List<LoginException>) {
|
||||
val adapterItems: List<AdapterItem> =
|
||||
listOf(AdapterItem.Header) + exceptions.map { AdapterItem.Item(it) } + listOf(
|
||||
AdapterItem.DeleteButton
|
||||
)
|
||||
submitList(adapterItems)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int) = when (getItem(position)) {
|
||||
AdapterItem.DeleteButton -> LoginExceptionsDeleteButtonViewHolder.LAYOUT_ID
|
||||
AdapterItem.Header -> LoginExceptionsHeaderViewHolder.LAYOUT_ID
|
||||
is AdapterItem.Item -> LoginExceptionsListItemViewHolder.LAYOUT_ID
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
val view = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
|
||||
|
||||
return when (viewType) {
|
||||
LoginExceptionsDeleteButtonViewHolder.LAYOUT_ID -> LoginExceptionsDeleteButtonViewHolder(
|
||||
view,
|
||||
interactor
|
||||
)
|
||||
LoginExceptionsHeaderViewHolder.LAYOUT_ID -> LoginExceptionsHeaderViewHolder(view)
|
||||
LoginExceptionsListItemViewHolder.LAYOUT_ID -> LoginExceptionsListItemViewHolder(
|
||||
view,
|
||||
interactor
|
||||
)
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (holder is LoginExceptionsListItemViewHolder) {
|
||||
val adapterItem = getItem(position) as AdapterItem.Item
|
||||
holder.bind(adapterItem.item)
|
||||
}
|
||||
}
|
||||
|
||||
private object DiffCallback : DiffUtil.ItemCallback<AdapterItem>() {
|
||||
override fun areItemsTheSame(oldItem: AdapterItem, newItem: AdapterItem) =
|
||||
areContentsTheSame(oldItem, newItem)
|
||||
|
||||
@Suppress("DiffUtilEquals")
|
||||
override fun areContentsTheSame(oldItem: AdapterItem, newItem: AdapterItem) =
|
||||
oldItem == newItem
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/* 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.loginexceptions
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import kotlinx.android.synthetic.main.fragment_exceptions.view.*
|
||||
import kotlinx.coroutines.Dispatchers.IO
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.feature.logins.exceptions.LoginException
|
||||
import mozilla.components.lib.state.ext.consumeFrom
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.StoreProvider
|
||||
import org.mozilla.fenix.ext.requireComponents
|
||||
import org.mozilla.fenix.ext.showToolbar
|
||||
|
||||
/**
|
||||
* Displays a list of sites that are exempted from saving logins,
|
||||
* along with controls to remove the exception.
|
||||
*/
|
||||
class LoginExceptionsFragment : Fragment() {
|
||||
private lateinit var exceptionsStore: ExceptionsFragmentStore
|
||||
private lateinit var exceptionsView: LoginExceptionsView
|
||||
private lateinit var exceptionsInteractor: LoginExceptionsInteractor
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
showToolbar(getString(R.string.preference_exceptions))
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = inflater.inflate(R.layout.fragment_exceptions, container, false)
|
||||
exceptionsStore = StoreProvider.get(this) {
|
||||
ExceptionsFragmentStore(
|
||||
ExceptionsFragmentState(
|
||||
items = listOf()
|
||||
)
|
||||
)
|
||||
}
|
||||
exceptionsInteractor =
|
||||
LoginExceptionsInteractor(::deleteOneItem, ::deleteAllItems)
|
||||
exceptionsView = LoginExceptionsView(view.exceptionsLayout, exceptionsInteractor)
|
||||
subscribeToLoginExceptions()
|
||||
return view
|
||||
}
|
||||
|
||||
private fun subscribeToLoginExceptions(): Observer<List<LoginException>> {
|
||||
return Observer<List<LoginException>> { exceptions ->
|
||||
exceptionsStore.dispatch(ExceptionsFragmentAction.Change(exceptions))
|
||||
}.also { observer ->
|
||||
requireComponents.core.loginExceptionStorage.getLoginExceptions().asLiveData()
|
||||
.observe(viewLifecycleOwner, observer)
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
consumeFrom(exceptionsStore) {
|
||||
exceptionsView.update(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteAllItems() {
|
||||
viewLifecycleOwner.lifecycleScope.launch(IO) {
|
||||
requireComponents.core.loginExceptionStorage.deleteAllLoginExceptions()
|
||||
}
|
||||
}
|
||||
|
||||
private fun deleteOneItem(item: LoginException) {
|
||||
viewLifecycleOwner.lifecycleScope.launch(IO) {
|
||||
requireComponents.core.loginExceptionStorage.removeLoginException(item)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/* 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.loginexceptions
|
||||
|
||||
import mozilla.components.feature.logins.exceptions.LoginException
|
||||
|
||||
/**
|
||||
* Interactor for the exceptions screen
|
||||
* Provides implementations for the ExceptionsViewInteractor
|
||||
*/
|
||||
class LoginExceptionsInteractor(
|
||||
private val deleteOne: (LoginException) -> Unit,
|
||||
private val deleteAll: () -> Unit
|
||||
) : ExceptionsViewInteractor {
|
||||
override fun onDeleteAll() {
|
||||
deleteAll.invoke()
|
||||
}
|
||||
|
||||
override fun onDeleteOne(item: LoginException) {
|
||||
deleteOne.invoke(item)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
/* 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.loginexceptions
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import android.widget.FrameLayout
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.component_exceptions.view.*
|
||||
import mozilla.components.feature.logins.exceptions.LoginException
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
/**
|
||||
* Interface for the ExceptionsViewInteractor. This interface is implemented by objects that want
|
||||
* to respond to user interaction on the ExceptionsView
|
||||
*/
|
||||
interface ExceptionsViewInteractor {
|
||||
/**
|
||||
* Called whenever all exception items are deleted
|
||||
*/
|
||||
fun onDeleteAll()
|
||||
|
||||
/**
|
||||
* Called whenever one exception item is deleted
|
||||
*/
|
||||
fun onDeleteOne(item: LoginException)
|
||||
}
|
||||
|
||||
/**
|
||||
* View that contains and configures the Exceptions List
|
||||
*/
|
||||
class LoginExceptionsView(
|
||||
override val containerView: ViewGroup,
|
||||
val interactor: LoginExceptionsInteractor
|
||||
) : LayoutContainer {
|
||||
|
||||
val view: FrameLayout = LayoutInflater.from(containerView.context)
|
||||
.inflate(R.layout.component_exceptions, containerView, true)
|
||||
.findViewById(R.id.exceptions_wrapper)
|
||||
|
||||
private val exceptionsAdapter = LoginExceptionsAdapter(interactor)
|
||||
|
||||
init {
|
||||
view.exceptions_learn_more.isVisible = false
|
||||
view.exceptions_empty_message.text =
|
||||
view.context.getString(R.string.preferences_passwords_exceptions_description_empty)
|
||||
view.exceptions_list.apply {
|
||||
adapter = exceptionsAdapter
|
||||
layoutManager = LinearLayoutManager(containerView.context)
|
||||
}
|
||||
}
|
||||
|
||||
fun update(state: ExceptionsFragmentState) {
|
||||
view.exceptions_empty_view.isVisible = state.items.isEmpty()
|
||||
view.exceptions_list.isVisible = state.items.isNotEmpty()
|
||||
exceptionsAdapter.updateData(state.items)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
/* 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.loginexceptions.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.delete_exceptions_button.view.*
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.loginexceptions.LoginExceptionsInteractor
|
||||
|
||||
class LoginExceptionsDeleteButtonViewHolder(
|
||||
view: View,
|
||||
private val interactor: LoginExceptionsInteractor
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
private val deleteButton = view.removeAllExceptions
|
||||
|
||||
init {
|
||||
deleteButton.setOnClickListener {
|
||||
interactor.onDeleteAll()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.delete_logins_exceptions_button
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/* 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.loginexceptions.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.exceptions_description.view.*
|
||||
import org.mozilla.fenix.R
|
||||
|
||||
class LoginExceptionsHeaderViewHolder(
|
||||
view: View
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.exceptions_description
|
||||
}
|
||||
|
||||
init {
|
||||
view.exceptions_description.text =
|
||||
view.context.getString(R.string.preferences_passwords_exceptions_description)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/* 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.loginexceptions.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.exception_item.view.*
|
||||
import mozilla.components.feature.logins.exceptions.LoginException
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.loadIntoView
|
||||
import org.mozilla.fenix.loginexceptions.LoginExceptionsInteractor
|
||||
|
||||
/**
|
||||
* View holder for a single website that is exempted from Tracking Protection.
|
||||
*/
|
||||
class LoginExceptionsListItemViewHolder(
|
||||
view: View,
|
||||
private val interactor: LoginExceptionsInteractor
|
||||
) : RecyclerView.ViewHolder(view) {
|
||||
|
||||
private val favicon = view.favicon_image
|
||||
private val url = view.webAddressView
|
||||
private val deleteButton = view.delete_exception
|
||||
|
||||
private var item: LoginException? = null
|
||||
|
||||
init {
|
||||
deleteButton.setOnClickListener {
|
||||
item?.let {
|
||||
interactor.onDeleteOne(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun bind(item: LoginException) {
|
||||
this.item = item
|
||||
url.text = item.origin
|
||||
}
|
||||
|
||||
private fun updateFavIcon(url: String) {
|
||||
favicon.context.components.core.icons.loadIntoView(favicon, url)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val LAYOUT_ID = R.layout.exception_item
|
||||
}
|
||||
}
|
|
@ -25,7 +25,7 @@ import org.mozilla.fenix.utils.view.addToRadioGroup
|
|||
|
||||
/**
|
||||
* Displays the toggle for tracking protection, options for tracking protection policy and a button
|
||||
* to open info about the tracking protection [org.mozilla.fenix.exceptions.ExceptionsFragment].
|
||||
* to open info about the tracking protection [org.mozilla.fenix.settings.TrackingProtectionFragment].
|
||||
*/
|
||||
class TrackingProtectionFragment : PreferenceFragmentCompat() {
|
||||
|
||||
|
@ -56,7 +56,8 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() {
|
|||
showToolbar(getString(R.string.preference_enhanced_tracking_protection))
|
||||
|
||||
// Tracking Protection Switch
|
||||
val preferenceTP = requirePreference<SwitchPreference>(R.string.pref_key_tracking_protection)
|
||||
val preferenceTP =
|
||||
requirePreference<SwitchPreference>(R.string.pref_key_tracking_protection)
|
||||
|
||||
preferenceTP.isChecked = requireContext().settings().shouldUseTrackingProtection
|
||||
preferenceTP.setOnPreferenceChangeListener<Boolean> { preference, trackingProtectionOn ->
|
||||
|
@ -86,7 +87,8 @@ class TrackingProtectionFragment : PreferenceFragmentCompat() {
|
|||
getString(R.string.app_name)
|
||||
)
|
||||
|
||||
val preferenceExceptions = requirePreference<Preference>(R.string.pref_key_tracking_protection_exceptions)
|
||||
val preferenceExceptions =
|
||||
requirePreference<Preference>(R.string.pref_key_tracking_protection_exceptions)
|
||||
preferenceExceptions.onPreferenceClickListener = exceptionsClickListener
|
||||
}
|
||||
|
||||
|
|
|
@ -133,6 +133,13 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|||
}
|
||||
}
|
||||
|
||||
requirePreference<Preference>(R.string.pref_key_login_exceptions).apply {
|
||||
setOnPreferenceClickListener {
|
||||
navigateToLoginExceptionFragment()
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
requirePreference<SwitchPreference>(R.string.pref_key_autofill_logins).apply {
|
||||
isChecked = context.settings().shouldAutofillLogins
|
||||
onPreferenceChangeListener = object : SharedPreferenceUpdater() {
|
||||
|
@ -322,6 +329,12 @@ class SavedLoginsAuthFragment : PreferenceFragmentCompat(), AccountObserver {
|
|||
findNavController().navigate(directions)
|
||||
}
|
||||
|
||||
private fun navigateToLoginExceptionFragment() {
|
||||
val directions =
|
||||
SavedLoginsAuthFragmentDirections.actionSavedLoginsAuthFragmentToLoginExceptionsFragment()
|
||||
findNavController().navigate(directions)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SHORT_DELAY_MS = 100L
|
||||
private const val LOG_TAG = "LoginsFragment"
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* 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.exceptions
|
||||
package org.mozilla.fenix.trackingprotectionexceptions
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
|
@ -10,9 +10,9 @@ import androidx.recyclerview.widget.DiffUtil
|
|||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import mozilla.components.concept.engine.content.blocking.TrackingProtectionException
|
||||
import org.mozilla.fenix.exceptions.viewholders.ExceptionsDeleteButtonViewHolder
|
||||
import org.mozilla.fenix.exceptions.viewholders.ExceptionsHeaderViewHolder
|
||||
import org.mozilla.fenix.exceptions.viewholders.ExceptionsListItemViewHolder
|
||||
import org.mozilla.fenix.trackingprotectionexceptions.viewholders.ExceptionsDeleteButtonViewHolder
|
||||
import org.mozilla.fenix.trackingprotectionexceptions.viewholders.ExceptionsHeaderViewHolder
|
||||
import org.mozilla.fenix.trackingprotectionexceptions.viewholders.ExceptionsListItemViewHolder
|
||||
|
||||
/**
|
||||
* Adapter for a list of sites that are exempted from Tracking Protection,
|
|
@ -2,7 +2,7 @@
|
|||
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.exceptions
|
||||
package org.mozilla.fenix.trackingprotectionexceptions
|
||||
|
||||
import mozilla.components.concept.engine.content.blocking.TrackingProtectionException
|
||||
import mozilla.components.lib.state.Action
|
|
@ -2,7 +2,7 @@
|
|||
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.exceptions
|
||||
package org.mozilla.fenix.trackingprotectionexceptions
|
||||
|
||||
import mozilla.components.concept.engine.content.blocking.TrackingProtectionException
|
||||
|
|
@ -2,7 +2,7 @@
|
|||
* 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.exceptions
|
||||
package org.mozilla.fenix.trackingprotectionexceptions
|
||||
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.text.style.UnderlineSpan
|
||||
|
@ -50,7 +50,10 @@ class ExceptionsView(
|
|||
.inflate(R.layout.component_exceptions, container, true)
|
||||
.findViewById(R.id.exceptions_wrapper)
|
||||
|
||||
private val exceptionsAdapter = ExceptionsAdapter(interactor)
|
||||
private val exceptionsAdapter =
|
||||
ExceptionsAdapter(
|
||||
interactor
|
||||
)
|
||||
|
||||
init {
|
||||
exceptions_list.apply {
|
|
@ -2,7 +2,7 @@
|
|||
* 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.exceptions
|
||||
package org.mozilla.fenix.trackingprotectionexceptions
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
|
@ -27,7 +27,7 @@ import org.mozilla.fenix.settings.SupportUtils
|
|||
* Displays a list of sites that are exempted from Tracking Protection,
|
||||
* along with controls to remove the exception.
|
||||
*/
|
||||
class ExceptionsFragment : Fragment() {
|
||||
class TrackingProtectionExceptionsFragment : Fragment() {
|
||||
|
||||
private lateinit var exceptionsStore: ExceptionsFragmentStore
|
||||
private lateinit var exceptionsView: ExceptionsView
|
||||
|
@ -54,8 +54,16 @@ class ExceptionsFragment : Fragment() {
|
|||
)
|
||||
}
|
||||
exceptionsInteractor =
|
||||
ExceptionsInteractor(::openLearnMore, ::deleteOneItem, ::deleteAllItems)
|
||||
exceptionsView = ExceptionsView(view.exceptionsLayout, exceptionsInteractor)
|
||||
ExceptionsInteractor(
|
||||
::openLearnMore,
|
||||
::deleteOneItem,
|
||||
::deleteAllItems
|
||||
)
|
||||
exceptionsView =
|
||||
ExceptionsView(
|
||||
view.exceptionsLayout,
|
||||
exceptionsInteractor
|
||||
)
|
||||
reloadExceptions()
|
||||
return view
|
||||
}
|
||||
|
@ -83,13 +91,17 @@ class ExceptionsFragment : Fragment() {
|
|||
searchTermOrURL = SupportUtils.getGenericSumoURLForTopic
|
||||
(SupportUtils.SumoTopic.TRACKING_PROTECTION),
|
||||
newTab = true,
|
||||
from = BrowserDirection.FromExceptions
|
||||
from = BrowserDirection.FromTrackingProtectionExceptions
|
||||
)
|
||||
}
|
||||
|
||||
private fun reloadExceptions() {
|
||||
trackingProtectionUseCases.fetchExceptions { resultList ->
|
||||
exceptionsStore.dispatch(ExceptionsFragmentAction.Change(resultList))
|
||||
exceptionsStore.dispatch(
|
||||
ExceptionsFragmentAction.Change(
|
||||
resultList
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,13 +2,13 @@
|
|||
* 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.exceptions.viewholders
|
||||
package org.mozilla.fenix.trackingprotectionexceptions.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.delete_exceptions_button.view.*
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.exceptions.ExceptionsInteractor
|
||||
import org.mozilla.fenix.trackingprotectionexceptions.ExceptionsInteractor
|
||||
|
||||
class ExceptionsDeleteButtonViewHolder(
|
||||
view: View,
|
|
@ -2,7 +2,7 @@
|
|||
* 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.exceptions.viewholders
|
||||
package org.mozilla.fenix.trackingprotectionexceptions.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
|
@ -2,14 +2,14 @@
|
|||
* 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.exceptions.viewholders
|
||||
package org.mozilla.fenix.trackingprotectionexceptions.viewholders
|
||||
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlinx.android.synthetic.main.exception_item.view.*
|
||||
import mozilla.components.concept.engine.content.blocking.TrackingProtectionException
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.exceptions.ExceptionsInteractor
|
||||
import org.mozilla.fenix.trackingprotectionexceptions.ExceptionsInteractor
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.loadIntoView
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<?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/. -->
|
||||
<com.google.android.material.button.MaterialButton xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/removeAllExceptions"
|
||||
style="@style/DestructiveButton"
|
||||
android:layout_marginHorizontal="16dp"
|
||||
android:text="@string/preferences_passwords_exceptions_remove_all" />
|
|
@ -3,10 +3,7 @@
|
|||
- 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/. -->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/exceptionsLayout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context="org.mozilla.fenix.exceptions.ExceptionsFragment">
|
||||
</LinearLayout>
|
||||
android:orientation="vertical"/>
|
||||
|
|
|
@ -27,35 +27,83 @@
|
|||
android:id="@+id/action_global_search"
|
||||
app:destination="@id/searchFragment" />
|
||||
|
||||
<action android:id="@+id/action_global_shareFragment" app:destination="@id/shareFragment" />
|
||||
<action android:id="@+id/action_global_crash_reporter" app:destination="@id/crashReporterFragment" />
|
||||
<action android:id="@+id/action_global_turn_on_sync" app:destination="@id/turnOnSyncFragment" />
|
||||
<action android:id="@+id/action_global_settings_addonsManagementFragment" app:destination="@id/addonsManagementFragment" />
|
||||
<action android:id="@+id/action_global_searchEngineFragment" app:destination="@id/searchEngineFragment" />
|
||||
<action android:id="@+id/action_global_accessibilityFragment" app:destination="@id/accessibilityFragment" />
|
||||
<action android:id="@+id/action_global_deleteBrowsingDataFragment" app:destination="@id/deleteBrowsingDataFragment" />
|
||||
<action android:id="@+id/action_global_webExtensionActionPopupFragment" app:destination="@id/webExtensionActionPopupFragment" />
|
||||
<action android:id="@+id/action_global_settingsFragment" app:destination="@id/settingsFragment" />
|
||||
<action android:id="@+id/action_global_syncedTabsFragment" app:destination="@+id/syncedTabsFragment" />
|
||||
<action android:id="@+id/action_global_privateBrowsingFragment" app:destination="@id/privateBrowsingFragment"/>
|
||||
<action android:id="@+id/action_global_bookmarkFragment" app:destination="@id/bookmarkFragment"/>
|
||||
<action android:id="@+id/action_global_historyFragment" app:destination="@id/historyFragment"/>
|
||||
<action android:id="@+id/action_global_accountProblemFragment" app:destination="@id/accountProblemFragment"/>
|
||||
<action android:id="@+id/action_global_SitePermissionsManagePhoneFeature" app:destination="@id/SitePermissionsManagePhoneFeature" />
|
||||
<action android:id="@+id/action_global_collectionCreationFragment" app:destination="@id/collectionCreationFragment" />
|
||||
<action android:id="@+id/action_global_bookmarkEditFragment" app:destination="@id/bookmarkEditFragment" />
|
||||
<action android:id="@+id/action_global_addonsManagementFragment" app:destination="@id/addonsManagementFragment" />
|
||||
<action android:id="@+id/action_global_trackingProtectionFragment" app:destination="@id/trackingProtectionFragment" />
|
||||
<action android:id="@+id/action_global_exceptionsFragment" app:destination="@id/exceptionsFragment" />
|
||||
<action android:id="@+id/action_global_accountSettingsFragment" app:destination="@id/accountSettingsFragment" />
|
||||
<action android:id="@+id/action_global_trackingProtectionPanelDialogFragment" app:destination="@id/trackingProtectionPanelDialogFragment" />
|
||||
<action android:id="@+id/action_global_quickSettingsSheetDialogFragment" app:destination="@id/quickSettingsSheetDialogFragment"/>
|
||||
<action android:id="@+id/action_global_tabTrayDialogFragment" app:destination="@id/tabTrayDialogFragment"/>
|
||||
<action
|
||||
android:id="@+id/action_global_shareFragment"
|
||||
app:destination="@id/shareFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_crash_reporter"
|
||||
app:destination="@id/crashReporterFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_turn_on_sync"
|
||||
app:destination="@id/turnOnSyncFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_settings_addonsManagementFragment"
|
||||
app:destination="@id/addonsManagementFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_searchEngineFragment"
|
||||
app:destination="@id/searchEngineFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_accessibilityFragment"
|
||||
app:destination="@id/accessibilityFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_deleteBrowsingDataFragment"
|
||||
app:destination="@id/deleteBrowsingDataFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_webExtensionActionPopupFragment"
|
||||
app:destination="@id/webExtensionActionPopupFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_settingsFragment"
|
||||
app:destination="@id/settingsFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_syncedTabsFragment"
|
||||
app:destination="@+id/syncedTabsFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_privateBrowsingFragment"
|
||||
app:destination="@id/privateBrowsingFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_bookmarkFragment"
|
||||
app:destination="@id/bookmarkFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_historyFragment"
|
||||
app:destination="@id/historyFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_accountProblemFragment"
|
||||
app:destination="@id/accountProblemFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_SitePermissionsManagePhoneFeature"
|
||||
app:destination="@id/SitePermissionsManagePhoneFeature" />
|
||||
<action
|
||||
android:id="@+id/action_global_collectionCreationFragment"
|
||||
app:destination="@id/collectionCreationFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_bookmarkEditFragment"
|
||||
app:destination="@id/bookmarkEditFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_addonsManagementFragment"
|
||||
app:destination="@id/addonsManagementFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_trackingProtectionFragment"
|
||||
app:destination="@id/trackingProtectionFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_trackingProtectionExceptionsFragment"
|
||||
app:destination="@id/trackingProtectionExceptionsFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_accountSettingsFragment"
|
||||
app:destination="@id/accountSettingsFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_trackingProtectionPanelDialogFragment"
|
||||
app:destination="@id/trackingProtectionPanelDialogFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_quickSettingsSheetDialogFragment"
|
||||
app:destination="@id/quickSettingsSheetDialogFragment" />
|
||||
<action
|
||||
android:id="@+id/action_global_tabTrayDialogFragment"
|
||||
app:destination="@id/tabTrayDialogFragment" />
|
||||
|
||||
<dialog
|
||||
android:id="@+id/tabTrayDialogFragment"
|
||||
android:name="org.mozilla.fenix.tabtray.TabTrayDialogFragment"
|
||||
tools:layout="@layout/fragment_tab_tray_dialog"/>
|
||||
tools:layout="@layout/fragment_tab_tray_dialog" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/homeFragment"
|
||||
|
@ -73,9 +121,9 @@
|
|||
app:argType="boolean" />
|
||||
<argument
|
||||
android:name="session_to_delete"
|
||||
android:defaultValue="@null"
|
||||
app:argType="string"
|
||||
app:nullable="true"
|
||||
android:defaultValue="@null" />
|
||||
app:nullable="true" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
@ -93,8 +141,8 @@
|
|||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="search_access_point"
|
||||
app:argType="org.mozilla.fenix.components.metrics.Event$PerformedSearch$SearchAccessPoint"
|
||||
android:defaultValue="NONE" />
|
||||
android:defaultValue="NONE"
|
||||
app:argType="org.mozilla.fenix.components.metrics.Event$PerformedSearch$SearchAccessPoint" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
@ -149,17 +197,17 @@
|
|||
tools:layout="@layout/fragment_browser">
|
||||
<action
|
||||
android:id="@+id/action_browserFragment_to_searchFragment"
|
||||
app:destination="@id/searchFragment"
|
||||
app:enterAnim="@anim/fade_in_up"
|
||||
app:popExitAnim="@anim/fade_out_down"
|
||||
app:destination="@id/searchFragment" />
|
||||
app:popExitAnim="@anim/fade_out_down" />
|
||||
<argument
|
||||
android:name="activeSessionId"
|
||||
app:argType="string"
|
||||
app:nullable="true" />
|
||||
<argument
|
||||
android:name="shouldAnimate"
|
||||
app:argType="boolean"
|
||||
android:defaultValue="false" />
|
||||
android:defaultValue="false"
|
||||
app:argType="boolean" />
|
||||
<action
|
||||
android:id="@+id/action_browserFragment_to_syncedTabsFragment"
|
||||
app:destination="@id/syncedTabsFragment" />
|
||||
|
@ -234,8 +282,8 @@
|
|||
app:destination="@id/bookmarkSelectFolderFragment" />
|
||||
<argument
|
||||
android:name="requiresSnackbarPaddingForToolbar"
|
||||
app:argType="boolean"
|
||||
android:defaultValue="false" />
|
||||
android:defaultValue="false"
|
||||
app:argType="boolean" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
@ -274,25 +322,32 @@
|
|||
android:label="@string/preferences_passwords_logins_and_passwords">
|
||||
<action
|
||||
android:id="@+id/action_savedLoginsAuthFragment_to_loginsListFragment"
|
||||
app:destination="@id/savedLoginsFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/savedLoginsFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_savedLoginsAuthFragment_to_turnOnSyncFragment"
|
||||
app:destination="@id/turnOnSyncFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/turnOnSyncFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_savedLoginsAuthFragment_to_savedLoginsSettingFragment"
|
||||
app:destination="@id/saveLoginSettingFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/saveLoginSettingFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_savedLoginsAuthFragment_to_loginExceptionsFragment"
|
||||
app:destination="@id/loginExceptionsFragment"
|
||||
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
|
||||
|
@ -313,8 +368,13 @@
|
|||
android:id="@+id/syncedTabsFragment"
|
||||
android:name="org.mozilla.fenix.sync.SyncedTabsFragment"
|
||||
android:label="@string/synced_tabs"
|
||||
tools:layout="@layout/fragment_synced_tabs">
|
||||
</fragment>
|
||||
tools:layout="@layout/fragment_synced_tabs"/>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/loginExceptionsFragment"
|
||||
android:name="org.mozilla.fenix.loginexceptions.LoginExceptionsFragment"
|
||||
android:label="@string/preferences_passwords_exceptions"
|
||||
tools:layout="@layout/fragment_exceptions"/>
|
||||
|
||||
<fragment
|
||||
android:id="@+id/loginDetailFragment"
|
||||
|
@ -323,12 +383,12 @@
|
|||
<argument
|
||||
android:name="savedLoginId"
|
||||
app:argType="string"
|
||||
app:nullable="false"/>
|
||||
app:nullable="false" />
|
||||
<action
|
||||
android:id="@+id/action_loginDetailFragment_to_editLoginFragment"
|
||||
app:destination="@id/editLoginFragment"
|
||||
app:popUpTo="@id/editLoginFragment"
|
||||
app:popUpToInclusive="true"/>
|
||||
app:popUpToInclusive="true" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
@ -338,12 +398,12 @@
|
|||
<argument
|
||||
android:name="savedLoginItem"
|
||||
app:argType="org.mozilla.fenix.settings.logins.SavedLogin"
|
||||
app:nullable="false"/>
|
||||
app:nullable="false" />
|
||||
<action
|
||||
android:id="@+id/action_editLoginFragment_to_loginDetailFragment"
|
||||
app:destination="@id/loginDetailFragment"
|
||||
app:popUpTo="@id/loginDetailFragment"
|
||||
app:popUpToInclusive="true"/>
|
||||
app:popUpToInclusive="true" />
|
||||
</fragment>
|
||||
|
||||
<fragment
|
||||
|
@ -352,124 +412,124 @@
|
|||
android:label="@string/settings_title">
|
||||
<action
|
||||
android:id="@+id/action_settingsFragment_to_dataChoicesFragment"
|
||||
app:destination="@id/dataChoicesFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/dataChoicesFragment"
|
||||
app:popUpTo="@+id/settingsFragment" />
|
||||
<action
|
||||
android:id="@+id/action_settingsFragment_to_sitePermissionsFragment"
|
||||
app:destination="@id/sitePermissionsFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/sitePermissionsFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_settingsFragment_to_savedLoginsAuthFragment"
|
||||
app:destination="@id/savedLoginsAuthFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/savedLoginsAuthFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_settingsFragment_to_accessibilityFragment"
|
||||
app:destination="@id/accessibilityFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/accessibilityFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_settingsFragment_to_accountSettingsFragment"
|
||||
app:destination="@id/accountSettingsFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/accountSettingsFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_settingsFragment_to_searchEngineFragment"
|
||||
app:destination="@id/searchEngineFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/searchEngineFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_settingsFragment_to_turnOnSyncFragment"
|
||||
app:destination="@id/turnOnSyncFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/turnOnSyncFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_settingsFragment_to_aboutFragment"
|
||||
app:destination="@id/aboutFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/aboutFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_settingsFragment_to_secretSettingsFragment"
|
||||
app:destination="@id/secretSettingsPreference"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/secretSettingsPreference" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_settingsFragment_to_customizationFragment"
|
||||
app:destination="@id/customizationFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/customizationFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_settingsFragment_to_privateBrowsingFragment"
|
||||
app:destination="@id/privateBrowsingFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/privateBrowsingFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_settingsFragment_to_trackingProtectionFragment"
|
||||
app:destination="@id/trackingProtectionFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/trackingProtectionFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_settingsFragment_to_deleteBrowsingDataFragment"
|
||||
app:destination="@id/deleteBrowsingDataFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/deleteBrowsingDataFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_settingsFragment_to_accountProblemFragment"
|
||||
app:destination="@id/accountProblemFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/accountProblemFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_settingsFragment_to_deleteBrowsingDataOnQuitFragment"
|
||||
app:destination="@id/deleteBrowsingDataOnQuitFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/deleteBrowsingDataOnQuitFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_settingsFragment_to_localeSettingsFragment"
|
||||
app:destination="@id/localeSettingsFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/localeSettingsFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_settingsFragment_to_addonsFragment"
|
||||
app:destination="@id/addonsManagementFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/addonsManagementFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/dataChoicesFragment"
|
||||
|
@ -481,19 +541,19 @@
|
|||
android:label="@string/preferences_site_permissions">
|
||||
<action
|
||||
android:id="@+id/action_site_permissions_to_manage_phone_features"
|
||||
app:destination="@id/SitePermissionsManagePhoneFeature"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/SitePermissionsManagePhoneFeature"
|
||||
app:popUpTo="@id/sitePermissionsFragment" />
|
||||
<action
|
||||
android:id="@+id/action_site_permissions_to_exceptions"
|
||||
app:destination="@id/sitePermissionsExceptionsFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/sitePermissionsExceptionsFragment"
|
||||
app:popUpTo="@id/sitePermissionsFragment" />
|
||||
</fragment>
|
||||
|
||||
|
@ -541,7 +601,7 @@
|
|||
|
||||
<fragment
|
||||
android:id="@+id/aboutFragment"
|
||||
android:name="org.mozilla.fenix.settings.about.AboutFragment"/>
|
||||
android:name="org.mozilla.fenix.settings.about.AboutFragment" />
|
||||
<fragment
|
||||
android:id="@+id/secretSettingsPreference"
|
||||
android:name="org.mozilla.fenix.settings.SecretSettingsFragment"
|
||||
|
@ -566,27 +626,27 @@
|
|||
android:name="org.mozilla.fenix.settings.TrackingProtectionFragment">
|
||||
<action
|
||||
android:id="@+id/action_trackingProtectionFragment_to_exceptionsFragment"
|
||||
app:destination="@id/trackingProtectionExceptionsFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/exceptionsFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
<action
|
||||
android:id="@+id/action_trackingProtectionFragment_to_trackingProtectionBlockingFragment"
|
||||
app:destination="@id/trackingProtectionBlockingFragment"
|
||||
app:enterAnim="@anim/slide_in_right"
|
||||
app:exitAnim="@anim/slide_out_left"
|
||||
app:popEnterAnim="@anim/slide_in_left"
|
||||
app:popExitAnim="@anim/slide_out_right"
|
||||
app:destination="@id/trackingProtectionBlockingFragment" />
|
||||
app:popExitAnim="@anim/slide_out_right" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/deleteBrowsingDataFragment"
|
||||
android:name="org.mozilla.fenix.settings.deletebrowsingdata.DeleteBrowsingDataFragment"
|
||||
android:label="@string/preferences_delete_browsing_data" />
|
||||
<fragment
|
||||
android:id="@+id/exceptionsFragment"
|
||||
android:name="org.mozilla.fenix.exceptions.ExceptionsFragment"
|
||||
android:label="@string/preference_exceptions"/>
|
||||
android:id="@+id/trackingProtectionExceptionsFragment"
|
||||
android:name="org.mozilla.fenix.trackingprotectionexceptions.TrackingProtectionExceptionsFragment"
|
||||
android:label="@string/preference_exceptions" />
|
||||
<dialog
|
||||
android:id="@+id/collectionCreationFragment"
|
||||
android:name="org.mozilla.fenix.collections.CollectionCreationFragment"
|
||||
|
@ -795,6 +855,6 @@
|
|||
<argument
|
||||
android:name="webExtensionTitle"
|
||||
app:argType="string"
|
||||
app:nullable="true"/>
|
||||
app:nullable="true" />
|
||||
</fragment>
|
||||
</navigation>
|
||||
|
|
|
@ -185,4 +185,6 @@
|
|||
<string name="pref_key_search_widget_cfr_manually_dismissed" translatable="false">pref_key_search_widget_cfr_manually_dismissed</string>
|
||||
<string name="pref_key_is_in_search_widget_experiment" translatable="false">pref_key_is_in_search_widget_experiment</string>
|
||||
<string name="pref_key_show_search_widget_cfr" translatable="false">pref_key_show_search_widget_cfr</string>
|
||||
|
||||
<string name="pref_key_login_exceptions" translatable="false">pref_key_login_exceptions</string>
|
||||
</resources>
|
||||
|
|
|
@ -1217,6 +1217,8 @@
|
|||
<string name="preferences_passwords_exceptions_description_empty">Logins and passwords that are not saved will be shown here.</string>
|
||||
<!-- Description of list of login exceptions that we never save logins for -->
|
||||
<string name="preferences_passwords_exceptions_description">Logins and passwords will not be saved for these sites.</string>
|
||||
<!-- Text on button to remove all saved login exceptions -->
|
||||
<string name="preferences_passwords_exceptions_remove_all">Delete all exceptions</string>
|
||||
<!-- Hint for search box in logins list -->
|
||||
<string name="preferences_passwords_saved_logins_search">Search logins</string>
|
||||
<!-- Option to sort logins list A-Z, alphabetically -->
|
||||
|
|
|
@ -2,7 +2,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/. -->
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<Preference
|
||||
android:key="@string/pref_key_save_logins_settings"
|
||||
android:summary="@string/preferences_passwords_save_logins_ask_to_save"
|
||||
|
@ -18,4 +19,9 @@
|
|||
<Preference
|
||||
android:key="@string/pref_key_saved_logins"
|
||||
android:title="@string/preferences_passwords_saved_logins" />
|
||||
<androidx.preference.Preference
|
||||
android:key="@string/pref_key_login_exceptions"
|
||||
android:title="@string/preferences_passwords_exceptions"
|
||||
app:icon="@drawable/ic_internet" />
|
||||
</PreferenceScreen>
|
||||
|
||||
|
|
|
@ -2,19 +2,21 @@
|
|||
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.exceptions
|
||||
package org.mozilla.fenix.loginexceptions
|
||||
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import mozilla.components.feature.logins.exceptions.LoginException
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotSame
|
||||
import org.junit.Test
|
||||
|
||||
class ExceptionsFragmentStoreTest {
|
||||
class LoginExceptionFragmentStoreTest {
|
||||
@Test
|
||||
fun onChange() = runBlocking {
|
||||
val initialState = emptyDefaultState()
|
||||
val store = ExceptionsFragmentStore(initialState)
|
||||
val newExceptionsItem = ExceptionItem("URL")
|
||||
val newExceptionsItem: LoginException = mockk()
|
||||
|
||||
store.dispatch(ExceptionsFragmentAction.Change(listOf(newExceptionsItem))).join()
|
||||
assertNotSame(initialState, store.state)
|
|
@ -0,0 +1,36 @@
|
|||
/* 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.loginexceptions
|
||||
|
||||
import io.mockk.mockk
|
||||
import mozilla.components.feature.logins.exceptions.LoginException
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class LoginExceptionsInteractorTest {
|
||||
|
||||
@Test
|
||||
fun onDeleteAll() {
|
||||
var onDeleteAll = false
|
||||
val interactor = LoginExceptionsInteractor(
|
||||
mockk(),
|
||||
{ onDeleteAll = true }
|
||||
)
|
||||
interactor.onDeleteAll()
|
||||
assertEquals(true, onDeleteAll)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onDeleteOne() {
|
||||
var exceptionsItemReceived: LoginException? = null
|
||||
val exceptionsItem: LoginException = mockk()
|
||||
val interactor = LoginExceptionsInteractor(
|
||||
{ exceptionsItemReceived = exceptionsItem },
|
||||
mockk()
|
||||
)
|
||||
interactor.onDeleteOne(exceptionsItem)
|
||||
assertEquals(exceptionsItemReceived, exceptionsItem)
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
* 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.exceptions
|
||||
package org.mozilla.fenix.trackingprotectionexceptions
|
||||
|
||||
import io.mockk.mockk
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
|
@ -11,10 +11,10 @@ import org.junit.Assert.assertEquals
|
|||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.exceptions.viewholders.ExceptionsDeleteButtonViewHolder
|
||||
import org.mozilla.fenix.exceptions.viewholders.ExceptionsHeaderViewHolder
|
||||
import org.mozilla.fenix.exceptions.viewholders.ExceptionsListItemViewHolder
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
import org.mozilla.fenix.trackingprotectionexceptions.viewholders.ExceptionsDeleteButtonViewHolder
|
||||
import org.mozilla.fenix.trackingprotectionexceptions.viewholders.ExceptionsHeaderViewHolder
|
||||
import org.mozilla.fenix.trackingprotectionexceptions.viewholders.ExceptionsListItemViewHolder
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
|
@ -2,7 +2,7 @@
|
|||
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.exceptions
|
||||
package org.mozilla.fenix.trackingprotectionexceptions
|
||||
|
||||
import io.mockk.mockk
|
||||
import mozilla.components.concept.engine.content.blocking.TrackingProtectionException
|
||||
|
@ -14,11 +14,12 @@ class ExceptionsInteractorTest {
|
|||
@Test
|
||||
fun onLearnMore() {
|
||||
var learnMoreClicked = false
|
||||
val interactor = ExceptionsInteractor(
|
||||
{ learnMoreClicked = true },
|
||||
mockk(),
|
||||
mockk()
|
||||
)
|
||||
val interactor =
|
||||
ExceptionsInteractor(
|
||||
{ learnMoreClicked = true },
|
||||
mockk(),
|
||||
mockk()
|
||||
)
|
||||
interactor.onLearnMore()
|
||||
assertEquals(true, learnMoreClicked)
|
||||
}
|
||||
|
@ -26,11 +27,12 @@ class ExceptionsInteractorTest {
|
|||
@Test
|
||||
fun onDeleteAll() {
|
||||
var onDeleteAll = false
|
||||
val interactor = ExceptionsInteractor(
|
||||
mockk(),
|
||||
mockk(),
|
||||
{ onDeleteAll = true }
|
||||
)
|
||||
val interactor =
|
||||
ExceptionsInteractor(
|
||||
mockk(),
|
||||
mockk(),
|
||||
{ onDeleteAll = true }
|
||||
)
|
||||
interactor.onDeleteAll()
|
||||
assertEquals(true, onDeleteAll)
|
||||
}
|
||||
|
@ -38,12 +40,14 @@ class ExceptionsInteractorTest {
|
|||
@Test
|
||||
fun onDeleteOne() {
|
||||
var exceptionsItemReceived: TrackingProtectionException? = null
|
||||
val exceptionsItem = ExceptionItem("url")
|
||||
val interactor = ExceptionsInteractor(
|
||||
mockk(),
|
||||
{ exceptionsItemReceived = exceptionsItem },
|
||||
mockk()
|
||||
)
|
||||
val exceptionsItem =
|
||||
ExceptionItem("url")
|
||||
val interactor =
|
||||
ExceptionsInteractor(
|
||||
mockk(),
|
||||
{ exceptionsItemReceived = exceptionsItem },
|
||||
mockk()
|
||||
)
|
||||
interactor.onDeleteOne(exceptionsItem)
|
||||
assertEquals(exceptionsItemReceived, exceptionsItem)
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
* 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.exceptions
|
||||
package org.mozilla.fenix.trackingprotectionexceptions
|
||||
|
||||
import android.text.Spannable
|
||||
import android.text.method.LinkMovementMethod
|
|
@ -0,0 +1,35 @@
|
|||
/* 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.trackingprotectionexceptions
|
||||
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertNotSame
|
||||
import org.junit.Test
|
||||
|
||||
class TrackingProtectionExceptionsFragmentStoreTest {
|
||||
@Test
|
||||
fun onChange() = runBlocking {
|
||||
val initialState = emptyDefaultState()
|
||||
val store =
|
||||
ExceptionsFragmentStore(
|
||||
initialState
|
||||
)
|
||||
val newExceptionsItem =
|
||||
ExceptionItem("URL")
|
||||
|
||||
store.dispatch(ExceptionsFragmentAction.Change(listOf(newExceptionsItem))).join()
|
||||
assertNotSame(initialState, store.state)
|
||||
assertEquals(
|
||||
store.state.items,
|
||||
listOf(newExceptionsItem)
|
||||
)
|
||||
}
|
||||
|
||||
private fun emptyDefaultState(): ExceptionsFragmentState =
|
||||
ExceptionsFragmentState(
|
||||
items = listOf()
|
||||
)
|
||||
}
|
|
@ -2,7 +2,7 @@
|
|||
* 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.exceptions.viewholders
|
||||
package org.mozilla.fenix.trackingprotectionexceptions.viewholders
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
|
@ -15,7 +15,7 @@ import org.junit.Before
|
|||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.exceptions.ExceptionsInteractor
|
||||
import org.mozilla.fenix.trackingprotectionexceptions.ExceptionsInteractor
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
|
@ -2,7 +2,7 @@
|
|||
* 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.exceptions.viewholders
|
||||
package org.mozilla.fenix.trackingprotectionexceptions.viewholders
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
|
@ -15,7 +15,7 @@ import org.junit.Assert.assertEquals
|
|||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.exceptions.ExceptionsInteractor
|
||||
import org.mozilla.fenix.trackingprotectionexceptions.ExceptionsInteractor
|
||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||
|
||||
@RunWith(FenixRobolectricTestRunner::class)
|
|
@ -106,6 +106,7 @@ object Deps {
|
|||
const val mozilla_feature_pwa = "org.mozilla.components:feature-pwa:${Versions.mozilla_android_components}"
|
||||
const val mozilla_feature_toolbar = "org.mozilla.components:feature-toolbar:${Versions.mozilla_android_components}"
|
||||
const val mozilla_feature_findinpage = "org.mozilla.components:feature-findinpage:${Versions.mozilla_android_components}"
|
||||
const val mozilla_feature_logins = "org.mozilla.components:feature-logins:${Versions.mozilla_android_components}"
|
||||
const val mozilla_feature_site_permissions = "org.mozilla.components:feature-sitepermissions:${Versions.mozilla_android_components}"
|
||||
const val mozilla_feature_readerview = "org.mozilla.components:feature-readerview:${Versions.mozilla_android_components}"
|
||||
const val mozilla_feature_tab_collections = "org.mozilla.components:feature-tab-collections:${Versions.mozilla_android_components}"
|
||||
|
|
Loading…
Reference in New Issue