Android How can I detect autofill pressed? - android

I use AutofillManager https://developer.android.com/reference/android/view/autofill/AutofillManager and can't understand how can I detect is user enter value himself or user select any autofill option.
I try to use editText.autofillValue but it contains value both cases
Anyone knows how can I resolve it? Help me, please!)
P.S. code
I have function to request autofill
fun allowAutoFill(view: View?) {
if (view != null) {
val afm = requireContext().getSystemService(AutofillManager::class.java)
if (afm.isAutofillSupported && afm.isEnabled) {
afm?.requestAutofill(view)
}
}
}
After that i want to know user enter something or select value from autofill. It's needed for analytics.
private fun isAutoFillApplied() = binding?.editPassword?.autofillValue?.textValue?.isNotEmpty() == true
but binding?.editPassword?.autofillValue contains value if user enter something and if user select autofill option

I couldn't find cool answer, so I used bad dirty hack.
I have two edittext in screen. If user select any autofill option this edittexts filled the same time. So if time difference on text changed not so big, I fill isAutofill value as true.
This is really not pretty solution, but I can't find another.
This is looks like this
var lastChangedTime = 0L
var isAutoFill = false
var lastRepeatPassword: String? = null
editPassword.addTextChangedListener { text ->
lastChangedTime = System.currentTimeMillis()
}
editRepeatPassword.addTextChangedListener { text ->
if (text.toString() != lastRepeatPassword) {
lastRepeatPassword = text.toString()
isAutoFill = (System.currentTimeMillis() - lastChangedTime) < 50
}
}

Related

Android AccessibilityService performAction() method not working

