Skip to content

Commit d560705

Browse files
imhappihunterstich
authored andcommitted
[A11y] Prevent hide on scroll when Talkback is on
PiperOrigin-RevId: 728848785
1 parent 08a8893 commit d560705

File tree

7 files changed

+118
-9
lines changed

7 files changed

+118
-9
lines changed

catalog/java/io/material/catalog/bottomappbar/BottomAppBarMainDemoFragment.java

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818

1919
import io.material.catalog.R;
2020

21+
import android.os.Build.VERSION;
22+
import android.os.Build.VERSION_CODES;
2123
import android.os.Bundle;
2224
import androidx.appcompat.app.AppCompatActivity;
2325
import androidx.appcompat.widget.Toolbar;
@@ -28,6 +30,8 @@
2830
import android.view.View;
2931
import android.view.ViewGroup;
3032
import android.view.accessibility.AccessibilityEvent;
33+
import android.view.accessibility.AccessibilityManager;
34+
import android.widget.LinearLayout;
3135
import androidx.activity.BackEventCompat;
3236
import androidx.activity.OnBackPressedCallback;
3337
import androidx.annotation.LayoutRes;
@@ -55,6 +59,8 @@
5559
/** A fragment that displays the main Bottom App Bar demos for the Catalog app. */
5660
public class BottomAppBarMainDemoFragment extends DemoFragment {
5761

62+
private AccessibilityManager am;
63+
5864
private final OnBackPressedCallback bottomDrawerOnBackPressedCallback =
5965
new OnBackPressedCallback(/* enabled= */ false) {
6066
@Override
@@ -125,6 +131,7 @@ public View onCreateDemoView(
125131
toolbar.setNavigationOnClickListener(v -> getActivity().onBackPressed());
126132

127133
coordinatorLayout = view.findViewById(R.id.coordinator_layout);
134+
LinearLayout content = view.findViewById(R.id.bottomappbar_content);
128135
bar = view.findViewById(R.id.bar);
129136
((AppCompatActivity) getActivity()).setSupportActionBar(bar);
130137
barNavView = bar.getChildAt(0);
@@ -140,9 +147,16 @@ public View onCreateDemoView(
140147
return false;
141148
});
142149

150+
if (VERSION.SDK_INT >= VERSION_CODES.M) {
151+
am = getContext().getSystemService(AccessibilityManager.class);
152+
if (am != null && am.isTouchExplorationEnabled()) {
153+
bar.setHideOnScroll(false);
154+
bar.post(() -> content.setPadding(0, content.getPaddingTop(), 0, bar.getMeasuredHeight()));
155+
}
156+
}
157+
143158
setUpDemoControls(view);
144159
setUpBottomAppBarShapeAppearance();
145-
146160
return view;
147161
}
148162

@@ -173,7 +187,13 @@ private void setUpDemoControls(@NonNull View view) {
173187
MaterialSwitch barScrollSwitch = view.findViewById(R.id.bar_scroll_switch);
174188
barScrollSwitch.setChecked(bar.getHideOnScroll());
175189
barScrollSwitch.setOnCheckedChangeListener(
176-
(buttonView, isChecked) -> bar.setHideOnScroll(isChecked));
190+
(buttonView, isChecked) -> {
191+
if (am != null && am.isTouchExplorationEnabled()) {
192+
bar.setHideOnScroll(false);
193+
} else {
194+
bar.setHideOnScroll(isChecked);
195+
}
196+
});
177197
}
178198

179199
@Override

catalog/java/io/material/catalog/bottomappbar/res/layout/cat_bottomappbar_content.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
app:layout_behavior="@string/appbar_scrolling_view_behavior">
2424

2525
<LinearLayout
26+
android:id="@+id/bottomappbar_content"
2627
android:layout_width="match_parent"
2728
android:layout_height="wrap_content"
2829
android:paddingTop="16dp"
@@ -65,7 +66,7 @@
6566
android:layout_width="wrap_content"
6667
android:layout_height="wrap_content"
6768
android:padding="10dp"
68-
android:text="@string/cat_bottomappbar_lorem_ipsum" />
69+
android:text="@string/cat_bottomappbar_lorem_ipsum"/>
6970
</LinearLayout>
7071
</androidx.core.widget.NestedScrollView>
7172

catalog/java/io/material/catalog/dockedtoolbar/DockedToolbarMainDemoFragment.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,23 @@
1717

1818
import io.material.catalog.R;
1919

