calabash-android 0.4.22.pre4 → 0.5.0.pre1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/bin/calabash-android +66 -45
  3. data/lib/calabash-android/calabash_steps.rb +0 -8
  4. data/lib/calabash-android/canned_steps.md +2 -30
  5. data/lib/calabash-android/lib/TestServer.apk +0 -0
  6. data/lib/calabash-android/operations.rb +199 -23
  7. data/lib/calabash-android/removed_actions.txt +52 -0
  8. data/lib/calabash-android/steps/assert_steps.rb +9 -24
  9. data/lib/calabash-android/steps/check_box_steps.rb +2 -2
  10. data/lib/calabash-android/steps/context_menu_steps.rb +11 -4
  11. data/lib/calabash-android/steps/date_picker_steps.rb +2 -2
  12. data/lib/calabash-android/steps/enter_text_steps.rb +13 -13
  13. data/lib/calabash-android/steps/l10n_steps.rb +5 -5
  14. data/lib/calabash-android/steps/map_steps.rb +12 -12
  15. data/lib/calabash-android/steps/navigation_steps.rb +12 -12
  16. data/lib/calabash-android/steps/press_button_steps.rb +16 -12
  17. data/lib/calabash-android/steps/progress_steps.rb +19 -22
  18. data/lib/calabash-android/steps/search_steps.rb +2 -2
  19. data/lib/calabash-android/steps/spinner_steps.rb +10 -2
  20. data/lib/calabash-android/steps/time_picker_steps.rb +2 -2
  21. data/lib/calabash-android/version.rb +1 -1
  22. data/lib/calabash-android/wait_helpers.rb +29 -1
  23. data/test-server/instrumentation-backend/antlr/UIQuery.g +11 -11
  24. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/CalabashInstrumentationTestRunner.java +3 -2
  25. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/InstrumentationBackend.java +9 -4
  26. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/Result.java +16 -12
  27. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/application/Backdoor.java +55 -0
  28. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/gestures/LongPressCoordinate.java +19 -2
  29. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/JavaScriptExecuter.java +105 -0
  30. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/QueryHelper.java +27 -30
  31. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ViewMapper.java +27 -18
  32. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/ComparisonOperator.java +7 -1
  33. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTPredicate.java +16 -4
  34. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryASTWith.java +13 -6
  35. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/query/ast/UIQueryUtils.java +94 -48
  36. metadata +5 -59
  37. data/lib/calabash-android/steps/additions_manual_steps.rb +0 -11
  38. data/lib/calabash-android/steps/app_steps.rb +0 -10
  39. data/lib/calabash-android/steps/list_steps.rb +0 -41
  40. data/lib/calabash-android/steps/rotation_steps.rb +0 -7
  41. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/button/PressButtonNumber.java +0 -22
  42. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/button/PressButtonText.java +0 -27
  43. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/button/PressImageButtonDescription.java +0 -39
  44. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/button/PressImageButtonNumber.java +0 -22
  45. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/button/WaitForButton.java +0 -47
  46. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/checkbox/ToggleCheckboxNumber.java +0 -22
  47. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/contextmenu/LongPressText.java +0 -22
  48. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/contextmenu/LongPressTextAndSelectFromMenuById.java +0 -26
  49. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/contextmenu/LongPressTextAndSelectFromMenuByIndex.java +0 -22
  50. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/contextmenu/LongPressTextAndSelectFromMenuByText.java +0 -26
  51. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/helpers/InspectCurrentDialog.java +0 -76
  52. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/list/GetListData.java +0 -85
  53. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/list/GetListItemProperties.java +0 -194
  54. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/list/GetListItemText.java +0 -136
  55. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/list/LongPressListItems.java +0 -22
  56. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/list/PressListItems.java +0 -22
  57. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/scrolling/ScrollDown.java +0 -22
  58. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/scrolling/ScrollUp.java +0 -22
  59. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/search/EnterQueryByIndex.java +0 -29
  60. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/softkey/SelectFromMenuByText.java +0 -24
  61. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/spinner/GetSelectedSpinnerItemText.java +0 -36
  62. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/spinner/SelectSpinnerItemByContentDescription.java +0 -43
  63. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/AssertGridViewContainsNoDuplicates.java +0 -72
  64. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/AssertText.java +0 -31
  65. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/AssertTextOfSpecificTextViewByContentDescription.java +0 -32
  66. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/ClearTextById.java +0 -30
  67. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/ClearTextByIndex.java +0 -22
  68. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/ClearTextFieldByContentDescription.java +0 -33
  69. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/ClickOnText.java +0 -22
  70. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/EnterTextByContentDescription.java +0 -32
  71. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/EnterTextById.java +0 -33
  72. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/EnterTextByIndex.java +0 -22
  73. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/text/GetTextById.java +0 -42
  74. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/AssertViewProperty.java +0 -141
  75. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/ClickOnViewByDescription.java +0 -46
  76. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/ClickOnViewById.java +0 -56
  77. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/GetViewProperty.java +0 -101
  78. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/HasView.java +0 -31
  79. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/IsEnabled.java +0 -30
  80. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/LongPressOnViewById.java +0 -34
  81. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/Press.java +0 -89
  82. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/view/SelectTab.java +0 -110
  83. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/Wait.java +0 -24
  84. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForDialogClose.java +0 -21
  85. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForProgress.java +0 -47
  86. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForScreen.java +0 -54
  87. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForTab.java +0 -108
  88. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForText.java +0 -37
  89. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForView.java +0 -43
  90. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/wait/WaitForViewById.java +0 -58
  91. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/JavaScriptOperation.java +0 -44
  92. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/PressByCssSelector.java +0 -70
  93. data/test-server/instrumentation-backend/src/sh/calaba/instrumentationbackend/actions/webview/SetText.java +0 -64
