How to efficiently edit the text in an EditText in Android - java

I am new to java and android development and I am working on an application that takes input from the user through an EditText. However, sometimes the input data has some characters with an encoding that is different from what the app expects.
I have created a hashmap with all the possible mapping (about 460 elements) and whenever the user types in (or paste) something I use the afterTextChanged method to go through the text and swap the ones that matches the incorrect encoding with the correct ones that displays correctly.
My code works fine for small sized entry, however the longer it is the slower it gets because afterTextChanged will be called recursively after every swap I do. And when pasting a text that has about 30+ characters with incorrect encoding the application will crash.
here is my code:
public void afterTextChanged(Editable editable) {
substituteText(editable);
}
public CharSequence substituteText(Editable txt) {
if (txt.length() == 0) {
return txt;
}
for (int i = 0; i < txt.length(); i++) {
char ch = txt.charAt(i);
CharSequence ret = myHashMap.get(ch);
if (ret != null) {
txt.replace(i, i + 1, ret);
}
}
return txt;
}
I thought about cloning "editable" to some, change it and then copy it back to the edittext editable variable so it will only see it as one change but I failed to do it since I couldn't find an easy way to clone it.
Is there a more efficient way to do this?

If you use onTextChanged instead of afterTextChanged, you'll get passed the new character(s) the user typed and where they are, and you'd only need to check those.

Related

Storing text as an arraylist from JTextArea