20+
import android.os.Build.VERSION;
21+
import android.os.Build.VERSION_CODES;
2022
import android.os.Bundle;
2123
import androidx.appcompat.app.AppCompatActivity;
2224
import androidx.appcompat.widget.PopupMenu;
2325
import androidx.appcompat.widget.Toolbar;
2426
import android.view.LayoutInflater;
2527
import android.view.View;
2628
import android.view.ViewGroup;
29+
import android.view.accessibility.AccessibilityManager;
2730
import android.widget.Button;
31+
import android.widget.LinearLayout;
2832
import androidx.annotation.LayoutRes;
2933
import androidx.annotation.MenuRes;
3034
import androidx.annotation.NonNull;
3135
import androidx.annotation.Nullable;
36+
import androidx.coordinatorlayout.widget.CoordinatorLayout;
3237
import com.google.android.material.dockedtoolbar.DockedToolbarLayout;
3338
import com.google.android.material.snackbar.Snackbar;
3439
import io.material.catalog.feature.DemoFragment;
@@ -59,10 +64,21 @@ public View onCreateDemoView(
5964
setupSnackbarOnClick(addButton);
6065
setupSnackbarOnClick(tabButton);
6166

67+
LinearLayout bodyContainer = view.findViewById(R.id.body_container);
68+
6269
Button overflowClick = view.findViewById(R.id.docked_toolbar_button_overflow_button);
6370

6471
overflowClick.setOnClickListener(v -> showMenu(v, R.menu.overflow_menu));
6572

73+
if (VERSION.SDK_INT >= VERSION_CODES.M) {
74+
AccessibilityManager am = getContext().getSystemService(AccessibilityManager.class);
75+
if (am != null && am.isTouchExplorationEnabled()) {
76+
((CoordinatorLayout.LayoutParams) dockedToolbar.getLayoutParams()).setBehavior(null);
77+
dockedToolbar.post(
78+
() -> bodyContainer.setPadding(0, 0, 0, dockedToolbar.getMeasuredHeight()));
79+
}
80+
}
81+
6682
return view;
6783
}
6884

docs/components/BottomAppBar.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,34 @@ in the menu:
7171
</menu>
7272
```
7373

74+
#### Talkback
75+
76+
Bottom app bar can optionally hide on scroll with the `app:hideOnScroll`
77+
attribute. When this attribute is set to true, scrolling will hide the bottom
78+
app bar and prevent it from being seen by any screen readers which may be
79+
confusing for users.
80+
81+
When Talkback is enabled, this behavior should be disabled by setting
82+
`bottomAppBar.setHideOnScroll(false)`. Additionally, disabling this behavior
83+
causes any content to be obscured, make sure to add the appropriate bottom
84+
padding of the height of the bottom app bar to the content. See below for an
85+
example:
86+
87+
```
88+
val am = context.getSystemService(AccessibilityManager::class.java)
89+
if (am != null && am.isTouchExplorationEnabled) {
90+
bar.setHideOnScroll(false)
91+
bar.post {
92+
content.setPadding(
93+
content.paddingLeft,
94+
content.paddingTop,
95+
content.paddingRight,
96+
content.paddingBottom + bar.measuredHeight
97+
)
98+
}
99+
}
100+
```
101+
74102
## Bottom app bar
75103

76104
Bottom app bars provide access to up to four actions, including the

docs/components/FloatingToolbar.md

Lines changed: 42 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,9 @@ Here's what a typical layout would look like:
112112
A Floating Toolbar is a `FrameLayout` that provides additional styling and functionality.
113113
You may add children to it as you would to a `FrameLayout`.
114114

115-
Floating toolbars can hide on scroll if inside a `CoordinatorLayout` by setting the following `CoordinatorLayout.Behavior` through the `app:layout_behavior` attribute:
115+
Floating toolbars can hide on scroll if inside a `CoordinatorLayout` by setting
116+
the following `CoordinatorLayout.Behavior` through the `app:layout_behavior`
117+
attribute:
116118

117119
```xml
118120
<com.google.android.material.floatingtoolbar.FloatingToolbarLayout
@@ -125,6 +127,8 @@ Floating toolbars can hide on scroll if inside a `CoordinatorLayout` by setting
125127
</com.google.android.material.floatingtoolbar.FloatingToolbarLayout>
126128
```
127129

130+
This behavior will be disabled when Talkback is enabled for a11y reasons. See [how to make floating toolbars accessible](#making-floating-toolbar-accessible).
131+
128132
Note that the default M3 style is the horizontal standard color styling. Vibrant color or vertical styles should be explicitly set on the `FloatingToolbarLayout`. M3 stylings for specific components may also be defined, such as for icon buttons. These are recommended to be set explicitly on the corresponding components inside `FloatingToolbarLayout`. See the full list of [styles](https://github.com/material-components/material-components-android/tree/master/lib/java/com/google/android/material/floatingtoolbar/res/values/styles.xml).
129133

130134
API and source code:
@@ -135,12 +139,15 @@ API and source code:
135139

136140
### Making Floating Toolbar accessible
137141

138-
You should set a `contentDescription` on all the actions in the Floating Toolbar so that screen
139-
readers like TalkBack can properly announce what each action represents.
142+
You should set a `contentDescription` on all the actions in the Floating Toolbar
143+
so that screen readers like TalkBack can properly announce what each action
144+
represents.
140145

141-
You can also control the ordering of the Talkback focus through the `accessibilityTraversalBefore` and `accessibilityTraversalAfter` flags.
146+
You can also control the ordering of the Talkback focus through the
147+
`accessibilityTraversalBefore` and `accessibilityTraversalAfter` flags.
142148

143-
For example, if you want the Floating Toolbar to gain Talkback focus first, you can set these accessibility flags like below:
149+
For example, if you want the Floating Toolbar to gain Talkback focus first, you
150+
can set these accessibility flags like below:
144151

145152
```xml
146153
<!-- sample screen content -->
@@ -161,14 +168,43 @@ For example, if you want the Floating Toolbar to gain Talkback focus first, you
161168
</androidx.core.widget.NestedScrollView>
162169

