diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt index 8791d3e07..f42604992 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelDialogFragment.kt @@ -73,7 +73,8 @@ class TrackingProtectionPanelDialogFragment : AppCompatDialogFragment(), UserInt args.url, args.trackingProtectionEnabled, listTrackers = listOf(), - mode = TrackingProtectionState.Mode.Normal + mode = TrackingProtectionState.Mode.Normal, + lastAccessedCategory = "" ) ) } diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelView.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelView.kt index 898afca5c..cd79060be 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelView.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionPanelView.kt @@ -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 { /** diff --git a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStore.kt b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStore.kt index 66f31dfb0..49f326bad 100644 --- a/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStore.kt +++ b/app/src/main/java/org/mozilla/fenix/trackingprotection/TrackingProtectionStore.kt @@ -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, - 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) diff --git a/app/src/main/res/layout/component_tracking_protection_panel.xml b/app/src/main/res/layout/component_tracking_protection_panel.xml index 700a41cb6..53adf0e5b 100644 --- a/app/src/main/res/layout/component_tracking_protection_panel.xml +++ b/app/src/main/res/layout/component_tracking_protection_panel.xml @@ -43,6 +43,7 @@ + app:layout_constraintTop_toBottomOf="@id/trackingProtectionSwitch" + tools:targetApi="p" /> + app:layout_constraintTop_toBottomOf="@id/tracking_content" + tools:targetApi="p" /> + app:layout_constraintTop_toBottomOf="@id/line_divider_details" + tools:targetApi="p" />