Giter Site home page Giter Site logo

android / codelab-android-accessibility Goto Github PK

View Code? Open in Web Editor NEW
110.0 23.0 81.0 14.26 MB

Basic Android Accessibility: making sure everyone can use what you create!

Home Page: https://codelabs.developers.google.com/codelabs/basic-android-accessibility/

License: Apache License 2.0

Java 52.64% Kotlin 47.36%

codelab-android-accessibility's Introduction

basic-android-accessibility

This folder contains the source code for the following codelabs:

  • BasicAndroidAccessibility: a first course in understanding the basics of Android accessibility. It is available in Java and Kotlin versions.

  • GlobalActionBarService: a codelab for learning how to create an accessibility service from scratch.

License

Copyright 2016 Google, Inc.

Licensed to the Apache Software Foundation (ASF) under one or more contributor
license agreements. See the NOTICE file distributed with this work for
additional information regarding copyright ownership. The ASF licenses this
file to you under the Apache License, Version 2.0 (the "License"); you may not
use this file except in compliance with the License. You may obtain a copy of
the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
License for the specific language governing permissions and limitations under
the License.

codelab-android-accessibility's People

Contributors

calren avatar gustl22 avatar khufdev avatar mco-gh avatar osuleymanova avatar shailen avatar tiembo avatar yenerm avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

codelab-android-accessibility's Issues

Gesture isn't dispatched accurately

When I add up to 20 to 30 paths per second, it starts skipping paths and dispatches gestures that are not exactly the same as I expected. Is there a way to solve these problems?

No Global Action Bar Service in Settings > Accessibility

When I run app as in tutorial (select nothing activity and press green button) it builds and stops. After it i don't see Global Action Bar Service in Settings > Accessibility as it said in tutorial and there is not toolbar on the screen. Am i doing something wrong?

Providing Name, Role, and State for EditText element

The guidance in the codelab for Content Labeling is similar to guidance found elsewhere in Android SDK help content.

Hint: It is a best practice to use android:hint instead of contentDescription on EditTexts.

For WCAG2 4.1.2 we want to be able to expose a name and a value separately through the accessibility API. Ref: Name, Role, Value: Understanding SC 4.1.2

The issue with both android:hint and android:contentDescription is that the placeholder that serves as the name disappears after a user has begun typing a value into the edit field. Both attributes only temporarily provide a name.

I suggest the lab be modified to include a TextView with the text "Favorite Song". This can then be associated with the EditText using android:labelFor. This will allow the field to always supply a valid name and value to Talkback throughout the interaction.

CounterInstrumentedTest.testIncrement fails with IllegalAccessError with AccessibilityChecks.enable()

Cloned the repo form master commit, run CounterInstrumentedTest.testIncrement() from Android studio, passes ok. Uncomment AccessibilityChecks.enable() rerun but now get the following error.

Running on Pixel 3: Android 12.

java.lang.IllegalAccessError: Illegal class access: 'androidx.test.core.app.ListFuture' attempting to access 'androidx.concurrent.futures.DirectExecutor' (declaration of 'androidx.test.core.app.ListFuture' appears in /data/app/~~b3Jx6X51YqdMTTUNCqksUQ==/com.example.android.accessibility.counter.test-MHSAY5Inv8Zy8Mdz4MPhUQ==/base.apk)
	at androidx.test.core.app.ListFuture.directExecutor(ListFuture.java:139)
	at androidx.test.core.app.ListFuture.init(ListFuture.java:101)
	at androidx.test.core.app.ListFuture.<init>(ListFuture.java:84)
	at androidx.test.core.app.DeviceCapture$forceRedrawGlobalWindowViews$1.run(DeviceCapture.kt:151)
	at android.os.Handler.handleCallback(Handler.java:938)
	at android.os.Handler.dispatchMessage(Handler.java:99)
	at android.os.Looper.loopOnce(Looper.java:201)
	at android.os.Looper.loop(Looper.java:288)
	at android.app.ActivityThread.main(ActivityThread.java:7842)
	at java.lang.reflect.Method.invoke(Native Method)
	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)

Missing Google repository in build.gradle

ERROR: Failed to resolve: com.android.support.test.espresso:espresso-core:2.2.2
Add Google Maven repository and sync project
Show in Project Structure dialog
Affected Modules: app

ERROR: Failed to resolve: com.android.support:appcompat-v7:24.2.1
Add Google Maven repository and sync project
Show in Project Structure dialog
Affected Modules: app

This site about accessibility can't be navigated using the keyboard.

When attempting to read the pages of this site using the keyboard to scroll, neither the arrow keys, nor the page up and down keys, nor the space bar can be used to scroll down the page. Instead, the keyboard can only be used to switch between pages, without being able to scroll down to read their content.