163170
<com.google.android.material.floatingtoolbar.FloatingToolbarLayout
164-
android:id="@+id/floating_toolbar"
171+
android:id="@+id/floating_toolbar"
165172
android:layout_width="wrap_content"
166173
android:layout_height="wrap_content"
167174
android:accessibilityTraversalBefore="@id/content">
168175
...
169176
</com.google.android.material.floatingtoolbar.FloatingToolbarLayout>
170177
```
171178

179+
#### Talkback
180+
181+
If optionally using the `CoordinatorLayout.Behavior` `HideViewOnScrollBehavior`
182+
to hide the floating toolbar on scroll, make sure to disable it when Talkback
183+
is enabled. Screen readers such as Talkback will not be able to see it if the
184+
floating toolbar is hidden when scrolled.
185+
186+
If using a Floating toolbar in a layout that obscures any content when
187+
hide on scroll is disabled, make sure to add the appropriate padding to the
188+
content. For example, if the floating toolbar is on the bottom and it is
189+
obscuring the content, bottom padding should be added to the content.
190+
191+
See below for an example:
192+
193+
```
194+
val am = context.getSystemService(AccessibilityManager::class.java)
195+
if (am != null && am.isTouchExplorationEnabled) {
196+
(bar.layoutParams as? CoordinatorLayout.LayoutParams)?.behavior = null
197+
bar.post {
198+
content.setPadding(
199+
content.paddingLeft,
200+
content.paddingTop,
201+
content.paddingRight,
202+
content.paddingBottom + bar.measuredHeight
203+
)
204+
}
205+
}
206+
```
207+
172208
### Anatomy and key properties
173209

174210
The following is an anatomy diagram for the floating toolbar:

lib/java/com/google/android/material/behavior/HideBottomViewOnScrollBehavior.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,10 @@
4444
* The {@link Behavior} for a View within a {@link CoordinatorLayout} to hide the view off the
4545
* bottom of the screen when scrolling down, and show it when scrolling up.
4646
*
47+
* <p> If Talkback is enabled, the hide on scroll behavior should be disabled until Talkback
48+
* is disabled. Ensure that the content is not obscured due to disabling this behavior by
49+
* adding padding to the content.
50+
*
4751
* @deprecated Use {@link HideViewOnScrollBehavior} instead.
4852
* <p>TODO(b/378132394): Migrate usages of this class to {@link HideViewOnScrollBehavior}.
4953
*/

lib/java/com/google/android/material/behavior/HideViewOnScrollBehavior.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@
5252
*
5353
* <p>Supports hiding the View off of three screen edges: {@link HideOnScrollView#EDGE_RIGHT},
5454
* {@link HideOnScrollView#EDGE_BOTTOM} and {@link HideOnScrollView#EDGE_LEFT}.
55+
*
56+
* <p>If Talkback is enabled, the hide on scroll behavior should be disabled until Talkback is
57+
* disabled. Ensure that the content is not obscured due to disabling this behavior by adding
58+
* padding to the content.
5559
*/
5660
public class HideViewOnScrollBehavior<V extends View> extends Behavior<V> {
5761

0 commit comments

Comments
 (0)