@@ -1,7 +1,7 @@
1
1
  Then /^I enter "([^\"]*)" into search field$/ do |text|
2
- performAction('enter_query_into_numbered_field', text, 1)
2
+ enter_text("android.widget.SearchView index:0", text)
3
3
  end
4
4
 
5
5
  Then /^I enter "([^\"]*)" into search field number (\d+)$/ do |text, number|
6
- performAction('enter_query_into_numbered_field', text, number)
6
+ enter_text("android.widget.SearchView index:#{number.to_i-1}", text)
7
7
  end
@@ -1,3 +1,11 @@
1
- Then /^I select "([^\"]*)" from "([^\"]*)"$/ do |item_text, spinner_content_description|
2
- performAction('select_item_from_named_spinner', spinner_content_description, item_text)
1
+ Then /^I select "([^\"]*)" from "([^\"]*)"$/ do |item_text, spinner_identifier|
2
+ spinner = query("android.widget.Spinner marked:'#{spinner_identifier}'")
3
+
4
+ if spinner.empty?
5
+ tap_when_element_exists("android.widget.Spinner * marked:'#{spinner_identifier}'")
6
+ else
7
+ touch(spinner)
8
+ end
9
+
10
+ tap_when_element_exists("android.widget.PopupWindow$PopupViewContainer * marked:'#{item_text}'")
3
11
  end
@@ -1,8 +1,8 @@
1
1
 
2
2
  Given /^I set the time to "(\d\d:\d\d)" on TimePicker with index "([^\"]*)"$/ do |time, index|
3
- performAction('set_time_with_index', time, index)
3
+ perform_action('set_time_with_index', time, index)
4
4
  end
5
5
 
6
6
  Given /^I set the "([^\"]*)" time to "(\d\d:\d\d)"$/ do |content_description, time|
7
- performAction('set_time_with_description', content_description, time)
7
+ perform_action('set_time_with_description', content_description, time)
8
8
  end
@@ -1,5 +1,5 @@
1
1
  module Calabash
2
2
  module Android
3
- VERSION = "0.4.22.pre4"
3
+ VERSION = "0.5.0.pre1"
4
4
  end
5
5
  end
@@ -83,6 +83,11 @@ module Calabash
83
83
  end
84
84
  end
85
85
 
86
+ #options for wait_for apply
87
+ def wait_for_element_exists(uiquery, options={})
88
+ wait_for_elements_exist([uiquery], options)
89
+ end
90
+
86
91
  #options for wait_for apply
87
92
  def wait_for_elements_exist(elements_arr, options={})
88
93
  if elements_arr.is_a?(String) || elements_arr.is_a?(Symbol)
@@ -93,6 +98,12 @@ module Calabash
93
98
  elements_arr.all? { |q| element_exists(q) }
94
99
  end
95
100
  end
101
+
102
+ #options for wait_for apply
103
+ def wait_for_element_do_not_exist(uiquery, options={})
104
+ wait_for_elements_do_not_exist([uiquery], options)
105
+ end
106
+
96
107
  #options for wait_for apply
97
108
  def wait_for_elements_do_not_exist(elements_arr, options={})