I am developing an accessibility service for Android. The service calls an app, and that app has a RecyclerView. Then I want to click on an element of the RecyclerView with performAction(AccessibilityNodeInfo.ACTION_CLICK) but it is not working. I know there are a few similar questions but none of them works for me. Also I checked the official documentation for the class of the performAction method https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo
This is my code:
#Override
public void onAccessibilityEvent(Accessibility event){
AccessibilityNodeInfo source = event.getSource();
if(source != null){
List<AccessibilityNodeInfo> list = source.findAccessibilityNOdeInfosByText("mystring");
list.get(0).performAction(AccessibilityNodeInfo.ACTION_CLICK);
}
}
This is my configuration xml file:
<accessibility-srvice xmlns...
android:accessibilityFeedbackType = "feedbackGeneric"
android:AccessibilityFlags = "flagDefault"
android:canPerformGestures = "true"
android:canRetrieveWIndowCOntent = "true"
I think I misunderstood something, but i don't know what can be. Any help is appreciated.
The simple answer is that while finding the node by text is fine, that particular node was not the node with the desired onClick event. The solution is to call
list.get(0).getParent().performAction(AccessibilityNodeInfo.ACTION_CLICK)
The clarifying discussion is below
I think .performAction(AccessibilityNodeInfo.ACTION_CLICK) is right, but there might be some other concerns. Sorry for posting as an answer but a comment is too small.
Are you sure the onAccessibilityEvent is being called? I don't think that is the right event, but I can't be sure. Maybe put a log in there to ensure it's calling the event when you expect it to be called.
Also, looking at the source might restrict your search, maybe instead of event.getSource() try using rootInActiveWindow (I use Kotlin so it might have a method, see https://developer.android.com/reference/android/accessibilityservice/AccessibilityService#getRootInActiveWindow(int))
EDIT: 28 March 2022
I have run this code on my own accessibility service and it does click the button. But it's very prone to overflow.
var ranOnce = false // prevent overflow
override fun onAccessibilityEvent(event: AccessibilityEvent?) {
if (event == null) return
if (event.eventType == TYPE_WINDOW_STATE_CHANGED) return
if (event.source != null && !ranOnce) {
val nodeList = rootInActiveWindow.findAccessibilityNodeInfosByText("Menu")
//event.source.findAccessibilityNodeInfosByText("Menu") // <-- always nothing in list
Log.d("onAccessibilityEvent", "List of nodes: $nodeList")
if (nodeList.size > 0) {
android.util.Log.d("onAccessibilityEvent", "Node info: ${nodeList[0]}")
ranOnce = true
nodeList[0].performAction(AccessibilityNodeInfo.ACTION_CLICK) //<-- caused an infinite loop!
} else {
Log.d("onAccessibilityEvent", "No nodes found")
}
} else {
Log.d("onAccessibilityEvent", "event.source is null!")
}
}

Set app as default button interaction with UI automator

I have a test case where in the app "Set as default" prompt is opened. I want to test that with UI automator, and I had success with testing that case, but not 100% reliable. Unfortunately, some devices have "Set as default" prompt button written in caps, and some of those don't, so I'm not able to create 100% reliable tests for this test case. I have written this code below, but when fetching "Set as default" button by text, case of the letters don't play a role, but when I want to interact with that button, text case is important. Switching the IF-ELSE cases doesn't fix the problem in this case. And somehow, none of the dialog buttons ids work (button1, button2..) when I want to press those.
if (roleManager.isRoleAvailable(android.app.role.ASSISTANT)) {
if (!roleManager.isRoleHeld(android.app.role.ASSISTANT)) {
val myApp = device.findObject(UiSelector().textMatches(InstrumentationRegistry.getInstrumentation().targetContext.getString(R.string.app_name)))
myApp.click()
sleepLong()
var setAsDefaultButton: UiObject? = null
if (device.findObject(UiSelector().text("Set as default")) != null) {
setAsDefaultButton = device.findObject(UiSelector().text("Set as default"))
setAsDefaultButton?.click()
} else if (device.findObject(UiSelector().text("SET AS DEFAULT")) != null) {
setAsDefaultButton = device.findObject(UiSelector().text("SET AS DEFAULT"))
setAsDefaultButton?.click()
} else {
clickDialogPositiveButton()
}
}
}
You can use the Pattern object instead of using a string.
You can use in your code like:
val pattern = Pattern.compile("Set as default", Pattern.CASE_INSENSITIVE)
val setDefaultText = device.findObject(UiSelector().text(pattern))
if(setDefaultText != null)) {
setDefaultText.click()
} else {
clickDialogPositiveButton()
}
Based on the Jordan's example and hint, solution to this is to find an object with the Pattern. With pattern, you can search for UIObject with By.text(pattern). Take a note that the object found with the pattern needs to be UIObject2 instead of the UIObject.
val pattern = Pattern.compile("Set as the default", Pattern.CASE_INSENSITIVE)
if(device.findObject(UiSelector().text(pattern.toString())) != null) {
device.findObject(By.text(pattern)).click()
}

Using Chromecast SDK to determine if closed captions are enabled on the receiver for android

I would like to be able to determine if my receiver (CAF receiver) has captions being displayed. This will be so that I can rely on the receiver to tell the sender that captions are enabled, rather than saving the previous state of closed captions on the sender. Is there a method or a way of doing this using the remoteMediaClient?
I'm uncertain if you mean the sender or receiver, but I'll give you both :)
It is possible to get it on Android like so
private val SUB_TITLE_TYPES = intArrayOf(MediaTrack.SUBTYPE_SUBTITLES, MediaTrack.SUBTYPE_CAPTIONS)
fun getActiveMediaTracks(context: Context): LongArray =
getRemoteMediaClient(context)?.mediaStatus?.activeTrackIds ?: longArrayOf()
fun getSubtitleTracks(context: Context): List<MediaTrack> =
getActiveMediaTracks(context).filter {
it.type == MediaTrack.TYPE_TEXT && it.subtype in SUB_TITLE_TYPES
}
or on the Chromecast Receiver (TextTracksManager)
cast.framework.CastReceiverContext.getInstance().getTextTracksManager().getActiveTracks()
EDIT:
Can see that I mixed up the two functions when I copied the code from our IDE. There are active ids and all media tracks (this includes audio, video, texts). There might be a difference between MediaTrack.SUBTYPE_SUBTITLES*, guess that depends on the stream.
Heres how to find the active text tracks
val remoteMediaClient = CastContext.getSharedInstance(context).sessionManager?.currentCastSession?.remoteMediaClient
remoteMediaClient?.mediaInfo?.mediaTracks?.filter {
it.type == MediaTrack.TYPE_TEXT && it.subtype in SUB_TITLE_TYPES
}?.let {
textTracks ->
val activeTrackIds = remoteMediaClient.mediaStatus?.activeTrackIds?.filter { activeTrackId ->
textTracks.none { track -> track.id == activeTrackId }
}.toLongArray()
activeTrackIds.size > 0
}