TextView android:layout_below attributes in step #6 causes errors

The instructions in step #6 tell the reader to split out a RelativeLayout containing several TextView elements into multiple RelativeLayout containers. However, some of the TextView elements are aligned to each other using android:layout_below, which causes errors when they are no longer sibling elements within the same container.

Consider refactoring the TextView elements so that they no longer rely on each other for alignment.

Remove unnecessary android:id attribute in content_grouping.xml

The layout file content_grouping.xml used in Step 6 of the codelab features a RelativeLayout element with an unnecessary android:id attribute. I recommend removing this, as it can easily trip up the reader in a later step, when they are told to split the RelativeLayout into several new elements.

Can't launch GlobalActionBarService in step 3

Step 3 of the tutorial invites the user to launch the application, but you can't do that until you set the launch activity to Nothing, which isn't explained until the end of step 4.

findScrollableNode always returns null

I have implemented the function

private AccessibilityNodeInfo findScrollableNode(AccessibilityNodeInfo root) {
   Deque<AccessibilityNodeInfo> deque = new ArrayDeque<>();
   deque.add(root);
   while (!deque.isEmpty()) {
       AccessibilityNodeInfo node = deque.removeFirst();
       if (node.getActionList().contains(AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD)) {
           return node;
       }
       for (int i = 0; i < node.getChildCount(); i++) {
           deque.addLast(node.getChild(i));
       }
   }
   return null;
}

And it never returns a node, no matter what screen I am in.

IllegalAccessException in StrictMode

When I enable StrictMode, I can see IllegalAccessException in the log.

What is the correct way to show overlay in AccessibilityService?

    @Override
    protected void onServiceConnected() {
        super.onServiceConnected();
        debugStrictMode();

        WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
        FrameLayout mLayout = new FrameLayout(this);
        WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
        lp.type = WindowManager.LayoutParams.TYPE_ACCESSIBILITY_OVERLAY;
        lp.format = PixelFormat.TRANSLUCENT;
        lp.flags |= WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
        lp.width = WindowManager.LayoutParams.WRAP_CONTENT;
        lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
        lp.gravity = Gravity.TOP;
        LayoutInflater inflater = LayoutInflater.from(this);
        inflater.inflate(R.layout.action_bar, mLayout);
        wm.addView(mLayout, lp);
    }

    void debugStrictMode() {
        StrictMode.VmPolicy.Builder policyBuilder = (new StrictMode.VmPolicy.Builder())
                .detectAll()
                .penaltyLog()
                .penaltyListener(getMainExecutor(), v -> Log.d("StrictMode", "onVmViolation: " + v));
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            policyBuilder.detectUnsafeIntentLaunch();
        }
        StrictMode.setVmPolicy(policyBuilder.build());
    }
E/StrictMode: Tried to access the API:ViewConfiguration which needs to have proper configuration from a non-UI Context:com.example.android.globalactionbarservice.GlobalActionBarService@b1bf96a The API:ViewConfiguration needs a proper configuration. Use UI contexts such as an activity or a context created via createWindowContext(Display, int, Bundle) or  createConfigurationContext(Configuration) with a proper configuration.
    java.lang.IllegalAccessException: Tried to access the API:ViewConfiguration which needs to have proper configuration from a non-UI Context:com.example.android.globalactionbarservice.GlobalActionBarService@b1bf96a
        at android.os.StrictMode.assertConfigurationContext(StrictMode.java:2296)
        at android.view.ViewConfiguration.get(ViewConfiguration.java:522)
        at android.view.View.<init>(View.java:5307)
        at android.view.View.<init>(View.java:5452)
        at android.view.ViewGroup.<init>(ViewGroup.java:702)
        at android.view.ViewGroup.<init>(ViewGroup.java:698)
        at android.view.ViewGroup.<init>(ViewGroup.java:694)
        at android.view.ViewGroup.<init>(ViewGroup.java:690)
        at android.widget.FrameLayout.<init>(FrameLayout.java:85)
        at com.example.android.globalactionbarservice.GlobalActionBarService.onServiceConnected(GlobalActionBarService.java:36)
        at android.accessibilityservice.AccessibilityService.dispatchServiceConnected(AccessibilityService.java:762)
        at android.accessibilityservice.AccessibilityService.access$100(AccessibilityService.java:236)
        at android.accessibilityservice.AccessibilityService$2.onServiceConnected(AccessibilityService.java:2160)
        at android.accessibilityservice.AccessibilityService$IAccessibilityServiceClientWrapper.executeMessage(AccessibilityService.java:2404)
        at com.android.internal.os.HandlerCaller$MyHandler.handleMessage(HandlerCaller.java:44)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7842)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
D/StrictMode: StrictMode policy violation: android.os.strictmode.IncorrectContextUseViolation: The API:LayoutInflater needs a proper configuration. Use UI contexts such as an activity or a context created via createWindowContext(Display, int, Bundle) or  createConfigurationContext(Configuration) with a proper configuration.
        at android.os.StrictMode.onIncorrectContextUsed(StrictMode.java:2261)
        at android.os.StrictMode.assertConfigurationContext(StrictMode.java:2297)
        at android.view.LayoutInflater.<init>(LayoutInflater.java:260)
        at com.android.internal.policy.PhoneLayoutInflater.<init>(PhoneLayoutInflater.java:44)
        at android.app.SystemServiceRegistry$33.createService(SystemServiceRegistry.java:534)
        at android.app.SystemServiceRegistry$33.createService(SystemServiceRegistry.java:531)
        at android.app.SystemServiceRegistry$CachedServiceFetcher.getService(SystemServiceRegistry.java:1848)
        at android.app.SystemServiceRegistry.getSystemService(SystemServiceRegistry.java:1525)
        at android.app.ContextImpl.getSystemService(ContextImpl.java:2064)
        at android.content.ContextWrapper.getSystemService(ContextWrapper.java:857)
        at android.accessibilityservice.AccessibilityService.getSystemService(AccessibilityService.java:2075)
        at android.view.LayoutInflater.from(LayoutInflater.java:288)
        at com.example.android.globalactionbarservice.GlobalActionBarService.onServiceConnected(GlobalActionBarService.java:44)
        at android.accessibilityservice.AccessibilityService.dispatchServiceConnected(AccessibilityService.java:762)
        at android.accessibilityservice.AccessibilityService.access$100(AccessibilityService.java:236)
        at android.accessibilityservice.AccessibilityService$2.onServiceConnected(AccessibilityService.java:2160)
        at android.accessibilityservice.AccessibilityService$IAccessibilityServiceClientWrapper.executeMessage(AccessibilityService.java:2404)
        at com.android.internal.os.HandlerCaller$MyHandler.handleMessage(HandlerCaller.java:44)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loopOnce(Looper.java:201)
        at android.os.Looper.loop(Looper.java:288)
        at android.app.ActivityThread.main(ActivityThread.java:7842)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003)
     Caused by: java.lang.IllegalAccessException: Tried to access the API:LayoutInflater which needs to have proper configuration from a non-UI Context:com.example.android.globalactionbarservice.GlobalActionBarService@b1bf96a
        at android.os.StrictMode.assertConfigurationContext(StrictMode.java:2296)
        at android.view.LayoutInflater.<init>(LayoutInflater.java:260) 
        at com.android.internal.policy.PhoneLayoutInflater.<init>(PhoneLayoutInflater.java:44) 
        at android.app.SystemServiceRegistry$33.createService(SystemServiceRegistry.java:534) 
        at android.app.SystemServiceRegistry$33.createService(SystemServiceRegistry.java:531) 
        at android.app.SystemServiceRegistry$CachedServiceFetcher.getService(SystemServiceRegistry.java:1848) 
        at android.app.SystemServiceRegistry.getSystemService(SystemServiceRegistry.java:1525) 
        at android.app.ContextImpl.getSystemService(ContextImpl.java:2064) 
        at android.content.ContextWrapper.getSystemService(ContextWrapper.java:857) 
        at android.accessibilityservice.AccessibilityService.getSystemService(AccessibilityService.java:2075) 
        at android.view.LayoutInflater.from(LayoutInflater.java:288) 
        at com.example.android.globalactionbarservice.GlobalActionBarService.onServiceConnected(GlobalActionBarService.java:44) 
        at android.accessibilityservice.AccessibilityService.dispatchServiceConnected(AccessibilityService.java:762) 
        at android.accessibilityservice.AccessibilityService.access$100(AccessibilityService.java:236) 
        at android.accessibilityservice.AccessibilityService$2.onServiceConnected(AccessibilityService.java:2160) 
        at android.accessibilityservice.AccessibilityService$IAccessibilityServiceClientWrapper.executeMessage(AccessibilityService.java:2404) 
        at com.android.internal.os.HandlerCaller$MyHandler.handleMessage(HandlerCaller.java:44) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loopOnce(Looper.java:201) 
        at android.os.Looper.loop(Looper.java:288) 
        at android.app.ActivityThread.main(ActivityThread.java:7842) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:548) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1003) 

accessibilityLiveRegion issue

Hi,

I'm learning accessibility development best practices through your very good tutorial.
Unfortunately, I am struggling with the accessibilityLiveRegion attribute: When I change a radio button state, it generates the "checked", but it scraps the "Correct/Incorrect". Thanks to the "Display speech output" function, I can see that the "Correct/Incorrect" appears briefly before "checked".
I am working on a samsung galaxy s5 that I've restored to the factory level so it's running Android version 6.0.1 and Talkback is updated to it's latest version 5.2.1.
I've tried both methods: accessibilityLiveRegion and using the ViewCompat as per the tutorial, but it makes no difference. Is there a specific configuration that I should implement? Or am I doing something wrong?

Thank you very much for your help in advance.

Liam

Grouping non-focusable content with `android:focusable` shouldn't be recommended

First of all, I appreciate all the work Google is putting together about accessibility. It's great to have more materials about Android Accessibility.

I'm an accessibility professional myself that have been working closely with Android applications. I'd like to share a few thoughts/problems about the subject of grouping content.

Focus navigation vs Accessibility Focus

Here's a quick definition before we continue:

  • Focus navigation: Similar to desktop keyboard navigation (tab, arrows keys, enter, esc). Keyboard or Directional Pad users. Focus is placed only on items that require user interaction (e.g. clickable views or buttons, inputs like EditText, CheckBox, Switch, etc).

  • Accessibility Focus: Focus for accessibility services, primarily screen readers such as TalkBack. Focus is placed on all meaningful views of the screen, including non-focusable views such as TextView or ImageView, which has either text or content descriptions.

The problem

Google has been advocating a technique to group non-focusable items by using android:focusable="true" to the parent ViewGroup so that its contents are read all at once for screen reader users.

You can see this recommendation in one of the Google Code Labs courses.

While grouping content is good for screen reader users, it creates a problem for keyboard users.

The problem is that android:focusable is meant for focus navigation and not accessibility focus. The consequences are:

  1. Views with no user interaction will now have focus via keyboard navigation. For example, focus on a ViewGroup that only has text views and no actions.
  2. These views will probably not have a focusable indicator (added as a StateListDrawable). Keyboard users won't be able to tell where the focus is at anymore. Even if we add a focusable indicator to these views, there is still no reason to have focus on TextViews for example.

Solutions

Unless if the statements above are wrong, then I recommend the following:

  1. Make it more evident the distinction between focus navigation and accessibility focus in Google's documentation and training material. I believe this distinction is only mentioned here in the notes.
  2. Suggest using android:importantForAccessibility instead of android:focusable. The only drawback is the API level since it was only introduced at API 16.

Focus behavior in a ViewGroup

Regardless of the issue mentioned above, there's an interesting thing about allowing a ViewGroup to receive focus. Consider the following code:

<LinearLayout
    android:orientation="vertical"
    android:focusable="true"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:text="Heading One"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <Button
        android:text="Action"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <TextView
        android:text="Heading Two"
        android:background="@drawable/focusable"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

What's the focus behavior for focus navigation (Keyboard)?

  1. Focus moves to the LinearLayout.
  2. Focus moves to the Button.

This is the expected behavior since we've asked for the LinearLayout to receive focus.

What's the accessibility focus behavior?

  1. Focus moves to the LinearLayout, announcing all its text content except for the Button text: "Heading one, Heading Two".
  2. Focus moves to the Button, announcing the button: "Action, Button".

This creates a problem since the order here matters and the original order is different then the one being announced.

Solutions

There are two ways that developers have been solving this issue:

  1. Remove android:focusable from the container (no need to group text content if they are separated by focusable views) or...
  2. Add android:focusable to the two TextView inside.

Removing android:focusable from the container is not always an option and having focus on a container can actually be important. For example, a ViewPager will announce that you are on a multi-page view, which page it is currently displaying, and allowing us to use gestures or keyboard arrows to switch pages. We still want to keep this behavior. ViewPager is a very popular widget and it is quite common to find this problem on many Android apps.

Adding android:focusable to the two TextView is a solution but it makes non-focusable views now focusable to keyboard users (the issue mentioned at the beginning). For ViewPager, developers must also provide at least an android:contentDescription to the ViewPager, otherwise the it won't gain focus since it has no description.

Adding android:importantForAccessibility="yes" to the two TextView doesn't help since they are already set to yes by default. What dictates the accessibility focus behavior here is wether child views is focusable or not.

Luckily, we can set a view focusable only to accessibility services. To do this, we have to set to true the setFocusable(boolean focusable) method in the AccessibilityNodeInfo using an accessibility delegate instead of the setFocusable(boolean focusable) from a View.

It would be better, though, if Android could provide something like android:accessibilityFocusable. There could be other options like changing TalkBack behavior or fixing the most popular ViewGroups like ViewPager, RecyclerView, etc.


I'm looking for a feedback on this, see if any of my suggestions or assumptions are wrong, or even the possibility of Google to implement any of the suggestions.

Thanks!

Lacking a main activity

When you try to run this in Android Studio, it says "Default Activity is not found". And in fact there is no activity in the project.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.