I am working on the accessibility and currently I want to set the Button Role on the MenuItem. We have checked but not got the proper solution for the same. I tried by setting the custom action layout and then giving the custom action layout Button Role it detect it as button but click need to be handled by setting the click listener on action layout. Which I want to avoid, is there any possibility that we can set role to MenuItem. So it will announce like "Setting Button Double Tap To Activate"
`#JvmStatic
fun View.setCustomRole(roleInfo: String) {
ViewCompat.setAccessibilityDelegate(this,
object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(
v: View,
info: AccessibilityNodeInfoCompat
) {
super.onInitializeAccessibilityNodeInfo(v, info)
info.roleDescription = roleInfo
}
})
}`
Tried above method by setting action layout which work but I need to change the click handling from app which I want to avoid.
This was answered fairly recently, however I think I can clean it up somewhat.
Option 1
You can make MenuItem's buttons by default by ensuring you have the latest material library imported.
implementation 'com.google.android.material:material:1.7.0'
The sample app I created for my answer was 1.5.0 and it still had the default "button" announcement.
Option 2
In Material 1.7.0:
I didn't need any of this code to achieve the solution to the question
onInitializeAccessibilityNodeInfo host and info are not nullable!
Ensure that your MenuItem has an actionViewClass associated with it.
<item
...
android:icon="ICON_REFERENCE"
app:showAsAction="ifRoom"
app:actionViewClass="android.widget.ImageButton"
...
/>
Bonus to option 2:
To be able to customize a11y attributes, you can then get the item and assign custom role descriptions or extra actions:
// inside onCreateOptionsMenu
val menuActionView = menu
.findItem(R.id.action_settings)
.actionView as ImageButton
ViewCompat.setAccessibilityDelegate(menuActionView, object: AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(
host: View?,
info: AccessibilityNodeInfoCompat?
) {
super.onInitializeAccessibilityNodeInfo(host, info)
info?.apply {
// not required as this is already a button
// always use a built in class as this will be localized
// automatically for you
// roleDescription = Button::class.java.simpleName
// I found I had to set this here, and not in the menu xml
contentDescription = getString(R.string.action_settings)
// to replace the term "activate" in "double tap to activate"
// in production apps, use a localized string!
addAction(
AccessibilityNodeInfoCompat.AccessibilityActionCompat(
AccessibilityNodeInfoCompat.ACTION_CLICK, "Open menu"
)
)
}
}
})
Related
I am trying to change accessibility content description for Android menu item.
Here is my code and talk back announcing => Test search, Search, double tap to activate.
<item
android:id="#+id/menuItemSearch"
android:icon="#drawable/search"
android:iconTintMode="src_atop"
android:title="Search"
android:visible="false"
app:iconTint="#color/primary"
app:contentDescription="Test Search"
app:showAsAction="always"/>
How can change it to => Search. Button. Double tap to search.
Solution
Add -> app:actionViewClass="android.widget.ImageButton"
<item
android:id="#+id/menuItemSearch"
android:icon="#drawable/icongel_search"
android:iconTintMode="src_atop"
android:title="#string/toolbarSearchIcon"
android:visible="false"
app:iconTint="#color/primary"
app:showAsAction="always"
app:actionViewClass="android.widget.ImageButton"/>
Then
menu.findItem(R.id.menuItemSearch).apply {
val searchIcon = this.actionView as ImageButton
searchIcon.apply {
setImageResource(R.drawable.search)
setColorFilter(ContextCompat.getColor(context, R.color.primary), PorterDuff.Mode.SRC_ATOP)
contentDescription = getString(R.string.toolbarSearchIcon)
setBackgroundColor(ContextCompat.getColor(context, R.color.transparent))
ViewCompat.setAccessibilityDelegate(
this,
object : AccessibilityDelegateCompat() {
override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfoCompat) {
super.onInitializeAccessibilityNodeInfo(host, info)
info.addAction(
AccessibilityNodeInfoCompat.AccessibilityActionCompat(
AccessibilityNodeInfoCompat.ACTION_CLICK, getString(R.string.toolbarSearchIcon)
)
)
}
}
)
}
}
There are 2 issues here:
1. Menu items not announcing as a button
You need to import the latest material library in your app's build.gradle file.
implementation 'com.google.android.material:material:1.7.0'
Be wary as there may be other dependencies.
2. Create a custom action label
Currently this is not possible as you would need to get access to the view in the Toolbar. Then you could use the following method as described in the documentation:
ViewCompat.replaceAccessibilityAction(
// View that contains touch & hold action
itemView, // <-- this is what we don't have
AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_LONG_CLICK,
// Announcement read by TalkBack to surface this action
getText(R.string.favorite),
null
)
You could probably raise a bug on the Issue Tracker for problem 2.
I want to show a badge on a toolbar action. The badge number is updated by a LiveData value.
This is how I attach the badge:
BadgeUtils.attachBadgeDrawable(inboxBadgeDrawable, toolbar, R.id.menu_inbox);
I tried different places for that call, including Activity.onCreateOptionsMenu(), Activity.onPrepareOptionsMenu() and androidx.lifgecycle.Observer.onChanged().
When anything changes (toolbar or badge content), the badge is misplaced, traveling down left. Or it is duplicated to another action.
I guess attachBadgeDrawable tries to find the container view of R.id.menu_inbox inside the toolbar, inserts the badge and updates it's offsets. If the container view of the menu item changes, the old container view still has the old badge and there is no (sensible) way to remove it. Also, application of the offsets seems to stack.
So, is there any other intended way of using the BadgeDrawable on a toolbar action icon?
I understand that this feature is still experimental. Will this issue be addressed and if yes, how long will it approximately take? (I use com.google.android.material:material:1.3.0-beta01 right now.)
This question is mainly addressed to the developers of the component because usage questions should be asked here according to https://github.com/material-components/material-components-android.
EDIT: I also created an issue (feature request) on the project's tracker: https://github.com/material-components/material-components-android/issues/1967
I'm not sure it is an official solution but this is still a workaround. I ended up with detaching the BadgeDrawable on every onPrepareOptionsMenu, in case the menu items were changed or rearranged
// This is an indicator of whether we need to show the badge or not
private var isFilterOn: Boolean = false
private var filterBadge: BadgeDrawable? = null
#SuppressLint("UnsafeExperimentalUsageError")
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
val filterItem = menu.findItem(R.id.action_filter)
val toolbar = requireActivity().findViewById<Toolbar>(R.id.toolbar)
if(filterBadge != null) {
BadgeUtils.detachBadgeDrawable(filterBadge!!, toolbar, R.id.action_filter)
filterBadge = null
}
if(isFilterOn) {
filterBadge = BadgeDrawable.create(requireContext()).also {
BadgeUtils.attachBadgeDrawable(it, toolbar, R.id.action_filter)
}
}
}
When Accessibility Talkback is ON. I have a requirement to show my view with some custom actions when the user draws angular gesture i.e. swipe up and right.
Similar like Gmail messages as shown in the pic below
enter image description here
this popup is shown when the user draws action gesture i.e. swipe up and right when the focus is on any message.
Action menu
You can add a custom action to the AccessibilityNodeInfo object inside the onInitializeAccessibilityNodeInfo method of an AccessibilityDelegate.
If the action is then selected by the user, the performAccessibilityAction method is called on the View.
In the example "myCustomAction" is the text that is displayed to the user.
MyCustomView.kt
init {
accessibilityDelegate = object : View.AccessibilityDelegate() {
override fun onInitializeAccessibilityNodeInfo(host: View?, info: AccessibilityNodeInfo?) {
super.onInitializeAccessibilityNodeInfo(host, info)
info?.addAction(AccessibilityNodeInfo.AccessibilityAction(R.id.myCustomAccessibilityEvent, "myCustomAction"));
}
}
}
override fun performAccessibilityAction(action: Int, arguments: Bundle?): Boolean {
if (action == R.id.myCustomAccessibilityEvent) Log.d("TAG", "Accessibility event triggered")
return true;
}
res/values/accessibility.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="id" name="myCustomAccessibilityEvent"/>
</resources>
In order to add an accessibility action the below line of code suffices, using ViewCompat which is backwards compatible to API 21.
ViewCompat.addAccessibilityAction(viewToAddActionTo,"String to describe what the action does", (v,b) -> actionCalled);
//a sample request would look like
ViewCompat.addAccessibilityAction(swipeLayout,"Delete Item in List",(v,b) -> deleteItem(position);
Hopefully this suffices, the action to be done i.e delete, star, archive (Similar to Gmail) can be added to custom views using this.
Key Points are to figure out which View needs the action and what should the action perform.
I'm trying to implement the share functionality within my app. So far it works fine and I can share text to all other apps. The problem is the way it's shown.
I want something like just the share icon visible and then when user taps on it, it opens the OS dialog and lets user choose the app they want to share content to.
var share_article = menu.FindItem (Resource.Id.action_share);
var share_article_provider = (Android.Support.V7.Widget.ShareActionProvider) Android.Support.V4.View.MenuItemCompat.GetActionProvider (share_article);
share_article_provider.SetShareIntent (CreateIntent ());
and the xml:
<item
android:id="#+id/action_share"
myapp:showAsAction="ifRoom"
android:title="share"
myapp:actionProviderClass="android.support.v7.widget.ShareActionProvider" />
My app currently looks like this:
There's also a white border around it that I don't like.
Is there any way to change the icon??
How do I fix it??
You just want to turn off your share history.There is no official API to do this, but you can make your own ShareActionProvider. Actually there are two similar question on SO:
How do you turn off share history when using ShareActionProvider?
How to hide the share action (which use most) icon near the share action provider?
Wish these could help you.
As mentioned here when using support library this can be fixed really easily. This method won't turn off the share history but will hide the icons from actionbar.
I just needed to subclass the Android.Support.V7.Widget.ShareActionProvider like the following: (C# using Xamarin)
public class MyShareActionProvider : Android.Support.V7.Widget.ShareActionProvider
{
public SingleArticleShareActionProvider (Context context) : base (context)
{}
public override View OnCreateActionView ()
{
return null;
}
}
and then inside OnCreateOptionsMenu use the MyShareActionProvider like:
var share_article = menu.FindItem (Resource.Id.action_share);
var share = new SingleArticleShareActionProvider (globalContext);
Android.Support.V4.View.MenuItemCompat.SetActionProvider (share_article, share);
share_article.SetIcon (Resource.Drawable.abc_ic_menu_share_mtrl_alpha);
share.SetShareIntent (CreateIntent ());
You can use any icon you like with the method SetIcon.
I've looking for a way to add a menu to my app, that way when someone clicks the "menu" button they will get a custom menu that I will create. however there are no relevant posts about this. I'm looking for something similar to this:
win.addEventListner("android:menu", function(e){//DO SOMTHING});
You cannot add an event to menu button, however, you can use the default menu.
Take a look at the doc: http://developer.appcelerator.com/apidoc/mobile/latest/Titanium.Android.Menu-object
This is what I use:
// make sure window is already opened before calling this
Ti.UI.currentWindow.activity.onCreateOptionsMenu = function(e) {
var menu = e.menu;
var backItem = menu.add({title: 'Back',enabled: true, itemId: '1',visible:true});
backItem.addEventListener('click',function(){
// do the event handling here
});
}
When the menu button is clicked one of these two functions of the current activity is called :
onCreateOptionsMenu
onPrepareOptionsMenu
So we can do something like:
Ti.UI.currentWindow.activity.onCreateOptionsMenu = function(e) {//DO THE CUSTOM MENU };
Ti.UI.currentWindow.activity.onPrepareOptionsMenu = function(e) {//DO THE CUSTOM MENU};