/* * Copyright (C) 2011 Baidu.com Inc * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.baidu.cafe.local.traveler; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import android.text.InputType; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.EditText; import android.widget.GridView; import android.widget.ListView; import android.widget.ProgressBar; import android.widget.ScrollView; import android.widget.TextView; import android.widget.ViewFlipper; /** * define view's action * * @author luxiaoyu01@baidu.com * @date 2011-11-28 * @version * @todo */ public class ViewHelper { public final static String ACTION_CLICK = "click"; public final static String ACTION_TEXT = "text"; public final static String ACTION_LONG_CLICK = "longclick"; public final static String ACTION_SLIDE = "slide"; public final static String ACTION_LISTVIEW = "listview"; public static ArrayList<String> mBlackList = new ArrayList<String>(); private static int mGloablStep = 0; private static boolean mIsLogged = false; private static ArrayList<View> mList = new ArrayList<View>(); private static ArrayList<AbstractList> mAbstractLists = new ArrayList<AbstractList>(); private final static int SLEEP_TIME = 3000; private final static int INTERNET_SLEEP_TIME = 5000; private final static int TIMEOUT_WAIT_FOR_LOADING = 1000 * 5; private final static int INTERVAL_WAIT_FOR_LOADING = 500; private final static int TYPE_PASSWORD = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD; private final static int TYPE_VISIBLE_PASSWORD = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD; private final static int RET_PASS = 0; private final static int RET_IN_WHITE_LIST = 1; private final static int RET_IN_BLACK_LIST = 2; private final static int RET_NOT_SUITABLE = 3; private final static int RET_NO_ACTION = 4; private final static int RET_NO_LISTENER = 5; private final static int RET_NOT_IN_SCREEN = 6; private final static int RET_IN_LIST = 7; public static class AbstractList { public View list; public int indexAtActivity = 0; public int cursor = 0; public AbstractList(View list, int indexAtActivity, int cursor) { this.list = list; this.indexAtActivity = indexAtActivity; this.cursor = cursor; } } public static void sleep() { APPTraveler.local.sleep(getSleepTime()); } private static int getSleepTime() { // if (mUseInternet && mGloablStep++ <= 5) { // Logger.println("INTERNET_SLEEP_TIME: " + INTERNET_SLEEP_TIME); // return INTERNET_SLEEP_TIME; // } return SLEEP_TIME; } public static <T extends View> ArrayList<T> getAllOperatableViews(Class<T> classToFilterBy) { ArrayList<View> operatableViews = getAllOperatableViews(); ArrayList<T> views = new ArrayList<T>(); for (View view : operatableViews) { if (classToFilterBy.isAssignableFrom(view.getClass())) { views.add(classToFilterBy.cast(view)); } } operatableViews = null; return views; } public static ArrayList<View> sortByDistanceYFromCenter(ArrayList<View> views) { final int centerY = APPTraveler.mDisplayY / 2; Comparator<View> comparator = new Comparator<View>() { @Override public int compare(View lhs, View rhs) { int distanceLeft = Math.abs(APPTraveler.local.getViewCenter(lhs)[1] - centerY); int distanceRight = Math.abs(APPTraveler.local.getViewCenter(rhs)[1] - centerY); return distanceLeft - distanceRight; } }; Collections.sort(views, comparator); return views; } public static ArrayList<View> getAllOperatableViews() { ArrayList<View> views = APPTraveler.local.getCurrentViews(); // ArrayList<View> views = CafeCaller.getLocal().getViews();//not onlySufficientlyVisible ArrayList<View> operatableViews = new ArrayList<View>(); if (views.size() == 0) { Logger.println("views.size() == 0"); } for (View view : views) { if (!view.isShown() || APPTraveler.local.isSize0(view)) { continue; } if (view instanceof ViewGroup) { operatableViews.addAll(getListItems(view)); } else { if (isAvailable(view, true)) { operatableViews.add(view); } } } if (operatableViews.size() == 0) { Logger.println("operatableViews.size() ==0"); } return sortByDistanceYFromCenter(operatableViews); } public static boolean handlePasswordEditText() { if (mIsLogged) { return false; } // fill user name & password EditText password = getPasswordEditText(); EditText userName = null; if (null == password) { return false; } else { userName = getUserNameEditText(password); } if (null == userName) { return false; } Logger.println(String.format("FIND username [%s] & password [%s]", userName, password)); Logger.println("username:" + APPTraveler.mUsername); Logger.println("password:" + APPTraveler.mPassword); enterText(userName, APPTraveler.mUsername); enterText(password, APPTraveler.mPassword); if (clickLoginButton(password)) { return mIsLogged = true; } return false; } private static boolean clickLoginButton(EditText password) { // ArrayList<View> viewsBelow = getViewsBelow(password); View loginButton = getLoginButton(APPTraveler.local.getCurrentViews()); if (null == loginButton) { Logger.println("null == loginButton"); return false; // int[] xy = new int[2]; // password.getLocationOnScreen(xy); // loginButton = getNearestView(xy[0], xy[1], viewsBelow); } int[] xy = new int[2]; loginButton.getLocationOnScreen(xy); APPTraveler.screenShot(xy); APPTraveler.local.clickViewWithoutAssert(loginButton); return true; } private static View getLoginButton(ArrayList<View> buttons) { String loginString1 = APPTraveler.local.getTestRString("login1"); String loginString2 = APPTraveler.local.getTestRString("login2"); for (View button : buttons) { if (button instanceof Button) { String text = ((Button) button).getText().toString(); if (text.contains(loginString1) && text.contains(loginString2)) { Logger.println("loginButton: " + text); return button; } } } return null; } private static ArrayList<View> getViewsBelow(View anchor) { int[] xyAnchor = new int[2]; anchor.getLocationOnScreen(xyAnchor); ArrayList<View> viewsBelow = new ArrayList<View>(); for (View view : APPTraveler.local.getCurrentViews()) { int[] xy = new int[2]; view.getLocationOnScreen(xy); if (xy[1] > xyAnchor[1]) { viewsBelow.add(view); } } return viewsBelow; } private static EditText getPasswordEditText() { ArrayList<EditText> editTexts = APPTraveler.local.getCurrentViews(EditText.class); for (EditText editText : editTexts) { int type = editText.getInputType(); Logger.println("" + editText + "InputType:" + type); if (type == TYPE_PASSWORD || type == TYPE_VISIBLE_PASSWORD) { return editText; } } return null; } private static EditText getUserNameEditText(EditText password) { int[] passwordXY = new int[2]; password.getLocationOnScreen(passwordXY); // get edittexts above password edittext ArrayList<EditText> editTexts = APPTraveler.local.getCurrentViews(EditText.class); ArrayList<View> upEditTexts = new ArrayList<View>(); for (EditText editText : editTexts) { int[] xy = new int[2]; editText.getLocationOnScreen(xy); if (xy[1] < passwordXY[1]) { upEditTexts.add(editText); } } return (EditText) getNearestView(passwordXY[0], passwordXY[1], upEditTexts); } private static View getNearestView(int x, int y, ArrayList<View> candidate) { double minDistance = Double.MAX_VALUE; View nearestView = null; for (View view : candidate) { int[] xy = new int[2]; view.getLocationOnScreen(xy); double distance = Util.getDistance(x, y, xy[0], xy[1]); if (distance < minDistance) { minDistance = distance; nearestView = view; } } return nearestView; } private static ArrayList<View> getListItems(View view) { ArrayList<View> items = new ArrayList<View>(); // if (mList.contains(view)) { // Logger.println(view + " is old"); // return items; // } if (view instanceof ListView) { items.add(view); } else if (view instanceof ScrollView) { items = getVisibleViews(getScrollViewListItems((ViewGroup) view)); } else if (view instanceof GridView) { // TODO } // if (items.size() != 0) { // Logger.println("add " + view + " to list"); // mList.add(view); // } return items; } private static ArrayList<View> getListViewListItems(ListView listView) { ArrayList<View> items = new ArrayList<View>(); Logger.println("getListViewListItems: " + listView); for (int i = listView.getFirstVisiblePosition(); i <= listView.getLastVisiblePosition(); i++) { Logger.println("listitem: " + listView.getChildAt(i).toString()); items.add(listView.getChildAt(i)); } return items; } private static boolean isSuitableTextView(TextView textView) { if (isInList(textView)) { return false; } return true; } private static boolean isInList(View view) { View parent = view; while ((parent = getParent(parent)) != null) { if (isList(parent)) { return true; } } return false; } public static String getViewText(View view) { return view instanceof TextView ? ((TextView) view).getText().toString() : ""; } private static boolean hasListener(View view) { if (APPTraveler.local.getListener(view, View.class, "mOnClickListener") != null) { return true; } if (APPTraveler.local.getListener(view, View.class, "mOnLongClickListener") != null) { return true; } if (APPTraveler.local.getListener(view, View.class, "mOnTouchListener") != null) { return true; } if (APPTraveler.local.getListener(view, View.class, "mOnKeyListener") != null) { return true; } return false; } private static ArrayList<View> getVisibleViews(ArrayList<View> views) { ArrayList<View> visibleViews = new ArrayList<View>(); for (View view : views) { if (APPTraveler.local.isInScreen(view)) { visibleViews.add(view); } } return visibleViews; } private static ArrayList<View> getScrollViewListItems(ViewGroup viewGroup) { ArrayList<View> children = new ArrayList<View>(); if (hasListItems(viewGroup)) { for (int i = 0; i < viewGroup.getChildCount(); i++) { children.add(viewGroup.getChildAt(i)); } return children; } for (int i = 0; i < viewGroup.getChildCount(); i++) { View child = viewGroup.getChildAt(i); if (child instanceof ViewGroup) { ArrayList<View> grandson = getScrollViewListItems((ViewGroup) child); if (grandson.size() != 0) { Logger.println("father: " + getViewText(child)); return grandson; } } } return children; } private static boolean hasListItems(ViewGroup viewGroup) { if (viewGroup.getChildCount() < 3) { return false; } int width = viewGroup.getChildAt(0).getWidth(); for (int i = 0; i < viewGroup.getChildCount(); i++) { View child = viewGroup.getChildAt(i); // Children must be clickable. if (!child.isClickable()) { return false; } // Width of children must be same. if (child.getWidth() != width) { return false; } } return true; } private static int getListIndex(View targetList) { ArrayList<? extends View> lists = APPTraveler.local.getCurrentViews(targetList.getClass()); for (int i = 0; i < lists.size(); i++) { if (lists.get(i).equals(targetList)) { return i; } } Logger.println("Can not find " + targetList.toString() + "!!!"); return -1; } private static void addAbstractList(View list, int indexAtActivity) { if (getAbstractList(list) == null) { mAbstractLists.add(new AbstractList(list, indexAtActivity, 1)); } } public static AbstractList getAbstractList(View list) { for (AbstractList abstractList : mAbstractLists) { if (abstractList.list.equals(list)) { return abstractList; } } return null; } public static ArrayList<Operation> getViewOperations(View view, int viewIndex) { ArrayList<String> actions = getViewActions(view); ArrayList<Operation> operations = new ArrayList<Operation>(); for (String action : actions) { Operation operation = new Operation(view, viewIndex, action, APPTraveler.remote.getTopActivity()); // Logger.println("operation: " + operation); operations.add(operation); } return operations; } public static ArrayList<String> getViewActions(View view) { ArrayList<String> actions = new ArrayList<String>(); if (view instanceof ListView) { ListView listView = (ListView) view; int lines = listView.getLastVisiblePosition() - listView.getFirstVisiblePosition(); // Ignore the last line in case that the last line is not visible completely. for (int i = 1; i <= lines; i++) { // Logger.println("add ACTION_LISTVIEW" + i); actions.add(ACTION_LISTVIEW + i); } addAbstractList(listView, getListIndex(listView)); return actions; } // else if (view instanceof ScrollView) { // ScrollView scrollView = (ScrollView) view; // int lines = getScrollViewVisibleChildCount(scrollView); // for (int i = 1; i <= lines; i++) { // actions.add(ACTION_SCROLLVIEW + i); // } // addAbstractList(scrollView, getListIndex(scrollView)); // return actions; // } else if (view instanceof GridView) { // // } if (view instanceof EditText) { actions.add(ACTION_TEXT); return actions; } if (isInList(view)) { return actions; } // TextView is not clickable but it's parent is clickable if (view.isClickable() || isClickableTextView(view)) { actions.add(ACTION_CLICK); } // TODO EditText longclick is not repeatable, so it is disable. // if (view.isLongClickable() && !(view instanceof EditText)) { // actions.add(ACTION_LONG_CLICK); // } if (view instanceof ViewFlipper) { actions.add(ACTION_SLIDE); } // content menu // menu // TODO how to make sure that there is a menu ? return actions; } public static void waitForLoading() { long end = System.currentTimeMillis() + TIMEOUT_WAIT_FOR_LOADING; while (ViewHelper.hasLoadingView()) { try { Thread.sleep(INTERVAL_WAIT_FOR_LOADING); } catch (InterruptedException e) { e.printStackTrace(); } if (System.currentTimeMillis() > end) { Logger.println("waitForLoading timeout!"); break; } } } /** * Find indeterminate ProgressBar from current visibile views. * * @param views * @return */ private static boolean hasLoadingView() { ArrayList<View> views = APPTraveler.local.getCurrentViews(); for (View view : views) { if (view instanceof ProgressBar) { ProgressBar progressBar = (ProgressBar) view; if (progressBar.isIndeterminate()) { return true; } } } return false; } private static boolean isClickableTextView(View view) { if (!(view instanceof TextView)) { return false; } return hasClickableParent(view); } private static boolean hasClickableParent(View view) { View parentView = view; while ((parentView = getParent(parentView)) != null) { if (parentView.isClickable()) { return true; } } return false; } private static View getParent(View view) { if (!(view.getParent() instanceof View)) { return null; } return (View) view.getParent(); } private static boolean isList(View view) { return view instanceof ListView || view instanceof ScrollView || view instanceof GridView ? true : false; } private static boolean isInWhiteList(View view) { return false; } private static boolean isInBlackList(View view) { for (String str : mBlackList) { if (APPTraveler.local.getViewText(view).equals(str)) { return true; } } return false; } public static boolean isAvailable(View view, boolean careListener) { int ret = isTargetView(view, careListener); String retString = Util.getReturnValueName(ViewHelper.class, ret); if (ret <= RET_IN_WHITE_LIST) { // Logger.println("Passed [" + retString + "] " + view.toString() + " " + getViewText(view)); return true; } else { // Logger.println("Failed [" + retString + "] " + view.toString() + " " + getViewText(view)); return false; } } private static int isTargetView(View view, boolean careListener) { if (isInBlackList(view)) { return RET_IN_BLACK_LIST; } if (isInWhiteList(view)) { return RET_IN_WHITE_LIST; } if (!isSuitable(view)) { return RET_NOT_SUITABLE; } if (getViewActions(view).size() == 0) { return RET_NO_ACTION; } if (!APPTraveler.local.isInScreen(view)) { return RET_NOT_IN_SCREEN; } // if (careListener && !hasListener(view)) { // return RET_NO_LISTENER; // } if (view instanceof TextView && !isSuitableTextView((TextView) view)) { return RET_IN_LIST; } return RET_PASS; } private static boolean isSuitable(View view) { if (!view.isShown()) { return false; } // if (view instanceof ViewGroup) { // return false; // } if (!view.isEnabled()) { return false; } return true; } public static void goBack(String reason) { Logger.println("goBack because of [" + reason + "]"); APPTraveler.local.hideInputMethod(); APPTraveler.remote.sleep(SLEEP_TIME); APPTraveler.remote.goBack(); APPTraveler.remote.sleep(SLEEP_TIME); } public static void enterText(EditText editText, String text) { // if text == null will cause app crash! if (null == text || text.length() == 0) { Logger.println("null == text || text.length() == 0"); } else { APPTraveler.local.enterText(editText, text); APPTraveler.local.sleep(500); } } }