I am new to accessibility stuff in android and can't really understand what's happening in the below case.
I have got a custom edittext in a custom AlertDialog, I have a requirement to select all the text in the edittext when the dialog is shown. To achieve this, I am using
editText.setSelectAllOnFocus(true)
Because of this, My Accessibility Talkback speaks action:share whenever the edit text is focused. Initially, I thought this is because of the local context menu of the edittext and then I disabled the local context menu using below code
editText.customSelectionActionModeCallback = object : ActionMode.Callback {
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
return false
}
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
return false
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
return false
}
override fun onDestroyActionMode(mode: ActionMode?) {}
}
Even then I could not get rid of the Talkback speaking "action:share". Further, I override the "onInitializeAccessibilityNodeInfo" function on edittext to debug what actions are allocated to the Talkback and I see that some Unknown action has the label "Share" (refer to the screenshot below)
I am able to remove the "action:share" with the code below, but not sure if this is the right way and will have any side effects
editText.setAccessibilityDelegate(object : AccessibilityDelegate() {
override fun onInitializeAccessibilityNodeInfo(host: View?, info: AccessibilityNodeInfo?) {
super.onInitializeAccessibilityNodeInfo(host, info)
info?.actionList?.clear()// removes the action share
}
})
Please advise, If there is any better way for handling actions for Accessibility in Android.
Note: I tested this on Pixel 1(7.1) and Samsung Note 8(7.1.1)
Related
On text selection, I do not want the user to see the Call option when they try to select a number from the TextView.
Is there a way to disable the call intent itself for the app?
I tried below, but I could not find the call option in the Menu to remove it.
fun TextView.disableCall() {
setCustomSelectionActionModeCallback(
object : ActionMode.Callback {
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?) = false
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
menu?.apply {
//Could not find the call option menu to remove
removeItem(android.R.id.copy)
removeItem(android.R.id.cut)
}
return true
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?) = false
override fun onDestroyActionMode(mode: ActionMode?) {
// no-op
}
}
)
}
I need to hide the copy,cut,select all and paste option when I double tap or long click the edit text view in Android application. I have checked several ways and the below is one of them that I checked. I also checked with android:longClickable="false" and android:textIsSelectable="false" in xml file. It needs to work in all the OS versions.Please check the attached screenshot.
Check the options that is displaying when long press the edit text view Check the different options displaying when double tap the edit text view
val callback: ActionMode.Callback = object : Callback, ActionMode.Callback {
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
menu?.apply {
removeItem(android.R.id.copy)
removeItem(android.R.id.cut)
removeItem(android.R.id.paste)
removeItem(android.R.id.shareText) // Share
removeItem(android.R.id.textAssist)
}
return true
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
menu?.removeItem(android.R.id.paste)
return true
}
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
return false
}
override fun onDestroyActionMode(mode: ActionMode?) {}
}
editText.setCustomInsertionActionModeCallback(callback)
editText.setCustomSelectionActionModeCallback(callback)
I have a list activity in which the user can do open action modes A and B. The user can also open action mode A then B on top of it.
The problem is that when action mode A is shown and action mode B is shown on top of it, A gets closed automatically when B is shown. The user can't navigate back from B to A.
I thought I could take note of action mode A visibility when action mode B is opened then restore it when action mode B is closed, but that doesn't work. It seems that it's not possible to immediately show another action mode after closing one. However this issue only happens if the action mode A is closed with the back arrow. When closed by code (ActionMode.finish()), action mode B can be shown, but the closing animation doesn't make it look like user is navigating back from B to A. So this solution is out of question.
So is there a way to open an action mode on top of another, or at least replace the menu layout of an action mode programatically then change it back?
Action mode A is actually the search action mode, from which the user can select results which opens action mode B if that can help to understand.
I think I reached desired behavior through postDelayed() from onDestroyActionMode of action mode B.
This is not very elegant solution, but it works.
My code snippet:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
listView.onItemClickListener = AdapterView.OnItemClickListener { adapterView, view, p2, p3 ->
startActionModeA()
true
}
}
var actionModeA: ActionMode? = null
val actionModeCallbackA = object : ActionMode.Callback {
override fun onActionItemClicked(mode: ActionMode?, p1: MenuItem?): Boolean {
Log.wtf("ACTION MODE", "onActionItemClicked")
actionModeB = startActionMode(actionModeCallbackB)
return true
}
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
Log.wtf("ACTION MODE", "onCreateActionMode")
val inflater = mode?.getMenuInflater()
inflater?.inflate(R.menu.context_menu, menu)
return true
}
override fun onPrepareActionMode(p0: ActionMode?, p1: Menu?): Boolean {
Log.wtf("ACTION MODE", "onPrepareActionMode")
return false
}
override fun onDestroyActionMode(p0: ActionMode?) {
Log.wtf("ACTION MODE", "onDestroyActionMode")
actionModeA = null
}
}
var actionModeB: ActionMode? = null
val actionModeCallbackB = object : ActionMode.Callback {
override fun onActionItemClicked(mode: ActionMode?, p1: MenuItem?): Boolean {
Log.wtf("ACTION MODE 2", "onActionItemClicked")
return true
}
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
Log.wtf("ACTION MODE 2", "onCreateActionMode")
val inflater = mode?.getMenuInflater()
inflater?.inflate(R.menu.context_menu2, menu)
return true
}
override fun onPrepareActionMode(p0: ActionMode?, p1: Menu?): Boolean {
Log.wtf("ACTION MODE 2", "onPrepareActionMode")
return false
}
override fun onDestroyActionMode(mode: ActionMode?) {
Log.wtf("ACTION MODE 2", "onDestroyActionMode")
actionModeB = null
listView.postDelayed({
startActionModeA()
}, 100)
}
}
private fun startActionModeA() {
actionModeA = startActionMode(actionModeCallbackA)
}
I have an Implementation of ActionMode to display the number of multi
selected items in a RecyclerView.
I would like to know when the back button in the actionMode is tapped so as to correspondingly reset the recyclerView but while implementing the ActionMode.Callback, i noticed that onDestroyActionMode is called whenever ActionMode is updated thus actionMode?.setTitle($selectedItems.size), which makes it impossible reset the recyclerView - remove selected items, remove overlay color and notify the recyclerview of data set changed.
Here's my Callback
inner class ActionModeCallback : ActionMode.Callback {
override fun onActionItemClicked(mode: ActionMode?, item: MenuItem?): Boolean {
when (item?.getItemId()) {
R.id.action_delete -> {
myAdapter?.deleteSelectedIds()
actionMode?.setTitle("") //remove item count from action mode.
actionMode?.finish()
return true
}
}
return false
}
override fun onCreateActionMode(mode: ActionMode?, menu: Menu?): Boolean {
val inflater = mode?.getMenuInflater()
inflater?.inflate(R.menu.action_mode_menu, menu)
return true
}
override fun onPrepareActionMode(mode: ActionMode?, menu: Menu?): Boolean {
menu?.findItem(R.id.action_delete)?.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS)
return true
}
override fun onDestroyActionMode(mode: ActionMode?) {
Log.d(TAG, "onDestroyActionMode Called")
//myAdapter?.selectedIds?.clear()
//myAdapter?.notifyDataSetChanged()
actionMode = null
}
}
How Can i know when the ActionMode back button is tapped?
Full source code Here =>https://github.com/Edge-Developer/RecyclerViewMultiSelectExample
Holy Gosh. It was my fault, i was starting a New ActionMode each time an item is selected (through an interface on MainActivity) instead of checking if it has already been started before starting a new One.
Here was my code
actionMode = startActionMode(ActionModeCallback())
Here is the updated code
if (actionMode == null) actionMode = startActionMode(ActionModeCallback())
now, the onDestroyActionMode is called only once, after the actionMode is dismissed!
You can check the github repo on how's it was implemented
This problem was faced while implementing multiselection on a recyclerView.
So, I have Fragment with buttons and when the user clicks on one of them, they get selected and look like this:
To set the button as selected I first set onTouchListener to the Button which does that:
override fun onTouch(view: View?, event: MotionEvent?): Boolean {
view?.isPressed = true
if (event?.actionMasked == ACTION_UP) {
view?.performClick()
}
return true
}
but when I pull the StatusBar down, the Button gets deselected and look like this:
What I want is the Button to stay selected all the time, but onResume is not called when I pull up the StatusBar so I don't know if that happened or not.
Why does that happen? Is there any callback that is called when the status bar is pulled up or down?
Turns out that the only callback that is called in this situation is the onWindowFocusChanged(hasFocus: Boolean) in the Activity, but nothing happens in the Fragment, so I created an interface:
interface ActivityActionsListener {
fun onWindowFocusChanged(hasFocus: Boolean)
}
that I implement in the Fragment and call from the Activity like this:
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
(currentFragment as? ActivityActionsListener)?.onWindowFocusChanged(hasFocus)
}
and in the Fragment:
fun onWindowFocusChanged() {
setupButtons()
}
You Can try Setting Selection again when activity resumes
in The OnResume Method
Hope This Helps
Or Else Post Your Code