I need to create a program to store all words in an array list. Then check the user input from the textfield to see if it starts with anything other than numbers and punctuation. Otherwise it will need to display an error and prvent the string to be added to the arraylist and display an appropriate error.
https://pastebin.com/8UwDm4nE
Heres the ActionEvent listener that contins the code to check that. Im not really sure how to get it working.
#Override
public void actionPerformed(ActionEvent e) {
for(int i = 0; i < 1; i++) {
String str = tf.getText(); // MUST BE STORED ON AN ARRAY LIST
ta.append(str + "\n"); // Append the text on new line each
if(str.startsWith(String.valueOf(nums))) { // Check input for a number at the start
error.setText("Error: Word starts a number. Please try again!");
error.setForeground(Color.RED);
ta.append("");
} else if (str.startsWith(String.valueOf(punct))) { // Check if input contains a punctuation at the start
error.setText("Error: Word starts with an illegal character. Please try again!");
error.setForeground(Color.RED);
ta.append("");
}
}
}
I'm going to rephrase your problem a bit as clarification, please correct me if I'm misunderstanding.
You have a text field and a text area. You want a user to type a word into the text field and submit it. If that word starts with a number or punctuation, then indicate an error to the user. Otherwise, add it to the text area (on a new line) and the inner ArrayList.
To solve this problem, there are a couple things you'll need:
An ArrayList<String> that is a class member variable where you can store your words
An event handler that handles the button click.
The event handler should:
Parse the string from the text field (using getText(), as you already are).
Do the error checks you're already doing.
If neither of the error conditions are hit (so add an else clause for this), add the word to the text area (which you're already doing) and add it to the ArrayList.
Hopefully this helps you get a clearer idea of how to approach the problem. If not, please post a code sample of what you tried and what error you're specifically running into.
EDIT:
Here is some pseudocode for your if-else error-handling block of code, assuming you declare a new ArrayList to hold your words as a class member:
// as class member variable
List<String> wordList = new ArrayList<>();
// word handler code
if (str starts with a number) {
// handle error
} else if (str starts with punctuation) {
// handle error
} else {
ta.append(str + "\n");
wordList.add(str);
}

Selenium sendKeys are not sending all characters

I'm using Java, Selenium, and Chrome for test automation. Our developers recently upgraded our UI from AngularJS to Angular2 (not sure if that matters). But since then, sendKeys is inputting incomplete characters in to the text field. Here's an example:
public void enterCustomerDetails()
{
txtFirstName.sendKeys("Joh201605130947AM");
txtSurname.sendKeys("Doe201605130947AM");
txtEmail.sendKeys("johndoe#gmail.com");
}
I also tried using executeScript. It didn't work. It can enter complete characters but the form thinks the field is null.
public void forceSendKeys(WebElement element, String text)
{
if (element != null)
((JavascriptExecutor) this.webDriver).executeScript("arguments[0].value=arguments[1]", element, text);
}
public void enterCustomerDetails()
{
forceSendKeys(txtFirstName, "Joh201605130947AM");
forceSendKeys(txtSurname, "Doe201605130947AM");
forceSendKeys(txtEmail, "johndoe#gmail.com");
}
I also tried using .click() before .sendKeys and adding in sleep time. They didn't work too.
I got an idea to enter the characters 1 by 1 from this post: How to enter characters one by one in to a text field in selenium webdriver?
It worked but that means I have to rewrite all my codes from sendKeys to the new function:
public void sendChar(WebElement element, String value)
{
element.clear();
for (int i = 0; i < value.length(); i++){
char c = value.charAt(i);
String s = new StringBuilder().append(c).toString();
element.sendKeys(s);
}
}
public void enterCustomerDetails()
{
sendChar(txtFirstName, "Joh201605130947AM");
sendChar(txtSurname, "Doe201605130947AM");
sendChar(txtEmail, "johndoe#gmail.com");
}
If you guys know a better way, please help! :)
I assume this is caused by this Angular2 issue https://github.com/angular/angular/issues/5808
Angular can't process input events when they arrive too fast.
As a workaround you would need to send single characters with a small delay between each.
I stumbled upon this error when doing integration tests with NightwatchJS (which uses selenium).
So I'm writing this for people coming here in the future.
I wrote this extension command for nightwatch:
exports.command = function (selector, value, using) {
var self = this;
self.elements(using || 'css selector', selector, function (elems) {
elems.value.forEach(function (element) {
for (var c of value.split('')) {
self.elementIdValue(element.ELEMENT, c);
}
});
});
return this;
};
Which can be used in this way:
var username = 'integration#test.com';
browser.setValueSlow('input[ngcontrol=username]', username); //Works with ng2!
This issue was also discussed on NightwatchJS's github here
By using Actions class, this issue solved for me
Tried many ways as mentioned above. Also tried to setvalue by js executescript
Finally, found this code and it worked well for grid component built on angular
actions.sendKeys(webElement,
modifiedValue).perform();
I was getting this error too in Java, Selenium. You might also be getting this error too while writing your codes - "sendKeys (CharSequence) from the type Webelement refers to the missing type charSequence"
I tried varying the wait time and even typing extra characters before the main characters, they did not work.
The simple trick I used was to change the Java Compiler version from JRE 9 to JRE 10.
This is due to a bug in Angular apps. Workaround is to put a sleep function.
public void setText(By element, String text) {
sleep(100); // Angular version < 2 apps require this sleep due to a bug
driver.findElement(element).clear();
driver.findElement(element).sendKeys(text);
}
i had the same problem, if you see it carefully selenium is changing the characters, some numbers perform a backspace or other symbols, i read it happens when using selenium with vncserver, i changed to firefox.... and it worked.
if that's not your problem, maybe sending the data in parts:
input1="Joh201605130947AM"
txtFirstName.sendKeys(input1[0:7])
txtFirstName.sendKeys(input1[8:end])
Using
Chromium 78.0.3904.70,
Vaadin Flow Framework 14.1.3,
Selenium 3.141.59
and OpenJDK 11.0.5
the behavior also occurs and is even worse:
I see that the character is typed in and suddenly after that it disappears.
A workaround is to be persistent and just try it again. And again. Until the character is finally typed in.
// Type in every single character
for (int i = 0; i < textToType.length(); i++) {
boolean typingCharacterWasSuccessful = false;
// If typing was not successful before, just type again
while (!typingCharacterWasSuccessful) {
// Type in the character
char singleCharacterToType = textToType.charAt(i);
htmlTextfeld.sendKeys(Character.toString(singleCharacterToType));
// Wait a little. Maybe alternatively/additionally wait.until(...)
Thread.sleep(200);
// Check typed in string.
String inputValueAfterTyping = htmlTextfeld.getAttribute("value");
if (inputValueAfterTyping.length() > i + 1) {
// Alternatively: delete everything and start all over
throw new Exception("Input value too long. Maybe character typed in twice!?");
}
// Typing was successful if the value in the input field is as expected (up to now)
typingCharacterWasSuccessful
= inputValueAfterTyping.equals(textToType.substring(0, i + 1));
}
}
I had a similar issue for big texts in Java.
I overcome the issue using the copy and paste of the text in the keyboard-related methods, as in the following method:
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
public static void clickAndSetMultilineText(WebElement element, String text) {
/** click on element **/
element.click();
/** clear older content of the text using keyboard functionality **/
element.sendKeys(Keys.CONTROL + "a"); // select all text
element.sendKeys(Keys.DELETE); // delete old text
StringSelection stringSelection= new StringSelection(text);
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(stringSelection, null); // copy text to the keyboard
element.sendKeys(Keys.CONTROL+"v"); // paste text to the field
}
Hope this is helpful.
try this code.. other way to set values using javascript
WebDriver driver = new FirefoxDriver();
JavascriptExecutor jse = (JavascriptExecutor)driver;
jse.executeScript("document.getElementsByName('body')[0].setAttribute('type', 'text');");
driver.findElement(By.xpath("//input[#name='body']")).clear();
driver.findElement(By.xpath("//input[#name='body']")).sendKeys("Ripon: body text");