98
109
  if elements_arr.is_a?(String)
@@ -167,10 +178,27 @@ module Calabash
167
178
  action = { :action => lambda { touch uiquery } }
168
179
  opts = DEFAULT_OPTS.merge(action).merge(opts)
169
180
  wait_for_elements_exist([uiquery], opts)
170
- opts[:action].call
181
+
182
+ if opts[:action].parameters.length == 0
183
+ opts[:action].call
184
+ else
185
+ opts[:action].call(uiquery)
186
+ end
187
+ end
188
+
189
+ def wait_for_text(text, options={})
190
+ wait_for_element_exists("* {text CONTAINS[c] '#{text}'}", options)
171
191
  end
172
192
 
193
+ def wait_for_text_to_disappear(text, options={})
194
+ wait_for_element_do_not_exist("* {text CONTAINS[c] '#{text}'}", options)
195
+ end
173
196
 
197
+ def wait_for_activity(activity_name, options={})
198
+ wait_for(options) do
199
+ perform_action('get_activity_name')['message'] == activity_name
200
+ end
201
+ end
174
202
  end
175
203
  end
176
204
  end
@@ -32,13 +32,13 @@ options {
32
32
  }
33
33
  }
34
34
 
35
- query : expr (WHITE! expr)*
35
+ query : expr (WHITE! expr)*
36
36
  ;
37
-
38
37
 
39
- expr : (className | filter | visibility | predicate | DIRECTION^)
38
+
39
+ expr : (className | filter | visibility | predicate | DIRECTION^)
40
40
  ;
41
-
41
+
42
42
  DIRECTION : 'descendant' | 'child' | 'parent' | 'sibling'
43
43
  ;
44
44
 
@@ -66,12 +66,12 @@ BEGINPRED : '{'
66
66
  ;
67
67
  ENDPRED : '}'
68
68
  ;
