1
0
Fork 0

For #1667: Screen reader can't reach other UI items when QAB expanded (#4695)

master
Colin Lee 2019-08-14 11:56:29 -05:00 committed by Sawyer Blatz
parent 1328d686aa
commit 5b4a441bcb
9 changed files with 1447 additions and 81 deletions

View File

@ -19,7 +19,6 @@ import androidx.fragment.app.activityViewModels
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.google.android.material.bottomsheet.BottomSheetBehavior
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.component_search.*
import kotlinx.android.synthetic.main.fragment_browser.*
@ -72,6 +71,7 @@ import org.mozilla.fenix.ext.enterToImmersiveMode
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.ext.requireComponents
import org.mozilla.fenix.ext.toTab
import org.mozilla.fenix.quickactionsheet.QuickActionSheetBehavior
import org.mozilla.fenix.settings.SupportUtils
import org.mozilla.fenix.utils.Settings
@ -173,7 +173,7 @@ abstract class BaseBrowserFragment : Fragment(), BackHandler, SessionManager.Obs
it.flags = Intent.FLAG_ACTIVITY_NEW_TASK
},
currentSessionAsTab = session.toTab(context!!),
bottomSheetBehavior = BottomSheetBehavior.from(nestedScrollQuickAction)
bottomSheetBehavior = QuickActionSheetBehavior.from(nestedScrollQuickAction)
)
browserInteractor =

View File