How to enter characters one by one in to a text field in selenium webdriver?

How to enter characters one by one in to a text field in selenium webdriver? I have used the below code but it's not working
getDriver().findElement(By.id("PhoneNumber")).sendKeys(Keys.chord("9876544322"));
Can anybody suggest how to resolve this?
Here is how I am sending character by character using Selenium Webdriver (in Java). This way in the back-end, I verify at each letter press if the character exists in the input. Normal element.sendKeys() is not working well for me 2 out of 5 times - the last letter is missing, I guess something is buggy with Selenium Webdriver, I don't know. Try the code below, it works 100% of the time for me.
public void TypeInField(String xpath, String value){
String val = value;
WebElement element = driver.findElement(By.xpath(xpath));
element.clear();
for (int i = 0; i < val.length(); i++){
char c = val.charAt(i);
String s = new StringBuilder().append(c).toString();
element.sendKeys(s);
}
}
As you see, I get the value needed to be typed and in the for loop, I take each character, convert it to string and send it to textbox. Also, I have a search for xpath, you can change that to id, or classname, or whatever you want.
If you want to make your sendKeys more human like, I've used something like this:
private static void sendHumanKeys(WebElement element, String text) {
Random r = new Random();
for(int i = 0; i < text.length(); i++) {
try {
Thread.sleep((int)(r.nextGaussian() * 15 + 100));
} catch(InterruptedException e) {}
String s = new StringBuilder().append(text.charAt(i)).toString();
element.sendKeys(s);
}
}
It sends the keys with a 100ms delay, but only an average of 100ms. It creates a normal distribution with average 100ms and std. deviation 15ms.
sendKeys() does enter characters in sequence, but it can at times run quickly enough to be perceived as a copy/paste action. Though, it is in fact intended to simulate a user entering text by typing. Per the sendKeys() JavaDoc:
/** Use this method to simulate typing into an element, which may set its value. */
If you wanted to slow it down, you could make a method that accepts WebElement and String args, convert the String to charsequence[], then use a for loop and enter each index of the array in the .sendKeys() followed by a Thread.sleep(). This seems horribly inefficient, though, as sendKeys() recognizes Strings as charsequence[] (String is a charsequence[] in Java). Adding the Thread.sleep() will only slow your test needlessly.
Honestly, sendKeys() fits your described needs. It's the best way to simulate a user at a keyboard, it just does it really fast.
.chord() will press all keys simultaneously. Not very well suited for field input.
The .sendKeys() method will accept a String. Simply pass your input as such and have a go with it.
driver.findElement(By.id("element")).sendKeys("Field Input Text");
I created a Python function of the Java Selenium code.
Find attached below and tweak based on the elements you want to use:
def TypeInField(xpath, myValue):
val = myValue
elem = driver.find_element_by_xpath(xpath)
c = ""
for i in range(0, len(val)):
c += val[i]
elem.send_keys(c)
time.sleep(3)
elem.send_keys(Keys.ENTER)
I use this function in my test when I want to type a string letter by letter.
public void typeOnLetterByLetter(WebElement webElement, String value, long waitBetweenLetters, ChronoUnit unitTime) {
clear(webElement);
Arrays.asList(value.toCharArray()).forEach(letter -> {
typeOn(webElement, String.valueOf(letter));
pause(waitBetweenLetters, unitTime);
});
}
private void pause(long time, ChronoUnit unitTime) {
try {
Thread.sleep(Duration.of(time, unitTime).toMillis());
} catch (InterruptedException ignore) {
}
}
Here is how I did it in C#
public void TypeInFieldCharByChar(IWebElement element,string text)
{
var strBuilder = new StringBuilder();
for (int i = 0; i < text.Length; i++)
{
Thread.Sleep(100);
strBuilder.Append(text[i]);
element.SendKeys(strBuilder.ToString());
strBuilder.Clear();
}
}

Windowlicker is not working on OS X

I have an issue with windowlicker on OS X ( everything works ok on Windows ).
The issue is that when I try to simulate user input to any text field the data is not inserted properly ( some letters are cut out ).
For instance:
JTextFieldDriver txField = new JTextFieldDriver(this,
JTextField.class,
named(fieldName));
txField.focusWithMouse();
txField.typeText(input);
Preceding code will result in that I will observe windowlicker inserting input to a text field named fieldName and the input will be incomplete ( Peter will be Peer or Fred will be Fre and so on ). Everything works properly on windows.
I am not sure if all that has anything to do with a warning. I get similar on windows. The warning is:
"WARNING: could not load keyboard layout Mac-, using fallback layout with reduced capabilities (JAR entry com/objogate/wl/keyboard/Mac- not found in /Users/odo/.m2/repository/com/googlecode/windowlicker/windowlicker-core/r268/windowlicker-core-r268.jar)"
Windowlicker seems not to be very popular tool. Nevertheless I managed to figure out the root cause. The warning stating that keyboard layout can't be set is displayed because I am not using english locale. It looks like windowlicker supports only Mac-GB keyboard layout.
The warning will go away if an appropriate system property is set.
For instance:
System.setProperty("com.objogate.wl.keyboard", "Mac-GB");
However this will not solve the main problem. After couple trials I figured out that only 'a' and 'd' characters are trimmed. This is because windowlicker inserts them as if user would hold the 'a' or 'd' key for a bit. Holding those keys results in a helper menu invocation which allows to select special characters. In order to fix that I played with the JTextComponentDriver and found a workaround. The solution is not to use driver's typeText to insert text. JTextComponentDriver's component() method can be used to retrieve the actual guy component and then having an instance setText() can be invoked to set the text.
Below I present my helper class which is using described solution:
public class TextTyper {
private final String inputText;
privte TextTyper(String inputText) {
this.inputText = inputText;
}
public static TextTyper typeText( final String inputText ){
return new TextTyper( inputText );
}
public void into( JTextComponentDriver<?> driver ) throws Exception{
driver.focusWithMouse();
driver.clearText();
Component cmp = driver.component().component();
if(cmp instanceof JPasswordField ){
JPasswordField pwField = (JPasswordField) cmp;
pwField.setText(this.inputText);
}
else if( cmp instanceof JTextField){
JTextField txField = (JTextField) cmp;
txField.setText(this.inputText);
}
else
throw new Exception("Component is not an instance of JTextField or JPasswordField");
}
}

Write file with SuperCsv preserving leading zeros while opening in excel

I was wondering if there is a way to keep the leading 0 while using SuperCsv.
My problem is that I have a few columns which have numbers with leading 0. I want to keep the 0, but excel keeps stripping it, I've also tried to append a few characters at the beginning of the number like ' = " but no good result.
Excel is displaying the first character which I've added at the beginning of the number, so the column value looks like =0222333111 , and that's because probably supercsv is wrapping the output between quotes.
I didn't find anything on the superCsv website and I guess I am not the only one who has this problem.
Should I migrate the to an Excel Java lib, or there is a workaround?
The CSV file format does not allow you to specify how the cells are treated by external programs. Even if the leading zeroes are written to the CSV file (please check that, if you have not already done so), Excel might think that it's smarter than you, that the leading zeroes are there by accident and discard them.
Even if there where some workarounds like adding all sorts of invisible Unicode characters, this is just a hack that is not guaranteed to work with other versions of Excel.
Therefore, CSV seems not to be an adequate file format for your requirements. Either switch to a different file format, or configure Excel to treat all cells as strings instead of numbers (I don't know how or if the latter is possible).
In supercsv, you can use custom cellprocessor below, it will append = in your cell value
public class PreserveLeadingZeroes extends CellProcessorAdaptor {
private static final Logger LOG = LoggerFactory.getLogger(PreserveLeadingZeroes.class);
public PreserveLeadingZeroes() {
super();
}
public PreserveLeadingZeroes(CellProcessor next) {
super(next);
}
public Object execute(Object value, CsvContext context) {
if (value == null) {
// LOG.debug("null customer code");
final String result = "";
return next.execute(result, context);
}
// LOG.debug("parse customer code : " + value.toString());
final String result = "=\"" + value.toString() + "\"";
return next.execute(result, context);
}
}

Categories

Resources