69
-
70
- RELATION : | '=' | '>' | '>=' | '<' | '<=' |
71
- (( 'BEGINSWITH' | 'ENDSWITH' | 'CONTAINS' | 'LIKE'
69
+
70
+ RELATION : | '=' | '>' | '>=' | '<' | '<=' | '!=' | '<>' |
71
+ (( 'BEGINSWITH' | 'ENDSWITH' | 'CONTAINS' | 'LIKE'
72
72
  | 'beginswith' | 'endswith' | 'contains' | 'like') ('[' ('a'..'z' | 'A'..'Z')* ']')?)
73
-
74
- ;
73
+
74
+ ;
75
75
 
76
76
  INT : '0'..'9'+
77
77
  ;
@@ -86,8 +86,8 @@ NAME : ('a'..'z'|'A'..'Z'|'_') ('a'..'z'|'A'..'Z'|'0'..'9'|'_'|'$')*
86
86
  ;
87
87
 
88
88
  STRING
89
- : '\'' ( ESC_SEQ | ~('\\'|'\'') )* '\''
90
- | '"' ( DOUBLE_ESC_SEQ | ~('\\'|'"') )* '"'
89
+ : '\'' ( ESC_SEQ | ~('\\'|'\'') )* '\''
90
+ | '"' ( DOUBLE_ESC_SEQ | ~('\\'|'"') )* '"'
91
91
  ;
92
92
 
93
93
  WHITE : ' '+ ;
@@ -2,8 +2,8 @@ package sh.calaba.instrumentationbackend;
2
2
 
3
3
  import java.lang.reflect.Method;
4
4
 
5
- import sh.calaba.instrumentationbackend.actions.HttpServer;
6
5
  import android.app.Activity;
6
+ import sh.calaba.instrumentationbackend.actions.HttpServer;
7
7
  import android.content.Context;
8
8
  import android.os.Bundle;
9
9
  import android.test.InstrumentationTestRunner;
@@ -17,6 +17,7 @@ public class CalabashInstrumentationTestRunner extends InstrumentationTestRunner
17
17
  Method method = c.getDeclaredMethod ("LoadApplication", Context.class, String.class, String[].class);
18
18
  method.invoke (null, context, null, new String[]{context.getApplicationInfo ().sourceDir});
19
19
  System.out.println("Calabash loaded Mono");
20
+ InstrumentationBackend.mainActivity = Class.forName(arguments.getString("main_activity")).asSubclass(Activity.class);
20
21
  } catch (Exception e) {
21
22
  System.out.println("Calabash did not load Mono. This is only a problem if you are trying to test a Mono application");
22
23
  }
@@ -28,7 +29,7 @@ public class CalabashInstrumentationTestRunner extends InstrumentationTestRunner
28
29
  InstrumentationBackend.extras = arguments;
29
30
 
30
31
  try {
31
- InstrumentationBackend.mainActivity = arguments.getString("main_activity");
32
+ InstrumentationBackend.mainActivityName = arguments.getString("main_activity");
32
33
  } catch (Exception e) {
33
34
  throw new RuntimeException(e);
34
35
  }
@@ -24,7 +24,8 @@ import java.util.concurrent.atomic.AtomicReference;
24
24
 
25
25
  public class InstrumentationBackend extends ActivityInstrumentationTestCase2<Activity> {
26
26
  public static String testPackage;
27
- public static String mainActivity;
27
+ public static String mainActivityName;
28
+ public static Class<? extends Activity> mainActivity;
28
29
  public static Bundle extras;
29
30
 
30
31
  private static final String TAG = "InstrumentationBackend";
@@ -35,13 +36,17 @@ public class InstrumentationBackend extends ActivityInstrumentationTestCase2<Act
35
36
  public static Actions actions;
36
37
 
37
38
  public InstrumentationBackend() {
38
- super(null);
39
+ super((Class<Activity>)mainActivity);
39
40
  }
40
41
 
41
42
  @Override
42
43
  public Activity getActivity() {
44
+ if (mainActivity != null) {
45
+ return super.getActivity();
46
+ }
47
+
43
48
  try {
44
- setMainActivity(Class.forName(mainActivity).asSubclass(Activity.class));
49
+ setMainActivity(Class.forName(mainActivityName).asSubclass(Activity.class));
45
50
  return super.getActivity();
46
51
  } catch (ClassNotFoundException e) {
47
52
  throw new RuntimeException(e);
@@ -62,7 +67,7 @@ public class InstrumentationBackend extends ActivityInstrumentationTestCase2<Act
62
67
  protected void setUp() throws Exception {
63
68
  super.setUp();
64
69
  Intent i = new Intent(Intent.ACTION_MAIN);
65
- i.setClassName(testPackage, mainActivity);
70
+ i.setClassName(testPackage, mainActivityName);
66
71
  i.addCategory("android.intent.category.LAUNCHER");
67
72
  i.putExtras(extras);
68
73
  setActivityIntent(i);
@@ -6,18 +6,18 @@ import java.util.ArrayList;
6
6
  import java.util.List;
7
7
 
8
8
  public class Result {
9
-
9
+
10
10
  boolean success;
11
11
  String message;
12
12
  List<String> bonusInformation = new ArrayList<String>();
13
-
13
+
14
14
  public Result() {
15
15
  }
16
-
16
+
17
17
  public Result(boolean success) {
18
18
  this(success, "");
19
19
  }
20
-
20
+
21
21
  public Result(boolean success, String message) {
22
22
  this.success = success;
23
23
  this.message = message;
@@ -32,19 +32,19 @@ public class Result {
32
32
  public String getMessage() {
33
33
  return message;
34
34
  }
35
-
35
+
36
36
  public void setMessage(String message) {
37
37
  this.message = message;
38
38
  }
39
-
39
+
40
40
  public boolean isSuccess() {
41
41
  return success;
42
42
  }
43
-
43
+
44
44
  public void setSuccess(boolean success) {
45
45
  this.success = success;
46
46
  }
47
-
47
+
48
48
  public void addBonusInformation(String information) {
49
49
  bonusInformation.add(information);
50
50
  }
@@ -52,11 +52,11 @@ public class Result {
52
52
  public List<String> getBonusInformation() {
53
53
  return bonusInformation;
54
54
  }
55
-
55
+
56
56
  public void setExtras(List<String> bonusInformation) {
57
57
  this.bonusInformation = bonusInformation;
58
58
  }
59
-
59
+
60
60
  public static Result fromThrowable(Throwable t) {
61
61
  Result r = new Result(false, t.getMessage());
62
62
  CharArrayWriter caw = new CharArrayWriter();
@@ -64,11 +64,15 @@ public class Result {
64
64
  r.addBonusInformation("Exception stack trace:\n" + caw.toString());
65
65
  return r;
66
66
  }
67
-
67
+
68
68
  public static Result successResult() {
69
69
  return new Result(true);
70
70
  }
71
71
 
72
+ public static Result successResult(final String message) {
73
+ return new Result(true, message);
74
+ }
75
+
72
76
  public static Result failedResult() {
73
77
  return new Result(false);
74
78
  }
@@ -76,7 +80,7 @@ public class Result {
76
80
  public static Result failedResult(final String message) {
77
81
  return new Result(false, message);
78
82
  }
79
-
83
+
80
84
  public String toString() {
81
85
  return "Success: " + success + ", message: " + message;
82
86
  }
@@ -0,0 +1,55 @@
1
+ package sh.calaba.instrumentationbackend.actions.application;
2
+
3
+ import java.util.ArrayList;
4
+ import java.util.List;
5
+
6
+ import android.app.Activity;
7
+ import android.app.Application;
8
+ import android.content.Context;
9
+
10
+ import sh.calaba.instrumentationbackend.InstrumentationBackend;
11
+ import sh.calaba.instrumentationbackend.Result;
12
+ import sh.calaba.instrumentationbackend.actions.Action;
13
+ import sh.calaba.instrumentationbackend.query.Operation;
14
+ import sh.calaba.instrumentationbackend.query.InvocationOperation;
15
+ import sh.calaba.instrumentationbackend.query.QueryResult;
16
+ import sh.calaba.instrumentationbackend.query.UIQueryResultVoid;
17
+
18
+
19
+ public class Backdoor implements Action {
20
+
21
+ private static final String TAG = "Backdoor";
22
+
23
+ @Override
24
+ public Result execute(String... args) {
25
+ if (args.length != 2) {
26
+ return Result.failedResult("You must provide method name and an argument.");
27
+ }
28
+
29
+ String methodName = args[0];
30
+ List arguments = new ArrayList(1);
31
+ arguments.add(args[1]);
32
+ // create invocation operation to call method
33
+ Operation op = new InvocationOperation(methodName, arguments);
34
+ // get an application object to call operation on
35
+ Context app = InstrumentationBackend.solo.getCurrentActivity().getApplication();
36
+ String backdoorResult = null;
37
+ try {
38
+ backdoorResult = (String)op.apply(app);
39
+ } catch (Exception e) {
40
+ android.util.Log.e(TAG, android.util.Log.getStackTraceString(e));
41
+ return Result.failedResult("No such backdoor method found: public String " + op.getName() + "(String arg)");
42
+ }
43
+
44
+ // set backdoor result as bonus
45
+ Result result = Result.successResult();
46
+ result.addBonusInformation(backdoorResult);
47
+ return result;
48
+ }
49
+
50
+ @Override
51
+ public String key() {
52
+ return "backdoor";
53
+ }
54
+
55
+ }
@@ -5,6 +5,19 @@ import sh.calaba.instrumentationbackend.InstrumentationBackend;
5
5
  import sh.calaba.instrumentationbackend.Result;
6
6
  import sh.calaba.instrumentationbackend.actions.Action;
7
7
 
8
+ /**
9
+ * <p>
10
+ * Action that performs a long press on given coordinates.
11
+ * </p>
12
+ * <p>
13
+ * Parameters:
14
+ * <ul>
15
+ * <li>args[0]: x coordinate (float)</li>
16
+ * <li>args[1]: y coordinate (float)</li>
17
+ * <li>args[2]: length of the long press in millisecond (optional, integer)</li>
18
+ * </ul>
19
+ * </p>
20
+ */
8
21
  public class LongPressCoordinate implements Action {
9
22
  @Override
10
23
  public Result execute(String... args) {
@@ -12,8 +25,12 @@ public class LongPressCoordinate implements Action {
12
25
 
13
26
  float x = Float.parseFloat(args[0]);
14
27
  float y = Float.parseFloat(args[1]);
15
-
16
- InstrumentationBackend.solo.clickLongOnScreen(x, y);
28
+ if (args.length > 2) {
29
+ int time = Integer.parseInt(args[2]);
30
+ InstrumentationBackend.solo.clickLongOnScreen(x, y, time);
31
+ } else {
32
+ InstrumentationBackend.solo.clickLongOnScreen(x, y);
33
+ }
17
34
 
18
35
  return Result.successResult();
19
36
  }
@@ -0,0 +1,105 @@
1
+ package sh.calaba.instrumentationbackend.actions.webview;
2
+
3
+ import java.lang.reflect.Constructor;
4
+ import java.lang.reflect.Field;
5
+ import java.lang.reflect.InvocationTargetException;
6
+ import java.lang.reflect.Method;
7
+
8
+ import android.os.Build;
9
+ import android.webkit.WebView;
10
+
11
+ public class JavaScriptExecuter {
12
+ private WebView webView;
13
+ private Object provider;
14
+
15
+ public JavaScriptExecuter(WebView webView) {
16
+ this.webView = webView;
17
+ }
18
+
19
+ public void executeJavaScript(String javaScript) {
20
+ try {
21
+ provider = getProvider();
22
+ loadUrlImpl("javascript:" + javaScript);
23
+ } catch (Exception e) {
24
+ throw new RuntimeException(e);
25
+ }
26
+ }
27
+
28
+ private void callSwitchOutDrawHistory() throws InvocationTargetException, IllegalAccessException, NoSuchMethodException {
29
+ Method method = provider.getClass().getDeclaredMethod("switchOutDrawHistory");
30
+ method.setAccessible(true);
31
+ method.invoke(provider);
32
+ }
33
+
34
+ private void loadUrlImpl(String url) throws NoSuchMethodException, IllegalAccessException, NoSuchFieldException, InstantiationException, InvocationTargetException, ClassNotFoundException {
35
+ callSwitchOutDrawHistory();
36
+ sendMessage(url);
37
+ }
38
+
39
+ private void sendMessage(String url) throws NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InstantiationException, InvocationTargetException, ClassNotFoundException {
40
+ Object arg = getUrlData(url);
41
+ Object webViewCore = getWebViewCore();
42
+
43
+ int messageIdentifier = loadUrlMessage();
44
+
45
+ Method sendMessageMethod = webViewCore.getClass().getDeclaredMethod("sendMessage", int.class, Object.class);
46
+ sendMessageMethod.setAccessible(true);
47
+ sendMessageMethod.invoke(webViewCore, messageIdentifier, arg);
48
+ }
49
+
50
+ private final String EVENT_HUB_CLASS_NAME = "android.webkit.WebViewCore$EventHub";
51
+
52
+ private int loadUrlMessage() throws NoSuchFieldException, ClassNotFoundException, IllegalAccessException {
53
+ Class<?> eventHubClass = Class.forName(EVENT_HUB_CLASS_NAME);
54
+ Field loadUrlField = eventHubClass.getDeclaredField("LOAD_URL");
55
+ loadUrlField.setAccessible(true);
56
+
57
+ return loadUrlField.getInt(String.class);
58
+ }
59
+
60
+ private final String GET_URL_DATA_CLASS_NAME = "android.webkit.WebViewCore$GetUrlData";
61
+
62
+ private Object getUrlData(String url) throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException, InstantiationException {
63
+ // For Android OS pre 2.2 the url was a string
64
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.FROYO) {
65
+ return url;
66
+ } else {
67
+ Class<?> getUrlDataClass = Class.forName(GET_URL_DATA_CLASS_NAME);
68
+ Constructor<?> constructor = getUrlDataClass.getDeclaredConstructor();
69
+ constructor.setAccessible(true);
70
+
71
+ Field mUrlField = getUrlDataClass.getDeclaredField("mUrl");
72
+ mUrlField.setAccessible(true);
73
+ Field mExtraHeadersField = getUrlDataClass.getDeclaredField("mExtraHeaders");
74
+ mExtraHeadersField.setAccessible(true);
75
+
76
+ Object getUrlDataInstance = constructor.newInstance();
77
+ mUrlField.set(getUrlDataInstance, url);
78
+ mExtraHeadersField.set(getUrlDataInstance, null);
79
+
80
+ return getUrlDataInstance;
81
+ }
82
+ }
83
+
84
+ private Object getProvider() throws NoSuchFieldException, IllegalAccessException {
85
+ // For Android OS pre 4.1 the implementation of loadUrl was within the WebView widget class
86
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
87
+ return webView;
88
+ } else {
89
+ Field fieldProvider = WebView.class.getDeclaredField("mProvider");
90
+ fieldProvider.setAccessible(true);
91
+
92
+ return fieldProvider.get(webView);
93
+ }
94
+ }
95
+
96
+ private Object getWebViewCore() throws IllegalStateException, IllegalAccessException, NoSuchFieldException {
97
+ if (provider == null) throw new IllegalStateException("Provider is null");
98
+
99
+ Field fieldCore = provider.getClass().getDeclaredField("mWebViewCore");
100
+ fieldCore.setAccessible(true);
101
+ Object webViewCore = fieldCore.get(provider);
102
+
103
+ return webViewCore;
104
+ }
105
+ }