PageRenderTime 40ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/robotium-solo/src/main/java/com/robotium/solo/ActivityUtils.java

http://github.com/jayway/robotium
Java | 501 lines | 289 code | 81 blank | 131 comment | 54 complexity | 465e1dcba2dd016a4abbb530cc3e32e3 MD5 | raw file
Possible License(s): Apache-2.0
  1. package com.robotium.solo;
  2. import java.lang.ref.WeakReference;
  3. import java.util.ArrayList;
  4. import java.util.Iterator;
  5. import java.util.Stack;
  6. import java.util.Timer;
  7. import com.robotium.solo.Solo.Config;
  8. import junit.framework.Assert;
  9. import android.app.Activity;
  10. import android.app.Instrumentation;
  11. import android.app.Instrumentation.ActivityMonitor;
  12. import android.content.IntentFilter;
  13. import android.os.SystemClock;
  14. import android.util.Log;
  15. import android.view.KeyEvent;
  16. /**
  17. * Contains activity related methods. Examples are:
  18. * getCurrentActivity(), getActivityMonitor(), setActivityOrientation(int orientation).
  19. *
  20. * @author Renas Reda, renas.reda@robotium.com
  21. *
  22. */
  23. class ActivityUtils {
  24. private final Config config;
  25. private final Instrumentation inst;
  26. private ActivityMonitor activityMonitor;
  27. private Activity activity;
  28. private final Sleeper sleeper;
  29. private final String LOG_TAG = "Robotium";
  30. private final int MINISLEEP = 100;
  31. private Stack<WeakReference<Activity>> activityStack;
  32. private WeakReference<Activity> weakActivityReference;
  33. private Stack<String> activitiesStoredInActivityStack;
  34. private Timer activitySyncTimer;
  35. private volatile boolean registerActivities;
  36. Thread activityThread;
  37. /**
  38. * Constructs this object.
  39. *
  40. * @param config the {@code Config} instance
  41. * @param inst the {@code Instrumentation} instance.
  42. * @param activity the start {@code Activity}
  43. * @param sleeper the {@code Sleeper} instance
  44. */
  45. public ActivityUtils(Config config, Instrumentation inst, Activity activity, Sleeper sleeper) {
  46. this.config = config;
  47. this.inst = inst;
  48. this.activity = activity;
  49. this.sleeper = sleeper;
  50. createStackAndPushStartActivity();
  51. activitySyncTimer = new Timer();
  52. activitiesStoredInActivityStack = new Stack<String>();
  53. setupActivityMonitor();
  54. setupActivityStackListener();
  55. }
  56. /**
  57. * Creates a new activity stack and pushes the start activity.
  58. */
  59. private void createStackAndPushStartActivity(){
  60. activityStack = new Stack<WeakReference<Activity>>();
  61. if (activity != null && config.trackActivities){
  62. WeakReference<Activity> weakReference = new WeakReference<Activity>(activity);
  63. activity = null;
  64. activityStack.push(weakReference);
  65. }
  66. }
  67. /**
  68. * Returns a {@code List} of all the opened/active activities.
  69. *
  70. * @return a {@code List} of all the opened/active activities
  71. */
  72. public ArrayList<Activity> getAllOpenedActivities()
  73. {
  74. ArrayList<Activity> activities = new ArrayList<Activity>();
  75. Iterator<WeakReference<Activity>> activityStackIterator = activityStack.iterator();
  76. while(activityStackIterator.hasNext()){
  77. Activity activity = activityStackIterator.next().get();
  78. if(activity!=null)
  79. activities.add(activity);
  80. }
  81. return activities;
  82. }
  83. /**
  84. * This is were the activityMonitor is set up. The monitor will keep check
  85. * for the currently active activity.
  86. */
  87. private void setupActivityMonitor() {
  88. if(config.trackActivities){
  89. try {
  90. IntentFilter filter = null;
  91. activityMonitor = inst.addMonitor(filter, null, false);
  92. } catch (Exception e) {
  93. e.printStackTrace();
  94. }
  95. }
  96. }
  97. /**
  98. * Returns true if registration of Activites should be performed
  99. *
  100. * @return true if registration of Activities should be performed
  101. */
  102. public boolean shouldRegisterActivities() {
  103. return registerActivities;
  104. }
  105. /**
  106. * Set true if registration of Activities should be performed
  107. * @param registerActivities true if registration of Activities should be performed
  108. *
  109. */
  110. public void setRegisterActivities(boolean registerActivities) {
  111. this.registerActivities = registerActivities;
  112. }
  113. /**
  114. * This is were the activityStack listener is set up. The listener will keep track of the
  115. * opened activities and their positions.
  116. */
  117. private void setupActivityStackListener() {
  118. if(activityMonitor == null){
  119. return;
  120. }
  121. setRegisterActivities(true);
  122. activityThread = new RegisterActivitiesThread(this);
  123. activityThread.start();
  124. }
  125. void monitorActivities() {
  126. if(activityMonitor != null){
  127. Activity activity = activityMonitor.waitForActivityWithTimeout(2000L);
  128. if(activity != null){
  129. if (activitiesStoredInActivityStack.remove(activity.toString())){
  130. removeActivityFromStack(activity);
  131. }
  132. if(!activity.isFinishing()){
  133. addActivityToStack(activity);
  134. }
  135. }
  136. }
  137. }
  138. /**
  139. * Removes a given activity from the activity stack
  140. *
  141. * @param activity the activity to remove
  142. */
  143. private void removeActivityFromStack(Activity activity){
  144. Iterator<WeakReference<Activity>> activityStackIterator = activityStack.iterator();
  145. while(activityStackIterator.hasNext()){
  146. Activity activityFromWeakReference = activityStackIterator.next().get();
  147. if(activityFromWeakReference == null){
  148. activityStackIterator.remove();
  149. }
  150. if(activity != null && activityFromWeakReference != null && activityFromWeakReference.equals(activity)){
  151. activityStackIterator.remove();
  152. }
  153. }
  154. }
  155. /**
  156. * Returns the ActivityMonitor used by Robotium.
  157. *
  158. * @return the ActivityMonitor used by Robotium
  159. */
  160. public ActivityMonitor getActivityMonitor(){
  161. return activityMonitor;
  162. }
  163. /**
  164. * Sets the Orientation (Landscape/Portrait) for the current activity.
  165. *
  166. * @param orientation An orientation constant such as {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE} or {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_PORTRAIT}
  167. */
  168. public void setActivityOrientation(int orientation)
  169. {
  170. Activity activity = getCurrentActivity();
  171. if(activity != null){
  172. activity.setRequestedOrientation(orientation);
  173. }
  174. }
  175. /**
  176. * Returns the current {@code Activity}, after sleeping a default pause length.
  177. *
  178. * @param shouldSleepFirst whether to sleep a default pause first
  179. * @return the current {@code Activity}
  180. */
  181. public Activity getCurrentActivity(boolean shouldSleepFirst) {
  182. return getCurrentActivity(shouldSleepFirst, true);
  183. }
  184. /**
  185. * Returns the current {@code Activity}, after sleeping a default pause length.
  186. *
  187. * @return the current {@code Activity}
  188. */
  189. public Activity getCurrentActivity() {
  190. return getCurrentActivity(true, true);
  191. }
  192. /**
  193. * Adds an activity to the stack
  194. *
  195. * @param activity the activity to add
  196. */
  197. private void addActivityToStack(Activity activity){
  198. activitiesStoredInActivityStack.push(activity.toString());
  199. weakActivityReference = new WeakReference<Activity>(activity);
  200. activity = null;
  201. activityStack.push(weakActivityReference);
  202. }
  203. /**
  204. * Waits for an activity to be started if one is not provided
  205. * by the constructor.
  206. */
  207. private final void waitForActivityIfNotAvailable(){
  208. if(activityStack.isEmpty() || activityStack.peek().get() == null){
  209. if (activityMonitor != null) {
  210. Activity activity = activityMonitor.getLastActivity();
  211. while (activity == null){
  212. sleeper.sleepMini();
  213. activity = activityMonitor.getLastActivity();
  214. }
  215. addActivityToStack(activity);
  216. }
  217. else if(config.trackActivities){
  218. sleeper.sleepMini();
  219. setupActivityMonitor();
  220. waitForActivityIfNotAvailable();
  221. }
  222. }
  223. }
  224. /**
  225. * Returns the name of the most recent Activity
  226. *
  227. * @return the name of the current {@code Activity}
  228. */
  229. public String getCurrentActivityName(){
  230. if(!activitiesStoredInActivityStack.isEmpty()){
  231. return activitiesStoredInActivityStack.peek();
  232. }
  233. return "";
  234. }
  235. /**
  236. * Returns the current {@code Activity}.
  237. *
  238. * @param shouldSleepFirst whether to sleep a default pause first
  239. * @param waitForActivity whether to wait for the activity
  240. * @return the current {@code Activity}
  241. */
  242. public Activity getCurrentActivity(boolean shouldSleepFirst, boolean waitForActivity) {
  243. if(shouldSleepFirst){
  244. sleeper.sleep();
  245. }
  246. if(!config.trackActivities){
  247. return activity;
  248. }
  249. if(waitForActivity){
  250. waitForActivityIfNotAvailable();
  251. }
  252. if(!activityStack.isEmpty()){
  253. activity=activityStack.peek().get();
  254. }
  255. return activity;
  256. }
  257. /**
  258. * Check if activity stack is empty.
  259. *
  260. * @return true if activity stack is empty
  261. */
  262. public boolean isActivityStackEmpty() {
  263. return activityStack.isEmpty();
  264. }
  265. /**
  266. * Returns to the given {@link Activity}.
  267. *
  268. * @param name the name of the {@code Activity} to return to, e.g. {@code "MyActivity"}
  269. */
  270. public void goBackToActivity(String name)
  271. {
  272. ArrayList<Activity> activitiesOpened = getAllOpenedActivities();
  273. boolean found = false;
  274. for(int i = 0; i < activitiesOpened.size(); i++){
  275. if(activitiesOpened.get(i).getClass().getSimpleName().equals(name)){
  276. found = true;
  277. break;
  278. }
  279. }
  280. if(found){
  281. while(!getCurrentActivity().getClass().getSimpleName().equals(name))
  282. {
  283. try{
  284. inst.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
  285. }catch(SecurityException ignored){}
  286. }
  287. }
  288. else{
  289. for (int i = 0; i < activitiesOpened.size(); i++){
  290. Log.d(LOG_TAG, "Activity priorly opened: "+ activitiesOpened.get(i).getClass().getSimpleName());
  291. }
  292. Assert.fail("No Activity named: '" + name + "' has been priorly opened");
  293. }
  294. }
  295. /**
  296. * Returns a localized string.
  297. *
  298. * @param resId the resource ID for the string
  299. * @return the localized string
  300. */
  301. public String getString(int resId)
  302. {
  303. Activity activity = getCurrentActivity(false);
  304. if(activity == null){
  305. return "";
  306. }
  307. return activity.getString(resId);
  308. }
  309. /**
  310. * Finalizes the solo object.
  311. */
  312. @Override
  313. public void finalize() throws Throwable {
  314. activitySyncTimer.cancel();
  315. stopActivityMonitor();
  316. super.finalize();
  317. }
  318. /**
  319. * Removes the ActivityMonitor
  320. */
  321. private void stopActivityMonitor(){
  322. try {
  323. // Remove the monitor added during startup
  324. if (activityMonitor != null) {
  325. inst.removeMonitor(activityMonitor);
  326. activityMonitor = null;
  327. }
  328. } catch (Exception ignored) {}
  329. }
  330. /**
  331. * All activites that have been opened are finished.
  332. */
  333. public void finishOpenedActivities(){
  334. // Stops the activityStack listener
  335. activitySyncTimer.cancel();
  336. if(!config.trackActivities){
  337. useGoBack(3);
  338. return;
  339. }
  340. ArrayList<Activity> activitiesOpened = getAllOpenedActivities();
  341. // Finish all opened activities
  342. for (int i = activitiesOpened.size()-1; i >= 0; i--) {
  343. sleeper.sleep(MINISLEEP);
  344. finishActivity(activitiesOpened.get(i));
  345. }
  346. activitiesOpened = null;
  347. sleeper.sleep(MINISLEEP);
  348. // Finish the initial activity, pressing Back for good measure
  349. finishActivity(getCurrentActivity(true, false));
  350. stopActivityMonitor();
  351. setRegisterActivities(false);
  352. this.activity = null;
  353. sleeper.sleepMini();
  354. useGoBack(1);
  355. clearActivityStack();
  356. }
  357. /**
  358. * Sends the back button command a given number of times
  359. *
  360. * @param numberOfTimes the number of times to press "back"
  361. */
  362. private void useGoBack(int numberOfTimes){
  363. for(int i = 0; i < numberOfTimes; i++){
  364. try {
  365. inst.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
  366. sleeper.sleep(MINISLEEP);
  367. inst.sendKeyDownUpSync(KeyEvent.KEYCODE_BACK);
  368. } catch (Throwable ignored) {
  369. // Guard against lack of INJECT_EVENT permission
  370. }
  371. }
  372. }
  373. /**
  374. * Clears the activity stack.
  375. */
  376. private void clearActivityStack(){
  377. activityStack.clear();
  378. activitiesStoredInActivityStack.clear();
  379. }
  380. /**
  381. * Finishes an activity.
  382. *
  383. * @param activity the activity to finish
  384. */
  385. private void finishActivity(Activity activity){
  386. if(activity != null) {
  387. try{
  388. activity.finish();
  389. }catch(Throwable e){
  390. e.printStackTrace();
  391. }
  392. }
  393. }
  394. private static final class RegisterActivitiesThread extends Thread {
  395. public static final long REGISTER_ACTIVITY_THREAD_SLEEP_MS = 16L;
  396. private final WeakReference<ActivityUtils> activityUtilsWR;
  397. RegisterActivitiesThread(ActivityUtils activityUtils) {
  398. super("activityMonitorThread");
  399. activityUtilsWR = new WeakReference<ActivityUtils>(activityUtils);
  400. setPriority(Thread.MIN_PRIORITY);
  401. }
  402. @Override
  403. public void run() {
  404. while (shouldMonitor()) {
  405. monitorActivities();
  406. SystemClock.sleep(REGISTER_ACTIVITY_THREAD_SLEEP_MS);
  407. }
  408. }
  409. private boolean shouldMonitor() {
  410. ActivityUtils activityUtils = activityUtilsWR.get();
  411. return activityUtils != null && activityUtils.shouldRegisterActivities();
  412. }
  413. private void monitorActivities() {
  414. ActivityUtils activityUtils = activityUtilsWR.get();
  415. if (activityUtils != null) {
  416. activityUtils.monitorActivities();
  417. }
  418. }
  419. }
  420. }