Appium: How to iterate over a listview of unknown length? - java

I have a list view with a hierarchy I theoretically have no knowledge of. I am attempting to accept a String array and create MobileElements for each string in it, but due to the way I've automated (PageFactory) defining my elements via annotations, they cannot use variables. I also don't know that it's valid or proper to define my annotations inside a method.
The code I've written, which obviously does not compile follows:
public void selectLocation(String[] location) {
List<MobileElement> locationsList = new ArrayList<>();
for(int i = 0; i < location.length; i++) {
#iOSFindBy(accessibility = location[i])
#AndroidFindBy(xpath = "//android.widget.TextView[#text='" + location[i] + "']")
locationsList.add(i);
}
for (int i = 0; i < location.length; i++) {
locationsList.get(i).click();
}
}
I'm assuming the proper way to do this is wholly different from the way I've implemented.
My list hierarchy is similar to the following; my end point could vary depending on the branch I go down:
Continent 1
City 1
Room 1
Room 2
City 2
Building 1
Room 1
Room 2
Building 2
Room 1
Room 2

I now look for a matching element. If I don't find it, I swipe further into the list. If the element doesn't exist I obviously run into problems, but not really an issue in my case since that’d be a failing test.
while (!driver.findElementById(currentLocation).isDisplayed()) {
driver.swipe(startX, startY, startX, endY, 100);
}
driver.findElementById(currentLocation).click();
Yes, I also realize .swipe() is deprecated, but it still works for me and I'd rather not rewrite all my code with TouchActions until necessary.

I ended up using the "FindsBys" functions to create an array of all matching elements. I then loop through those elements looking for a match to one of my strings.
#AndroidFindBys({#AndroidFindBy(xpath = "//android.widget.TextView")})
#iOSFindBys({#iOSFindBy(xpath = "//XCUIElementTypeStaticText")})
private List<MobileElement> locationsList;
...
public void selectLocation(String[] location)
{
for(int i = 0; i < locationsList.size(); i++)
for(int p = 0; p < location.length; p++) {
if (locationsList.get(i).getText().equals(location[p])) {
locationsList.get(i).click();
}
}
}
It's not foolproof (if you have duplicate strings at different levels of your hierarchy you may run into issues), but it works for my use-case and should be able to guide anyone looking for a stronger solution.

You can just loop over the elements themselves.
....
for(MobileElement location: locationsList) {
for(int p = 0; p < location.length; p++) {
if (location.getText().equals(location[p])) {
location.click();
}
}
}

Related

incrementing an object in a 2d array

Good day, so I intend for my code to loop through my array and increment the row index of object by 1 position. I used timer task because I want the object to move forward after certain amount of time. This is the code I have tried. I have looked but I have struggled to find solution relevant to my problem. Would appreciate the help.
class cat_function extends TimerTask {
public void run() {
synchronized (game.board) {
for (int i = 0; i < game.board.length; i++) {
for (int k = 0; k < game.board[0].length; k++) {
if (game.board[i][k] instanceof cat) {
cat garfield = new cat(0, 0);
game.board[i][k] = garfield;
game.board[i][k + 1] = garfield;
}
}
}
}
}
}
Assuming:
game.board is defined as a Cat[][]
an empty cell's value is null
Then all you have to do is
if (game.board[i][k] instanceof cat) {
game.board[i][k + 1] = game.board[i][k]; // Put cat in new location
game.board[i][k] = null; // Remove cat from previous location
}
However, this code still has two problems
What do you do when you reach the edge of the board. You'll have to add logic to make it do something different so you don't fall of the edge.
There's no need to scan the entire game board every time just to find the Cat. Keep the cat's location (indexes) separately so you always know where it is and don't have to look for it.
If there can be more than one cat on the board you will also need logic to decide what happens if two cats "collide" when moving (i.e. you try to move a cat into a cell that already contains a cat).
Solving those problems is left as an exercise for you.

How to look for exact same color in an array in Java

I have an array of 24 colors. Occasionally when I generate it I get colors that have the exact same RGB values. What I want to do is find the duplicates in the array and change the value of the match by like 1 or 2. Just so that it doesn't match anymore.
Here is my array of colors. Notice that there are 3 of the exact shade of red. I want each one to be slightly different from each other. Keep the R channel the same but change the G and or B channels to be like 1 or 2 numbers higher.
Color Array: [java.awt.Color[r=255,g=194,b=23], java.awt.Color[r=255,g=0,b=0], java.awt.Color[r=255,g=0,b=0], java.awt.Color[r=0,g=67,b=255], java.awt.Color[r=0,g=255,b=175], java.awt.Color[r=255,g=13,b=10], java.awt.Color[r=255,g=115,b=43], java.awt.Color[r=2,g=92,b=255], java.awt.Color[r=241,g=219,b=255], java.awt.Color[r=255,g=194,b=0], java.awt.Color[r=231,g=210,b=255], java.awt.Color[r=0,g=43,b=255], java.awt.Color[r=255,g=80,b=0], java.awt.Color[r=255,g=205,b=27], java.awt.Color[r=255,g=0,b=9], java.awt.Color[r=205,g=199,b=255], java.awt.Color[r=0,g=44,b=255], java.awt.Color[r=255,g=0,b=0], java.awt.Color[r=21,g=255,b=219], java.awt.Color[r=255,g=199,b=51], java.awt.Color[r=255,g=82,b=2], java.awt.Color[r=215,g=198,b=255], java.awt.Color[r=0,g=255,b=203], java.awt.Color[r=255,g=98,b=28]]
What I was thinking was iterating through the array.
for(int i = 0; i < colorArray.length; i++){
if(colorArray[i] == //not sure what this would be equal to as it will be checking all the values in the array ) {
colorArray[i].getBlue() = matchingColor.getBlue() += 1;
colorArray[i].getGreen() matchingColor.getGreen() += 2; //These are example values just to make sure that they have no match
}
}
Learning the APIs of the Classes you use is really helpful.
for(int i = 0; i < colorArray.length; i++) {
// use while in case the color is black/white. (May thrash for few iterations)
while (colorArray[i].equals(matchingColor)) {
colorArray[i] = (Math.random() < 0.5)?colorArray[i].brighter():colorArray[i].darker();
}
}
Here is a solution using a Set to maintain uniqueness. This saves re-inventing all the functionality to check for duplicates. It takes advantage of the fact that the add method of the Set interface returns true if the object has been added to the set. Under the covers, of course, it uses the equals method of Color.
In the event that a duplicate is found, we repeatedly try modifying the Color a little, by doing a bitwise exclusive-or of its three components, until we come to a unique one.
public void deduplicate(Color[] colors) {
Set<Color> uniqueColors = new HashSet<>();
for (int which = 0; which < colors.length; which++) {
Color originalColor = colors[which];
boolean unique = uniqueColors.add(originalColor);
for (int modifyBy = 1; !unique ; modifyBy++) {
colors[which] = new Color(
originalColor.getRed() ^ modifyBy,
originalColor.getGreen() ^ modifyBy,
originalColor.getBlue() ^ modifyBy);
unique = uniqueColors.add(colors[which]);
}
}
}

Is there a way to set a maximum size for a list?

I am looking for some help with the below problem. This is not my original code and I am quite new to Java, so I may be missing something obvious.
The code spawns some effects in a game I have, however there does not seem to be any upper limit on how many effects are spawned. Is there any way I can restrict the upper value of effects to some integer value? Say 20? Or can I make this value a random integer from say 20 to 30?
I have been trying to restrict the upper size of the list size but it doesn't seem to work.
Any suggestions appreciated.
I have also tried adjusting the value for(int j = 0; j < i; j++) to something like for(int j = 0; j < 20; j++), but this seems to have no effect either and the effects spawn as before.
I have also tried removing int i = list.size(); and Actor actor = (Actor)list.get(j); so that the code just relies on the for loop, but the code will not compile: it seems to require the code Actor actor = (Actor)list.get(j);. I get a cannot resolve symbol error for the following:
symbol : class pos
location: package actor
if(!flag && actor.pos.getAbsPoint().distance(point3d) < 10000D && (actor instanceof TypeBomber) && actor.getArmy() != myArmy)
symbol : variable actor
location: class com.maddox.il2.objects.vehicles.stationary.CandC$FireUnit
if(!flag && actor.pos.getAbsPoint().distance(point3d) < 10000D && (actor instanceof TypeBomber) && actor.getArmy() != myArmy)
symbol : variable actor
location: class com.maddox.il2.objects.vehicles.stationary.CandC$FireUnit
if(!flag && actor.pos.getAbsPoint().distance(point3d) < 10000D && (actor instanceof TypeBomber) && actor.getArmy() != myArmy)
public boolean danger()
{
boolean flag = false;
Point3d point3d = new Point3d();
pos.getAbs(point3d);
List list = Engine.targets();
World.MaxVisualDistance = 50000F;
int i = list.size();
for(int j = 0; j < i; j++)
{
Actor actor = (Actor)list.get(j);
if(!flag && actor.pos.getAbsPoint().distance(point3d) < 10000D && (actor instanceof TypeBomber) && actor.getArmy() != myArmy)
{
Random random = new Random();
int k = random.nextInt(500);
int l = k - 250;
k = random.nextInt(500);
int i1 = k - 250;
Eff3DActor.New(this, null, new Loc(l, i1, 0.0D, 0.0F, 90F, 0.0F), 1.0F, "Effects/Smokes/CityFire.eff", 600F);
flag = true;
int j1 = random.nextInt(10);
wait = (float)(1.0D + (double)j1 * 0.10000000000000001D);
}
}
Thanks for any help. Like I said, I'm sorry if I am missing something obvious.
I cannot say for sure what all of that code is doing, however, it appears that your function actually returns flag past the point that you pasted? It also appears that the size of the list of Actors is the upper bound of how many smoke effects can be made (limited by how many pass the if statement). That said I would try something like this (in order to preserve the rest of the function)
if(!flag && actor.pos.getAbsPoint().distance(point3d) < 10000D && (actor instanceof TypeBomber) && actor.getArmy() != myArmy)
{
if (renderedCount < 20)
{
Random random = new Random();
int k = random.nextInt(500);
int l = k - 250;
k = random.nextInt(500);
int i1 = k - 250;
Eff3DActor.New(this, null, new Loc(l, i1, 0.0D, 0.0F, 90F, 0.0F), 1.0F, "Effects/Smokes/CityFire.eff", 600F);
int j1 = random.nextInt(10);
wait = (float)(1.0D + (double)j1 * 0.10000000000000001D);
renderedCount += 1;
}
flag = true;
}
Make sure you declare int renderedCount before the for loop.
Now this should limit the function to creating 20 smoke effects each time it is called. Which brings us to the problems you are having where this kind of approach does not appear to work. Since you mentioned this is a game, I would be willing to bet that this function is called many times a minute (more than likely once per 'game tick'). If that is the case, limiting the number of smoke effects created by the function will not appear to do much of anything in the game. In order to limit the number completely you would have to do something a little more complex where you keep track of the number of smoke effects currently going globally. Which would probably involve keeping track of how much time has passed since the effect was started... (which it appears that wait might be the time the effect continues after it is started).
As for suggestions about how to keep track of things globally I might recommend a singleton and some methods on the singleton canCreate() and didCreate(float timeValue) then replace renderedCount < 20 with canCreate() and replace renderedCount += 1 with didCreate(wait).
Anyway, I hope this helps you some.
p.s. the complexity that goes with limiting this is a prime example of why ModelViewController is so important when designing something like this...
EDIT: rough singleton
public class SmokeCountSingleton {
private static SmokeCountSingleton instance = null;
private List<Long> smokeList = new ArrayList<Long>();
protected SmokeCountSingleton() {
}
public static SmokeCountSingleton getInstance() {
if(instance == null) {
instance = new SmokeCountSingleton();
}
return instance;
}
public boolean canCreate() {
cleanList()
return (smokeList.size() <= 20)
}
public void didCreate(float duration) {
//duration appears to be in seconds but we want it in milliseconds
smokeList.add(System.currentTimeMillis() + duration*1000)
}
private void cleanList(){
//remove items from list who have exceeded their duration
// have a value less than System.currentTimeMillis()
}
}
I do not have time to finish fleshing it out but hopefully that is enough to get you going.
The point of a singleton is that there is only ever one instance of it, it's kind of like having a database or some other external server that your program can use but without as much overhead. This way you don't ever have to declare it either, so you don't have to worry about where you would need to declare it, but it still acts as if you have a global list. To access the singleton instance (and the methods) you would just do:
SmokeCountSingleton.getInstance().canCreate()
SmokeCountSingleton.getInstance().didCreate(someDurationValue)
Like I said earlier, my Java is a little rusty, and I just mocked this up in the edit window; so apologies if it doesn't work 'out of the box'.
Also, as a side note, you may very well have multiple threads running that call functions where this would get accessed. So you may want to check out synchronized key word in Java...
use list.size() directly, instead of passing it to i. It will return number of objects the list is holding.
You can also use an Iterator or ListIterator to iterate through the list elements, as it will be easier, If you use these.
Example:-
Actor actor; //It would be better if you create variable here and use the same variable for different objects.
Iterator iter = list.iterator();
while(iter.hasNext()){
actor = (Actor)iter.next();
....//Rest of the code
}
I'll answer the question regarding whether we can set a maximum for a list. Hope that will help you to get to know Java better :)
Generally, an implementation of List does not have a maximum size, you can always add more element at the end of it.
In order to work around this would be to use a primitive array of Actor:
private static final int MAX_NUMBER_OF_ACTORS = 20
Actor[] actors = new Actor[MAX_NUMBER_OF_ACTORS];
That said, I would encourage you to use what the List interface has to offer to you, via its various implementations. Use, for example, an Arraylist and ovverride its add method:
public class ActorList extends ArrayList<Actor> {
private static final int MAX_NUMBER_OF_ACTORS = 20
#Override
public boolean add(Store s) {
boolean result = false;
if (this.size() <= MAX_NUMBER_OF_ACTORS){
result = super.add(s);
}else{
// Notify number maximum of actors have been reached (logging, whatever processing you want here...)
}
return result;
}
}
That would give you a nive control over the maximum size of your list of Actor instances.

Java String.matches("regex")

I don't know regex very well. I am trying to find strings that start with digits 2,3,5 or 7, are 1,3,7 or 9 throughout the middle, and end with 3 or 7.
My attempt was [2357][1379]*[37]. It does not work. I'd appreciate a correction. Remember that this is meant for the Java String.matches() function. Thanks in advance
for (int s = 0; s < primes.size(); ++s) {
String p = primes.get(s);
if (!p.matches([REGEX GOES HERE])) {
System.out.println(p);
primes.remove(s);
}
}
The standard method of iterating over a collection you remove from in the loop is to iterate downwards that way removals don't affect the index of subsequent elements:
for (int s = primes.size() - 1; s >= 0; s--) {
String p = primes.get(s);
if (!p.matches("[2357][1379]*[37]")) {
System.out.println(p);
primes.remove(s);
}
}
No need now to worry about implications of removing elements.
The following code "works" just fine:
List<String> primes = new ArrayList<String>();
primes.add("1");
primes.add("2");
primes.add("7");
primes.add("23");
primes.add("213");
primes.add("243");
primes.add("2113");
primes.add("2193");
for (int s = 0; s < primes.size(); ++s) {
String p = primes.get(s);
if (!p.matches("[2357][1379]*[37]")) {
System.out.println(p);
primes.remove(s);
}
}
It outputs:
1
7
243
You may have expected it to output:
1
2
7
243
However, the primes.remove(s) is messing up your loop. That can't really be the intent of your design. (But who knows?!) The following is one of many solutions to avoid messing up your loop:
for (String prime : new ArrayList<String>(primes)) {
if (!prime.matches("[2357][1379]*[37]")) {
System.out.println(prime);
primes.remove(prime);
}
}
Hi I am not sure I am mistaken .. but i dont see anything wrong in your initial pattern for example
String aa = "[2357][1379]*[37]";
String bb = "2977313";
boolean matches = Pattern.matches(aa, bb);
System.out.println("1) "+matches);
I started with no 2 and then ended with no 3 and added 1379 in between and it works as expected. Please correct me if i am wrong
Your regex works well. However, remove shifts any subsequent elements to the left, so the String ordinarily at position s + 1 is moved to s, so the next element to check is at position s instead of s + 1. Fix:
for (int s = 0; s < primes.size();) {
String p = primes.get(s);
if (!p.matches("[2357][1379]*[37]")) {
System.out.println(p);
primes.remove(s);
} else
++s;
}

A collision check method works on a mac, but not on PC

So I am stumped. Here is my collision check method`
public void checkCollision ()
{
for (int i = 0; i < bullets.size()-1; i ++)
{
for (int j = 0; j < enemiesLaunched.size()-1; j++)
{
Rectangle temp = enemiesLaunched.get(j).getRectangle();
Rectangle temp2 = bullets.get(i).getRectangle();
`
if (temp2.intersects (temp))
{
String str = bullets.get(i).getPath();
// since the bullets are selective, the following code is to check
// if the right bullets hit the right germs
if (str.equals("oil gland.png")) // bullet is from oil gland
{
if (enemiesLaunched.get(j).getInfo().equals("highAcid"))
{
enemiesLaunched.get(j).setVisible(false);
bullets.remove(i);
}
}
else if (str.equals ("sweat gland.png"))
{
if (enemiesLaunched.get(j).getInfo().equals("lysozome"))
{
enemiesLaunched.get(j).setVisible(false);
bullets.remove(i);
}
}
else
{
if (enemiesLaunched.get(j).getInfo().equals("mucus"))
{
enemiesLaunched.get(j).setVisible(false);
bullets.remove(i);
}
}
`
On my mac, it works exactly how I intended. However, on my PC, it does not. To make matters more baffling, I have implemented the same logic on games further along in the game, and it works just fine on both the mac and pc, any help would be greatly appreciated!
How are you doing your time delta, and what is the velocity on the two objects? If your time delta is sufficiently large enough, you might not detect the collision as the two objects could have pass right through each other between checks. Have a look here for an explaination.
What tears attention is size()-1 - sure? But bullets.remove(i); certainly should be followed by --i; as otherwise the for-incrementing would skip the next bullet.
Optimized it would be by keeping get(i) and get(j) in their own variables.
I'd rather use for-loops like this if possible to ensure I don't have some wrong indexes due to typos or something:
List<Enemy> enemies = new ArrayList<Enemy>;
for (Enemy enemy : enemies) {
...
}
For example with this loop:
for (int i = 0; i < enemies.size()-1; ++i)
you will always leave the last "enemy" untouched.
And then, to be sure I'm not screwing up my Lists and iterations I would keep references to objects that need to be removed and would remove them afterwards, because I'm not sure what happens when removeing items from a collection while iterating over the same collection. The behaviour might be collectiontype and implementation (of the collection) specific.

Categories

Resources