/* MonkeyTalk - a cross-platform functional testing tool
Copyright (C) 2012 Gorilla Logic, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */
package com.gorillalogic.fonemonkey.automators;
import java.util.List;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.AdapterView.OnItemLongClickListener;
import android.widget.AdapterView.OnItemSelectedListener;
import com.gorillalogic.fonemonkey.Log;
import com.gorillalogic.monkeytalk.automators.AutomatorConstants;
/**
* Base automator for ListView and GridView.
*/
public class AdapterViewAutomator extends ViewAutomator implements OnItemClickListener,
OnItemSelectedListener, OnItemLongClickListener {
static {
Log.log("Initializing AdapterViewAutomator");
}
@Override
public void record(String operation, String... args) {
if (operation.equalsIgnoreCase(AutomatorConstants.ACTION_DRAG)
|| operation.equalsIgnoreCase(AutomatorConstants.ACTION_SWIPE)
|| operation.equalsIgnoreCase(AutomatorConstants.ACTION_TAP)) {
// ignore Drag, Swipe, and Tap (which will be recorded as a Select)
return;
}
super.record(operation, args);
}
protected AdapterView<?> getAdapterView() {
return (AdapterView<?>) getComponent();
}
@Override
public String play(String action, String... args) {
if (action.equalsIgnoreCase(AutomatorConstants.ACTION_SELECT)
|| action.equalsIgnoreCase(AutomatorConstants.ACTION_LONG_SELECT)) {
assertArgCount(action, args, 1);
int position = findPosition(args[0]);
if (position != -1) {
scrollToPositionAndSelect(position, true,
action.equalsIgnoreCase(AutomatorConstants.ACTION_LONG_SELECT));
return null;
}
throw new IllegalArgumentException("Unable to find " + getComponentType()
+ " item with value " + args[0]);
} else if (action.equalsIgnoreCase(AutomatorConstants.ACTION_SELECT_ROW)
|| action.equalsIgnoreCase(AutomatorConstants.ACTION_SELECT_INDEX)
|| action.equalsIgnoreCase(AutomatorConstants.ACTION_LONG_SELECT_INDEX)) {
assertArgCount(action, args, 1);
int row = getIndexArg(action, args[0]);
int max = getAdapterView().getCount();
assertMaxArg(row, max);
scrollToPositionAndSelect(row - 1, true,
action.equalsIgnoreCase(AutomatorConstants.ACTION_LONG_SELECT_INDEX));
return null;
}
return super.play(action, args);
}
/**
* Scroll then tap.
*
* @param position
* 0-based item index
* @param shouldTap
* if true, then tap on the item
* @param isLong
* if true, then tap should be a long press
*/
private void scrollToPositionAndSelect(int position, boolean shouldTap, boolean isLong) {
scrollToPosition(position);
if (shouldTap) {
tapPosition(position, isLong);
}
}
/** Scroll to the given position (0-based) */
// Typically overwritten in subclass.
protected void scrollToPosition(int position) {
getAdapterView().setSelection(position);
}
/** Tap at the given position (0-based), and optionally make it a long press. */
protected void tapPosition(int position, boolean isLong) {
int i = position - getAdapterView().getFirstVisiblePosition();
View v = getAdapterView().getChildAt(i);
if (isLong) {
// Long press (down, then up after 1025ms) on the AdapterView at...
tap(v.getLeft() + v.getWidth() / 2, v.getTop() + v.getHeight() / 2, 1025);
} else {
// Tap on the AdapterView at...
tap(v.getLeft() + v.getWidth() / 2, v.getTop() + v.getHeight() / 2);
}
}
/**
* Find the position (0-based) for the given item.
*
* @param value
* the item value
* @return the position
*/
protected int findPosition(String value) {
for (int i = 0; i < getAdapterView().getAdapter().getCount(); i++) {
Object obj = getAdapterView().getItemAtPosition(i);
if (value.equals((obj.toString()).trim())) {
return i;
}
}
return -1;
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
recordSelection(position, false);
}
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
recordSelection(position, false);
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
// do nothing
}
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
recordSelection(position, true);
return false;
}
/**
* @param position
* 0-based index
*/
private void recordSelection(int position, boolean isLong) {
Object obj = getAdapterView().getItemAtPosition(position);
if (obj instanceof String) {
record(isLong ? AutomatorConstants.ACTION_LONG_SELECT
: AutomatorConstants.ACTION_SELECT, (String) obj);
} else {
record(isLong ? AutomatorConstants.ACTION_LONG_SELECT_INDEX
: AutomatorConstants.ACTION_SELECT_INDEX, String.valueOf(position + 1));
}
}
@Override
public boolean hides(View v) {
return false;
}
public String getValue() {
Object obj = getAdapterView().getSelectedItem();
if (obj instanceof String) {
return obj.toString();
}
int pos = getAdapterView().getSelectedItemPosition();
if (pos != AdapterView.INVALID_POSITION) {
return String.valueOf(pos + 1);
}
return obj.toString();
}
@Override
protected String getProperty(String propertyPath) {
int size = getAdapterView().getCount();
if (propertyPath.equals("size")) {
return String.valueOf(size);
}
return super.getProperty(propertyPath);
}
@Override
protected String getArrayItem(String name, List<Integer> indices) {
if (name.equals("item")) {
int position = indices.get(0);
Object obj = getAdapterView().getItemAtPosition(position);
return obj.toString();
} else if (name.equals("detail")) {
throw new IllegalStateException(getComponent() + " does not support 'detail' property");
}
return super.getArrayItem(name, indices);
}
}