@ -26,6 +26,7 @@ import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.home.sessioncontrol.Tab
import org.mozilla.fenix.lib.Do
import org.mozilla.fenix.quickactionsheet.QuickActionSheetBehavior
/**
* An interface that handles the view manipulation of the BrowserToolbar, triggered by the Interactor
@ -47,7 +48,7 @@ class DefaultBrowserToolbarController(
private val getSupportUrl: () -> String,
private val openInFenixIntent: Intent,
private val currentSessionAsTab: Tab,
private val bottomSheetBehavior: BottomSheetBehavior<NestedScrollView>
private val bottomSheetBehavior: QuickActionSheetBehavior<NestedScrollView>
) : BrowserToolbarController {
override fun handleToolbarClick() {

View File

@ -11,17 +11,15 @@ import android.view.View
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import android.widget.LinearLayout
import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.widget.NestedScrollView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import mozilla.components.browser.toolbar.BrowserToolbar
import org.mozilla.fenix.R
import kotlinx.android.synthetic.main.layout_quick_action_sheet.view.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import org.mozilla.fenix.R
import org.mozilla.fenix.utils.Settings
const val POSITION_SNAP_BUFFER = 1f
@ -35,7 +33,7 @@ class QuickActionSheet @JvmOverloads constructor(
private val scope = MainScope()
private lateinit var quickActionSheetBehavior: QuickActionSheetBehavior
private lateinit var quickActionSheetBehavior: QuickActionSheetBehavior<NestedScrollView>
init {
inflate(context, R.layout.layout_quick_action_sheet, this)
@ -43,8 +41,8 @@ class QuickActionSheet @JvmOverloads constructor(
override fun onAttachedToWindow() {
super.onAttachedToWindow()
quickActionSheetBehavior = BottomSheetBehavior.from(quick_action_sheet.parent as View)
as QuickActionSheetBehavior
quickActionSheetBehavior =
QuickActionSheetBehavior.from(quick_action_sheet.parent as NestedScrollView)
quickActionSheetBehavior.isHideable = false
setupHandle()
}
@ -76,21 +74,21 @@ class QuickActionSheet @JvmOverloads constructor(
}
class HandleAccessibilityDelegate(
private val quickActionSheetBehavior: QuickActionSheetBehavior
private val quickActionSheetBehavior: QuickActionSheetBehavior<NestedScrollView>
) : View.AccessibilityDelegate() {
private var finalState = BottomSheetBehavior.STATE_COLLAPSED
get() = when (quickActionSheetBehavior.state) {
BottomSheetBehavior.STATE_EXPANDED,
BottomSheetBehavior.STATE_HIDDEN,
BottomSheetBehavior.STATE_COLLAPSED -> {
quickActionSheetBehavior.state
get() = when (quickActionSheetBehavior.state) {
BottomSheetBehavior.STATE_EXPANDED,
BottomSheetBehavior.STATE_HIDDEN,
BottomSheetBehavior.STATE_COLLAPSED -> {
quickActionSheetBehavior.state
}
else -> field
}
set(value) {
field = value
quickActionSheetBehavior.state = value
}
else -> field
}
set(value) {
field = value
quickActionSheetBehavior.state = value
}
override fun performAccessibilityAction(host: View?, action: Int, args: Bundle?): Boolean {
finalState = when (action) {
@ -113,11 +111,13 @@ class QuickActionSheet @JvmOverloads constructor(
override fun onInitializeAccessibilityNodeInfo(host: View?, info: AccessibilityNodeInfo?) {
super.onInitializeAccessibilityNodeInfo(host, info)
info?.addAction(when (finalState) {
BottomSheetBehavior.STATE_COLLAPSED,
BottomSheetBehavior.STATE_HIDDEN -> AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND
else -> AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE
})
info?.addAction(
when (finalState) {
BottomSheetBehavior.STATE_COLLAPSED,
BottomSheetBehavior.STATE_HIDDEN -> AccessibilityNodeInfo.AccessibilityAction.ACTION_EXPAND
else -> AccessibilityNodeInfo.AccessibilityAction.ACTION_COLLAPSE
}
)
}
}
@ -126,44 +126,3 @@ class QuickActionSheet @JvmOverloads constructor(
const val BOUNCE_ANIMATION_PAUSE_LENGTH = 2000L
}
}
class QuickActionSheetBehavior(
context: Context,
attrs: AttributeSet
) : BottomSheetBehavior<NestedScrollView>(context, attrs) {
override fun layoutDependsOn(parent: CoordinatorLayout, child: NestedScrollView, dependency: View): Boolean {
if (dependency is BrowserToolbar) {
return true
}
return super.layoutDependsOn(parent, child, dependency)
}
override fun onDependentViewChanged(
parent: CoordinatorLayout,
child: NestedScrollView,
dependency: View
): Boolean {
return if (dependency is BrowserToolbar) {
repositionQuickActionSheet(child, dependency)
true
} else {
false
}
}
private fun repositionQuickActionSheet(quickActionSheetContainer: NestedScrollView, toolbar: BrowserToolbar) {
if (toolbar.translationY >= toolbar.height.toFloat() - POSITION_SNAP_BUFFER) {
state = STATE_HIDDEN
} else if (state == STATE_HIDDEN || state == STATE_SETTLING) {
state = STATE_COLLAPSED
}
quickActionSheetContainer.translationY = toolbar.translationY + toolbar.height * -1.0f
}
companion object {
fun from(view: NestedScrollView) =
BottomSheetBehavior.from(view) as QuickActionSheetBehavior
}
}

View File

@ -11,7 +11,6 @@ import androidx.annotation.DrawableRes
import androidx.core.content.edit
import androidx.core.view.isVisible
import androidx.core.widget.NestedScrollView
import com.google.android.material.bottomsheet.BottomSheetBehavior
import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.fragment_browser.*
import kotlinx.android.synthetic.main.layout_quick_action_sheet.*
@ -31,6 +30,7 @@ interface QuickActionSheetViewInteractor {
fun onQuickActionSheetAppearancePressed()
fun onQuickActionSheetOpenLinkPressed()
}
/**
* View for the quick action sheet that slides out from the toolbar.
*/
@ -47,13 +47,14 @@ class QuickActionSheetView(
private val quickActionSheetBehavior = QuickActionSheetBehavior.from(nestedScrollQuickAction)
init {
quickActionSheetBehavior.setBottomSheetCallback(object : BottomSheetBehavior.BottomSheetCallback() {
override fun onStateChanged(v: View, state: Int) {
updateImportantForAccessibility(state)
quickActionSheetBehavior.setQuickActionSheetCallback(object :
QuickActionSheetBehavior.QuickActionSheetCallback {
override fun onStateChanged(bottomSheet: View, newState: Int) {
updateImportantForAccessibility(newState)
if (state == BottomSheetBehavior.STATE_EXPANDED) {
if (newState == QuickActionSheetBehavior.STATE_EXPANDED) {
interactor.onQuickActionSheetOpened()
} else if (state == BottomSheetBehavior.STATE_COLLAPSED) {
} else if (newState == QuickActionSheetBehavior.STATE_COLLAPSED) {
interactor.onQuickActionSheetClosed()
}
}
@ -86,7 +87,7 @@ class QuickActionSheetView(
R.id.quick_action_open_app_link -> interactor.onQuickActionSheetOpenLinkPressed()
else -> return
}
quickActionSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED
quickActionSheetBehavior.state = QuickActionSheetBehavior.STATE_COLLAPSED
}
/**
@ -102,7 +103,7 @@ class QuickActionSheetView(
*/
private fun updateImportantForAccessibility(state: Int) {
view.quick_action_buttons_layout.importantForAccessibility = when (state) {
BottomSheetBehavior.STATE_COLLAPSED, BottomSheetBehavior.STATE_HIDDEN ->
QuickActionSheetBehavior.STATE_COLLAPSED, QuickActionSheetBehavior.STATE_HIDDEN ->
View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS
else ->
View.IMPORTANT_FOR_ACCESSIBILITY_AUTO

View File

@ -26,8 +26,8 @@
android:id="@+id/nestedScrollQuickAction"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:behavior_hideable="true"
app:behavior_peekHeight="12dp"
app:mozac_behavior_hideable="true"
app:mozac_behavior_peekHeight="12dp"
app:layout_behavior="org.mozilla.fenix.quickactionsheet.QuickActionSheetBehavior" />
<ViewStub

View File

@ -48,4 +48,56 @@
<declare-styleable name="OnboardingRadioButton">
<attr name="onboardingKey" format="reference" />
</declare-styleable>
<declare-styleable name="QuickActionSheetBehavior_Layout">
<!-- The height of the bottom sheet when it is collapsed. -->
<attr format="dimension" name="mozac_behavior_peekHeight">
<!-- Peek at the 16:9 ratio keyline of its parent -->
<enum name="auto" value="-1"/>
</attr>
<!-- Whether this bottom sheet can be hidden by dragging it further downwards -->
<attr format="boolean" name="mozac_behavior_hideable"/>
<!-- Skip the collapsed state once expanded; no effect unless it is hideable -->
<attr format="boolean" name="mozac_behavior_skipCollapsed"/>
<!-- Whether height of expanded sheet wraps content or not -->
<attr format="boolean" name="mozac_behavior_fitToContents"/>
<!-- The ratio to be used to set the height of half-expanded state in proportion to parent, when
fitToContents is false. Defaults to true half, 0.5, if not explicitly set. Ratio must be a
float value between 0 and 1 and produce a half-expanded state height larger than the
peek height for the half-expanded state to be operational -->
<attr format="reference|float" name="mozac_behavior_halfExpandedRatio"/>
<!-- The top offset of the BottomSheet in the expanded-state when fitsToContent is false.
The default value is 0, which results in the sheet matching the parent's top. -->
<attr format="reference|integer" name="mozac_behavior_expandedOffset"/>
<!-- Shape appearance style reference for BottomSheet. Attribute declaration is in the shape
package. -->
<attr name="shapeAppearance"/>
<!-- Shape appearance overlay style reference for BottomSheet. To be used to augment attributes
declared in the shapeAppearance. Attribute declaration is in the shape package. -->
<attr name="shapeAppearanceOverlay"/>
<!-- Background color used by the BottomSheetBehavior background drawable when shape theming is
enabled. Accepts a ColorStateList or ColorInt. If shape theming is not enabled,
android:background should instead be utilized to set the background resource. -->
<attr name="backgroundTint"/>
<!-- Behavior properties will be saved and restored by evaluating each flag.
usage: app:behavior_saveFlags=”hideable|skipCollapsed” -->
<attr name="mozac_behavior_saveFlags">
<!-- This flag will preserve the peekHeight on configuration change. -->
<flag name="peekHeight" value="0x1"/>
<!-- This flag will preserve the fitToContents boolean value on configuration change. -->
<flag name="fitToContents" value="0x2"/>
<!-- This flag will preserve the hideable boolean value on configuration change. -->
<flag name="hideable" value="0x4"/>
<!-- This flag will preserve the skipCollapsed boolean value on configuration change. -->
<flag name="skipCollapsed" value="0x8"/>
<!-- This flag will preserve the all the aforementioned values on configuration change. -->
<flag name="all" value="-1"/>
<!-- This flag will not preserve the aforementioned values on configuration change. The only
value preserved will be the positional state, e.g. collapsed, hidden, expanded, etc.
This is the default behavior. -->
<flag name="none" value="0"/>
</attr>
<attr name="android:elevation"/>
</declare-styleable>
</resources>

View File

@ -34,6 +34,8 @@
<!--Quick Settings-->
<dimen name="quicksettings_item_height">46dp</dimen>
<dimen name="design_quick_action_sheet_peek_height_min">64dp</dimen>
<dimen name="onboarding_header_icon_height_width">32dp</dimen>
<!-- Bottom Sheet Fragment card -->

View File

@ -7,7 +7,6 @@ package org.mozilla.fenix.components.toolbar
import android.content.Intent
import androidx.core.widget.NestedScrollView
import androidx.navigation.NavController
import com.google.android.material.bottomsheet.BottomSheetBehavior
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
@ -37,6 +36,7 @@ import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.nav
import org.mozilla.fenix.home.sessioncontrol.Tab
import org.mozilla.fenix.home.sessioncontrol.TabCollection
import org.mozilla.fenix.quickactionsheet.QuickActionSheetBehavior
@ExperimentalCoroutinesApi
@ObsoleteCoroutinesApi
@ -54,7 +54,7 @@ class DefaultBrowserToolbarControllerTest {
private val getSupportUrl: () -> String = { "https://supportUrl.org" }
private val openInFenixIntent: Intent = mockk(relaxed = true)
private val currentSessionAsTab: Tab = mockk(relaxed = true)
private val bottomSheetBehavior: BottomSheetBehavior<NestedScrollView> = mockk(relaxed = true)
private val bottomSheetBehavior: QuickActionSheetBehavior<NestedScrollView> = mockk(relaxed = true)
private val metrics: MetricController = mockk(relaxed = true)
private val sessionUseCases: SessionUseCases = mockk(relaxed = true)
@ -236,7 +236,7 @@ class DefaultBrowserToolbarControllerTest {
controller.handleToolbarItemInteraction(item)
verify { bottomSheetBehavior.state = BottomSheetBehavior.STATE_COLLAPSED }
verify { bottomSheetBehavior.state = QuickActionSheetBehavior.STATE_COLLAPSED }
verify { findInPageLauncher() }
verify { metrics.track(Event.FindInPageOpened) }
}