For #8589 - Fix for accessibility navigation in ETP panel
parent
824022c28d
commit
d1005dd236
|
@ -73,7 +73,8 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), UserInt
|
|||
args.url,
|
||||
args.trackingProtectionEnabled,
|
||||
listTrackers = listOf(),
|
||||
mode = TrackingProtectionState.Mode.Normal
|
||||
mode = TrackingProtectionState.Mode.Normal,
|
||||
lastAccessedCategory = ""
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -7,11 +7,17 @@ package org.mozilla.fenix.trackingprotection
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.net.toUri
|
||||
import androidx.core.view.AccessibilityDelegateCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
|
||||
import androidx.core.view.isGone
|
||||
import androidx.core.view.isVisible
|
||||
import kotlinx.android.extensions.LayoutContainer
|
||||
import kotlinx.android.synthetic.main.component_tracking_protection_panel.*
|
||||
import kotlinx.android.synthetic.main.component_tracking_protection_panel.details_blocking_header
|
||||
import kotlinx.android.synthetic.main.switch_with_description.view.*
|
||||
import mozilla.components.support.ktx.android.net.hostWithoutCommonPrefixes
|
||||
import org.mozilla.fenix.R
|
||||
|
@ -55,6 +61,7 @@ interface TrackingProtectionPanelViewInteractor {
|
|||
/**
|
||||
* View that contains and configures the Tracking Protection Panel
|
||||
*/
|
||||
@SuppressWarnings("TooManyFunctions")
|
||||
class TrackingProtectionPanelView(
|
||||
override val containerView: ViewGroup,
|
||||
val interactor: TrackingProtectionPanelInteractor
|
||||
|
@ -68,6 +75,8 @@ class TrackingProtectionPanelView(
|
|||
|
||||
private var bucketedTrackers = TrackerBuckets()
|
||||
|
||||
private var shouldFocusAccessibilityView: Boolean = true
|
||||
|
||||
fun update(state: TrackingProtectionState) {
|
||||
if (state.mode != mode) {
|
||||
mode = state.mode
|
||||
|
@ -82,6 +91,8 @@ class TrackingProtectionPanelView(
|
|||
mode.categoryBlocked
|
||||
)
|
||||
}
|
||||
|
||||
setAccessibilityViewHierarchy(details_back, category_title)
|
||||
}
|
||||
|
||||
private fun setUIForNormalMode(state: TrackingProtectionState) {
|
||||
|
@ -99,6 +110,40 @@ class TrackingProtectionPanelView(
|
|||
blocking_header.isGone = bucketedTrackers.blockedIsEmpty()
|
||||
updateCategoryVisibility()
|
||||
setCategoryClickListeners()
|
||||
focusAccessibilityLastUsedCategory(state.lastAccessedCategory)
|
||||
}
|
||||
|
||||
/**
|
||||
* Will force accessibility focus to last entered details category.
|
||||
* Called when user returns from details_mode.
|
||||
* */
|
||||
private fun focusAccessibilityLastUsedCategory(categoryTitle: String) {
|
||||
if (categoryTitle.isNotEmpty()) {
|
||||
val viewToFocus = getLastUsedCategoryView(categoryTitle)
|
||||
if (viewToFocus != null && viewToFocus.isVisible && shouldFocusAccessibilityView) {
|
||||
viewToFocus.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
|
||||
shouldFocusAccessibilityView = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLastUsedCategoryView(categoryTitle: String) = when (categoryTitle) {
|
||||
CROSS_SITE_TRACKING_COOKIES.name -> {
|
||||
cross_site_tracking
|
||||
}
|
||||
SOCIAL_MEDIA_TRACKERS.name -> {
|
||||
if (social_media_trackers.isGone) social_media_trackers_loaded else social_media_trackers
|
||||
}
|
||||
FINGERPRINTERS.name -> {
|
||||
if (fingerprinters.isGone) fingerprinters_loaded else fingerprinters
|
||||
}
|
||||
TRACKING_CONTENT.name -> {
|
||||
if (tracking_content.isGone) tracking_content_loaded else tracking_content
|
||||
}
|
||||
CRYPTOMINERS.name -> {
|
||||
if (cryptominers.isGone) cryptominers_loaded else cryptominers
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
|
||||
private fun updateCategoryVisibility() {
|
||||
|
@ -131,6 +176,7 @@ class TrackingProtectionPanelView(
|
|||
override fun onClick(v: View) {
|
||||
val category = getCategory(v) ?: return
|
||||
v.context.metrics.track(Event.TrackingProtectionTrackerList)
|
||||
shouldFocusAccessibilityView = true
|
||||
interactor.openDetails(category, categoryBlocked = !isLoaded(v))
|
||||
}
|
||||
|
||||
|
@ -155,6 +201,9 @@ class TrackingProtectionPanelView(
|
|||
details_back.setOnClickListener {
|
||||
interactor.onBackPressed()
|
||||
}
|
||||
|
||||
details_back.requestFocus()
|
||||
details_back.sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED)
|
||||
}
|
||||
|
||||
private fun bindUrl(url: String) {
|
||||
|
@ -183,6 +232,21 @@ class TrackingProtectionPanelView(
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes sure [view1] is followed by [view2] when navigating in accessibility mode.
|
||||
* */
|
||||
private fun setAccessibilityViewHierarchy(view1: View, view2: View) {
|
||||
ViewCompat.setAccessibilityDelegate(view2, object : AccessibilityDelegateCompat() {
|
||||
override fun onInitializeAccessibilityNodeInfo(
|
||||
host: View?,
|
||||
info: AccessibilityNodeInfoCompat?
|
||||
) {
|
||||
info?.setTraversalAfter(view1)
|
||||
super.onInitializeAccessibilityNodeInfo(host, info)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
|
|
|
@ -50,13 +50,16 @@ sealed class TrackingProtectionAction : Action {
|
|||
* @property isTrackingProtectionEnabled Current status of tracking protection for this session (ie is an exception)
|
||||
* @property listTrackers Current Tracker Log list of blocked and loaded tracker categories
|
||||
* @property mode Current Mode of TrackingProtection
|
||||
* @property lastAccessedCategory Remembers the last accessed details category, used to move
|
||||
* accessibly focus after returning from details_moode
|
||||
*/
|
||||
data class TrackingProtectionState(
|
||||
val session: Session?,
|
||||
val url: String,
|
||||
val isTrackingProtectionEnabled: Boolean,
|
||||
val listTrackers: List<TrackerLog>,
|
||||
val mode: Mode
|
||||
val mode: Mode,
|
||||
val lastAccessedCategory: String
|
||||
) : State {
|
||||
sealed class Mode {
|
||||
object Normal : Mode()
|
||||
|
@ -112,7 +115,8 @@ fun trackingProtectionStateReducer(
|
|||
mode = TrackingProtectionState.Mode.Details(
|
||||
action.category,
|
||||
action.categoryBlocked
|
||||
)
|
||||
),
|
||||
lastAccessedCategory = action.category.name
|
||||
)
|
||||
is TrackingProtectionAction.TrackerBlockingChanged ->
|
||||
state.copy(isTrackingProtectionEnabled = action.isTrackingProtectionEnabled)
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
<TextView
|
||||
android:id="@+id/blocking_header"
|
||||
style="@style/QuickSettingsText"
|
||||
android:accessibilityHeading="true"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/tracking_protection_item_height"
|
||||
android:text="@string/enhanced_tracking_protection_blocked"
|
||||
|
@ -50,7 +51,8 @@
|
|||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/trackingProtectionSwitch" />
|
||||
app:layout_constraintTop_toBottomOf="@id/trackingProtectionSwitch"
|
||||
tools:targetApi="p" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/cross_site_tracking"
|
||||
|
@ -106,12 +108,14 @@
|
|||
android:id="@+id/not_blocking_header"
|
||||
style="@style/QuickSettingsText"
|
||||
android:layout_width="wrap_content"
|
||||
android:accessibilityHeading="true"
|
||||
android:layout_height="@dimen/tracking_protection_item_height"
|
||||
android:text="@string/enhanced_tracking_protection_allowed"
|
||||
android:textStyle="bold"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/tracking_content" />
|
||||
app:layout_constraintTop_toBottomOf="@id/tracking_content"
|
||||
tools:targetApi="p" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/fingerprinters_loaded"
|
||||
|
@ -227,6 +231,7 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/details_blocking_header"
|
||||
android:accessibilityHeading="true"
|
||||
style="@style/QuickSettingsText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="@dimen/tracking_protection_item_height"
|
||||
|
@ -237,7 +242,8 @@
|
|||
android:text="@string/enhanced_tracking_protection_blocked"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/line_divider_details" />
|
||||
app:layout_constraintTop_toBottomOf="@id/line_divider_details"
|
||||
tools:targetApi="p" />
|
||||
|
||||
<androidx.core.widget.NestedScrollView
|
||||
android:id="@+id/blocking_scrollview"
|
||||
|
|
|
@ -33,6 +33,7 @@ class TrackingProtectionStoreTest {
|
|||
store.state.mode,
|
||||
TrackingProtectionState.Mode.Details(TrackingProtectionCategory.FINGERPRINTERS, true)
|
||||
)
|
||||
assertEquals(store.state.lastAccessedCategory, TrackingProtectionCategory.FINGERPRINTERS.name)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -46,6 +47,7 @@ class TrackingProtectionStoreTest {
|
|||
store.state.mode,
|
||||
TrackingProtectionState.Mode.Normal
|
||||
)
|
||||
assertEquals(store.state.lastAccessedCategory, initialState.lastAccessedCategory)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -133,7 +135,8 @@ class TrackingProtectionStoreTest {
|
|||
url = "www.mozilla.org",
|
||||
isTrackingProtectionEnabled = true,
|
||||
listTrackers = listOf(),
|
||||
mode = TrackingProtectionState.Mode.Normal
|
||||
mode = TrackingProtectionState.Mode.Normal,
|
||||
lastAccessedCategory = ""
|
||||
)
|
||||
|
||||
private fun detailsState(): TrackingProtectionState = TrackingProtectionState(
|
||||
|
@ -141,6 +144,7 @@ class TrackingProtectionStoreTest {
|
|||
url = "www.mozilla.org",
|
||||
isTrackingProtectionEnabled = true,
|
||||
listTrackers = listOf(),
|
||||
mode = TrackingProtectionState.Mode.Details(TrackingProtectionCategory.CRYPTOMINERS, true)
|
||||
mode = TrackingProtectionState.Mode.Details(TrackingProtectionCategory.CRYPTOMINERS, true),
|
||||
lastAccessedCategory = TrackingProtectionCategory.CRYPTOMINERS.name
|
||||
)
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue