Triggering Android TalkBack Text Programmatically Via A WebView

Short Answer

Use a visually hidden aria-live region and copy the relevant text into that region. Additionally use a <button> not a <a href="#" for interactive content.

Long Answer

Messing with the user focus without a defined purpose (such as focusing the close button in a modal when it opens)...bad idea, the last thing you want to do is make a user lose their place on the page.

Additionally this would mean you would need to add a load of tabindex="-1" attributes throughout the page on any element you want to read out that doesn't normally receive focus, making maintainability a nightmare.

If you want to have text read out then may i suggest you use an aria-live region.

aria-live on an element is designed to speak out any content that is contained within that element out loud. This happens every time the element's content gets updated.

You will notice I added aria-live="polite" as that makes sure they announcement doesn't interrupt other announcements on the page.

For completeness there is also aria-live="assertive" (but you should only really use that for things like warnings and errors / status updates as it will cut off any currently spoken sentence) and aria-live="off" (which is useful for disabling your talk back feature as it is effectively the same as having no aria-live on the element)

What I would recommend is that you copy the text out of the corresponding element you want read out and into a visually hidden aria-live region.

In case you aren't aware visually hidden text is screen reader accessible text that is not visible on the screen. Although there are many libraries out there with a sr-only class built in I would encourage you to use the visually hidden class in the fiddle below due to better compatibility and future proofing as I explained in this answer

I also demonstrated the concept for how to make it reusable across your web view, apologies for the use of jQuery, it's late here and I wanted to put the fiddle together quickly for you!

Try the below in a screen reader, I think the code is self explanatory but any questions just ask. Finally depending on your "verbosity" settings the third button shows that things get announced the same (with semantic meaning such as "heading" and "link") if you copy those into the aria-live region...if you don't hear these your settings may need changing.

example

$('button').on('click', function(e){
    console.log($(this).data('target'));
    
    var targetID = $(this).data('target');
    var text = $('#' + targetID).html();
    console.log(text);
    $('.speak-aloud').html(text);
    
});
.visually-hidden { 
    border: 0;
    padding: 0;
    margin: 0;
    position: absolute !important;
    height: 1px; 
    width: 1px;
    overflow: hidden;
    clip: rect(1px 1px 1px 1px); /* IE6, IE7 - a 0 height clip, off to the bottom right of the visible 1px box */
    clip: rect(1px, 1px, 1px, 1px); /*maybe deprecated but we need to support legacy browsers */
    clip-path: inset(50%); /*modern browsers, clip-path works inwards from each corner*/
    white-space: nowrap; /* added line to stop words getting smushed together (as they go onto seperate lines and some screen readers do not understand line feeds as a space */
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="visually-hidden speak-aloud" aria-live="polite"></div>

<button data-target="p2">Read text in paragraph 2</button>
<button data-target="p4">Read text in paragraph 4</button>
<button data-target="longer-text">Read the section contents</button>

<p>This is some other text</p>
<p id="p2">This is the text to be read aloud in paragraph 2</p>
<p>Some more filler text</p>
<p id="p4">paragraph 4, will be read aloud when the second button is clicked.</p>



<section id="longer-text">
<h2>Section Header</h2>
<a href="https://google.com">Go to google link for an example of more complex text</a>
</section>

A final word on semantics based on your example.

For accessibility - semantics matter.

Anchors should be used for a page change (or in a SPA a URL change if the whole thing is AJAX powered), buttons should be used for interactive content on the same page. So use a <button> instead of <a href="#"> as that lets a screen reader user know what to expect (if I click a link I expect a page change, if I press a button I expect some form of interactive content).


Step 1: Create a Javascript Interface

public class JavascriptSpeakInterface {
    private Context context;
    private TextToSpeech tts;

    public JavascriptSpeakInterface(Context context, TextToSpeech tts){
       this.context = context;
       this.tts = tts;
    }

    @JavascriptInterface
    public void speak(String msg){
       tts.speak(msg, TextToSpeech.QUEUE_FLUSH, null, null);
    }
}

Step 2: Connect the interface with your webview

webView.addJavascriptInterface(new JavascriptSpeakInterface(getContext(), new TextToSpeech(getContext(), this)), "android");

Step 3: Invoke speak function in javascript

android.speak(string);

Note: Javascript must be enabled in your webview.

Explanation:

Step 1, The class is created to have an interface between Android and Web Code.

Step 2, connects the Android and web code together, where "android" can be used to access the methods of the interface. (You can change it to whatever you like)

Step 3, invokes the speak method of the interface from javascript, using tag "android".