/** ** Copyright (C) SAS Institute, All rights reserved. ** General Public License: http://www.opensource.org/licenses/gpl-license.php **/ package com.jayway.android.robotium.remotecontrol.client.processor; import java.io.BufferedInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Properties; import java.util.Vector; import junit.framework.AssertionFailedError; import junit.framework.ComparisonFailure; import android.app.Activity; import android.app.Instrumentation.ActivityMonitor; import android.content.res.Resources.NotFoundException; import android.graphics.Bitmap; import android.graphics.PointF; import android.graphics.Rect; import android.os.Environment; import android.util.Base64; import android.view.Display; import android.view.View; import android.view.WindowManager; import android.widget.AbsListView; import android.widget.Button; import android.widget.CheckBox; import android.widget.DatePicker; import android.widget.EditText; import android.widget.GridView; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.ListView; //import android.widget.NumberPicker; import android.widget.ProgressBar; import android.widget.RadioButton; import android.widget.ScrollView; import android.widget.SlidingDrawer; import android.widget.Spinner; import android.widget.TextView; import android.widget.TimePicker; import android.widget.ToggleButton; import com.jayway.android.robotium.remotecontrol.Condition; import com.jayway.android.robotium.remotecontrol.ObjectCollection; import com.jayway.android.robotium.remotecontrol.client.RobotiumTestCase; import com.jayway.android.robotium.remotecontrol.client.RobotiumTestRunner; import com.jayway.android.robotium.remotecontrol.client.SoloMessage; import com.jayway.android.robotium.remotecontrol.solo.Message; import com.jayway.android.robotium.solo.By; import com.jayway.android.robotium.solo.RCSolo; import com.jayway.android.robotium.solo.RobotiumUtils; import com.jayway.android.robotium.solo.Solo; import com.jayway.android.robotium.solo.Timeout; import com.jayway.android.robotium.solo.WebElement; /** * This class is used to process the "remote command" from the robotium-remote-control<br> * * The input parameters are in a Properties object, and the results will be put into<br> * that properties object, which will be sent back to robotium-remote-control<br> * They will be in key-value format in the properties object.<br> * * The key is defined as constant in {@link SoloMessage}<br> * Refer to the following page to know what key to get and what key to set for each "command"<br> * http://safsdev.sourceforge.net/doc/com/jayway/android/robotium/remotecontrol/solo/Solo.html<br> * * <p> * For example:<br> * For command "assertCurrentActivityClass", at the Returns part, you can see<br> * (in ):KEY_TARGET=target_solo<br> * (in ):KEY_COMMAND=cmd_assertcurrentactivityclass<br> * (out):KEY_ISREMOTERESULT=true<br> * (out):KEY_REMOTERESULTCODE=String:int:0=success/normal=SoloMessage.STATUS_REMOTERESULT_OK<br> * .....<br> * When it marks a 'in', you should get; while it marks a 'out', you should set it with a result<br> * </p> * * @author Lei Wang, SAS Institute, Inc * @since Feb 16, 2012<br> * May 17, 2013 (SBJLWA) Update to support Robotium 4.1<br> * May 23, 2013 (SBJLWA) Modify method takeScreenshot()<br> * Jun 21, 2013 (SBJLWA) Update to support Robotium 4.1+<br> * Jun 25, 2013 (CANAGL) Update to support Robotium 4.2<br> * Jul 18, 2013 (SBJLWA) Modify method getViewByName(): try to get view under package 'android'.<br> */ public class SoloProcessor extends AbstractProcessor implements CacheReferenceInterface{ public static String TAG = SoloProcessor.class.getSimpleName(); private static boolean DEBUG = true; /** * The main {@link Activity} object. */ Activity mainApp = null; /** * The instance of {@link com.jayway.android.robotium.solo.RCSolo}, it is used<br> * to do the real work for handling the messages from 'solo remote control'<br> */ RCSolo solo = null; RobotiumTestCase activityrunner = null; RobotiumTestRunner robotiumTestrunner = null; /** * local cache for containing the {@link ActivityMonitor} * <b>Note:</b> Don't manipulate it directly like activityMonitorCache.get(key) etc.<br> * Use the cache-manipulation-methods defined in {@link AbstractProcessor}<br> * @see AbstractProcessor#getCachedItem(Hashtable, Object) * @see AbstractProcessor#removeCachedItem(Hashtable, Object) * @see AbstractProcessor#putCachedItem(Hashtable, Object, Object) */ protected Hashtable<String,ActivityMonitor> activityMonitorCache = new Hashtable<String,ActivityMonitor>(INITIAL_CACHE_SIZE); /** * local cache for containing the {@link Activity} * <b>Note:</b> Don't manipulate it directly like activityCache.get(key) etc.<br> * Use the cache-manipulation-methods defined in {@link AbstractProcessor}<br> * @see AbstractProcessor#getCachedItem(Hashtable, Object) * @see AbstractProcessor#removeCachedItem(Hashtable, Object) * @see AbstractProcessor#putCachedItem(Hashtable, Object, Object) */ protected Hashtable<String,Activity> activityCache = new Hashtable<String,Activity>(INITIAL_CACHE_SIZE); /** * local cache for containing the {@link View} * <b>Note:</b> Don't manipulate it directly like viewCache.get(key) etc.<br> * Use the cache-manipulation-methods defined in {@link AbstractProcessor}<br> * @see AbstractProcessor#getCachedItem(Hashtable, Object) * @see AbstractProcessor#removeCachedItem(Hashtable, Object) * @see AbstractProcessor#putCachedItem(Hashtable, Object, Object) */ protected Hashtable<String,View> viewCache = new Hashtable<String,View>(INITIAL_CACHE_SIZE); /** * local cache for containing the {@link WebElement} * <b>Note:</b> Don't manipulate it directly like webElementCache.get(key) etc.<br> * Use the cache-manipulation-methods defined in {@link AbstractProcessor}<br> * @see AbstractProcessor#getCachedItem(Hashtable, Object) * @see AbstractProcessor#removeCachedItem(Hashtable, Object) * @see AbstractProcessor#putCachedItem(Hashtable, Object, Object) */ protected Hashtable<String,WebElement> webElementCache = new Hashtable<String,WebElement>(INITIAL_CACHE_SIZE); public SoloProcessor(RobotiumTestRunner robotiumTestrunner){ super(robotiumTestrunner); this.robotiumTestrunner = robotiumTestrunner; activityrunner = robotiumTestrunner.getActivityrunner(); } /** * Test if the solo is null. If it is, try to set it with that of {@link RobotiumTestRunner}<br> * * @see RobotiumTestRunner#getSolo() */ protected boolean checkSolo(){ if(solo!=null){ return true; }else{ solo = robotiumTestrunner.getSolo(); } return (solo!=null); } /** * Before calling this method, you should call setRemoteCommand() * * @param props The Properties object containing the in and out parameters * */ public void processProperties(Properties props){ String debugPrefix = TAG + ".processProperties() "; if(remoteCommand==null){ debug(debugPrefix +"remoteCommand is null, this is a programming error. Call SoloProcessor.setRemoteCommand(command)"); //Try to get the command from properties. remoteCommand = props.getProperty(SoloMessage.KEY_COMMAND); if(remoteCommand==null){ setGeneralError(props, SoloMessage.RESULT_INFO_COMMAND_ISNULL); return; } } debug(debugPrefix +"Begin processing '"+remoteCommand+"' ... "); try{ if(remoteCommand.equals(SoloMessage.cmd_startmainlauncher)){ //This will initialize the solo object of RobotiumTestRunner startMainLauncher(props); }else{ debug(debugPrefix +"For command different from '"+SoloMessage.cmd_startmainlauncher+"', we need to check the Solo object."); if(!checkSolo()){ debug(debugPrefix +"The robotium solo object is null, you need to call Solo.launchApplication()."); setGeneralError(props, SoloMessage.RESULT_INFO_SOLO_ISNULL); return; } if(remoteCommand.equals(SoloMessage.cmd_assertcurrentactivityname) || remoteCommand.equals(SoloMessage.cmd_assertnewcurrentactivityname) || remoteCommand.equals(SoloMessage.cmd_assertcurrentactivityclass) || remoteCommand.equals(SoloMessage.cmd_assertnewcurrentactivityclass)){ assertCurrentActivity(props); }else if(remoteCommand.equals(SoloMessage.cmd_assertmemorynotlow)){ assertMemoryNotLow(props); }else if(remoteCommand.equals(SoloMessage.cmd_clickonscreen) || remoteCommand.equals(SoloMessage.cmd_clickonscreenntimes)){ clickOnScreen(props, false); }else if(remoteCommand.equals(SoloMessage.cmd_clicklongonscreen) || remoteCommand.equals(SoloMessage.cmd_clicklongtimeonscreen)){ clickOnScreen(props, true); }else if(remoteCommand.equals(SoloMessage.cmd_clickonactionbarhomebutton)){ clickOnActionBarHomeButton(props); }else if(remoteCommand.equals(SoloMessage.cmd_clickonactionbaritem)){ clickOnActionBarItem(props); }else if(remoteCommand.equals(SoloMessage.cmd_clickonbutton) || remoteCommand.equals(SoloMessage.cmd_clickontogglebutton)){ clickOnViewByName(props); }else if(remoteCommand.equals(SoloMessage.cmd_clickoncheckboxindex) || remoteCommand.equals(SoloMessage.cmd_clickonedittextindex) || remoteCommand.equals(SoloMessage.cmd_clickonimage) || remoteCommand.equals(SoloMessage.cmd_clickonimagebutton) || remoteCommand.equals(SoloMessage.cmd_clickonbuttonindex) || remoteCommand.equals(SoloMessage.cmd_clickonradiobuttonindex)){ clickOnViewByIndex(props); }else if(remoteCommand.equals(SoloMessage.cmd_clickinlist) || remoteCommand.equals(SoloMessage.cmd_clickinlistindex)){ clickInList(props, false); }else if(remoteCommand.equals(SoloMessage.cmd_clicklonginlist) || remoteCommand.equals(SoloMessage.cmd_clicklonginlistindex) || remoteCommand.equals(SoloMessage.cmd_clicklongtimeinlistindex)){ clickInList(props, true); }else if(remoteCommand.equals(SoloMessage.cmd_clickonmenuitem)){ clickOnMenuItem(props, false); }else if(remoteCommand.equals(SoloMessage.cmd_clickonsubmenuitem)){ clickOnMenuItem(props, true); }else if(remoteCommand.equals(SoloMessage.cmd_clickonview) || remoteCommand.equals(SoloMessage.cmd_clickonviewimmediately)){ clickOnView(props, false); }else if(remoteCommand.equals(SoloMessage.cmd_clicklongonview) || remoteCommand.equals(SoloMessage.cmd_clicklongtimeonview)){ clickOnView(props, true); }else if(remoteCommand.equals(SoloMessage.cmd_clickontext) || remoteCommand.equals(SoloMessage.cmd_clickontextmatch) || remoteCommand.equals(SoloMessage.cmd_clickontextmatchscroll) ){ clickOnText(props, false); }else if(remoteCommand.equals(SoloMessage.cmd_clicklongontext) || remoteCommand.equals(SoloMessage.cmd_clicklongontextmatch) || remoteCommand.equals(SoloMessage.cmd_clicklongontextmatchscroll) || remoteCommand.equals(SoloMessage.cmd_clicklongtimeontextmatch) || remoteCommand.equals(SoloMessage.cmd_clicklongpressontext)){ clickOnText(props, true); }else if(remoteCommand.equals(SoloMessage.cmd_clearedittextindex)|| remoteCommand.equals(SoloMessage.cmd_clearedittextreference)){ clearEditText(props); }else if(remoteCommand.equals(SoloMessage.cmd_drag)){ drag(props); }else if(remoteCommand.equals(SoloMessage.cmd_entertextindex) || remoteCommand.equals(SoloMessage.cmd_entertextreference) || remoteCommand.equals(SoloMessage.cmd_typetext) || remoteCommand.equals(SoloMessage.cmd_typetextuid)){ enterText(props); }else if(remoteCommand.equals(SoloMessage.cmd_finishopenedactivities)){ finishOpenedActivities(props); }else if(remoteCommand.equals(SoloMessage.cmd_finalizeremotesolo)){ finalizeRemoteSolo(props); }else if(remoteCommand.equals(SoloMessage.cmd_goback)){ goBack(props); }else if(remoteCommand.equals(SoloMessage.cmd_gobacktoactivity)){ goBackToActivity(props); }else if(remoteCommand.equals(SoloMessage.cmd_getactivitymonitor)){ getActivityMonitor(props); }else if(remoteCommand.equals(SoloMessage.cmd_getallopenactivities)){ getAllOpenActivities(props); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentactivity)){ getCurrentActivity(props); }else if(remoteCommand.equals(SoloMessage.cmd_getbutton) || remoteCommand.equals(SoloMessage.cmd_getedittext) || remoteCommand.equals(SoloMessage.cmd_getimage) || remoteCommand.equals(SoloMessage.cmd_getimagebutton) || remoteCommand.equals(SoloMessage.cmd_gettext) || remoteCommand.equals(SoloMessage.cmd_getviewclass)){ getViewByIndex(props); }else if(remoteCommand.equals(SoloMessage.cmd_getbuttontext) || remoteCommand.equals(SoloMessage.cmd_getbuttonvisible) || remoteCommand.equals(SoloMessage.cmd_getedittexttext) || remoteCommand.equals(SoloMessage.cmd_getedittextvisible) || remoteCommand.equals(SoloMessage.cmd_gettexttext) || remoteCommand.equals(SoloMessage.cmd_gettextvisible)){ getViewByText(props); }else if(remoteCommand.equals(SoloMessage.cmd_getviewid)){ getViewById(props); }else if(remoteCommand.equals(SoloMessage.cmd_getviewbyname) || remoteCommand.equals(SoloMessage.cmd_getviewbynamematch)){ getViewByName(props); }else if(remoteCommand.equals(SoloMessage.cmd_getparentviews)){ getViewsInParent(props); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentbuttons) || remoteCommand.equals(SoloMessage.cmd_getcurrentcheckboxes) || remoteCommand.equals(SoloMessage.cmd_getcurrentdatepickers) || remoteCommand.equals(SoloMessage.cmd_getcurrentedittexts) || remoteCommand.equals(SoloMessage.cmd_getcurrentgridviews) || remoteCommand.equals(SoloMessage.cmd_getcurrentimagebuttons) || remoteCommand.equals(SoloMessage.cmd_getcurrentimageviews) || remoteCommand.equals(SoloMessage.cmd_getcurrentlistviews) || remoteCommand.equals(SoloMessage.cmd_getcurrentprogressbars) || remoteCommand.equals(SoloMessage.cmd_getcurrentradiobuttons) || remoteCommand.equals(SoloMessage.cmd_getcurrentscrollviews) || remoteCommand.equals(SoloMessage.cmd_getcurrentslidingdrawers) || remoteCommand.equals(SoloMessage.cmd_getcurrentspinners) || remoteCommand.equals(SoloMessage.cmd_getcurrenttextviews) || remoteCommand.equals(SoloMessage.cmd_getcurrenttimepickers) || remoteCommand.equals(SoloMessage.cmd_getcurrenttogglebuttons) || remoteCommand.equals(SoloMessage.cmd_getcurrentviews) || // remoteCommand.equals(SoloMessage.cmd_getcurrentnumberpickers) || remoteCommand.equals(SoloMessage.cmd_getviews) || remoteCommand.equals(SoloMessage.cmd_getcurrentviewsbyclass) || remoteCommand.equals(SoloMessage.cmd_getcurrentviewsbyclassandparent)){ getCurrentViews(props); }else if(remoteCommand.equals(SoloMessage.cmd_getstring)){ getString(props); }else if(remoteCommand.equals(SoloMessage.cmd_gettopparent)){ getTopParent(props); }else if(remoteCommand.equals(SoloMessage.cmd_ischeckboxchecked) || remoteCommand.equals(SoloMessage.cmd_isradiobuttonchecked) || remoteCommand.equals(SoloMessage.cmd_isspinnertextselectedindex) || remoteCommand.equals(SoloMessage.cmd_istogglebuttonchecked)){ isViewByIndexChecked(props); }else if(remoteCommand.equals(SoloMessage.cmd_ischeckboxcheckedtext) || remoteCommand.equals(SoloMessage.cmd_isradiobuttoncheckedtext) || remoteCommand.equals(SoloMessage.cmd_isspinnertextselected) || remoteCommand.equals(SoloMessage.cmd_istextchecked) || remoteCommand.equals(SoloMessage.cmd_istogglebuttoncheckedtext)){ isViewByTextChecked(props); }else if(remoteCommand.equals(SoloMessage.cmd_pressmenuitem) || remoteCommand.equals(SoloMessage.cmd_presssubmenuitem)){ pressMenuItem(props); }else if(remoteCommand.equals(SoloMessage.cmd_pressspinneritem)){ pressSpinnerItem(props); }else if(remoteCommand.equals(SoloMessage.cmd_scrolldown) || remoteCommand.equals(SoloMessage.cmd_scrollup) || remoteCommand.equals(SoloMessage.cmd_scrolltotop) || remoteCommand.equals(SoloMessage.cmd_scrolltobottom) || remoteCommand.equals(SoloMessage.cmd_scrolllisttotop) || remoteCommand.equals(SoloMessage.cmd_scrolllisttotopuid) || remoteCommand.equals(SoloMessage.cmd_scrolllisttobottom) || remoteCommand.equals(SoloMessage.cmd_scrolllisttobottomuid) || remoteCommand.equals(SoloMessage.cmd_scrolllisttoline) || remoteCommand.equals(SoloMessage.cmd_scrolllisttolineuid) || remoteCommand.equals(SoloMessage.cmd_scrolldownlist) || remoteCommand.equals(SoloMessage.cmd_scrolldownlistuid) || remoteCommand.equals(SoloMessage.cmd_scrolluplistuid) || remoteCommand.equals(SoloMessage.cmd_scrolluplist)){ scroll(props); }else if(remoteCommand.equals(SoloMessage.cmd_scrolltoside) || remoteCommand.equals(SoloMessage.cmd_scrollviewtoside)){ scrollToSide(props); }else if(remoteCommand.equals(SoloMessage.cmd_searchbutton) || remoteCommand.equals(SoloMessage.cmd_searchbuttonvisible) || remoteCommand.equals(SoloMessage.cmd_searchbuttonmatch) || remoteCommand.equals(SoloMessage.cmd_searchbuttonmatchvisible) || remoteCommand.equals(SoloMessage.cmd_searchedittext) || remoteCommand.equals(SoloMessage.cmd_searchtext) || remoteCommand.equals(SoloMessage.cmd_searchtextvisible) || remoteCommand.equals(SoloMessage.cmd_searchtextmatch) || remoteCommand.equals(SoloMessage.cmd_searchtextmatchscroll) || remoteCommand.equals(SoloMessage.cmd_searchtextmatchscrollvisible) || remoteCommand.equals(SoloMessage.cmd_searchtogglebutton) || remoteCommand.equals(SoloMessage.cmd_searchtogglebuttonmatch)){ searchView(props); }else if(remoteCommand.equals(SoloMessage.cmd_setactivityorientation)){ setActivityOrientation(props); }else if(remoteCommand.equals(SoloMessage.cmd_setdatepickerreference) || remoteCommand.equals(SoloMessage.cmd_setdatepickerindex)){ setDatePicker(props); }else if(remoteCommand.equals(SoloMessage.cmd_settimepickerreference) || remoteCommand.equals(SoloMessage.cmd_settimepickerindex)){ setTimePicker(props); }else if(remoteCommand.equals(SoloMessage.cmd_setprogressbarreference) || remoteCommand.equals(SoloMessage.cmd_setprogressbarindex)){ setProgressBar(props); }else if(remoteCommand.equals(SoloMessage.cmd_setslidingdrawerreference) || remoteCommand.equals(SoloMessage.cmd_setslidingdrawerindex)){ setSlidingDrawer(props); }else if(remoteCommand.equals(SoloMessage.cmd_sendkey)){ sendKey(props); }else if(remoteCommand.equals(SoloMessage.cmd_sleep)){ sleep(props); }else if(remoteCommand.equals(SoloMessage.cmd_startscreenshotsequencemax)){ startScreenshotSequenceMax(props); }else if(remoteCommand.equals(SoloMessage.cmd_getscreenshotsequence) || remoteCommand.equals(SoloMessage.cmd_getscreenshotsequenceindex) || remoteCommand.equals(SoloMessage.cmd_getscreenshotsequenceszie)){ getScreenshotSequence(props); }else if(remoteCommand.equals(SoloMessage.cmd_waitforactivity) || remoteCommand.equals(SoloMessage.cmd_waitforactivitytimeout) || remoteCommand.equals(SoloMessage.cmd_waitforactivitybyclass) || remoteCommand.equals(SoloMessage.cmd_waitforactivitybyclasstimeout)){ waitForActivity(props); }else if(remoteCommand.equals(SoloMessage.cmd_waitforfragmentbytag)){ waitForFragmentByTag(props); }else if(remoteCommand.equals(SoloMessage.cmd_waitforfragmentbyid)){ waitForFragmentById(props); }else if(remoteCommand.equals(SoloMessage.cmd_waitfordialogtoclose) || remoteCommand.equals(SoloMessage.cmd_waitfordialogtoopen)){ waitForDialog(props); }else if(remoteCommand.equals(SoloMessage.cmd_waitforlogmessage)){ waitForLogMessage(props); }else if(remoteCommand.equals(SoloMessage.cmd_waitfortext) || remoteCommand.equals(SoloMessage.cmd_waitfortextmatchtimeout) || remoteCommand.equals(SoloMessage.cmd_waitfortextmatchtimeoutscroll) || remoteCommand.equals(SoloMessage.cmd_waitfortextmatchtimeoutscrollvisible)){ waitForText(props); }else if(remoteCommand.equals(SoloMessage.cmd_waitforviewclass) || remoteCommand.equals(SoloMessage.cmd_waitforviewclassmatchtimeout) || remoteCommand.equals(SoloMessage.cmd_waitforviewclassmatchtimeoutscroll)){ waitForView(props); }else if(remoteCommand.equals(SoloMessage.cmd_waitforviewreference) || remoteCommand.equals(SoloMessage.cmd_waitforviewreferencetimeoutscroll)){ waitForViewUID(props); }else if(remoteCommand.equals(SoloMessage.cmd_waitforviewid) || remoteCommand.equals(SoloMessage.cmd_waitforviewidtimeout) || remoteCommand.equals(SoloMessage.cmd_waitforviewidtimeoutscroll)){ waitForViewByID(props); }else if(remoteCommand.equals(SoloMessage.cmd_getscreensize) ){ getScreenSize(props); }else if(remoteCommand.equals(SoloMessage.cmd_getviewlocation) ){ getViewLocation(props); }else if(remoteCommand.equals(SoloMessage.cmd_gettextviewvalue) ){ getTextViewValue(props); }else if(remoteCommand.equals(SoloMessage.cmd_getguiimage) ){ getGuiImage(props); }else if(remoteCommand.equals(SoloMessage.cmd_getviewclassname)){ getObjectClassName(props, true); }else if(remoteCommand.equals(SoloMessage.cmd_getobjectclassname)){ getObjectClassName(props, false); }else if(remoteCommand.equals(SoloMessage.cmd_takescreenshot) || remoteCommand.equals(SoloMessage.cmd_takescreenshotquality)){ takeScreenshot(props); }else if(remoteCommand.equals(SoloMessage.cmd_waitforcondition) ){ waitForCondition(props); }else if(remoteCommand.equals(SoloMessage.cmd_cleartextinwebelement) ){ clearTextInWebElement(props); }else if(remoteCommand.equals(SoloMessage.cmd_clickonwebelement) || remoteCommand.equals(SoloMessage.cmd_clickonwebelementindex) || remoteCommand.equals(SoloMessage.cmd_clickonwebelementindexscroll)){ clickOnWebElement(props); }else if(remoteCommand.equals(SoloMessage.cmd_clickonwebelementuid) ){ clickOnWebElementByUID(props); }else if(remoteCommand.equals(SoloMessage.cmd_entertextinwebelement) || remoteCommand.equals(SoloMessage.cmd_typetextinwebelement) || remoteCommand.equals(SoloMessage.cmd_typetextinwebelementindex)){ enterTextInWebElement(props); }else if(remoteCommand.equals(SoloMessage.cmd_typetextinwebelementuid) ){ typeTextInWebElementByUID(props); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentwebelements) || remoteCommand.equals(SoloMessage.cmd_getcurrentwebelementsby) || remoteCommand.equals(SoloMessage.cmd_getwebelement)){ getCurrentWebElements(props); }else if(remoteCommand.equals(SoloMessage.cmd_getweburl) ){ setGeneralSuccessWithSpecialInfo(props, solo.getWebUrl()); }else if(remoteCommand.equals(SoloMessage.cmd_hidesoftkeyboard) ){ solo.hideSoftKeyboard(); setGeneralSuccess(props); }else if(remoteCommand.equals(SoloMessage.cmd_waitforwebelement) || remoteCommand.equals(SoloMessage.cmd_waitforwebelementtimeout) || remoteCommand.equals(SoloMessage.cmd_waitforwebelementminmatchtimeout)){ waitForWebElement(props); }else if(remoteCommand.equals(SoloMessage.cmd_utilsfilterviews) || remoteCommand.equals(SoloMessage.cmd_utilsfilterviewsbytext) || remoteCommand.equals(SoloMessage.cmd_utilsfilterviewstoset) || remoteCommand.equals(SoloMessage.cmd_utilsgetnumberofmatches) || remoteCommand.equals(SoloMessage.cmd_utilsremoveinvisibleviews) || remoteCommand.equals(SoloMessage.cmd_utilssortviewsbylocationonscreen) || remoteCommand.equals(SoloMessage.cmd_utilssortviewsbylocationonscreenyfirst)){ handleRobotiumUtilsCommand(props); }else if(remoteCommand.equals(SoloMessage.cmd_setlargetimeout) || remoteCommand.equals(SoloMessage.cmd_setsmalltimeout) || remoteCommand.equals(SoloMessage.cmd_getlargetimeout) || remoteCommand.equals(SoloMessage.cmd_getsmalltimeout)){ handleRobotiumTimeoutCommand(props); }else if(remoteCommand.equals(SoloMessage.cmd_pinchtozoom) || remoteCommand.equals(SoloMessage.cmd_rotatelarge) || remoteCommand.equals(SoloMessage.cmd_rotatesmall) || remoteCommand.equals(SoloMessage.cmd_swipe)){ handleZoomRotateSwipe(props); }else if(remoteCommand.equals(SoloMessage.cmd_clearlog) || remoteCommand.equals(SoloMessage.cmd_stopscreenshotsequence)){ handleComandWithoutParams(props); } else{ debug(debugPrefix +"Unknown command '"+remoteCommand+"'."); // "unkown/not executed" result already set. } } }catch(ProcessorException pe){ debug(remoteCommand+": Met ProcessorException: '"+pe.getMessage()+"'."); setGeneralError(props, SoloMessage.RESULT_INFO_PROCESSOR_EXCEPTION, pe.getMessage()); }catch(ComparisonFailure cf){ debug(remoteCommand+": Met ComparisonFailure: '"+cf.getMessage()+"'."); setGeneralError(props, SoloMessage.RESULT_INFO_COMPARAISON_FAIL, cf.getMessage()); }catch(AssertionFailedError afe){ debug(remoteCommand+": Met AssertionFailedError: '"+afe.getMessage()+"'."); setGeneralError(props, SoloMessage.RESULT_INFO_ASSERTION_FAIL, afe.getMessage()); }catch(Throwable e){ debug(remoteCommand+": Met Throwable: '"+e.getMessage()+"'. This maybe a program error!!!"); setGeneralError(props, SoloMessage.RESULT_INFO_EXCEPTION, e.getMessage()); } debug(debugPrefix +"Finish processing '"+remoteCommand+"'."); } /** * Set success result for test.<br> * Set 'generated UID' to {@link SoloMessage#KEY_REMOTERESULTINFO} for remote-caller's reference<br> * Set the view's class name to {@link SoloMessage#PARAM_CLASS} for remote-caller's reference<br> * * @param props The Properties object containing the in and out parameters * @param view View, the View object * @return * @see #convertToKey(Hashtable, Object) * @see #setViewClassResult(Properties, View) * @see #setGeneralSuccessWithSpecialInfo(Properties, String) */ private void setSuccessResultForView(Properties props, View view) throws ProcessorException{ String debugPrefix = TAG+".getViewByText() "; //Get the unique key for view from cache String uid = convertToKey(viewCache, view); if(uid==null){ debug(debugPrefix+" Can NOT generate UID for view "+ view.getClass().getSimpleName()); throw new ProcessorException(SoloMessage.RESULT_INFO_GENERATE_UID_NULL); }else{ debug(debugPrefix+" get uid '"+uid+"' for view "+view.getClass().getSimpleName()); } //we will set the PARAM_CLASS of the view for remote caller's reference setViewClassResult(props, view); setGeneralSuccessWithSpecialInfo(props, uid); } /** * Set the view's class name to {@link SoloMessage#PARAM_CLASS} for remote-caller's reference<br> * * @param props The Properties object containing the in and out parameters * @param view View, the View object whose class name be returned by {@link SoloMessage#PARAM_CLASS} * @return * @see #setSuccessResultForView(Properties, View) */ private void setViewClassResult(Properties props, View view) throws ProcessorException{ String debugPrefix = TAG + ".setViewClassResult() "; if(view==null){ throw new ProcessorException("The parameter view is null."); } try{ props.setProperty(SoloMessage.PARAM_CLASS, view.getClass().getName()); }catch(Exception e){ debug(debugPrefix+" Fail to set class name. Exception="+e.getMessage()); } } /** * Set the activity's class name and the activity's name for remote-caller's reference<br> * These two properties will be returned as part of result.<br> * * @param props The Properties object containing the in and out parameters * @return Activity, the current activity got by Solo */ private Activity setCurrentActivityClassName(Properties props) throws ProcessorException{ String debugPrefix = TAG + ".setCurrentActivityClassName() "; Activity activity = solo.getCurrentActivity(); if(activity!=null){ debug(debugPrefix +"set activity's name and classname to the returned Properties."); try{ props.setProperty(SoloMessage.PARAM_CLASS, activity.getClass().getName()); props.put(SoloMessage.PARAM_NAME, getActivityName(activity)); }catch(Exception e){ //setProperty() may throw NullPointerException if the value to be set is null debug(debugPrefix +" met Exception="+e.getMessage()); } }else{ debug(debugPrefix +"There is no current activity, can't set the activity's classname!"); throw new ProcessorException("The current activity is null."); } return activity; } /** * Get the activity name.<br> * It should be the activity's class name without the package name.<br> */ private String getActivityName(Activity activity){ if(activity ==null){ throw new NullPointerException(); } // ComponentName component = activity.getComponentName(); // String simpleClsName = component.getShortClassName(); // if(simpleClsName.startsWith(".")){ // simpleClsName = simpleClsName.substring(1); // } String simpleClsName = activity.getClass().getSimpleName(); return simpleClsName; } /** * This method will launch the main activity.<br> * It will initialize the {@link Solo} object of {@link RobotiumTestRunner}<br> * This Solo object will also be shared by this class<br> * If you want to use the Solo object, you MUST call this method firstly.<br> * * @param props The Properties object containing the in and out parameters * @see RobotiumTestRunner#launchApplication() */ protected void startMainLauncher(Properties props) throws ProcessorException{ String debugPrefix = TAG + ".startMainLauncher() "; robotiumTestrunner.launchApplication(); //Before using the field solo, need to set it firstly if(!checkSolo()){ debug(debugPrefix+" Begin start main launcher ... solo=null"); throw new ProcessorException("Can't get Solo, it is null! Error"); } mainApp = setCurrentActivityClassName(props); setGeneralSuccess(props); } /** * assert the current activity has the given name/class, with the possibility to verify that<br> * the expected Activity is a new instance of the Activity.<br> * * <p> * calling:<br> * {@link Solo#assertCurrentActivity(String, Class)}<br> * {@link Solo#assertCurrentActivity(String, Class, boolean)<br>}<br> * {@link Solo#assertCurrentActivity(String, String))}<br> * {@link Solo#assertCurrentActivity(String, String, boolean))}<br> * * @param props The Properties object containing the in and out parameters */ @SuppressWarnings("rawtypes") void assertCurrentActivity(Properties props) throws ProcessorException{ String debugPrefix = TAG + ".assertCurrentActivity() "; String assertFailMsg = props.getProperty(SoloMessage.PARAM_ERRORMSG); String activityClassString = null; String activityName = null; String resultInfo = ""; Class expectedClass = null; boolean assertWithClass = false; //Get the parameter name or class if (remoteCommand.equals(SoloMessage.cmd_assertnewcurrentactivityname) || remoteCommand.equals(SoloMessage.cmd_assertcurrentactivityname)) { activityName = SoloMessage.getString(props, SoloMessage.PARAM_NAME); debug(debugPrefix +" activity name is '"+activityName+"'"); resultInfo = activityName; } else if (remoteCommand.equals(SoloMessage.cmd_assertnewcurrentactivityclass) || remoteCommand.equals(SoloMessage.cmd_assertcurrentactivityclass)) { activityClassString = SoloMessage.getString(props, SoloMessage.PARAM_CLASS); debug(debugPrefix +" activity class name is '"+activityClassString+"'"); resultInfo = activityClassString; assertWithClass = true; } else { throw new ProcessorException(remoteCommand + " could not be processed in assertCurrentActivity()."); } setCurrentActivityClassName(props); //Try to get the Class object for a class name. if(assertWithClass){ try { // it is possible an Activity Class might not be available to us for Class.forName. // While a normal Solo test might actually have a reference to a real running // Activity from which it can grab the Class, we may not have this. // In that case, we may want to accept that we can instead grab hold of the current // running Activity and getClass().getName() and compare that against our String // classname instead of the call to assertCurrentActivity. expectedClass = Class.forName(activityClassString); debug(debugPrefix +" got Class for '"+activityClassString+"'"); } catch (ClassNotFoundException cnfe) { assertWithClass = false; activityName = SoloMessage.getSimpleClassName(activityClassString); if(activityName==null){ debug(debugPrefix +" can't get the activity name from class name '"+activityClassString+"' !"); throw new ProcessorException("Activity name is null."); } debug(debugPrefix +" Class '"+activityClassString+"' can't be found. Try to assert with class name '"+activityName+"'"); } } //Use Solo to do the real work. if(remoteCommand.equals(SoloMessage.cmd_assertcurrentactivityclass) || remoteCommand.equals(SoloMessage.cmd_assertcurrentactivityname)){ //If the name/class can't match, ComparisonFailure will be thrown //ComparisonFailure will be caught in processProperties() if(assertWithClass){ solo.assertCurrentActivity(assertFailMsg, expectedClass); }else{ solo.assertCurrentActivity(assertFailMsg, activityName); } setGeneralSuccess(props, resultInfo); }else if(remoteCommand.equals(SoloMessage.cmd_assertnewcurrentactivityclass) || remoteCommand.equals(SoloMessage.cmd_assertnewcurrentactivityname)){ boolean isNewInstance = SoloMessage.getBoolean(props, SoloMessage.PARAM_ISNEWINSTANCE); try{ //If the name/class can't match, ComparisonFailure will be thrown //If the isNewInstance can't match, AssertionFailedError will be thrown //we must catch them here, as we need to set the returned value for SoloMessage.PARAM_ISNEWINSTANCE if(assertWithClass){ solo.assertCurrentActivity(assertFailMsg, expectedClass, isNewInstance); }else{ solo.assertCurrentActivity(assertFailMsg, activityName, isNewInstance); } setGeneralSuccess(props, resultInfo); }catch (ComparisonFailure e) { //If we get ComparisonFailure, which means that activity's name can't match setGeneralError(props, SoloMessage.RESULT_INFO_COMPARAISON_FAIL, e.getMessage()); }catch (AssertionFailedError e) { //If we get AssertionFailedError, which means that 'isNewInstance' can't match //Set PARAM_ISNEWINSTANCE, and it contains the 'isNewInstance' of actual activity, //which is the opposite value of the given parameter 'isNewInstance' String isnew = String.valueOf(!isNewInstance); props.setProperty(SoloMessage.PARAM_ISNEWINSTANCE, isnew); setGeneralError(props, SoloMessage.RESULT_INFO_ASSERTION_FAIL, e.getMessage()); } }else{ throw new ProcessorException(remoteCommand+" could not be processed in assertCurrentActivityClass()."); } } /** * assert that the memory is enough<br> * * <p> * calling:<br> * {@link Solo#assertMemoryNotLow()}<br> * * @param props The Properties object containing the in and out parameters */ void assertMemoryNotLow(Properties props){ try{ //AssertionFailedError will be thrown out, if the memory is low solo.assertMemoryNotLow(); setGeneralSuccessWithSpecialInfo(props, String.valueOf(true)); }catch(AssertionFailedError e){ debug(SoloMessage.RESULT_INFO_MEMORY_ISLOW+" Met Exception="+e.getMessage()); setGeneralSuccessWithSpecialInfo(props, String.valueOf(false)); } } /** * clear the text in {@link android.widget.EditText}<br> * * <p> * calling:<br> * {@link Solo#clearEditText(EditText)}<br> * {@link Solo#clearEditText(int)}<br> * * @param props The Properties object containing the in and out parameters */ void clearEditText(Properties props) throws ProcessorException{ if(remoteCommand.equals(SoloMessage.cmd_clearedittextindex)){ int index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); //If editbox can't be edited, an AssertionFailedError will be thrown out //this exception will be handled in method processProperties() debug("Params: index="+index); solo.clearEditText(index); setGeneralSuccess(props, String.valueOf(index)); }else if(remoteCommand.equals(SoloMessage.cmd_clearedittextreference)){ String uid = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); debug("Params: uid="+uid); EditText editText = (EditText)getViewById(uid, EditText.class); //If editbox can't be edited, an AssertionFailedError will be thrown out //this exception will be handled in method processProperties() solo.clearEditText(editText); setGeneralSuccess(props, uid); }else{ throw new ProcessorException(remoteCommand+" could not be processed in clearEditText()."); } } /** * Handle the commands without parameters<br> * * <p> * calling: * <p> * {@link Solo#clearLog()} <b>Requires Robotium4.1+</b><br> * {@link Solo#stopScreenshotSequence()} <b>Requires Robotium4.2</b><br> * * @param props The Properties object containing the in and out parameters */ void handleComandWithoutParams(Properties props){ try{ if(remoteCommand.equals(SoloMessage.cmd_clearlog)){ solo.clearLog(); }else if(remoteCommand.equals(SoloMessage.cmd_stopscreenshotsequence)){ solo.stopScreenshotSequence(); }else { throw new ProcessorException(remoteCommand+" could not be processed in handleComandWithoutParams()."); } setGeneralSuccess(props); }catch(Throwable x){ debug("handleComandWithoutParams() "+SoloMessage.getStackTrace(x)); setGeneralError(props, x.getClass().getSimpleName()+": "+ x.getMessage()); } } /** * click an item in android.widget.ListView<br> * * <p> * calling:<br> * {@link Solo#clickInList(int)}<br> * {@link Solo#clickInList(int, int)}<br> * {@link Solo#clickLongInList(int)}<br> * {@link Solo#clickLongInList(int, int)}<br> * {@link Solo#clickLongInList(int, int, int)}<br> * * @param props The Properties object containing the in and out parameters * @param longtime Boolean, if a long-time click should be performed. */ void clickInList(Properties props, boolean longtime) throws ProcessorException{ int line = 0; int index = 0; List<TextView> viewList = null; line = SoloMessage.getInteger(props, SoloMessage.PARAM_LINE); debug("Params: longtime="+longtime+"; line="+line); if(remoteCommand.equals(SoloMessage.cmd_clickinlist) || remoteCommand.equals(SoloMessage.cmd_clicklonginlist)){ //If there is no ListView, an AssertionFailedError will be thrown out if(longtime){ viewList = solo.clickLongInList(line); }else{ viewList = solo.clickInList(line); } }else if(remoteCommand.equals(SoloMessage.cmd_clickinlistindex) || remoteCommand.equals(SoloMessage.cmd_clicklonginlistindex)){ index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); debug("Params: index="+index); //If there is no ListView, an AssertionFailedError will be thrown out if(longtime){ viewList = solo.clickLongInList(line, index); }else{ viewList = solo.clickInList(line, index); } }else if(remoteCommand.equals(SoloMessage.cmd_clicklongtimeinlistindex)){ index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); //If there is no ListView, an AssertionFailedError will be thrown out int time = SoloMessage.getInteger(props, SoloMessage.PARAM_TIME); debug("Params: index="+index+"; time="+time); viewList = solo.clickLongInList(line, index, time); }else{ throw new ProcessorException(remoteCommand+" could not be processed in clickInList()."); } //Store the text value to 'out parameter' SoloMessage.PARAM_TEXT if(viewList!=null){ String[] textArray = new String[viewList.size()]; for(int i=0;i<viewList.size();i++){ textArray[i] = viewList.get(i).getText().toString(); } props.put(SoloMessage.PARAM_TEXT, Message.convertToDelimitedString(textArray) ); }else{ //Normally, the List returned from solo#clickInList() and solo#clickLongInList() //will not return a null. You should never come here. debug("The returned textViewList is null."); } String[] items = convertToKeys(viewCache, viewList); setGeneralSuccessWithSpecialInfo(props, Message.convertToDelimitedString(items)); } /** * click some point on screen<br> * * <p> * calling:<br> * {@link Solo#clickOnScreen(float, float)}<br> * {@link Solo#clickLongOnScreen(float, float)}<br> * {@link Solo#clickLongOnScreen(float, float, int)}<br> * {@link Solo#clickOnScreen(float, float, int)} <b>Requires Robotium4.1+</b><br> * * @param props The Properties object containing the in and out parameters * @param longtime Boolean, if a long-time click should be performed. */ void clickOnScreen(Properties props, boolean longtime) throws ProcessorException{ float x = SoloMessage.getFloat(props, SoloMessage.PARAM_FLOATX); float y = SoloMessage.getFloat(props, SoloMessage.PARAM_FLOATY); debug("Params: longtime="+longtime+"; x="+x+"; y="+y); if(longtime){ if(remoteCommand.equals(SoloMessage.cmd_clicklongonscreen)){ solo.clickLongOnScreen(x, y); }else if(remoteCommand.equals(SoloMessage.cmd_clicklongtimeonscreen)){ int time = SoloMessage.getInteger(props, SoloMessage.PARAM_TIME);//milliseconds debug("Params: time="+time); solo.clickLongOnScreen(x, y, time); }else{ throw new ProcessorException(remoteCommand+" could not be processed in clickOnScreen()."); } }else{ if(remoteCommand.equals(SoloMessage.cmd_clickonscreen)){ solo.clickOnScreen(x, y); }else if(remoteCommand.equals(SoloMessage.cmd_clickonscreenntimes)){ int clicknumbers = SoloMessage.getInteger(props, SoloMessage.PARAM_CLICKNUMBER); solo.clickOnScreen(x, y, clicknumbers); }else{ throw new ProcessorException(remoteCommand+" could not be processed in clickOnScreen()."); } } setGeneralSuccess(props, "At point ("+x+","+y+")"); } /** * Click on the ActionBar Home Button.<br> * Requires Robotium 3.4.1 * <p> * calling:<br> * {@link Solo#clickOnActionBarHomeButton()}<br> * * @param props The Properties object containing the in and out parameters */ void clickOnActionBarHomeButton(Properties props){ try{ solo.clickOnActionBarHomeButton();} catch(Throwable x){ String msg = x.getClass().getSimpleName()+": "+ x.getMessage(); debug("clickOnActionBarHomeButton "+msg); setGeneralError(props, msg); return; } setGeneralSuccess(props); } /** * Clicks on an ActionBar item with a given resource id.<br> * Requires Robotium 3.6 * <p> * calling:<br> * {@link Solo#clickOnActionBarItem(int)}<br> * * @param props The Properties object containing the in and out parameters */ void clickOnActionBarItem(Properties props) throws ProcessorException{ int resourceid = SoloMessage.getInteger(props, SoloMessage.PARAM_RESID); try{ solo.clickOnActionBarItem(resourceid);} catch(Throwable x){ String msg = x.getClass().getSimpleName()+": "+ x.getMessage(); debug("clickOnActionBarItem "+msg); setGeneralError(props, msg); return; } setGeneralSuccess(props); } /** * click on a string within the current<br> * * <p> * calling:<br> * {@link Solo#clickOnText(String)}<br> * {@link Solo#clickOnText(String, int)}<br> * {@link Solo#clickOnText(String, int, boolean)}<br> * {@link Solo#clickLongOnText(String)}<br> * {@link Solo#clickLongOnText(String, int)}<br> * {@link Solo#clickLongOnText(String, int, boolean)}<br> * {@link Solo#clickLongOnText(String, int, int)}<br> * {@link Solo#clickLongOnTextAndPress(String, int)}<br> * * @param props The Properties object containing the in and out parameters * @param longtime Boolean, if a long-time click should be performed. */ void clickOnText(Properties props, boolean longtime) throws ProcessorException{ String text = SoloMessage.getString(props, SoloMessage.PARAM_TEXT); boolean scroll = false; int match = 0; int time = 0; debug("Params: longtime="+longtime+"; text="+text); if(longtime){ if(remoteCommand.equals(SoloMessage.cmd_clicklongontext)){ solo.clickLongOnText(text); }else if(remoteCommand.equals(SoloMessage.cmd_clicklongontextmatch)){ match = SoloMessage.getInteger(props, SoloMessage.PARAM_MATCH); debug("Params: match="+match); solo.clickLongOnText(text, match); }else if(remoteCommand.equals(SoloMessage.cmd_clicklongontextmatchscroll)){ match = SoloMessage.getInteger(props, SoloMessage.PARAM_MATCH); scroll = Boolean.parseBoolean(props.getProperty(SoloMessage.PARAM_SCROLL)); debug("Params: match="+match+"; scroll="+scroll); solo.clickLongOnText(text, match, scroll); }else if(remoteCommand.equals(SoloMessage.cmd_clicklongtimeontextmatch)){ match = SoloMessage.getInteger(props, SoloMessage.PARAM_MATCH); time = SoloMessage.getInteger(props, SoloMessage.PARAM_TIME); debug("Params: match="+match+"; time="+time); solo.clickLongOnText(text, match, time); }else if(remoteCommand.equals(SoloMessage.cmd_clicklongpressontext)){ int index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); debug("Params: index="+index); solo.clickLongOnTextAndPress(text, index); }else{ throw new ProcessorException(remoteCommand+" could not be processed in clickOnText()."); } }else{ if(remoteCommand.equals(SoloMessage.cmd_clickontext)){ solo.clickOnText(text); }else if(remoteCommand.equals(SoloMessage.cmd_clickontextmatch)){ match = SoloMessage.getInteger(props, SoloMessage.PARAM_MATCH); debug("Params: match="+match); solo.clickOnText(text, match); }else if(remoteCommand.equals(SoloMessage.cmd_clickontextmatchscroll)){ match = SoloMessage.getInteger(props, SoloMessage.PARAM_MATCH); scroll = Boolean.parseBoolean(props.getProperty(SoloMessage.PARAM_SCROLL)); debug("Params: match="+match+"; scroll="+scroll); solo.clickOnText(text, match, scroll); }else{ throw new ProcessorException(remoteCommand+" could not be processed in clickOnText()."); } } setGeneralSuccess(props, text); } /** * click on a {@link android.view.View}<br> * The view's id is given by parameter, according to this id,<br> * We will find the View from local cache {@link #viewCache} and click on it.<br> * This method has no relationship with method {@link #clickOnViewByIndex(Properties)}<br> * * <p> * calling:<br> * {@link Solo#clickOnView(View)}<br> * {@link Solo#clickOnView(View, boolean)} -- Robotium 4.1<br> * {@link Solo#clickLongOnView(View)}<br> * {@link Solo#clickLongOnView(View, int)}<br> * * @param props The Properties object containing the in and out parameters * @param longtime Boolean, if a long-time click should be performed. * @see Solo#clickOnView(View) */ void clickOnView(Properties props, boolean longtime) throws ProcessorException{ String uid = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); View view = null; try{ view = (View)getCachedObject(uid, true); }catch(ClassCastException x){ throw new ProcessorException(remoteCommand+" retrieved object was not an instanceof View!"); } debug("Params: longtime="+longtime+"; uid="+uid); if(longtime){ if(remoteCommand.equals(SoloMessage.cmd_clicklongonview)){ solo.clickLongOnView(view); }else if(remoteCommand.equals(SoloMessage.cmd_clicklongtimeonview)){ int time = SoloMessage.getInteger(props, SoloMessage.PARAM_TIME); debug("Params: time="+time); solo.clickLongOnView(view, time); }else{ throw new ProcessorException(remoteCommand+" could not be processed in clickOnView()."); } }else{ if(remoteCommand.equals(SoloMessage.cmd_clickonview)){ solo.clickOnView(view); }else if(remoteCommand.equals(SoloMessage.cmd_clickonviewimmediately)){ boolean immediately = SoloMessage.getBoolean(props, SoloMessage.PARAM_IMMEDIATELY); solo.clickOnView(view, immediately); }else{ throw new ProcessorException(remoteCommand+" could not be processed in clickOnView()."); } } if(DEBUG){ debug("Clicked on view '"+view.getClass()+"'"); } setViewClassResult(props, view); setGeneralSuccess(props, uid+" : "+view.getClass().getSimpleName()); } /** * click on the following {@link android.widget.TextView} by name<br> * {@link android.widget.Button}<br> * {@link android.widget.ToggleButton}<br> * * The name is given by parameter, according to that name<br> * Solo will click on the appropriate TextView.<br> * This method has no relationship with method {@link #clickOnView(Properties, boolean)}<br> * * <p> * calling:<br> * {@link Solo#clickOnButton(String)}<br> * {@link Solo#clickOnToggleButton(String)}<br> * * @param props The Properties object containing the in and out parameters */ void clickOnViewByName(Properties props) throws ProcessorException{ String name = SoloMessage.getString(props, SoloMessage.PARAM_NAME); debug("Params: name="+name); if(remoteCommand.equals(SoloMessage.cmd_clickonbutton)){ solo.clickOnButton(name); }else if(remoteCommand.equals(SoloMessage.cmd_clickontogglebutton)){ solo.clickOnToggleButton(name); }else{ throw new ProcessorException(remoteCommand+" could not be processed in clickOnViewByName()."); } setGeneralSuccess(props, name); } /** * click on the following {@link android.widget.TextView} by index<br> * {@link android.widget.CheckBox}<br> * {@link android.widget.EditText}<br> * {@link android.widget.ImageView}<br> * {@link android.widget.ImageButton}<br> * {@link android.widget.RadioButton}<br> * {@link android.widget.Button}<br> * * The index is given by parameter, according to that index<br> * Solo will click on the appropriate TextView.<br> * This method has no relationship with method {@link #clickOnView(Properties, boolean)}<br> * * <p> * calling:<br> * {@link Solo#clickOnCheckBox(int)}<br> * {@link Solo#clickOnEditText(int)}<br> * {@link Solo#clickOnImage(int)}<br> * {@link Solo#clickOnImageButton(int)}<br> * {@link Solo#clickOnRadioButton(int)}<br> * {@link Solo#clickOnButton(int)}<br> * * @param props The Properties object containing the in and out parameters */ void clickOnViewByIndex(Properties props) throws ProcessorException{ int index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); debug("Params: index="+index); if(remoteCommand.equals(SoloMessage.cmd_clickoncheckboxindex)){ solo.clickOnCheckBox(index); }else if(remoteCommand.equals(SoloMessage.cmd_clickonedittextindex)){ solo.clickOnEditText(index); }else if(remoteCommand.equals(SoloMessage.cmd_clickonimage)){ solo.clickOnImage(index); }else if(remoteCommand.equals(SoloMessage.cmd_clickonimagebutton)){ solo.clickOnImageButton(index); }else if(remoteCommand.equals(SoloMessage.cmd_clickonradiobuttonindex)){ solo.clickOnRadioButton(index); }else if(remoteCommand.equals(SoloMessage.cmd_clickonbuttonindex)){ solo.clickOnButton(index); }else{ throw new ProcessorException(remoteCommand+" could not be processed in clickOnViewByIndex()."); } setGeneralSuccess(props, String.valueOf(index)); } /** * click on a 'menu item' indicated by text<br> * * <p> * calling:<br> * {@link Solo#clickOnMenuItem(String)}<br> * {@link Solo#clickOnMenuItem(String, boolean)}<br> * * @param props The Properties object containing the in and out parameters */ void clickOnMenuItem(Properties props, boolean subMenu) throws ProcessorException{ String text = SoloMessage.getString(props, SoloMessage.PARAM_TEXT); String resultInfo = text; debug("Params: text="+text); if(subMenu){ solo.clickOnMenuItem(text, subMenu); resultInfo = "Sub Menu: "+resultInfo; }else{ solo.clickOnMenuItem(text); resultInfo = "Menu: "+resultInfo; } setGeneralSuccess(props, resultInfo); } /** * Simulate touching a given location and dragging it to a new location.<br> * * <p> * calling:<br> * {@link Solo#drag(float, float, float, float, int)}<br> * * @param props The Properties object containing the in and out parameters */ void drag(Properties props) throws ProcessorException{ float fromX = SoloMessage.getFloat(props, SoloMessage.PARAM_FROMX); float fromY = SoloMessage.getFloat(props, SoloMessage.PARAM_FROMY); float toX = SoloMessage.getFloat(props, SoloMessage.PARAM_TOX); float toY = SoloMessage.getFloat(props, SoloMessage.PARAM_TOY); int stepCount = SoloMessage.getInteger(props, SoloMessage.PARAM_STEPCOUNT); String resultInfo = " From("+fromX+","+fromY+") To("+toX+","+toY+") "; debug("Params: stepCount="+stepCount+"; "+resultInfo); solo.drag(fromX, toX, fromY, toY, stepCount); setGeneralSuccess(props, resultInfo); } /** * input the text in {@link android.widget.EditText}<br> * * <p> * calling:<br> * {@link Solo#enterText(int, String)}<br> * {@link Solo#enterText(EditText, String)}<br> * {@link Solo#typeText(int, String)} -- Robotium 3.6<br> * {@link Solo#typeText(EditText, String)} -- Robotium 3.6<br> * * @param props The Properties object containing the in and out parameters */ void enterText(Properties props) throws ProcessorException{ String text = SoloMessage.getString(props, SoloMessage.PARAM_TEXT); debug("Params: text="+text); if(remoteCommand.equals(SoloMessage.cmd_entertextindex)){ int index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); debug("Params: index="+index); solo.enterText(index, text); setGeneralSuccess(props, " index="+index+" : text='"+text+"' "); }else if(remoteCommand.equals(SoloMessage.cmd_entertextreference)){ String id = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); debug("Params: id="+id); EditText editText = (EditText)getViewById(id, EditText.class); solo.enterText(editText, text); setGeneralSuccess(props, String.valueOf(id)); }else if(remoteCommand.equals(SoloMessage.cmd_typetext)){ int index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); debug("Params: index="+index); solo.typeText(index, text); setGeneralSuccess(props, " index="+index+" : text='"+text+"' "); }else if(remoteCommand.equals(SoloMessage.cmd_typetextuid)){ String id = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); debug("Params: id="+id); EditText editText = (EditText)getViewById(id, EditText.class); solo.typeText(editText, text); setGeneralSuccess(props, String.valueOf(id)); }else{ throw new ProcessorException(remoteCommand+" could not be processed in clearEditText()."); } } /** * Finalizes the solo object and removes the ActivityMonitor.<br> * * <p> * calling:<br> * {@link Solo#finalize()}<br> * * @param props The Properties object containing the in and out parameters */ void finalizeRemoteSolo(Properties props){ String debugPrefix = TAG + ".finalizeRemoteSolo() "; try { solo.finalize(); mainApp = null; clearCache(false); activityMonitorCache.clear(); setGeneralSuccess(props); } catch (Throwable e) { String error = "Met error: "+e.getMessage(); debug(debugPrefix+error); setGeneralError(props,error); } } /** * All activities that have been active are finished.<br> * * <p> * calling:<br> * {@link Solo#finishOpenedActivities()}<br> * * @param props The Properties object containing the in and out parameters */ void finishOpenedActivities(Properties props){ solo.finishOpenedActivities(); mainApp = null; clearCache(false); setGeneralSuccess(props); } /** * Returns a String UID for the Robotium Solo Activity Monitor<br> * Use a cache to store the ActivityMonitor.<br> * If the ActivityMonitor doesn't exist in the cache, we generate an ID<br> * and put the ActivityMonitor into cache with that ID.<br> * If the ActivityMonitor exists in the cache, we just return the related ID.<br> * * <p> * calling:<br> * {@link Solo#getActivityMonitor()}<br> * * @param props The Properties object containing the in and out parameters */ void getActivityMonitor(Properties props) throws ProcessorException{ String debugPrefix = TAG + ".getActivityMonitor() "; ActivityMonitor am = solo.getActivityMonitor(); String activityMonitorID = ""; if(am==null){ throw new ProcessorException(SoloMessage.RESULT_INFO_ACTIVITYMONITOR_NULL); } activityMonitorID = convertToKey(activityMonitorCache, am); if(activityMonitorID==null){ debug(debugPrefix +" Could NOT generate ID for Activity Monitor!"); throw new ProcessorException(SoloMessage.RESULT_INFO_GENERATE_UID_NULL); }else{ debug(debugPrefix +" get UID '"+activityMonitorID+"' for Activity Monitor"); } setGeneralSuccessWithSpecialInfo(props, activityMonitorID); } /** * get all activities and return their ID in format ";ID;ID;ID"<br> * the ID string will be stored in remoteresultinfo of parameter props.<br> * * <p> * calling:<br> * {@link Solo#getAllOpenedActivities()}<br> * * @param props The Properties object containing the in and out parameters */ void getAllOpenActivities(Properties props) throws ProcessorException{ String debugPrefix = TAG + ".getAllOpenActivities() "; List<Activity> activities = solo.getAllOpenedActivities(); if(activities==null || activities.size()==0){ debug(debugPrefix+" Can't get any activities!"); throw new ProcessorException("Can't get any activities!"); } if(DEBUG){ String[] activityNames = new String[activities.size()]; for(int i=0;i<activities.size();i++){ activityNames[i] = getActivityName(activities.get(i)); } debug("current activites: "+Message.convertToDelimitedString(activityNames)); } String[] uids = convertToKeys(activityCache, activities.toArray()); setGeneralSuccessWithSpecialInfo(props,Message.convertToDelimitedString(uids)); } /** * get the current activity and return its ID<br> * the ID string will be stored in remoteresultinfo of parameter props.<br> * <p> * Upon success the resultProperties should contain: * <p> * PARAM_CLASS=full Classname of the Activity, and <br> * PARAM_NAME=Local (short) classname of the Activity. * <p> * calling:<br> * {@link Solo#getCurrentActivity()}<br> * * @param props The Properties object containing the in and out parameters */ void getCurrentActivity(Properties props) throws ProcessorException{ String debugPrefix = TAG + ".getCurrentActivity() "; Activity activity = setCurrentActivityClassName(props); String uid = convertToKey(activityCache, activity); if(uid==null){ debug(debugPrefix+" Can NOT generate UID for activity "+activity.getLocalClassName()); throw new ProcessorException(SoloMessage.RESULT_INFO_GENERATE_UID_NULL); }else{ debug(debugPrefix +" get UID '"+uid+"' for activity "+activity.getLocalClassName()); } setGeneralSuccessWithSpecialInfo(props, uid); } /** * get all the {@link android.view.View}s of following type:<br> * {@link android.widget.Button}<br> * {@link android.widget.CheckBox}<br> * {@link android.widget.DatePicker}<br> * {@link android.widget.EditText}<br> * {@link android.widget.GridView}<br> * {@link android.widget.ImageButton}<br> * {@link android.widget.ImageView}<br> * {@link android.widget.ListView}<br> * {@link android.widget.ProgressBar}<br> * {@link android.widget.RadioButton}<br> * {@link android.widget.ScrollView}<br> * {@link android.widget.SlidingDrawer}<br> * {@link android.widget.Spinner}<br> * {@link android.widget.TextView}<br> * {@link android.widget.TimePicker}<br> * {@link android.widget.ToggleButton}<br> * {@link android.view.View}<br> * * <p> * calling:<br> * {@link Solo#getViews()} - Robotium 4.1<br> * {@link Solo#getCurrentViews()} - Robotium 4.1<br> * {@link Solo#getCurrentViews(Class)} - Robotium 4.1<br> * {@link Solo#getCurrentViews(Class, View)} - Robotium 4.1<br> * * @param props The Properties object containing the in and out parameters */ void getCurrentViews(Properties props) throws ProcessorException{ List<?> currentViewList = null; if(remoteCommand.equals(SoloMessage.cmd_getcurrentbuttons)){ currentViewList = solo.getCurrentViews(Button.class); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentcheckboxes)){ currentViewList = solo.getCurrentViews(CheckBox.class); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentdatepickers)){ currentViewList = solo.getCurrentViews(DatePicker.class); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentedittexts)){ currentViewList = solo.getCurrentViews(EditText.class); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentgridviews)){ currentViewList = solo.getCurrentViews(GridView.class); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentimagebuttons)){ currentViewList = solo.getCurrentViews(ImageButton.class); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentimageviews)){ currentViewList = solo.getCurrentViews(ImageView.class); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentlistviews)){ currentViewList = solo.getCurrentViews(ListView.class); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentprogressbars)){ currentViewList = solo.getCurrentViews(ProgressBar.class); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentradiobuttons)){ currentViewList = solo.getCurrentViews(RadioButton.class); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentscrollviews)){ currentViewList = solo.getCurrentViews(ScrollView.class); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentslidingdrawers)){ currentViewList = solo.getCurrentViews(SlidingDrawer.class); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentspinners)){ currentViewList = solo.getCurrentViews(Spinner.class); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrenttextviews)){ currentViewList = solo.getCurrentViews(TextView.class); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrenttimepickers)){ currentViewList = solo.getCurrentViews(TimePicker.class); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrenttogglebuttons)){ currentViewList = solo.getCurrentViews(ToggleButton.class); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentviews)){ currentViewList = solo.getCurrentViews(); }else if(remoteCommand.equals(SoloMessage.cmd_getviews)){ currentViewList = solo.getViews(); // }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentnumberpickers)){ // currentViewList = solo.getCurrentViews(NumberPicker.class); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentviewsbyclass)){ String className = SoloMessage.getString(props, SoloMessage.PARAM_CLASS); Class<View> viewClass = ClassViewForName(className); if(viewClass==null){ throw new ProcessorException(remoteCommand+": '"+className+"' could not be found."); } currentViewList = solo.getCurrentViews(viewClass); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentviewsbyclassandparent)){ String className = SoloMessage.getString(props, SoloMessage.PARAM_CLASS); String parentUID = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); Class<View> viewClass = ClassViewForName(className); if(viewClass==null){ throw new ProcessorException(remoteCommand+": '"+className+"' could not be found."); } View parent = null; try{ parent = (View) getCachedObject(parentUID, true); }catch(Exception e){ throw new ProcessorException(remoteCommand+": could not find view from cache by UID '"+parentUID+"'."); } currentViewList = solo.getCurrentViews(viewClass, parent); }else{ throw new ProcessorException(remoteCommand+" could not be processed in getCurrentViews()."); } String[] items = convertToKeys(viewCache, currentViewList); setGeneralSuccessWithSpecialInfo(props, Message.convertToDelimitedString(items)); } /** * get the current WebElements and return its ID<br> * the ID string will be stored in remoteresultinfo of parameter props.<br> * <p> * calling:<br> * {@link Solo#getCurrentWebElements()} -- Robotium 4.1<br> * {@link Solo#getCurrentWebElements(By)} -- Robotium 4.1<br> * {@link Solo#getWebElement(By, int)} -- Robotium 4.1<br> * * @param props The Properties object containing the in and out parameters */ void getCurrentWebElements(Properties props) throws ProcessorException{ List<WebElement> list = null; if(remoteCommand.equals(SoloMessage.cmd_getcurrentwebelements)){ list = solo.getCurrentWebElements(); }else if(remoteCommand.equals(SoloMessage.cmd_getcurrentwebelementsby)){ String objectStr = SoloMessage.getString(props, SoloMessage.PARAM_OBJECT); By by = SoloMessage.getSoloBy((com.jayway.android.robotium.remotecontrol.By) SoloMessage.decodeBase64Object(objectStr)); list = solo.getCurrentWebElements(by); }else if(remoteCommand.equals(SoloMessage.cmd_getwebelement)){ String objectStr = SoloMessage.getString(props, SoloMessage.PARAM_OBJECT); By by = SoloMessage.getSoloBy((com.jayway.android.robotium.remotecontrol.By) SoloMessage.decodeBase64Object(objectStr)); int index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); list = new ArrayList<WebElement>(); try{ list.add(solo.getWebElement(by, index)); }catch(Throwable t){ String debugmsg = "can't get WebElement by by="+by+"; index="+index+" due to "+Message.getStackTrace(t); debug(debugmsg); throw new ProcessorException(remoteCommand+debugmsg); } }else{ throw new ProcessorException(remoteCommand+" could not be processed in getCurrentWebElements()."); } String[] items = convertToKeys(webElementCache, list); setGeneralSuccessWithSpecialInfo(props, Message.convertToDelimitedString(items)); } /** * get the {@link android.view.View} by id (R.id)<br> * * The id is given by parameter, according to that id<br> * Solo will get the appropriate View.<br> * * <p> * calling:<br> * {@link #getViewById(int, Class)}<br> * * @param props The Properties object containing the in and out parameters */ void getViewById(Properties props) throws ProcessorException{ String debugPrefix = TAG+".getViewById(Properties) "; int id = SoloMessage.getInteger(props, SoloMessage.PARAM_ID); debug(debugPrefix+" Try to get view for id '"+id+"'"); //getViewById() will never return a null View view = getViewById(id, null); setSuccessResultForView(props, view); } /** * get the {@link android.view.View} by resource idname. <b>Requires Robotium4.1+</b><br> * The resource idname is stored in AUT layout xml file. <br> * For example, in xml a view is given id as "@+id/flipper", "flipper" is the idname.<br> * * The idname is given by parameter, according to that name, Solo will get the appropriate View.<br> * * <p> * calling:<br> * {@link Solo#getView(String)}<br> * {@link Solo#getView(String, int)}<br> * * @param props The Properties object containing the in and out parameters */ void getViewByName(Properties props) throws ProcessorException{ String debugPrefix = TAG+".getViewByName(Properties) "; String idname = SoloMessage.getString(props, SoloMessage.PARAM_NAME); View view = null; debug(debugPrefix+" Try to get view for resource name '"+idname+"'"); if(remoteCommand.equals(SoloMessage.cmd_getviewbyname)){ view = solo.getView(idname); if(view==null && !isFullResouceName(idname)){ String androidResName = getAndroidResouceName(idname, "id"); debug("Try to get view for resource name '"+androidResName+"'"); view = solo.getView(androidResName); } }else if(remoteCommand.equals(SoloMessage.cmd_getviewbynamematch)){ int index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); view = solo.getView(idname, index); if(view==null && !isFullResouceName(idname)){ String androidResName = getAndroidResouceName(idname, "id"); debug("Try to get view for resource name '"+androidResName+"'"); view = solo.getView(androidResName, index); } }else{ throw new ProcessorException(remoteCommand+" could not be processed in getViewByName()."); } if(view==null) throw new ProcessorException(remoteCommand+" could not get a view for resource name '"+idname+"'."); setSuccessResultForView(props, view); } /** * The resource name contains 3 parts: "package", "type" and "entry", it has format as "package:type/entry". * If a "resource name" contains both separators ":" and "/", we consider it as a full name. * @param resourceName, String, the resource name * @return boolean, if the resource name is fully qualified. */ private boolean isFullResouceName(String resourceName) throws ProcessorException{ if(resourceName==null) throw new ProcessorException("The resourceName is null."); return (resourceName.contains(":") && resourceName.contains("/")); } /** * According to the entryName and type, generate a resource name of package "android". * @param entryName, String, the entry name of the resource. * @param type, String, the type of the resource. * @return String, the full resource name of package "android" */ private String getAndroidResouceName(String entryName, String type) throws ProcessorException{ String androidResourceName = entryName; if(!isFullResouceName(entryName)){ androidResourceName = "android:"+type+"/"+entryName; } return androidResourceName; } /** * get the following {@link android.widget.TextView} by index<br> * {@link android.widget.Button}<br> * {@link android.widget.EditText}<br> * {@link android.widget.TextView}<br> * {@link android.widget.ImageView}<br> * {@link android.widget.ImageButton}<br> * * The index is given by parameter, according to that index<br> * Solo will get the appropriate TextView.<br> * * <p> * calling:<br> * {@link Solo#getButton(int)}<br> * {@link Solo#getEditText(int)}<br> * {@link Solo#getImage(int)}<br> * {@link Solo#getImageButton(int)}<br> * {@link Solo#getText(int)}<br> * * @param props The Properties object containing the in and out parameters */ void getViewByIndex(Properties props) throws ProcessorException{ int index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); View view = null; debug("Params: index="+index); if(remoteCommand.equals(SoloMessage.cmd_getbutton)){ view = solo.getButton(index); }else if(remoteCommand.equals(SoloMessage.cmd_getedittext)){ view = solo.getEditText(index); }else if(remoteCommand.equals(SoloMessage.cmd_getimage)){ view = solo.getImage(index); }else if(remoteCommand.equals(SoloMessage.cmd_getimagebutton)){ view = solo.getImageButton(index); }else if(remoteCommand.equals(SoloMessage.cmd_gettext)){ view = solo.getText(index); }else if(remoteCommand.equals(SoloMessage.cmd_getviewclass)){ String className = SoloMessage.getString(props, SoloMessage.PARAM_CLASS); debug("Params: className="+className); //TODO risk not find the class, But there is no solo.getView(index) to use!!! Class<View> viewClass = ClassViewForName(className); if(viewClass==null){ //TODO Need new Implementation throw new ProcessorException(remoteCommand+": '"+className+"' could not be found."); } view = solo.getView(viewClass, index); }else{ throw new ProcessorException(remoteCommand+" could not be processed in getViewByIndex()."); } //AssertionFailedError will be thrown if view can't be found, no need to check if view is null setSuccessResultForView(props, view); } /** * get the following {@link android.widget.TextView} by text<br> * {@link android.widget.Button}<br> * {@link android.widget.EditText}<br> * {@link android.widget.TextView}<br> * * * The text is given by parameter, according to that text<br> * Solo will get the appropriate TextView.<br> * * <p> * calling:<br> * {@link Solo#getButton(String)}<br> * {@link Solo#getButton(String, boolean)}<br> * {@link Solo#getEditBox(String)}<br> * {@link Solo#getEditBox(String, boolean)}<br> * {@link Solo#getText(String)}<br> * {@link Solo#getText(String, boolean)}<br> * * @param props The Properties object containing the in and out parameters */ void getViewByText(Properties props) throws ProcessorException{ String debugPrefix = TAG+".getViewByText() "; String text = SoloMessage.getString(props, SoloMessage.PARAM_TEXT); boolean onlyVisible = false; View view = null; debug(debugPrefix+" Try to get view according to text '"+text+"'"); if(remoteCommand.equals(SoloMessage.cmd_getbuttontext)){ view = solo.getButton(text); }else if(remoteCommand.equals(SoloMessage.cmd_getbuttonvisible)){ onlyVisible = SoloMessage.getBoolean(props, SoloMessage.PARAM_ONLYVISIBLE); debug("Params: onlyVisible="+onlyVisible); view = solo.getButton(text, onlyVisible); }else if(remoteCommand.equals(SoloMessage.cmd_getedittexttext)){ view = solo.getEditText(text); }else if(remoteCommand.equals(SoloMessage.cmd_getedittextvisible)){ onlyVisible = SoloMessage.getBoolean(props, SoloMessage.PARAM_ONLYVISIBLE); debug("Params: onlyVisible="+onlyVisible); view = solo.getEditText(text, onlyVisible); }else if(remoteCommand.equals(SoloMessage.cmd_gettexttext)){ view = solo.getText(text); if(DEBUG){ debug("Got TextView '"+((TextView)view).getText().toString()+"'"); } }else if(remoteCommand.equals(SoloMessage.cmd_gettextvisible)){ onlyVisible = SoloMessage.getBoolean(props, SoloMessage.PARAM_ONLYVISIBLE); debug("Params: onlyVisible="+onlyVisible); view = solo.getText(text, onlyVisible); }else{ throw new ProcessorException(remoteCommand+" could not be processed in getViewByText()."); } //AssertionFailedError will be thrown if view can't be found, no need to check if view is null setSuccessResultForView(props, view); } /** * Get all the views belonging to a parent view. The parent view is given <br> * by the parameter {@link SoloMessage#PARAM_REFERENCE}<br> * * <p> * calling:<br> * {@link Solo#getViews(View)}<br> */ void getViewsInParent(Properties props) throws ProcessorException{ String debugPrefix = TAG +".getViewsInParent(): "; List<View> views = null; if(remoteCommand.equals(SoloMessage.cmd_getparentviews)){ String pid = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); debug("Params: pid="+pid); View parent = getViewById(pid, null); if(parent==null){ debug(debugPrefix +"Can't find parent view by id '"+pid+"', program will return all the current views."); } views = solo.getViews(parent); if(views!=null && views.size()>0){ debug(debugPrefix+" Got "+views.size()+" items in "+parent.getClass().getSimpleName()); }else{ debug(debugPrefix+" Didn't get any item in "+parent.getClass().getSimpleName()); } }else{ throw new ProcessorException(remoteCommand+" could not be processed in getViewsInParent()."); } String[] items = convertToKeys(viewCache, views); setGeneralSuccessWithSpecialInfo(props, Message.convertToDelimitedString(items)); } /** * Returns a localized String from localized String resources, according to 'resource id'<br> * given by {@link SoloMessage#PARAM_RESID}<br> * * <p> * calling:<br> * {@link Solo#getString(int)}<br> */ void getString(Properties props) throws ProcessorException{ String debugPrefix = TAG +".getString(): "; int resId = SoloMessage.getInteger(props, SoloMessage.PARAM_RESID); debug("Params: resId="+resId); String resourceStr = ""; try{ resourceStr = solo.getString(resId); }catch(NotFoundException nfe){ debug(debugPrefix +" Can't find resource string for id '"+resId+"'. Exception: "+nfe.getMessage()); resourceStr = "null"; } setGeneralSuccessWithSpecialInfo(props, resourceStr); } /** * Get the top parent view of the given view, identified by the parameter {@link SoloMessage#PARAM_REFERENCE}<br> * * <p> * calling:<br> * {@link Solo#getView(int)}<br> * {@link Solo#getTopParent(View)}<br> */ void getTopParent(Properties props) throws ProcessorException{ String debugPrefix = TAG + ".getTopParent() "; String id = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); debug(debugPrefix+" Try to get view of id '"+id+"'"); View view = getViewById(id, null); debug(debugPrefix+" Try to get top parent for view "+view.getClass().getSimpleName()); View topparent = solo.getTopParent(view); //getTopParent() will never return null, ok. setSuccessResultForView(props, topparent); } /** * Simulates pressing the hardware back key. * * <p> * calling:<br> * {@link Solo#goBack()}<br> */ void goBack(Properties props) throws ProcessorException{ solo.goBack(); setGeneralSuccess(props); } /** * Returns to the given Activity. * * <p> * calling:<br> * {@link Solo#goBackToActivity(String)}<br> */ void goBackToActivity(Properties props) throws ProcessorException{ String name = SoloMessage.getString(props, SoloMessage.PARAM_NAME); debug("Params: name="+name); solo.goBackToActivity(name); //set the PARAM_CLASS AND PARAM_NAME of the current activity for caller's reference setCurrentActivityClassName(props); setGeneralSuccess(props, "Back to activity '"+name+"'"); } /** * test if the following {@link android.widget.TextView} is checked.<br> * {@link android.widget.CheckBox}<br> * {@link android.widget.RadioButton}<br> * {@link android.widget.Spinner}<br> * {@link android.widget.ToggleButton}<br> * * The index is given by parameter, according to that index<br> * Solo will get the appropriate View and test if it is checked.<br> * * <p> * calling:<br> * {@link Solo#isCheckBoxChecked(int)}<br> * {@link Solo#isRadioButtonChecked(int)}<br> * {@link Solo#isSpinnerTextSelected(int, String)}<br> * {@link Solo#isToggleButtonChecked(int)}<br> * * @param props The Properties object containing the in and out parameters */ void isViewByIndexChecked(Properties props) throws ProcessorException{ int index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); boolean checked = false; debug("Params: index="+index); if(remoteCommand.equals(SoloMessage.cmd_ischeckboxchecked)){ checked = solo.isCheckBoxChecked(index); }else if(remoteCommand.equals(SoloMessage.cmd_isradiobuttonchecked)){ checked = solo.isRadioButtonChecked(index); }else if(remoteCommand.equals(SoloMessage.cmd_isspinnertextselectedindex)){ String text = SoloMessage.getString(props, SoloMessage.PARAM_TEXT); checked = solo.isSpinnerTextSelected(index, text); }else if(remoteCommand.equals(SoloMessage.cmd_istogglebuttonchecked)){ checked = solo.isToggleButtonChecked(index); }else{ throw new ProcessorException(remoteCommand+" could not be processed in isViewByIndexChecked()."); } //Bring back the checked value by parameter KEY_REMOTERESULTINFO setGeneralSuccessWithSpecialInfo(props, String.valueOf(checked)); } /** * test if the following {@link android.widget.TextView} is checked.<br> * {@link android.widget.CheckBox}<br> * {@link android.widget.RadioButton}<br> * {@link android.widget.Spinner}<br> * {@link android.widget.ToggleButton}<br> * * The text is given by parameter, according to that text<br> * Solo will get the appropriate View and test if it is checked.<br> * * <p> * calling:<br> * {@link Solo#isCheckBoxChecked(String)}<br> * {@link Solo#isRadioButtonChecked(String)}<br> * {@link Solo#isSpinnerTextSelected(String)}<br> * {@link Solo#isTextChecked(String)}<br> * {@link Solo#isToggleButtonChecked(String)}<br> * * @param props The Properties object containing the in and out parameters */ void isViewByTextChecked(Properties props) throws ProcessorException{ String text = SoloMessage.getString(props, SoloMessage.PARAM_TEXT); boolean checked = false; debug("Params: text="+text); if(remoteCommand.equals(SoloMessage.cmd_ischeckboxcheckedtext)){ checked = solo.isCheckBoxChecked(text); }else if(remoteCommand.equals(SoloMessage.cmd_isradiobuttoncheckedtext)){ checked = solo.isRadioButtonChecked(text); }else if(remoteCommand.equals(SoloMessage.cmd_isspinnertextselected)){ checked = solo.isSpinnerTextSelected(text); }else if(remoteCommand.equals(SoloMessage.cmd_istextchecked)){ checked = solo.isTextChecked(text); }else if(remoteCommand.equals(SoloMessage.cmd_istogglebuttoncheckedtext)){ checked = solo.isToggleButtonChecked(text); }else{ throw new ProcessorException(remoteCommand+" could not be processed in isViewByTextChecked()."); } //Bring back the checked value by parameter KEY_REMOTERESULTINFO setGeneralSuccessWithSpecialInfo(props, String.valueOf(checked)); } /** * press an item in {@link android.widget.Menu}<br> * * <p> * calling:<br> * {@link Solo#pressMenuItem(int)}<br> * {@link Solo#pressMenuItem(int, int)}<br> * * @param props The Properties object containing the in and out parameters */ void pressMenuItem(Properties props) throws ProcessorException{ int index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); String resultinfo = ""; debug("Params: index="+index); if(remoteCommand.equals(SoloMessage.cmd_pressmenuitem)){ solo.pressMenuItem(index); resultinfo = "index="+index; }else if(remoteCommand.equals(SoloMessage.cmd_presssubmenuitem)){ int itemsPerRow = SoloMessage.getInteger(props, SoloMessage.PARAM_ITEMSPERROW); debug("Params: itemsPerRow="+itemsPerRow); solo.pressMenuItem(index, itemsPerRow); resultinfo = "index="+index+" : itemsPerRow"+ itemsPerRow; }else{ throw new ProcessorException(remoteCommand+" could not be processed in pressMenuItem()."); } setGeneralSuccess(props,resultinfo); } /** * press an item in {@link android.widget.Spinner}<br> * * <p> * calling:<br> * {@link Solo#pressSpinnerItem(int, int)}<br> * * @param props The Properties object containing the in and out parameters */ void pressSpinnerItem(Properties props) throws ProcessorException{ int index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); int itemIndex = SoloMessage.getInteger(props, SoloMessage.PARAM_ITEMINDEX); debug("Params: index="+index+"; itemIndex="+itemIndex); solo.pressSpinnerItem(index, itemIndex); setGeneralSuccess(props, "index="+index+" : itemIndex="+itemIndex); } /** * scroll vertically.<br> * * <p> * calling:<br> * {@link Solo#scrollDown()}<br> * {@link Solo#scrollDownList(int)}<br> * {@link Solo#scrollDownList(AbsListView)} -- Robotium 3.6<br> * {@link Solo#scrollUp()}<br> * {@link Solo#scrollToTop()} -- Robotium 3.4.1<br> * {@link Solo#scrollToBottom()} -- Robotium 3.4.1<br> * {@link Solo#scrollUpList(int)}<br> * {@link Solo#scrollUpList(AbsListView)} -- Robotium 3.6<br> * {@link Solo#scrollListToTop(int)} -- Robotium 3.4.1<br> * {@link Solo#scrollListToTop(AbsListView)} -- Robotium 3.6<br> * {@link Solo#scrollListToBottom(int)} -- Robotium 3.4.1<br> * {@link Solo#scrollListToBottom(AbsListView)} -- Robotium 3.6<br> * {@link Solo#scrollListToLine(AbsListView, int)} -- Robotium 3.6<br> * {@link Solo#scrollListToLine(int, int)} -- Robotium 3.6<br> * * @param props The Properties object containing the in and out parameters */ void scroll(Properties props) throws ProcessorException{ int index = 0; String uid = ""; int line = 0; boolean canBeScrolled = false; String debugPrefix = TAG +".scroll(): "; if(remoteCommand.equals(SoloMessage.cmd_scrolldown)){ canBeScrolled = solo.scrollDown(); }else if(remoteCommand.equals(SoloMessage.cmd_scrolldownlist)){ index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); debug(debugPrefix +" Params: index="+index); canBeScrolled = solo.scrollDownList(index); }else if(remoteCommand.equals(SoloMessage.cmd_scrolldownlistuid)){ uid = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); AbsListView listView = (AbsListView) getViewById(uid, AbsListView.class); debug(debugPrefix +" Params: reference="+uid+"; Got ListView="+listView); canBeScrolled = solo.scrollDownList(listView); }else if(remoteCommand.equals(SoloMessage.cmd_scrollup)){ canBeScrolled = solo.scrollUp(); }else if(remoteCommand.equals(SoloMessage.cmd_scrolltotop)){ solo.scrollToTop(); }else if(remoteCommand.equals(SoloMessage.cmd_scrolltobottom)){ solo.scrollToBottom(); }else if(remoteCommand.equals(SoloMessage.cmd_scrolluplist)){ index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); debug(debugPrefix +" Params: index="+index); canBeScrolled = solo.scrollUpList(index); }else if(remoteCommand.equals(SoloMessage.cmd_scrolluplistuid)){ uid = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); AbsListView listView = (AbsListView) getViewById(uid, AbsListView.class); debug(debugPrefix +" Params: reference="+uid+"; Got ListView="+listView); canBeScrolled = solo.scrollUpList(listView); }else if(remoteCommand.equals(SoloMessage.cmd_scrolllisttotop)){ index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); debug(debugPrefix +" Params: index="+index); canBeScrolled = solo.scrollListToTop(index); }else if(remoteCommand.equals(SoloMessage.cmd_scrolllisttotopuid)){ uid = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); AbsListView listView = (AbsListView) getViewById(uid, AbsListView.class); debug(debugPrefix +" Params: reference="+uid+"; Got ListView="+listView); canBeScrolled = solo.scrollListToTop(listView); }else if(remoteCommand.equals(SoloMessage.cmd_scrolllisttobottom)){ index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); debug(debugPrefix +" Params: index="+index); canBeScrolled = solo.scrollListToBottom(index); }else if(remoteCommand.equals(SoloMessage.cmd_scrolllisttobottomuid)){ uid = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); AbsListView listView = (AbsListView) getViewById(uid, AbsListView.class); debug(debugPrefix +" Params: reference="+uid+"; Got ListView="+listView); canBeScrolled = solo.scrollListToBottom(listView); }else if(remoteCommand.equals(SoloMessage.cmd_scrolllisttoline)){ index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); line = SoloMessage.getInteger(props, SoloMessage.PARAM_LINE); debug(debugPrefix +" Params: index="+index+" line="+line); solo.scrollListToLine(index, line); }else if(remoteCommand.equals(SoloMessage.cmd_scrolllisttolineuid)){ uid = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); line = SoloMessage.getInteger(props, SoloMessage.PARAM_LINE); debug(debugPrefix +" Params: line="+line); AbsListView listView = (AbsListView) getViewById(uid, AbsListView.class); debug(debugPrefix +" Params: reference="+uid+"; Got ListView="+listView); solo.scrollListToLine(listView, line); }else{ throw new ProcessorException(remoteCommand+" could not be processed in scroll()."); } if(!canBeScrolled){ debug(debugPrefix +" Reach the end, can't be scrolled no more."); } //Bring back the canBeScrolled value by parameter KEY_REMOTERESULTINFO setGeneralSuccessWithSpecialInfo(props, String.valueOf(canBeScrolled)); } /** * scroll horizontally.<br> * * <p> * calling:<br> * {@link Solo#scrollToSide(int)}<br> * {@link Solo#scrollViewToSide(View, int)} -- Robotium 3.6<br> * * @param props The Properties object containing the in and out parameters */ void scrollToSide(Properties props) throws ProcessorException{ int side = SoloMessage.getInteger(props, SoloMessage.PARAM_SIDE); String resultinfo = "Scroll to left."; debug("Params: side="+side); if(remoteCommand.equals(SoloMessage.cmd_scrolltoside)){ solo.scrollToSide(side); }else if(remoteCommand.equals(SoloMessage.cmd_scrollviewtoside)){ String uid = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); View view = getViewById(uid, null); debug(" Params: reference="+uid+"; Got View="+view); solo.scrollViewToSide(view, side); } if(com.jayway.android.robotium.solo.Solo.RIGHT==side){ resultinfo = "Scroll to right."; } setGeneralSuccess(props,resultinfo); } /** * search the following view.<br> * {@link android.widget.Button}<br> * {@link android.widget.EditText}<br> * {@link android.widget.TextView}<br> * {@link android.widget.ToggleButton}<br> * * <p> * calling:<br> * {@link Solo#searchButton(String)}<br> * {@link Solo#searchButton(String, boolean)}<br> * {@link Solo#searchButton(String, int)}<br> * {@link Solo#searchButton(String, int, boolean)}<br> * {@link Solo#searchEditText(String)}<br> * {@link Solo#searchText(String)}<br> * {@link Solo#searchText(String, boolean)}<br> * {@link Solo#searchText(String, int)}<br> * {@link Solo#searchText(String, int, boolean)}<br> * {@link Solo#searchToggleButton(String)}<br> * {@link Solo#searchToggleButton(String, int)}<br> * * @param props The Properties object containing the in and out parameters */ void searchView(Properties props) throws ProcessorException{ String text = SoloMessage.getString(props, SoloMessage.PARAM_TEXT); boolean onlyVisible = false; int minimumNumberOfMatches = 0; boolean scrollToFind = false; boolean found = false; debug("Params: text="+text); if(remoteCommand.equals(SoloMessage.cmd_searchbutton)){ found = solo.searchButton(text); }else if(remoteCommand.equals(SoloMessage.cmd_searchbuttonmatch)){ minimumNumberOfMatches = SoloMessage.getInteger(props, SoloMessage.PARAM_MINIMUMMATCHES); debug("Params: minimumNumberOfMatches="+minimumNumberOfMatches); found = solo.searchButton(text, minimumNumberOfMatches); }else if(remoteCommand.equals(SoloMessage.cmd_searchbuttonmatchvisible)){ onlyVisible = SoloMessage.getBoolean(props, SoloMessage.PARAM_ONLYVISIBLE); minimumNumberOfMatches = SoloMessage.getInteger(props, SoloMessage.PARAM_MINIMUMMATCHES); debug("Params: onlyVisible="+onlyVisible+"; minimumNumberOfMatches="+minimumNumberOfMatches); found = solo.searchButton(text, minimumNumberOfMatches, onlyVisible); }else if(remoteCommand.equals(SoloMessage.cmd_searchbuttonvisible)){ onlyVisible = SoloMessage.getBoolean(props, SoloMessage.PARAM_ONLYVISIBLE); debug("Params: onlyVisible="+onlyVisible); found = solo.searchButton(text, onlyVisible); }else if(remoteCommand.equals(SoloMessage.cmd_searchedittext)){ found = solo.searchEditText(text); }else if(remoteCommand.equals(SoloMessage.cmd_searchtext)){ found = solo.searchText(text); }else if(remoteCommand.equals(SoloMessage.cmd_searchtextmatch)){ minimumNumberOfMatches = SoloMessage.getInteger(props, SoloMessage.PARAM_MINIMUMMATCHES); debug("Params: minimumNumberOfMatches="+minimumNumberOfMatches); found = solo.searchText(text, minimumNumberOfMatches); }else if(remoteCommand.equals(SoloMessage.cmd_searchtextmatchscroll)){ minimumNumberOfMatches = SoloMessage.getInteger(props, SoloMessage.PARAM_MINIMUMMATCHES); scrollToFind = SoloMessage.getBoolean(props, SoloMessage.PARAM_SCROLL); debug("Params: scrollToFind="+scrollToFind+"; minimumNumberOfMatches="+minimumNumberOfMatches); found = solo.searchText(text, minimumNumberOfMatches, scrollToFind); }else if(remoteCommand.equals(SoloMessage.cmd_searchtextmatchscrollvisible)){ onlyVisible = SoloMessage.getBoolean(props, SoloMessage.PARAM_ONLYVISIBLE); minimumNumberOfMatches = SoloMessage.getInteger(props, SoloMessage.PARAM_MINIMUMMATCHES); scrollToFind = SoloMessage.getBoolean(props, SoloMessage.PARAM_SCROLL); debug("Params: scrollToFind="+scrollToFind+"; minimumNumberOfMatches="+minimumNumberOfMatches+"; scrollToFind="+scrollToFind); found = solo.searchText(text, minimumNumberOfMatches, scrollToFind, onlyVisible); }else if(remoteCommand.equals(SoloMessage.cmd_searchtextvisible)){ onlyVisible = SoloMessage.getBoolean(props, SoloMessage.PARAM_ONLYVISIBLE); debug("Params: onlyVisible="+onlyVisible); found = solo.searchText(text, onlyVisible); }else if(remoteCommand.equals(SoloMessage.cmd_searchtogglebutton)){ found = solo.searchToggleButton(text); }else if(remoteCommand.equals(SoloMessage.cmd_searchtogglebuttonmatch)){ minimumNumberOfMatches = SoloMessage.getInteger(props, SoloMessage.PARAM_MINIMUMMATCHES); debug("Params: minimumNumberOfMatches="+minimumNumberOfMatches); found = solo.searchToggleButton(text, minimumNumberOfMatches); }else{ throw new ProcessorException(remoteCommand+" could not be processed in searchView()."); } debug(remoteCommand+(found? " Found view.":" Not found view.")); //Set the boolean found to KEY_REMOTERESULTINFO setGeneralSuccessWithSpecialInfo(props, String.valueOf(found)); } /** * send a key to current view.<br> * * <p> * calling:<br> * {@link Solo#sendKey(int)}<br> * * @param props The Properties object containing the in and out parameters */ void sendKey(Properties props) throws ProcessorException{ int key = SoloMessage.getInteger(props, SoloMessage.PARAM_KEY); String resultInfo = "Key"; debug("Params: key="+key); solo.sendKey(key); switch (key) { case RCSolo.RIGHT: resultInfo += " Right"; break; case RCSolo.LEFT: resultInfo += " Left"; break; case RCSolo.UP: resultInfo += " Up"; break; case RCSolo.DOWN: resultInfo += " Down"; break; case RCSolo.ENTER: resultInfo += " Enter"; break; case RCSolo.MENU: resultInfo += " Menu"; break; case RCSolo.DELETE: resultInfo += " Delete"; break; default: resultInfo += " Unkonwn"; break; } setGeneralSuccess(props, resultInfo); } /** * set orientation of view.<br> * * <p> * calling:<br> * {@link Solo#setActivityOrientation(int)}<br> * * @param props The Properties object containing the in and out parameters */ void setActivityOrientation(Properties props) throws ProcessorException{ int orientation = SoloMessage.getInteger(props, SoloMessage.PARAM_ORIENTATION); String resultInfo = " Orientation "; debug("Params: orientation="+orientation); solo.setActivityOrientation(orientation); switch(orientation){ case RCSolo.LANDSCAPE: resultInfo += "Landscape"; break; case RCSolo.PORTRAIT: resultInfo += "Portrait"; break; default: resultInfo += "Unkonwn"; break; } setGeneralSuccess(props, resultInfo); } /** * set value of {@link android.widget.DatePicker}.<br> * * <p> * calling:<br> * {@link Solo#setDatePicker(DatePicker, int, int, int)}<br> * {@link Solo#setDatePicker(int, int, int, int)}<br> * * @param props The Properties object containing the in and out parameters */ void setDatePicker(Properties props) throws ProcessorException{ int year = SoloMessage.getInteger(props, SoloMessage.PARAM_YEAR); int monthOfYear = SoloMessage.getInteger(props, SoloMessage.PARAM_YEARMONTH); int dayOfMonth = SoloMessage.getInteger(props, SoloMessage.PARAM_MONTHDAY); String resultInfo = "date="+year+"/"+monthOfYear+"/"+dayOfMonth; debug("Params: "+resultInfo); if(remoteCommand.equals(SoloMessage.cmd_setdatepickerindex)){ int index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); debug("Params: index="+index); solo.setDatePicker(index, year, monthOfYear, dayOfMonth); }else if(remoteCommand.equals(SoloMessage.cmd_setdatepickerreference)){ String id = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); DatePicker datePicker = (DatePicker) getViewById(id, DatePicker.class); debug("Params: id="+id); solo.setDatePicker(datePicker, year, monthOfYear, dayOfMonth); }else{ throw new ProcessorException(remoteCommand+" could not be processed in setDatePicker()."); } setGeneralSuccess(props, resultInfo); } /** * set value of {@link android.widget.ProgressBar}.<br> * * <p> * calling:<br> * {@link Solo#setProgressBar(int, int)}<br> * {@link Solo#setProgressBar(ProgressBar, int)}<br> * * @param props The Properties object containing the in and out parameters */ void setProgressBar(Properties props) throws ProcessorException{ int progress = SoloMessage.getInteger(props, SoloMessage.PARAM_PROGRESS); String resultInfo = " value="+progress; debug("Params: progress="+progress); if(remoteCommand.equals(SoloMessage.cmd_setprogressbarindex)){ int index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); debug("Params: index="+index); solo.setProgressBar(index, progress); }else if(remoteCommand.equals(SoloMessage.cmd_setprogressbarreference)){ String id = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); ProgressBar progressBar = (ProgressBar) getViewById(id, ProgressBar.class); debug("Params: id="+id); solo.setProgressBar(progressBar, progress); }else{ throw new ProcessorException(remoteCommand+" could not be processed in setProgressBar()."); } setGeneralSuccess(props, resultInfo); } /** * set value of {@link android.widget.SlidingDrawer}.<br> * * <p> * calling:<br> * {@link Solo#setSlidingDrawer(int, int)}<br> * {@link Solo#setSlidingDrawer(android.widget.SlidingDrawer, int)}<br> * * @param props The Properties object containing the in and out parameters */ void setSlidingDrawer(Properties props) throws ProcessorException{ int status = SoloMessage.getInteger(props, SoloMessage.PARAM_STATUS); String resultInfo = " value="+status; debug("Params: status="+status); if(remoteCommand.equals(SoloMessage.cmd_setslidingdrawerindex)){ int index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); debug("Params: index="+index); solo.setSlidingDrawer(index, status); }else if(remoteCommand.equals(SoloMessage.cmd_setslidingdrawerreference)){ String id = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); SlidingDrawer slidingDrawer = (SlidingDrawer) getViewById(id, SlidingDrawer.class); debug("Params: id="+id); solo.setSlidingDrawer(slidingDrawer, status); }else{ throw new ProcessorException(remoteCommand+" could not be processed in setSlidingDrawer()."); } setGeneralSuccess(props, resultInfo); } /** * set value of {@link android.widget.TimePicker}.<br> * * <p> * calling:<br> * {@link Solo#setTimePicker(int, int, int)}<br> * {@link Solo#setTimePicker(android.widget.TimePicker, int, int)}<br> * * @param props The Properties object containing the in and out parameters */ void setTimePicker(Properties props) throws ProcessorException{ int hour = SoloMessage.getInteger(props, SoloMessage.PARAM_HOUR); int minute = SoloMessage.getInteger(props, SoloMessage.PARAM_MINUTE); String resultInfo = "time="+hour+":"+minute; debug("Params: "+resultInfo); if(remoteCommand.equals(SoloMessage.cmd_settimepickerindex)){ int index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); debug("Params: index="+index); solo.setTimePicker(index, hour, minute); }else if(remoteCommand.equals(SoloMessage.cmd_settimepickerreference)){ String id = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); TimePicker timePicker = (TimePicker) getViewById(id, TimePicker.class); debug("Params: id="+id); solo.setTimePicker(timePicker, hour, minute); }else{ throw new ProcessorException(remoteCommand+" could not be processed in setTimePicker()."); } setGeneralSuccess(props, resultInfo); } /** * Robotium will sleep for a specified time (milliseconds).<br> * * <p> * calling:<br> * {@link Solo#sleep(int)}<br> * * @param props The Properties object containing the in and out parameters */ void sleep(Properties props) throws ProcessorException{ int time = SoloMessage.getInteger(props, SoloMessage.PARAM_TIME); debug("Params: time="+time); solo.sleep(time); setGeneralSuccess(props, time+" millis "); } /** * Wait for {@link android.app.Activity}.<br> * * <p> * calling:<br> * {@link Solo#waitForActivity(String)}<br> * {@link Solo#waitForActivity(String, int)}<br> * {@link Solo#waitForActivity(Class)} -- Robotium 4.1<br> * {@link Solo#waitForActivity(Class, int)} -- Robotium 4.1<br> * * @param props The Properties object containing the in and out parameters */ @SuppressWarnings("unchecked") void waitForActivity(Properties props) throws ProcessorException{ String resultInfo = ""; boolean found = false; if(remoteCommand.equals(SoloMessage.cmd_waitforactivity) || remoteCommand.equals(SoloMessage.cmd_waitforactivitytimeout)){ String name = SoloMessage.getString(props, SoloMessage.PARAM_NAME); debug("Params: name="+name); if(remoteCommand.equals(SoloMessage.cmd_waitforactivity)){ found = solo.waitForActivity(name); resultInfo = name; }else if(remoteCommand.equals(SoloMessage.cmd_waitforactivitytimeout)){ int timeout = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); debug("Params: timeout="+timeout); found = solo.waitForActivity(name, timeout); resultInfo = name+" : in "+timeout+" millis "; } }else if(remoteCommand.equals(SoloMessage.cmd_waitforactivitybyclass) || remoteCommand.equals(SoloMessage.cmd_waitforactivitybyclasstimeout)){ String classname = SoloMessage.getString(props, SoloMessage.PARAM_CLASS); debug("Params: classname="+classname); if(remoteCommand.equals(SoloMessage.cmd_waitforactivitybyclass)){ try { resultInfo = classname; found = solo.waitForActivity((Class<? extends Activity>) Class.forName(classname)); }catch(ClassNotFoundException cnfe) { debug("Can't find class '"+classname+"'"); }catch(ClassCastException cce){ debug("Class '"+classname+"' is not a subclass of Activity."); } }else if(remoteCommand.equals(SoloMessage.cmd_waitforactivitybyclasstimeout)){ int timeout = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); debug("Params: timeout="+timeout); try { resultInfo = classname+" : in "+timeout+" millis "; found = solo.waitForActivity((Class<? extends Activity>) Class.forName(classname), timeout); }catch(ClassNotFoundException cnfe) { debug("Can't find class '"+classname+"'"); }catch(ClassCastException cce){ debug("Class '"+classname+"' is not a subclass of Activity."); } } }else{ throw new ProcessorException(remoteCommand+" could not be processed in waitForActivity()."); } //solo.waitForActivity() will test if the current activity is the same as the //activity described by the parameter. //so whether or not we get the waited Activity, we will set the //PARAM_CLASS AND PARAM_NAME of the current activity for caller's reference setCurrentActivityClassName(props); if(!found){ resultInfo = "Not Found "+resultInfo; }else{ resultInfo = "Found "+resultInfo; } debug(resultInfo); setGeneralSuccessWithSpecialInfo(props, String.valueOf(found)); } /** * Wait for a V4 Fragment by Tag.<br> * Requires Robotium 3.4.1 * <p> * calling:<br> * {@link Solo#waitForFragmentByTag(String, int)}<br> * * @param props The Properties object containing the in and out parameters */ void waitForFragmentByTag(Properties props) throws ProcessorException{ String name = SoloMessage.getString(props, SoloMessage.PARAM_NAME); int timeout = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); String resultInfo = ""; debug("Params: name="+name); debug("Params: time="+timeout); boolean found = solo.waitForFragmentByTag(name, timeout); resultInfo = name+" : in "+timeout+" millis "; //solo.waitForActivity() will test if the current activity is the same as the //activity described by the parameter. //so whether or not we get the waited Activity, we will set the //PARAM_CLASS AND PARAM_NAME of the current activity for caller's reference try{ setCurrentActivityClassName(props);}catch(Exception x){ debug("waitForFragmentByTag ignoring "+ x.getClass().getSimpleName()+": "+x.getMessage()); } if(!found){ resultInfo = "Did not find Fragment "+resultInfo; }else{ resultInfo = "Found Fragment "+resultInfo; } debug(resultInfo); setGeneralSuccessWithSpecialInfo(props, String.valueOf(found)); } /** * Wait for a V4 Fragment by Id.<br> * Requires Robotium 3.4.1 * <p> * calling:<br> * {@link Solo#waitForFragmentById(int, int)}<br> * * @param props The Properties object containing the in and out parameters */ void waitForFragmentById(Properties props) throws ProcessorException{ int id = SoloMessage.getInteger(props, SoloMessage.PARAM_ID); int timeout = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); String resultInfo = ""; debug("Params: id="+id); debug("Params: time="+timeout); boolean found = solo.waitForFragmentById(id, timeout); resultInfo = String.valueOf(id)+" : in "+timeout+" millis "; //solo.waitForActivity() will test if the current activity is the same as the //activity described by the parameter. //so whether or not we get the waited Activity, we will set the //PARAM_CLASS AND PARAM_NAME of the current activity for caller's reference try{ setCurrentActivityClassName(props);}catch(Exception x){ debug("waitForFragmentById ignoring "+ x.getClass().getSimpleName()+": "+x.getMessage()); } if(!found){ resultInfo = "Did not find Fragment "+resultInfo; }else{ resultInfo = "Found Fragment "+resultInfo; } debug(resultInfo); setGeneralSuccessWithSpecialInfo(props, String.valueOf(found)); } /** * Wait for {@link android.app.Dialog} to close/open.<br> * * <p> * calling:<br> * {@link Solo#waitForDialogToClose(long)}<br> * {@link Solo#waitForDialogToOpen(long)} -- Robotium 4.1<br> * * @param props The Properties object containing the in and out parameters */ void waitForDialog(Properties props) throws ProcessorException{ int timeout = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); boolean success = false; debug("Params: timeout="+timeout); if(remoteCommand.equals(SoloMessage.cmd_waitfordialogtoclose)){ success = solo.waitForDialogToClose(timeout); }else if(remoteCommand.equals(SoloMessage.cmd_waitfordialogtoopen)){ success = solo.waitForDialogToOpen(timeout); } if(success){ debug(remoteCommand+" succeed in "+timeout+" millis "); }else{ debug(remoteCommand+" doesn't succeed in "+timeout+" millis "); } setGeneralSuccessWithSpecialInfo(props, String.valueOf(success)); } /** * Wait for {@link android.widget.TextView}.<br> * * <p> * calling:<br> * {@link Solo#waitForText(String)}<br> * {@link Solo#waitForText(String, int, long))}<br> * {@link Solo#waitForText(String, int, long, boolean))}<br> * {@link Solo#waitForText(String, int, long, boolean, boolean))}<br> * * @param props The Properties object containing the in and out parameters */ void waitForText(Properties props) throws ProcessorException{ String text = SoloMessage.getString(props, SoloMessage.PARAM_TEXT); int minimumNumberOfMatches = 0; int timeout = 0; boolean scroll = false; boolean onlyVisible = false; String resultInfo = text; boolean found = false; debug("Params: text="+text); if(remoteCommand.equals(SoloMessage.cmd_waitfortext)){ found = solo.waitForText(text); }else if(remoteCommand.equals(SoloMessage.cmd_waitfortextmatchtimeout)){ minimumNumberOfMatches = SoloMessage.getInteger(props, SoloMessage.PARAM_MINIMUMMATCHES); timeout = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); debug("Params: timeout="+timeout+"; minimumNumberOfMatches="+minimumNumberOfMatches); found = solo.waitForText(text, minimumNumberOfMatches, timeout); resultInfo += " : minMatch="+minimumNumberOfMatches+" : in "+timeout+" millis "; }else if(remoteCommand.equals(SoloMessage.cmd_waitfortextmatchtimeoutscroll)){ minimumNumberOfMatches = SoloMessage.getInteger(props, SoloMessage.PARAM_MINIMUMMATCHES); timeout = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); scroll = SoloMessage.getBoolean(props, SoloMessage.PARAM_SCROLL); debug("Params: timeout="+timeout+"; minimumNumberOfMatches="+minimumNumberOfMatches+"; scroll="+scroll); found = solo.waitForText(text, minimumNumberOfMatches, timeout, scroll); resultInfo += " : minMatch="+minimumNumberOfMatches+" : in "+timeout+" millis "; }else if(remoteCommand.equals(SoloMessage.cmd_waitfortextmatchtimeoutscrollvisible)){ minimumNumberOfMatches = SoloMessage.getInteger(props, SoloMessage.PARAM_MINIMUMMATCHES); timeout = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); scroll = SoloMessage.getBoolean(props, SoloMessage.PARAM_SCROLL); onlyVisible = SoloMessage.getBoolean(props, SoloMessage.PARAM_ONLYVISIBLE); debug("Params: timeout="+timeout+"; minimumNumberOfMatches="+minimumNumberOfMatches+"; scroll="+scroll+"; onlyVisible="+onlyVisible); found = solo.waitForText(text, minimumNumberOfMatches, timeout, scroll, onlyVisible); resultInfo += " : minMatch="+minimumNumberOfMatches+" : in "+timeout+" millis "; }else{ throw new ProcessorException(remoteCommand+" could not be processed in waitForText()."); } if(!found){ resultInfo = "Not Found "+resultInfo; }else{ resultInfo = "Found "+resultInfo; } debug(resultInfo); setGeneralSuccessWithSpecialInfo(props, String.valueOf(found)); } /** * Wait for a specific log message.<br> * Requires Robotium 3.4.1 * <p> * calling:<br> * {@link Solo#waitForLogMessage(String)}<br> * * @param props The Properties object containing the in and out parameters */ void waitForLogMessage(Properties props) throws ProcessorException{ String text = SoloMessage.getString(props, SoloMessage.PARAM_TEXT); int timeout = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT);; String resultInfo = "'"+ text+"'"; boolean found = false; debug("Params: text="+text); debug("Params: time="+timeout); try{ found = solo.waitForLogMessage(text, timeout);} catch(Exception x){ debug("waitForLogMessage ignoring "+ x.getClass().getSimpleName()+": "+ x.getMessage()); } if(!found){ resultInfo = "Did not find "+ resultInfo; }else{ resultInfo = "Found "+resultInfo; } debug(resultInfo); setGeneralSuccessWithSpecialInfo(props, String.valueOf(found)); } /** * Wait for {@link android.view.View}.<br> * * <p> * calling:<br> * {@link Solo#waitForView(Class)}<br> * {@link Solo#waitForView(Class, int, int))}<br> * {@link Solo#waitForView(Class, int, int, boolean))}<br> * * @param props The Properties object containing the in and out parameters */ void waitForView(Properties props) throws ProcessorException{ String debugPrefix = TAG + ".waitForView() "; String classname = SoloMessage.getString(props, SoloMessage.PARAM_CLASS); Class<View> viewClass = null; int minimumNumberOfMatches = 0; int timeout = 0; boolean scroll = false; boolean found = false; String resultInfo = classname; debug("Params: classname="+classname); //TODO if Class.ForName can't work viewClass = ClassViewForName(classname); if(remoteCommand.equals(SoloMessage.cmd_waitforviewclass)){ if(viewClass!=null){ found = solo.waitForView(viewClass); }else{ //TODO debug(debugPrefix +"Need new implementation"); } }else if(remoteCommand.equals(SoloMessage.cmd_waitforviewclassmatchtimeout)){ minimumNumberOfMatches = SoloMessage.getInteger(props, SoloMessage.PARAM_MINIMUMMATCHES); timeout = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); debug("Params: minimumNumberOfMatches="+minimumNumberOfMatches+"; timeout="+timeout); if(viewClass!=null){ found = solo.waitForView(viewClass, minimumNumberOfMatches, timeout); }else{ //TODO debug(debugPrefix +"Need new implementation"); } resultInfo += " : minMatch="+minimumNumberOfMatches+" : in "+timeout+" millis "; }else if(remoteCommand.equals(SoloMessage.cmd_waitforviewclassmatchtimeoutscroll)){ minimumNumberOfMatches = SoloMessage.getInteger(props, SoloMessage.PARAM_MINIMUMMATCHES); timeout = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); scroll = SoloMessage.getBoolean(props, SoloMessage.PARAM_SCROLL); debug("Params: minimumNumberOfMatches="+minimumNumberOfMatches+"; timeout="+timeout+"; scroll="+scroll); if(viewClass!=null){ found = solo.waitForView(viewClass, minimumNumberOfMatches, timeout, scroll); }else{ //TODO debug(debugPrefix +"Need new implementation"); } resultInfo += " : minMatch="+minimumNumberOfMatches+" : in "+timeout+" millis "; }else{ throw new ProcessorException(remoteCommand+" could not be processed in waitForView()."); } if(!found){ resultInfo = "Not Found "+resultInfo; }else{ resultInfo = "Found "+resultInfo; } debug(resultInfo); setGeneralSuccessWithSpecialInfo(props, String.valueOf(found)); } /** * Wait for {@link android.view.View}.<br> * * <p> * calling:<br> * {@link Solo#waitForView(View)}<br> * {@link Solo#waitForView(View, int, boolean))}<br> * * @param props The Properties object containing the in and out parameters */ void waitForViewUID(Properties props) throws ProcessorException{ String uid = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); debug("Params: uid="+uid); String resultInfo = ""; boolean found = false; View view = null; try{ view = getViewById(uid, null); }catch(ProcessorException e){ debug(e.getMessage()); } if(view!=null){ debug("got view from cache"); if(remoteCommand.equals(SoloMessage.cmd_waitforviewreference)){ found = solo.waitForView(view); debug("found view = "+found); resultInfo = view.getClass().getSimpleName(); }else if(remoteCommand.equals(SoloMessage.cmd_waitforviewreferencetimeoutscroll)){ int timeout = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); boolean scroll = SoloMessage.getBoolean(props, SoloMessage.PARAM_SCROLL); debug("Params: timeout="+timeout+"; scroll="+scroll); found = solo.waitForView(view, timeout, scroll); resultInfo = view.getClass().getSimpleName()+" in "+timeout+" milliseconds "; }else{ throw new ProcessorException(remoteCommand+" could not be processed in waitForViewUID()."); } //Whether we get the waited View or not, //we will set the PARAM_CLASS of the view for remote caller's reference setViewClassResult(props, view); }else{ debug("couldn't get view from cache"); } if(!found){ resultInfo = "Not Found "+resultInfo; }else{ resultInfo = "Found "+resultInfo; } debug(resultInfo); setGeneralSuccessWithSpecialInfo(props, String.valueOf(found)); } /** * Wait for {@link android.view.View} according to ID (R.id) <b>Requires Robotium4.1+</b><br> * * <p> * calling:<br> * {@link Solo#waitForView(int)}<br> * {@link Solo#waitForView(int, int, int)}<br> * {@link Solo#waitForView(int, int, int, boolean)}<br> * * @param props The Properties object containing the in and out parameters */ void waitForViewByID(Properties props) throws ProcessorException{ int id = SoloMessage.getInteger(props, SoloMessage.PARAM_ID); debug("Params: id="+id); String resultInfo = "'"+id+"'"; boolean found = false; if(remoteCommand.equals(SoloMessage.cmd_waitforviewid)){ found = solo.waitForView(id); }else if(remoteCommand.equals(SoloMessage.cmd_waitforviewidtimeout)){ int timeout = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); int minimumNumberOfMatches = SoloMessage.getInteger(props, SoloMessage.PARAM_MINIMUMMATCHES); found = solo.waitForView(id, minimumNumberOfMatches, timeout); resultInfo += " in "+timeout+" milliseconds, minimumNumberOfMatches="+minimumNumberOfMatches; }else if(remoteCommand.equals(SoloMessage.cmd_waitforviewidtimeoutscroll)){ int timeout = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); int minimumNumberOfMatches = SoloMessage.getInteger(props, SoloMessage.PARAM_MINIMUMMATCHES); boolean scroll = SoloMessage.getBoolean(props, SoloMessage.PARAM_SCROLL); found = solo.waitForView(id, minimumNumberOfMatches, timeout, scroll); resultInfo += " in "+timeout+" milliseconds, minimumNumberOfMatches="+minimumNumberOfMatches; }else{ throw new ProcessorException(remoteCommand+" could not be processed in waitForViewByID()."); } if(!found){ resultInfo = "Not Found View for id "+resultInfo; }else{ resultInfo = "Found View for id "+resultInfo; } debug(resultInfo); setGeneralSuccessWithSpecialInfo(props, String.valueOf(found)); } /** * Wait for {@link com.jayway.android.robotium.solo.WebElement}. -- Robotium 4.1<br> * * <p> * calling:<br> * {@link Solo#waitForWebElement(By)} -- Robotium 4.1<br> * {@link Solo#waitForWebElement(By, int, boolean)} -- Robotium 4.1<br> * {@link Solo#waitForWebElement(By, int, int, boolean)} -- Robotium 4.1<br> * * @param props The Properties object containing the in and out parameters */ void waitForWebElement(Properties props) throws ProcessorException{ try{ boolean found = false; String objectStr = SoloMessage.getString(props, SoloMessage.PARAM_OBJECT); By by = SoloMessage.getSoloBy((com.jayway.android.robotium.remotecontrol.By) SoloMessage.decodeBase64Object(objectStr)); if(remoteCommand.equals(SoloMessage.cmd_waitforwebelement)){ found = solo.waitForWebElement(by); }else if(remoteCommand.equals(SoloMessage.cmd_waitforwebelementtimeout)){ int timeout = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); boolean scroll = SoloMessage.getBoolean(props, SoloMessage.PARAM_SCROLL); found = solo.waitForWebElement(by, timeout, scroll); }else if(remoteCommand.equals(SoloMessage.cmd_waitforwebelementminmatchtimeout)){ int timeout = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); boolean scroll = SoloMessage.getBoolean(props, SoloMessage.PARAM_SCROLL); int minimumNumberOfMatches = SoloMessage.getInteger(props, SoloMessage.PARAM_MATCH); found = solo.waitForWebElement(by, minimumNumberOfMatches, timeout, scroll); } debug("WebElement "+(found?"was found.":"was not found.")); setGeneralSuccessWithSpecialInfo(props, String.valueOf(found)); }catch(Exception e){ throw new ProcessorException(Message.getStackTrace(e)); } } /** * Get the screen's size.<br> * * @param props The Properties object containing the in and out parameters */ void getScreenSize(Properties props) throws ProcessorException{ try{ //If we get the WindowManager from getSystemService(Context.WINDOW_SERVICE), it will //give the real size of the device/emulator //If we get the WindowManager from getActivity().getWindowManager(), it will give the //adjust size of the device/emulator // Instrumentation inst = activityrunner.getInstrumentation(); // WindowManager wm = (WindowManager) inst.getContext().getSystemService(Context.WINDOW_SERVICE); WindowManager wm = solo.getCurrentActivity().getWindowManager(); if(wm==null){ debug("Can't get WindowManager!"); } Display display = wm.getDefaultDisplay(); if(display==null){ debug("Can't get Display!"); } debug("Screen size ("+display.getWidth()+","+display.getHeight()+")"); String resultInfo = ";"+display.getWidth()+";"+display.getHeight(); setGeneralSuccessWithSpecialInfo(props, resultInfo); }catch(Exception e){ throw new ProcessorException(e.getMessage()); } } /** * Get the View's location, (x,y,width,height).<br> * * @param props The Properties object containing the in and out parameters<br> * <in> UID, String, This is the view's UID according to which we will get a View. */ void getViewLocation(Properties props) throws ProcessorException{ try{ String uid = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); View view = null; try{ view = (View) getCachedObject(uid, true); }catch(Exception x){} if(view==null){ throw new ProcessorException(" View for id '"+uid+"' is null."); } int[] xy = new int[2]; view.getLocationOnScreen(xy); debug("view is at ("+xy[0]+","+xy[1]+")"); debug("view size ("+view.getWidth()+","+view.getHeight()+")"); String resultInfo = ";"+xy[0]+";"+xy[1]+";"+view.getWidth()+";"+view.getHeight(); setGeneralSuccessWithSpecialInfo(props, resultInfo); }catch(Exception e){ throw new ProcessorException(e.getMessage()); } } /** * According to the view's id, try to get a View of type 'TextView'.<br> * * @param props The Properties object containing the in and out parameters<br> * <in> UID, String, This is the view's UID according to which we will get a View. * @return */ void getTextViewValue(Properties props) throws ProcessorException{ try{ String uid = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); TextView view = (TextView) getViewById(uid, TextView.class); if(view==null){ throw new ProcessorException(" TextView for id '"+uid+"' is null."); }else{ String text = null; text = view.getText().toString(); debug("TextView's text is '"+text+"'"); setGeneralSuccessWithSpecialInfo(props, text); } }catch(Exception e){ throw new ProcessorException(e.getMessage()); } } /** * Waits for a condition to be satisfied.<BR> * * <p> * calling:<br> * {@link Solo#waitForCondition(com.jayway.android.robotium.solo.Condition, int)} -- Robotium 4.1<br> * * @param props The Properties object containing the in and out parameters<br> * <in> condition, Condition, the condition to wait for.<br> * <in> timeout, int, the amount of time in milliseconds to wait.<br> * <out> boolean, {@code true} if condition is satisfied; * {@code false} if it is not satisfied or the timeout is reached.<br> * */ void waitForCondition(Properties props) throws ProcessorException{ try{ int timeout = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); String objectStr = SoloMessage.getString(props, SoloMessage.PARAM_OBJECT); Condition condition = (Condition) SoloMessage.decodeBase64Object(objectStr); //Replace the viewUID by the real View object (stored in the cache) List<Object> viewIDs= condition.getObjects(); for(int i=0;i<viewIDs.size();i++){ debug("convert '"+viewIDs.get(i)+"' to View object"); condition.setObject(getCachedObject((String)viewIDs.get(i), true), i); } //Set the first object as the TestRunner condition.addObject(robotiumTestrunner, true); boolean isSatisfied = solo.waitForCondition(condition, timeout); debug("isSatisfied="+isSatisfied); setGeneralSuccessWithSpecialInfo(props, String.valueOf(isSatisfied)); }catch(Exception e){ throw new ProcessorException(Message.getStackTrace(e)); } } /** * Clears text in a WebElement matching the specified By object. * <p> * calling:<br> * {@link Solo#clearTextInWebElement(com.jayway.android.robotium.solo.By)} -- Robotium 4.1<br> * * @param props The Properties object containing the in and out parameters<br> * <in> by the By object. Examples are: {@code By.id("id")} and {@code By.name("name")}.<br> */ void clearTextInWebElement(Properties props) throws ProcessorException{ try{ String objectStr = SoloMessage.getString(props, SoloMessage.PARAM_OBJECT); By by = SoloMessage.getSoloBy((com.jayway.android.robotium.remotecontrol.By) SoloMessage.decodeBase64Object(objectStr)); solo.clearTextInWebElement(by); setGeneralSuccessWithSpecialInfo(props, getWebElementInfo(by)); }catch(Exception e){ throw new ProcessorException(Message.getStackTrace(e)); } } /** * Clicks a WebElement matching the specified By object. * <p> * calling:<br> * {@link Solo#clickOnWebElement(com.jayway.android.robotium.solo.By)} -- Robotium 4.1<br> * {@link Solo#clickOnWebElement(com.jayway.android.robotium.solo.By, int)} -- Robotium 4.1<br> * {@link Solo#clickOnWebElement(com.jayway.android.robotium.solo.By, int, boolean)} -- Robotium 4.1<br> * * @param props The Properties object containing the in and out parameters<br> * <in> by the By object. Examples are: {@code By.id("id")} and {@code By.name("name")}.<br> */ void clickOnWebElement(Properties props) throws ProcessorException{ try{ String objectStr = SoloMessage.getString(props, SoloMessage.PARAM_OBJECT); By by = SoloMessage.getSoloBy((com.jayway.android.robotium.remotecontrol.By) SoloMessage.decodeBase64Object(objectStr)); if(remoteCommand.equals(SoloMessage.cmd_clickonwebelement)){ solo.clickOnWebElement(by); }else if(remoteCommand.equals(SoloMessage.cmd_clickonwebelementindex)){ int match = SoloMessage.getInteger(props, SoloMessage.PARAM_MATCH); solo.clickOnWebElement(by, match); }else if(remoteCommand.equals(SoloMessage.cmd_clickonwebelementindexscroll)){ int match = SoloMessage.getInteger(props, SoloMessage.PARAM_MATCH); boolean scroll = SoloMessage.getBoolean(props, SoloMessage.PARAM_SCROLL); solo.clickOnWebElement(by, match, scroll); } setGeneralSuccessWithSpecialInfo(props, getWebElementInfo(by)); }catch(Exception e){ throw new ProcessorException(Message.getStackTrace(e)); } } /** * Clicks a WebElement stored in cache by reference UID. * <p> * calling:<br> * {@link Solo#clickOnWebElement(com.jayway.android.robotium.solo.WebElement)} -- Robotium 4.1<br> * * @param props The Properties object containing the in and out parameters<br> * <in> UID, String, the reference for a WebElement object stored in cache.<br> */ void clickOnWebElementByUID(Properties props) throws ProcessorException{ try{ String uid = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); WebElement webElement = (WebElement) getCachedObject(uid, true); solo.clickOnWebElement(webElement); setGeneralSuccessWithSpecialInfo(props, getWebElementInfo(webElement)); }catch(ClassCastException x){ throw new ProcessorException(remoteCommand+" retrieved object was not an instanceof WebElement!"); }catch(Throwable e){ throw new ProcessorException(Message.getStackTrace(e)); } } /** * Enters text in a WebElement matching the specified By object. * <p> * calling:<br> * {@link Solo#enterTextInWebElement(com.jayway.android.robotium.solo.By, String)} -- Robotium 4.1<br> * {@link Solo#typeTextInWebElement(com.jayway.android.robotium.solo.By, String)} -- Robotium 4.1<br> * {@link Solo#typeTextInWebElement(By, String, int)} -- Robotium 4.1<br> * * @param props The Properties object containing the in and out parameters<br> * <in> by the By object. Examples are: {@code By.id("id")} and {@code By.name("name")}.<br> * <in> text, String, the text to type into a WebElement.<br> * <in> index, int, decide into which WebElement to type.<br> */ void enterTextInWebElement(Properties props) throws ProcessorException{ try{ String objectStr = SoloMessage.getString(props, SoloMessage.PARAM_OBJECT); By by = SoloMessage.getSoloBy((com.jayway.android.robotium.remotecontrol.By) SoloMessage.decodeBase64Object(objectStr)); if(remoteCommand.equals(SoloMessage.cmd_entertextinwebelement)){ String text = SoloMessage.getString(props, SoloMessage.PARAM_TEXT); solo.enterTextInWebElement(by, text); }else if(remoteCommand.equals(SoloMessage.cmd_typetextinwebelement)){ String text = SoloMessage.getString(props, SoloMessage.PARAM_TEXT); solo.typeTextInWebElement(by, text); }else if(remoteCommand.equals(SoloMessage.cmd_typetextinwebelementindex)){ String text = SoloMessage.getString(props, SoloMessage.PARAM_TEXT); int index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); solo.typeTextInWebElement(by, text, index); } setGeneralSuccessWithSpecialInfo(props, getWebElementInfo(by)); }catch(Exception e){ throw new ProcessorException(Message.getStackTrace(e)); } } /** * Type text in a WebElement stored in cache by reference UID. * <p> * calling:<br> * {@link Solo#typeTextInWebElement(WebElement, String)} -- Robotium 4.1<br> * * @param props The Properties object containing the in and out parameters<br> * <in> UID, String, the reference for a WebElement object stored in cache.<br> * <in> text, String, the text to type into a WebElement.<br> */ void typeTextInWebElementByUID(Properties props) throws ProcessorException{ try{ String uid = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); WebElement webElement = (WebElement) getCachedObject(uid, true); String text = SoloMessage.getString(props, SoloMessage.PARAM_TEXT); solo.typeTextInWebElement(webElement, text); setGeneralSuccessWithSpecialInfo(props, getWebElementInfo(webElement)); }catch(ClassCastException x){ throw new ProcessorException(remoteCommand+" retrieved object was not an instanceof WebElement!"); }catch(Throwable e){ throw new ProcessorException(Message.getStackTrace(e)); } } private String getWebElementInfo(By by){ String info = ""; try{ WebElement webElement = solo.getWebElement(by, 0); info = getWebElementInfo(webElement); }catch(Exception e){ debug(Message.getStackTrace(e)); } return info; } private String getWebElementInfo(WebElement webElement){ String info = ""; try{ info = "\nID="+webElement.getId()+ "\nName="+webElement.getName()+ "\nTagName="+webElement.getTagName()+ "\nText="+webElement.getText(); }catch(Exception e){ debug(Message.getStackTrace(e)); } return info; } /** * According to the view's id, try to get a View as an 'expected class'.<br> * If not, a {@link ProcessorException} will be thrown out.<br> * If a view is returned, user can CAST it DIRECTLY to 'expected class'<br> * * @param uid int, This is the view's uid according to which we will get a View. * This uid is the R.id generated when building the AUT.<br> * @param exceptedClass Class, The expected class for the View that we will get. * If this parameter is null, we won't check the View's class name. * @return */ private <T extends View> View getViewById(int uid, Class<T> exceptedClass) throws ProcessorException{ View view = solo.getView(uid); String exceptedClassName = null; if(view==null){ throw new ProcessorException(" View for id '"+uid+"' is null."); } if(!checkClass(view, exceptedClass)){ throw new ProcessorException(" View for id '"+uid+"' is not a '"+exceptedClassName+"'."); } return view; } /** * According to the view's id, try to get a View as an 'expected class'.<br> * If not, a {@link ProcessorException} will be thrown out.<br> * If a view is returned, user can CAST it DIRECTLY to 'expected class'<br> * * @param uid String, This is the view's uid according to which we will get a View. * This uid is the key in local cache {@link #viewCache} * @param exceptedClass Class, The expected class for the View that we will get. * If this parameter is null, we won't check the View's class name. * @return */ private <T extends View> View getViewById(String uid, Class<T> exceptedClass) throws ProcessorException{ View view = null; String exceptedClassName = null; try{ view = (View) getCachedObject(uid, true); }catch(Exception x){} if(view==null){ throw new ProcessorException(" View for id '"+uid+"' is null."); } if(!checkClass(view, exceptedClass)){ exceptedClassName = exceptedClass==null? null : exceptedClass.getName(); throw new ProcessorException(" View for id '"+uid+"' is '"+view.getClass().getName()+"', not a '"+exceptedClassName+"'."); } return view; } /** * Check if the view is the exceptedClass or the child of exceptedClass.<br> * * @param view View, the view to be checked. * @param exceptedClass Class, The expected class for the View. * If this parameter is null, we consider the check pass. */ private <T extends View> boolean checkClass(View view, Class<T> exceptedClass) throws ProcessorException{ if(view==null){ throw new ProcessorException(" View is null!"); } if(exceptedClass!=null){ String exceptedClassName = exceptedClass.getName(); boolean matched = false; Class<?> clazz = view.getClass(); String clazzname = null; while(true && clazz!=null){ clazzname = clazz.getName(); if(clazzname.equals(Object.class.getName())){ break; }else if(clazzname.equals(exceptedClassName)){ matched = true; break; }else{ clazz = clazz.getSuperclass(); } } return matched; } return true; } /** * Create a Class object from the 'class name'<br> * <b>Note:</b> The class MUST be subclass of {@link android.view.View}<br> * * @param classname String, the class name to create Class object * @return Class object (for subclass of {@link android.view.View}). * {@code null}, if the class can't be found or it is not subclass of {@link View} * */ @SuppressWarnings("unchecked") private <T extends View> Class<T> ClassViewForName(String classname) { String debugPrefix = TAG + ".ClassViewForName() "; Class<T> viewClass = null; try { viewClass = (Class<T>) Class.forName(classname); } catch (ClassNotFoundException e) { debug(debugPrefix +"Class '" + classname + "' not found: " + e.getMessage()); } catch (ClassCastException e) { debug(debugPrefix +"Class '" + classname + "' is not subclass of "+ View.class.getName()); } return viewClass; } /** * Get image of a View.<br> * According to the view's id, try to get a View from cache.<br> * If not found, a {@link ProcessorException} will be thrown out.<br> * If a view is found, we will get the bitmap of that view and compress<br> * the bytes to String and return it through KEY_REMOTERESULTINFO<br> * * @param uid String, This is the view's uid according to which we will get a View. * This uid is the key in local cache {@link #viewCache} * @return */ void getGuiImage(Properties props) throws ProcessorException{ String dbPrefix = TAG +".getGuiImage(): "; boolean success = false; String message = null; try{ String uid = SoloMessage.getString(props, SoloMessage.PARAM_ID); View view = null; try{ view = (View) getCachedObject(uid, true); }catch(Exception x){} if(view==null){ throw new ProcessorException(" View for id '"+uid+"' is null."); }else{ debug(dbPrefix +" Try to get gui image for view "+view.getClass().getSimpleName()); Bitmap bitmap = getBitmapOfView(view); ByteArrayOutputStream outputstream = new ByteArrayOutputStream(); if (bitmap != null) { success = bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputstream); if (success) { message = Base64.encodeToString(outputstream.toByteArray(), Base64.DEFAULT); } else { message = "did not successfully compress Bitmap to PNG format."; } }else{ message = "did not get Bitmap for this View."; } if(success){ debug(dbPrefix +" Succeed to get the view's image."); setGeneralSuccessWithSpecialInfo(props, message); }else{ debug(dbPrefix +message); setGeneralError(props, message); } } }catch(Exception e){ throw new ProcessorException(e.getMessage()); } } /** * Get bitmap image of a View.<br> * This method call {@link View#getDrawingCache()} to get the bitmap image.<br> * For some special views, we can't generate the bitmap by this way. We can<br> * override this method and provide a special way in the subclass.<br> * * @param view View, The View object * * @return The bitmap object of a view. */ protected Bitmap getBitmapOfView(View view){ String dbPrefix = TAG +"getBitmapOfView(): "; Bitmap bitmap = null; Rect v = new Rect(); boolean isvisible = view.getGlobalVisibleRect(v); if(isvisible){ View root = view.getRootView(); root.setDrawingCacheEnabled(true); bitmap = Bitmap.createBitmap(root.getDrawingCache(), v.left, v.top, v.width(), v.height()); root.setDrawingCacheEnabled(false); debug(dbPrefix + " Getting Image for normal View."); }else{ debug(dbPrefix + " The View is not visible."); } return bitmap; } /** * Get class name of a View.<br> * According to the view's id, try to get a View from cache.<br> * If not found, a {@link ProcessorException} will be thrown out.<br> * If a view is found, we will get the name of that view and return<br> * it through KEY_REMOTERESULTINFO<br> * * @param uid String, This is the view's uid according to which we will get a View. * This uid is the key in local cache {@link #viewCache} * @return String, the class name of the view. * @deprecated we can use {@link #getObjectClassName(Properties, boolean)} instead */ void getViewClassName(Properties props) throws ProcessorException{ try{ String uid = SoloMessage.getString(props, SoloMessage.PARAM_ID); View view = null; try{ view = (View) getCachedObject(uid, true); }catch(Exception x){} if(view==null){ throw new ProcessorException(" View for id '"+uid+"' is null."); }else{ setGeneralSuccessWithSpecialInfo(props, view.getClass().getName()); } }catch(Exception e){ throw new ProcessorException(e.getMessage()); } } /** * Get class name of an object.<br> * According to the object's id, try to get it from cache.<br> * If not found, a {@link ProcessorException} will be thrown out.<br> * If it is found, we will get its class name and return<br> * through KEY_REMOTERESULTINFO<br> * * @param uid String, This is the object's uid according to which we will get the object. * This uid is the key in local cache {@link #viewCache} or {@link #activityCache} * or {@link #webElementCache} * @return String, the class name of the object. */ void getObjectClassName(Properties props, boolean isViewObject) throws ProcessorException{ try{ String uid = SoloMessage.getString(props, SoloMessage.PARAM_ID); Object object = getCachedObject(uid, true); //Just try to cast it to View object if(isViewObject) object = (View) object; if(object==null){ throw new ProcessorException(" Object for id '"+uid+"' is null."); }else{ setGeneralSuccessWithSpecialInfo(props, object.getClass().getName()); } }catch(Exception e){ throw new ProcessorException(e.getClass()+":"+e.getMessage()); } } /** * Takes a screenshot sequence and stores the images via the Robotium Solo API.<br> * Requires Robotium 4.2 * <p> * calling:<br> * {@link Solo#startScreenshotSequence(String,int,int,int)}<br> * * @param props The Properties object containing the in and out parameters */ void startScreenshotSequenceMax(Properties props){ String debugPrefix = TAG+".startScreenshotSequence() "; boolean success = false; try{ String filename = SoloMessage.getString(props, SoloMessage.PARAM_NAME); if(filename == null || filename.length() < 1) throw new ProcessorException(debugPrefix +"filename is null or invalid."); int quality = SoloMessage.getInteger(props, SoloMessage.PARAM_QUALITY); if (quality < 0 || quality > 100) throw new ProcessorException(debugPrefix +"quality value "+ quality +" must be 0-100."); int frameDelay = SoloMessage.getInteger(props, SoloMessage.PARAM_TIME); if (frameDelay < 0) throw new ProcessorException(debugPrefix +"frameDelay value "+ frameDelay +" cannot be less than 0."); int maxFrames = SoloMessage.getInteger(props, SoloMessage.PARAM_STEPCOUNT); if (maxFrames < 1) throw new ProcessorException(debugPrefix +"maxFrames value "+ maxFrames +" cannot be less than 1."); String message = Environment.getExternalStorageDirectory() + "/Robotium-Screenshots/"+filename; try{ solo.startScreenshotSequence(filename, quality, frameDelay, maxFrames); success = true; }catch(Exception x){ message = "Met Exception "+x.getClass().getSimpleName()+": "+x.getMessage(); debug(debugPrefix +"ignoring "+ x.getClass().getSimpleName()+": "+x.getMessage()); } if(success){ debug(debugPrefix +"success for screenshot sequence "+ message); setGeneralSuccessWithSpecialInfo(props, message); }else{ debug(debugPrefix + "failure for screenshot sequence "+ filename); setGeneralError(props, message); } } catch(Throwable x){ String msg = x.getClass().getSimpleName()+": "+ x.getMessage(); debug(TAG+".startScreenshotSequence(filename, quality, frameDelay, maxFrames) "+ msg); setGeneralError(props, msg); } } /** * Retrieve a sequence of screenshots, which are stored in "/sdcard/Robotium-Screenshots/".<br> * Requires Robotium 4.1+ * <p> * These screenshots are generated by calling:<br> * {@link Solo#startScreenshotSequence(String)} -- Robotium 4.1+<br> * {@link Solo#startScreenshotSequence(String, int, int, int)} -- Robotium 4.1+<br> * * @param props The Properties object containing the in and out parameters */ void getScreenshotSequence(Properties props){ String debugPrefix = TAG+".getScreenshotSequence() "; boolean success = false; String message = ""; try{ String filename = SoloMessage.getString(props, SoloMessage.PARAM_NAME); if(filename == null || filename.length() < 1) throw new ProcessorException(debugPrefix +"filename is null or invalid."); debug(debugPrefix +" Try to get completed screenshots..."); StringBuffer absoluteFilePath = new StringBuffer(); if(remoteCommand.equals(SoloMessage.cmd_getscreenshotsequence)){ boolean onlyLasttime = SoloMessage.getBoolean(props, SoloMessage.PARAM_ONLYVISIBLE); //absoluteFilePath will get back a set of valid filenames getScreenshotSequenceSize(filename, onlyLasttime, absoluteFilePath); message = absoluteFilePath.toString(); success = true; }else if(remoteCommand.equals(SoloMessage.cmd_getscreenshotsequenceszie)){ boolean onlyLasttime = SoloMessage.getBoolean(props, SoloMessage.PARAM_ONLYVISIBLE); //absoluteFilePath will get back a set of valid filenames message = String.valueOf(getScreenshotSequenceSize(filename, onlyLasttime, absoluteFilePath)); props.setProperty(SoloMessage.PARAM_NAME+"FILE", absoluteFilePath.toString()); success = true; }else if(remoteCommand.equals(SoloMessage.cmd_getscreenshotsequenceindex)){ int index = SoloMessage.getInteger(props, SoloMessage.PARAM_INDEX); message = getImageBase64EncodedString(filename +"_"+index, absoluteFilePath); props.setProperty(SoloMessage.PARAM_NAME+"FILE", absoluteFilePath.toString()); success = true; }else{ message = remoteCommand+" can't be handled in method getScreenshotSequence()!"; } if(success){ setGeneralSuccessWithSpecialInfo(props, message); }else{ debug(debugPrefix +message); setGeneralError(props, message); } }catch(Throwable x){ String msg = x.getClass().getSimpleName()+": "+ x.getMessage(); debug("getScreenshotSequence() met "+msg); setGeneralError(props, msg); } } /** * Takes a Screenshot and retrieve it from /sdcard/Robotium-Screenshots/.<br> * Requires Robotium 3.4.1 * <p> * calling:<br> * {@link Solo#takeScreenshot(String)}<br> * {@link Solo#takeScreenshot(String, int)} -- Robotium 4.1<br> * * @param props The Properties object containing the in and out parameters */ void takeScreenshot(Properties props){ String debugPrefix = TAG+".takeScreenshot(filename) "; boolean success = false; String message = ""; try{ String filename = SoloMessage.getString(props, SoloMessage.PARAM_NAME); if(filename == null || filename.length() < 1) throw new ProcessorException(debugPrefix +"filename is null or invalid."); try{ if(remoteCommand.equals(SoloMessage.cmd_takescreenshot)){ solo.takeScreenshot(filename); }else if(remoteCommand.equals(SoloMessage.cmd_takescreenshotquality)){ int quality = SoloMessage.getInteger(props, SoloMessage.PARAM_QUALITY); solo.takeScreenshot(filename, quality); } }catch(Exception x){ debug("takeScreenshot ignoring "+ x.getClass().getSimpleName()+": "+x.getMessage()); } debug(debugPrefix +" Try to get completed screenshot..."); StringBuffer absoluteFilePath = new StringBuffer(); try{ message = getImageBase64EncodedString(filename, absoluteFilePath); success = true; }catch(ProcessorException pe){ message = pe.getMessage(); success = false; } if(success){ props.setProperty(SoloMessage.PARAM_NAME+"FILE", absoluteFilePath.toString()); debug(debugPrefix +" Succeeded to get the screenshot "+ absoluteFilePath.toString()); setGeneralSuccessWithSpecialInfo(props, message); }else{ debug(debugPrefix +message); setGeneralError(props, message); } } catch(Throwable x){ String msg = x.getClass().getSimpleName()+": "+ x.getMessage(); debug("takeScreenshot(filename) "+msg); setGeneralError(props, msg); } } /** * Read a JPG file with name 'filename' from folder '/sdcard/Robotium-Screenshots/', then encode it to * a String and return, this method doesn't care when the image file is created. * * @param filename, String, in, the image file to read from mobile device. * @param absoluteFilePath, StringBuffer, out, the absolute name of the image file. * @return String, the Base64 encoded string of the image. * @see #getImageBase64EncodedString(String, StringBuffer, long) */ String getImageBase64EncodedString(String filename/*in*/, StringBuffer absoluteFilePath/*out*/) throws ProcessorException{ //We will read the image, no matter when the image is created. return getImageBase64EncodedString(filename, absoluteFilePath, -1); } /** * Read a JPG file with name 'filename' from folder '/sdcard/Robotium-Screenshots/', then encode it to * a String and return, this method will only read the image file created after the time provide * by parameter 'newerThan' * * @param filename, String, in, the image file to read from mobile device. * @param absoluteFilePath, StringBuffer, out, the absolute name of the image file. * @param newerThan, long, in, the time to compare with the file's last modified time, * if 'last modified time' is smaller than it, the file will not be read. * @return String, the Base64 encoded string of the image. * @see #getImageBase64EncodedString(String, StringBuffer) */ String getImageBase64EncodedString(String filename/*in*/, StringBuffer absoluteFilePath/*out*/, long newerThan/*in*/) throws ProcessorException{ String imgString = null; File fileToRead = getRobotiumScreenshotFile(filename); if(waitForFileExistAndReadable(fileToRead, 5000)){//if the image is very big, is 5 seconds enough? if(fileToRead.lastModified()<newerThan){ throw new ProcessorException(fileToRead.getAbsolutePath()+" is too old, we don't read it!"); } if(absoluteFilePath!=null) absoluteFilePath.append(fileToRead.getAbsolutePath()); BufferedInputStream inputstream = null; ByteArrayOutputStream outputstream = null; try { inputstream = new BufferedInputStream(new FileInputStream(fileToRead)); outputstream = new ByteArrayOutputStream(); int byt = 0; while(inputstream.available() > 0 && byt != -1){ byt = inputstream.read(); if(byt != -1) outputstream.write(byt); } outputstream.flush(); imgString = Base64.encodeToString(outputstream.toByteArray(), Base64.DEFAULT); if(imgString.length() <= 0){ throw new ProcessorException(" image data appears to be empty!"); } } catch (Exception e) { throw new ProcessorException(e.getClass()+":"+e.getMessage()); }finally{ if(inputstream!=null) try{ inputstream.close();}catch (Exception e) {} if(outputstream!=null) try{ outputstream.close();}catch (Exception e) {} } }else{ throw new ProcessorException(fileToRead.getAbsolutePath()+" does not exist or is not readable."); } return imgString; } /** * @param filename, String, in, the image file to read from mobile device. * @param onlyLasttime, boolean, in, if true, count only the sequence generated last time; if false, count all. * @param absoluteFilePath, StringBuffer, out, the absolute name of the image file. * @return int, the number of valid image file generated by {@link #startScreenshotSequenceMax(Properties)} */ private int getScreenshotSequenceSize(String filename/*in*/, boolean onlyLasttime/*in*/, StringBuffer absoluteFilePath/*out*/){ int size = 0; try{ long timeOfFirstImageOfSequence = 0; if (onlyLasttime) getLastModifyTime(getRobotiumScreenshotFile(filename +"_0")); File fileToRead = null; while(true){ absoluteFilePath.append(";"); fileToRead = getRobotiumScreenshotFile(filename+"_"+size); if(waitForFileExistAndReadable(fileToRead, 5000)){//if the image is very big, is 5 seconds enough? if(onlyLasttime && fileToRead.lastModified()<timeOfFirstImageOfSequence){ throw new ProcessorException(fileToRead.getAbsolutePath()+" is too old, we don't read it!"); } }else{ throw new ProcessorException(fileToRead.getAbsolutePath()+" does not exist or is not readable."); } //If we can arrive here, which means the image file is valid, we count it. absoluteFilePath.append(fileToRead.getAbsolutePath()); size++; } }catch(ProcessorException pe){ //We should ignore this Exception. //it is very possible the image file doesn't exist for one of the sequence images //if user has called the solo.stopScreenshotSequence(). //Or there are some older image file with the same prefix debug("Ignoring Exception "+pe.getMessage()); } return size; } /** * Use the file prefix to create a robotium screen shot File.<br> * The folder is /sdcard/Robotium-Screenshots/<br> * The suffix is .jpg<br> * * @param filePrefix, String, the filename prefix * @return File, the robotium screen shot File */ private File getRobotiumScreenshotFile(String filePrefix){ File directory = new File(Environment.getExternalStorageDirectory() + "/Robotium-Screenshots/"); //Robotium save file as a jpg image for now, but if one day it changes?? File fileToRead = new File(directory, filePrefix +".jpg"); return fileToRead; } /** * @param fileToRead, File, the file to get the last modified time * @return long, the last modified time of a file */ private long getLastModifyTime(File fileToRead){ long lastModifyTime = -1; if(waitForFileExistAndReadable(fileToRead, 5000)){//if the image is very big, is 5 seconds enough? lastModifyTime = fileToRead.lastModified(); } return lastModifyTime; } /** * @param fileToRead, File * @param timeout, time to wait for existence of the file, in milliseconds * @return boolean, true if the file exists and can be read */ private boolean waitForFileExistAndReadable(File fileToRead, int timeout/*millisecond*/){ long startTime = (new Date()).getTime(); while(!(fileToRead.exists()||fileToRead.canRead()) && ((new Date()).getTime() < (startTime+timeout))){ //Wait for for the image to be ready to read solo.sleep(100); } return fileToRead.exists() && fileToRead.canRead(); } /** * Handle the static methods of class RobotiumUtils.<br> * Requires Robotium 4.1 * <p> * calling:<br> * {@link RobotiumUtils#filterViews(Class, Iterable)} -- Robotium 4.1<br> * {@link RobotiumUtils#filterViewsByText(Iterable, java.util.regex.Pattern)} -- Robotium 4.1<br> * {@link RobotiumUtils#filterViewsByText(Iterable, String)} -- Robotium 4.1<br> * {@link RobotiumUtils#filterViewsToSet(Class[], Iterable)} -- Robotium 4.1<br> * {@link RobotiumUtils#sortViewsByLocationOnScreen(List)} -- Robotium 4.1<br> * {@link RobotiumUtils#sortViewsByLocationOnScreen(List, boolean)} -- Robotium 4.1<br> * {@link RobotiumUtils#getNumberOfMatches(String, TextView, java.util.Set)} -- Robotium 4.1<br> * * @param props The Properties object containing the in and out parameters */ @SuppressWarnings({ "rawtypes", "unchecked" }) void handleRobotiumUtilsCommand(Properties props){ try{ List resultUIDList = new ArrayList(); if(remoteCommand.equals(SoloMessage.cmd_utilsfilterviews) || remoteCommand.equals(SoloMessage.cmd_utilsfilterviewsbytext) || remoteCommand.equals(SoloMessage.cmd_utilsfilterviewstoset) || remoteCommand.equals(SoloMessage.cmd_utilsremoveinvisibleviews) || remoteCommand.equals(SoloMessage.cmd_utilssortviewsbylocationonscreen) || remoteCommand.equals(SoloMessage.cmd_utilssortviewsbylocationonscreenyfirst)){ List<String> uidList = SoloMessage.parseStringArrayList(SoloMessage.getString(props, SoloMessage.PARAM_REFERENCES)); //objectToUIDHash contains pair <View, UID> HashMap objectToUIDHash = new HashMap(); String UID = null; Object object = null; for (int i=0;i<uidList.size();i++) { UID = uidList.get(i); object = getCachedObject(UID, true); if(object==null){ debug("Can't find cached object for UID '"+UID+"'!!!!"); }else{ if(remoteCommand.equals(SoloMessage.cmd_utilsfilterviewsbytext)){ if(object instanceof TextView) objectToUIDHash.put(object, UID); }else{ objectToUIDHash.put(object, UID); } } } List resultObjectList = null; if(remoteCommand.equals(SoloMessage.cmd_utilsfilterviews)){ Class<?> classToFilterBy = Class.forName(SoloMessage.getString(props, SoloMessage.PARAM_CLASS)); resultObjectList = RobotiumUtils.filterViews(classToFilterBy, objectToUIDHash.keySet()); } else if (remoteCommand.equals(SoloMessage.cmd_utilsfilterviewsbytext)) { String regex = SoloMessage.getString(props, SoloMessage.PARAM_REGEX_STRING); resultObjectList = RobotiumUtils.filterViewsByText(objectToUIDHash.keySet(), regex); } else if (remoteCommand.equals(SoloMessage.cmd_utilsfilterviewstoset)) { List<String> classNameList = SoloMessage.parseStringArrayList(SoloMessage.getString(props, SoloMessage.PARAM_CLASSES)); List<Class<View>> clazzList = new ArrayList<Class<View>>(); for(int i=0;i<classNameList.size();i++){ clazzList.add((Class<View>) Class.forName(classNameList.get(i))); } resultObjectList = RobotiumUtils.filterViewsToSet(clazzList.toArray(new Class[0]), objectToUIDHash.keySet()); } else if (remoteCommand.equals(SoloMessage.cmd_utilsremoveinvisibleviews)) { resultObjectList = RobotiumUtils.removeInvisibleViews(objectToUIDHash.keySet()); } else if (remoteCommand.equals(SoloMessage.cmd_utilssortviewsbylocationonscreen)) { resultObjectList = new ArrayList<Object>(); resultObjectList.addAll(objectToUIDHash.keySet()); RobotiumUtils.sortViewsByLocationOnScreen(resultObjectList); } else if (remoteCommand.equals(SoloMessage.cmd_utilssortviewsbylocationonscreenyfirst)) { boolean yAxisFirst = SoloMessage.getBoolean(props, SoloMessage.PARAM_YAXISFIRST); resultObjectList = new ArrayList<Object>(); resultObjectList.addAll(objectToUIDHash.keySet()); RobotiumUtils.sortViewsByLocationOnScreen(resultObjectList, yAxisFirst); } for(int i=0;i<resultObjectList.size();i++){ resultUIDList.add(objectToUIDHash.get(resultObjectList.get(i))); } }else if (remoteCommand.equals(SoloMessage.cmd_utilsgetnumberofmatches)) { String uid = SoloMessage.getString(props, SoloMessage.PARAM_REFERENCE); TextView textView = (TextView) getCachedObject(uid, true); String regex = SoloMessage.getString(props, SoloMessage.PARAM_REGEX_STRING); HashSet<TextView> uniqueTextViews = new HashSet<TextView>(); int match = RobotiumUtils.getNumberOfMatches(regex, textView, uniqueTextViews); //if matched, match time should be 1, uniqueTextViews should contain only textView. //TODO Need to know the usage of RobotiumUtils.getNumberOfMatches() for future implementation debug("Match time is "+match+"; uniqueTextViews size="+uniqueTextViews.size()); if(uniqueTextViews.size()>0) resultUIDList.add(uid); } setGeneralSuccessWithSpecialInfo(props,Message.convertToDelimitedString(resultUIDList)); }catch(Throwable x){ debug("handleRobotiumUtilsCommand() "+SoloMessage.getStackTrace(x)); setGeneralError(props, x.getClass().getSimpleName()+": "+ x.getMessage()); } } /** * Handle the static methods of class Timeout.<br> * Requires Robotium 4.1+ * <p> * calling:<br> * {@link Timeout#setLargeTimeout(int)} -- Robotium 4.1+<br> * {@link Timeout#setSmallTimeout(int)} -- Robotium 4.1+<br> * {@link Timeout#getLargeTimeout} -- Robotium 4.1+<br> * {@link Timeout#getSmallTimeout} -- Robotium 4.1+<br> * * @param props The Properties object containing the in and out parameters */ void handleRobotiumTimeoutCommand(Properties props){ try{ int millisecond = -1; if(remoteCommand.equals(SoloMessage.cmd_setlargetimeout)){ millisecond = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); Timeout.setLargeTimeout(millisecond); } else if (remoteCommand.equals(SoloMessage.cmd_setsmalltimeout)) { millisecond = SoloMessage.getInteger(props, SoloMessage.PARAM_TIMEOUT); Timeout.setSmallTimeout(millisecond); } else if (remoteCommand.equals(SoloMessage.cmd_getlargetimeout)) { millisecond = Timeout.getLargeTimeout(); } else if (remoteCommand.equals(SoloMessage.cmd_getsmalltimeout)) { millisecond = Timeout.getSmallTimeout(); } setGeneralSuccessWithSpecialInfo(props,String.valueOf(millisecond)); }catch(Throwable x){ debug("handleRobotiumTimeoutCommand() "+SoloMessage.getStackTrace(x)); setGeneralError(props, x.getClass().getSimpleName()+": "+ x.getMessage()); } } /** * Handle the zoom, rotate, swipe methods of Robotium Solo.<br> * Requires Robotium 4.1+ * <p> * calling:<br> * {@link Solo#pinchToZoom(PointF, PointF, PointF, PointF)} -- Robotium 4.1+<br> * {@link Solo#rotateLarge(PointF, PointF) -- Robotium 4.1+<br> * {@link Solo#rotateSmall(PointF, PointF) -- Robotium 4.1+<br> * {@link Solo#swipe(PointF, PointF, PointF, PointF) -- Robotium 4.1+<br> * * @param props The Properties object containing the in and out parameters */ void handleZoomRotateSwipe(Properties props){ try{ String base64String = SoloMessage.getString(props, SoloMessage.PARAM_OBJECT); @SuppressWarnings("unchecked") ObjectCollection<com.jayway.android.robotium.remotecontrol.PointF> pointCollection = (ObjectCollection<com.jayway.android.robotium.remotecontrol.PointF>) SoloMessage.decodeBase64Object(base64String); List<PointF> points = SoloMessage.getAndroidPointFList(pointCollection.getObjectList()); if(remoteCommand.equals(SoloMessage.cmd_pinchtozoom)){ if(points.size()<4) throw new Exception("For command '"+remoteCommand+"', the parameters are not enough."); solo.pinchToZoom(points.get(0), points.get(1), points.get(2), points.get(3)); } else if (remoteCommand.equals(SoloMessage.cmd_rotatelarge)) { if(points.size()<2) throw new Exception("For command '"+remoteCommand+"', the parameters are not enough."); solo.rotateLarge(points.get(0), points.get(1)); } else if (remoteCommand.equals(SoloMessage.cmd_rotatesmall)) { if(points.size()<2) throw new Exception("For command '"+remoteCommand+"', the parameters are not enough."); solo.rotateSmall(points.get(0), points.get(1)); } else if (remoteCommand.equals(SoloMessage.cmd_swipe)) { if(points.size()<4) throw new Exception("For command '"+remoteCommand+"', the parameters are not enough."); solo.swipe(points.get(0), points.get(1), points.get(2), points.get(3)); } setGeneralSuccess(props); }catch(Throwable x){ debug("handleRobotiumTimeoutCommand() "+SoloMessage.getStackTrace(x)); setGeneralError(props, x.getClass().getSimpleName()+": "+ x.getMessage()); } } /** Used for CacheReferenceInterface instance storage. */ @SuppressWarnings("rawtypes") private Vector chainedCache = new Vector(); /** CacheReferenceInterface implementation. */ public Object getCachedObject(String key, boolean useChain) { Object item = getCachedItem(viewCache, key); if(item == null) item = getCachedItem(activityCache, key); if(item == null) item = getCachedItem(webElementCache, key); if(item == null) item = getCachedItem(activityMonitorCache, key); if(item == null && useChain){ for(int i=0;i<chainedCache.size()&&item==null;i++){ CacheReferenceInterface c = (CacheReferenceInterface) chainedCache.elementAt(i); item = c.getCachedObject(key, false);//avoid infinite circular references } } return item; } /** CacheReferenceInterface implementation. * @see CacheReferenceInterface#addCacheReferenceInterface(CacheReferenceInterface) */ @SuppressWarnings("unchecked") public void addCacheReferenceInterface(CacheReferenceInterface cache) { if(! chainedCache.contains(cache)) chainedCache.add(cache); } /** CacheReferenceInterface implementation. * @see CacheReferenceInterface#removeCacheReferenceInterface(CacheReferenceInterface) */ public void removeCacheReferenceInterface(CacheReferenceInterface cache) { if(chainedCache.contains(cache)) chainedCache.remove(cache); } /** CacheReferenceInterface implementation. * @see CacheReferenceInterface#clearCache(boolean) */ public void clearCache(boolean useChain) { resetExternalModeCache(viewCache); resetExternalModeCache(webElementCache); resetExternalModeCache(activityCache); //resetExternalModeCache(activityMonitorCache); //must not be cleared until testing is done if(useChain){ for(int i=0;i<chainedCache.size();i++){ CacheReferenceInterface c = (CacheReferenceInterface) chainedCache.elementAt(i); c.clearCache(false);//avoid infinite circular references } } } }