kotlin: How to get if EditText has setError enabled?

How can I validate if EditText has setError enabled ?
I want to disable a button if EditText has an error.
Any other way to achieve this.
It kinda works when I put view.calcbutton.setEnabled(false) inside the validateEditText-function, but I use the validateEditText-function to validate multiple EditTexts and only the last function-call disables the button.
if the first function-call disables the button, the second enables it again, and vice versa.
But I want do it outside this function because if one of the multiple EditTexts has setError the button should be disabled.
//global var blockcalcbutton
var blockcalcbutton = 0
//function to validate EditTexts and set blockcalcbutton=1 if setError
validateEditText(view.input_volt, view, getString(R.string.invalid_volt))
if(blockcalcbutton == 1) {
view.calcbutton.setEnabled(false)
view.calcbutton.setText(getString(R.string.calcbutton_disabled))
view.calcbutton.setBackgroundResource(R.color.buttonDisabled)
} else {
view.calcbutton.setEnabled(true)
view.calcbutton.setText(getString(R.string.calcbutton_enabled))
view.calcbutton.setBackgroundResource(R.color.buttonBackground)
}
fun validateEditText(editText: EditText, message: String) {
val myEditText = editText
myEditText.addTextChangedListener(object: TextWatcher {
override fun afterTextChanged(s: Editable?) {
if(myEditText.text.toString() == "" || myEditText.text.toString() == "." || myEditText.text.toString() == "0") {
//setError
myEditText.setError(message)
//var to disable Button
blockcalcbutton = 1
} else {
//delete setError
myEditText.setError(null)
//var to enable Button
blockcalcbutton = 0
}
}
You can create a callback to notify when you set an error or delete it.
interface EditTextErrorListener {
fun onErrorSet()
fun onErrorDeleted()
}
Here you can notify:
if(myEditText.text.toString() == "" || myEditText.text.toString() == "." || myEditText.text.toString() == "0") {
//setError
myEditText.setError(message)
---> listener.onErrorSet()
//var to disable Button
blockcalcbutton = 1
} else {
//delete setError
myEditText.setError(null)
---> listener.onErrorDeleted()
//var to enable Button
blockcalcbutton = 0
}
Try approaching the problem from further away; when you look at this issue, you have multiple inputs (all the fields in your form) and one boolean output:
All fields are OK -> Enable the button
One or more fields are NOT Ok -> disable the button.
Additionally, you have local validation on each field (to display the error, etc.).
I'd argue that the local validation on each field, is to be done at the callback from the edit text (onAfterText, etc.etc.). You are already doing this.
A way to ensure the final validation (of the form as a whole) is fast, you could use a reference counter. E.g.:
Each edit text, validates with afterTextChanged. Each one performs whatever validation you think is right (can be a shared one if they are all the same).
If validation fails, you keep a reference to the failed field.
This will not have side-effects because nothing happens whether the item is or is not on the list.
This is some pseudo-code:
// keep a list of fields (this is just a way to do it, there are many others)
var errorFields = MutableHashSet<EditText>
later in your "validation" (afterTextChanges for example):
if (xxx && yyy && zzz) { //your validations for the individual editText
//setError
myEditText.setError(message)
// Store the reference of the editField in error.
errorFields.add(theEditTextThatHasAFailure).
} else {
myEditText.setError(null)
// If the validation is ok, you remove it:
errorFields.remove(theEditTextThatHasFailure)
}
// The form may have changed, update the global button state.
updateButtonState();
All this method needs to do, is something like:
button.enabled = errorFields.isEmpty()
This will only be empty if there are no error fields.
This is just an idea you may need to combine with callbacks for further control, but remember this one thing:
EditTexts (or any other widget) is and should not be responsible for the business logic that drives the whole "Form"; they are merely individual pieces of a larger puzzle, and as such, it's incorrect to give them the responsibility to drive your Form's validations; they can (and should) however, validate themselves and handle their own error state (like you're doing), but that's as far as it should go.
They can inform of a state change (e.g. via the listener onAfterText, or after gaining/losing focus, etc.) but shouldn't make business logic decisions. EditTexts are designed to take user input and display it on screen, that's all.
Last but not least, don't forget to remove the references when you destroy your views
onDestroy() {
errorFields.clear()
}

where to put checking edittext syntax?

i need a favor.. i'm confused to put these codes to check whether the edittext is empty or not:
String input = editText.getText().toString();
if(input == null || input.trim().equals("")){
Toast.makeText(context, "Sorry you did't type anything"), Toast.LENGTH_SHORT).show();
}
where must i write these codes? is it between these codes?
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.menuawal);
...
...
...
JmlAhliWarisAnakLK = (EditText) findViewById(R.id.JmlAhliWarisAnakLK);
JmlAhliWarisAnakPR = (EditText)findViewById(R.id.JmlAhliWarisAnakPR);
or in this function after double sisa=0;??
public void cc() {
int JmlWarisAnakPR = Integer.parseInt(JmlAhliWarisAnakPR.getText().toString());
int JmlWarisAnakLK = Integer.parseInt(JmlAhliWarisAnakLK.getText().toString());
int JmlHarta = Integer.parseInt(JmlHartaPeninggalan.getText().toString());
double HasilSuami = 0;
double HasilIstri = 0;
double HasilAnakLK = 0;
double HasilAnakPR = 0;
double sisa = 0;
}
please correct me if i'm wrong.. :D
you are on the right track
After you set the layout using setContentView you need to add your EditText's which you are doing fine as follows.
JmlAhliWarisAnakLK = (EditText)findViewById(R.id.JmlAhliWarisAnakLK);
JmlAhliWarisAnakPR = (EditText)findViewById(R.id.JmlAhliWarisAnakPR);
You then need to store the value you get from the EditText's in some variable,
int JmlWarisAnakPR = Integer.parseInt(JmlAhliWarisAnakPR.getText().toString());
....
....
After you have stored your values you can then call some method that validates your input on click of a button(if you have):
public void validateinput()
{
if(input == null || input.trim().equals(""))
{
Toast.makeText(context, "Sorry you did't type anything"), Toast.LENGTH_SHORT).show();
}
}
According to me, you should put the check on some event, like if its login screen, then on click of submit button. or other wise on focus change it main instantly provide user with the toast that he left the field empty. or if other case, please provide more information for your query. thanks.
That depends on when you want to validate the editText..You propably have some button which "submits" the EditText so call this code in after onClick event gets fired on the button..
Put the input validation code when you have to navigate away from the current activity, either to go to another activity or to save the input details. That's the least annoying place to shove an error message onto the user.
Another approach is to validate when the focus leaves the EditText. But in this case the error notification should be more subtle (and therefore less annoying) like changing the EditText's background to lightred.
Ur questions does not seem to be clear. Are u asking where do u need to put the validation for empty edittext? If this is ur question then the general case would be to validate during any events such as BUTTON CLICK. Set the onClickListener for ur button and inside ur onclick perform the validation.
String input = editText.getText().toString();
if(input == null || input.trim().equals("")){
Toast.makeText(context, "Sorry you did't type anything"), Toast.LENGTH_SHORT).show();
}
Your above code is pretty much correct. You Must need to add above code whenever you want to take input from these edittext, Or whenever you want to save these value. make a function which will return true if edit text is empty so u can ask user to enter values
public boolean isETEmpty(){
String input = editText.getText().toString();
if(input == null || input.trim().equals("")){
Toast.makeText(context, "Sorry you did't type anything"), Toast.LENGTH_SHORT).show();
return true;
}
return false; // if not empty
}
call this function Whenever u want to use values from ET, if this function return true, you must let user enter values. Such as on Button Click to save etc

Categories

Resources